mirror of https://github.com/buster-so/buster.git
added in some tests and such for dashboard_ymls and metric_handlers
This commit is contained in:
parent
42f8226b2e
commit
2a33977a4e
|
@ -168,3 +168,267 @@ impl ToSql<Jsonb, Pg> for DashboardYml {
|
||||||
Ok(IsNull::No)
|
Ok(IsNull::No)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dashboard_yml_camel_case_serialization() {
|
||||||
|
// Test case: Verify that DashboardYml serializes to camelCase
|
||||||
|
// Expected: JSON fields should be in camelCase format
|
||||||
|
|
||||||
|
// Create a dashboard with one row
|
||||||
|
let dashboard = DashboardYml {
|
||||||
|
name: "Test Dashboard".to_string(),
|
||||||
|
description: Some("This is a test dashboard".to_string()),
|
||||||
|
rows: vec![
|
||||||
|
Row {
|
||||||
|
items: vec![
|
||||||
|
RowItem {
|
||||||
|
id: Uuid::parse_str("00000000-0000-0000-0000-000000000001").unwrap(),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
row_height: Some(400),
|
||||||
|
column_sizes: Some(vec![12]),
|
||||||
|
id: Some(1),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Serialize to JSON
|
||||||
|
let json = serde_json::to_value(&dashboard).unwrap();
|
||||||
|
|
||||||
|
// Verify camelCase field names in the output
|
||||||
|
assert!(json.get("name").is_some());
|
||||||
|
assert!(json.get("description").is_some());
|
||||||
|
assert!(json.get("rows").is_some());
|
||||||
|
|
||||||
|
// Check row fields are in camelCase
|
||||||
|
let row = &json["rows"][0];
|
||||||
|
assert!(row.get("items").is_some());
|
||||||
|
assert!(row.get("rowHeight").is_some());
|
||||||
|
assert!(row.get("columnSizes").is_some());
|
||||||
|
assert!(row.get("id").is_some());
|
||||||
|
|
||||||
|
// Verify snake_case field names are NOT present
|
||||||
|
assert!(row.get("row_height").is_none());
|
||||||
|
assert!(row.get("column_sizes").is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dashboard_yml_snake_case_deserialization() {
|
||||||
|
// Test case: Verify that DashboardYml deserializes from snake_case
|
||||||
|
// Expected: Both snake_case and camelCase fields should be accepted
|
||||||
|
|
||||||
|
// Create JSON with snake_case fields
|
||||||
|
let json = json!({
|
||||||
|
"name": "Test Dashboard",
|
||||||
|
"description": "This is a test dashboard",
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "00000000-0000-0000-0000-000000000001"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"row_height": 400,
|
||||||
|
"column_sizes": [12]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Convert to YAML and use the new method to assign IDs
|
||||||
|
let yaml = serde_yaml::to_string(&json).unwrap();
|
||||||
|
let dashboard = DashboardYml::new(yaml).unwrap();
|
||||||
|
|
||||||
|
// Verify fields were properly deserialized
|
||||||
|
assert_eq!(dashboard.name, "Test Dashboard");
|
||||||
|
assert_eq!(dashboard.description, Some("This is a test dashboard".to_string()));
|
||||||
|
assert_eq!(dashboard.rows.len(), 1);
|
||||||
|
assert_eq!(dashboard.rows[0].row_height, Some(400));
|
||||||
|
assert_eq!(dashboard.rows[0].column_sizes, Some(vec![12]));
|
||||||
|
|
||||||
|
// Check that a row ID was assigned
|
||||||
|
assert_eq!(dashboard.rows[0].id, Some(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dashboard_yml_camel_case_deserialization() {
|
||||||
|
// Test case: Verify that DashboardYml deserializes from camelCase
|
||||||
|
// Expected: camelCase fields should be properly deserialized
|
||||||
|
|
||||||
|
// Create JSON with camelCase fields
|
||||||
|
let json = json!({
|
||||||
|
"name": "Test Dashboard",
|
||||||
|
"description": "This is a test dashboard",
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "00000000-0000-0000-0000-000000000001"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rowHeight": 400,
|
||||||
|
"columnSizes": [12]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Convert to YAML and use the new method to assign IDs
|
||||||
|
let yaml = serde_yaml::to_string(&json).unwrap();
|
||||||
|
let dashboard = DashboardYml::new(yaml).unwrap();
|
||||||
|
|
||||||
|
// Verify fields were properly deserialized
|
||||||
|
assert_eq!(dashboard.name, "Test Dashboard");
|
||||||
|
assert_eq!(dashboard.description, Some("This is a test dashboard".to_string()));
|
||||||
|
assert_eq!(dashboard.rows.len(), 1);
|
||||||
|
assert_eq!(dashboard.rows[0].row_height, Some(400));
|
||||||
|
assert_eq!(dashboard.rows[0].column_sizes, Some(vec![12]));
|
||||||
|
|
||||||
|
// Check that a row ID was assigned
|
||||||
|
assert_eq!(dashboard.rows[0].id, Some(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_row_id_generation() {
|
||||||
|
// Test case: Verify that row IDs are properly generated
|
||||||
|
// Expected: Row IDs should increment by 1 for each row
|
||||||
|
|
||||||
|
// Create a dashboard from YAML without row IDs
|
||||||
|
let yaml = r#"
|
||||||
|
name: Test Dashboard
|
||||||
|
description: This is a test dashboard
|
||||||
|
rows:
|
||||||
|
- items:
|
||||||
|
- id: 00000000-0000-0000-0000-000000000001
|
||||||
|
rowHeight: 400
|
||||||
|
columnSizes: [12]
|
||||||
|
- items:
|
||||||
|
- id: 00000000-0000-0000-0000-000000000002
|
||||||
|
rowHeight: 320
|
||||||
|
columnSizes: [12]
|
||||||
|
- items:
|
||||||
|
- id: 00000000-0000-0000-0000-000000000003
|
||||||
|
rowHeight: 550
|
||||||
|
columnSizes: [12]
|
||||||
|
"#;
|
||||||
|
|
||||||
|
// Create dashboard using the new method (which should assign row IDs)
|
||||||
|
let dashboard = DashboardYml::new(yaml.to_string()).unwrap();
|
||||||
|
|
||||||
|
// Verify that row IDs were assigned in sequence
|
||||||
|
assert_eq!(dashboard.rows[0].id, Some(1));
|
||||||
|
assert_eq!(dashboard.rows[1].id, Some(2));
|
||||||
|
assert_eq!(dashboard.rows[2].id, Some(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_row_method() {
|
||||||
|
// Test case: Verify that the add_row method assigns the next available ID
|
||||||
|
// Expected: New rows get the next sequential ID
|
||||||
|
|
||||||
|
// Create a dashboard with one row
|
||||||
|
let mut dashboard = DashboardYml {
|
||||||
|
name: "Test Dashboard".to_string(),
|
||||||
|
description: None,
|
||||||
|
rows: vec![
|
||||||
|
Row {
|
||||||
|
items: vec![RowItem { id: Uuid::new_v4() }],
|
||||||
|
row_height: None,
|
||||||
|
column_sizes: None,
|
||||||
|
id: Some(1),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add a second row using the add_row method
|
||||||
|
dashboard.add_row(
|
||||||
|
vec![RowItem { id: Uuid::new_v4() }],
|
||||||
|
Some(400),
|
||||||
|
Some(vec![12]),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add a third row
|
||||||
|
dashboard.add_row(
|
||||||
|
vec![RowItem { id: Uuid::new_v4() }],
|
||||||
|
Some(320),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify that row IDs were assigned in sequence
|
||||||
|
assert_eq!(dashboard.rows[0].id, Some(1));
|
||||||
|
assert_eq!(dashboard.rows[1].id, Some(2));
|
||||||
|
assert_eq!(dashboard.rows[2].id, Some(3));
|
||||||
|
|
||||||
|
// Verify that get_next_row_id returns the expected value
|
||||||
|
assert_eq!(dashboard.get_next_row_id(), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_non_sequential_row_ids() {
|
||||||
|
// Test case: Verify that get_next_row_id works with non-sequential IDs
|
||||||
|
// Expected: Next ID should be max(id) + 1
|
||||||
|
|
||||||
|
// Create a dashboard with rows that have non-sequential IDs
|
||||||
|
let dashboard = DashboardYml {
|
||||||
|
name: "Test Dashboard".to_string(),
|
||||||
|
description: None,
|
||||||
|
rows: vec![
|
||||||
|
Row {
|
||||||
|
items: vec![RowItem { id: Uuid::new_v4() }],
|
||||||
|
row_height: None,
|
||||||
|
column_sizes: None,
|
||||||
|
id: Some(1),
|
||||||
|
},
|
||||||
|
Row {
|
||||||
|
items: vec![RowItem { id: Uuid::new_v4() }],
|
||||||
|
row_height: None,
|
||||||
|
column_sizes: None,
|
||||||
|
id: Some(5), // Intentionally out of sequence
|
||||||
|
},
|
||||||
|
Row {
|
||||||
|
items: vec![RowItem { id: Uuid::new_v4() }],
|
||||||
|
row_height: None,
|
||||||
|
column_sizes: None,
|
||||||
|
id: Some(3),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Verify that get_next_row_id returns max(id) + 1
|
||||||
|
assert_eq!(dashboard.get_next_row_id(), 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_explicitly_provided_id() {
|
||||||
|
// Test case: Verify that explicitly provided IDs are preserved during deserialization
|
||||||
|
// Expected: Row ID should match the provided value
|
||||||
|
|
||||||
|
// Create JSON with an explicit ID field
|
||||||
|
let json = json!({
|
||||||
|
"name": "Test Dashboard",
|
||||||
|
"description": "This is a test dashboard",
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "00000000-0000-0000-0000-000000000001"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rowHeight": 400,
|
||||||
|
"columnSizes": [12],
|
||||||
|
"id": 42 // Explicitly set ID
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Convert to YAML and use the new method
|
||||||
|
let yaml = serde_yaml::to_string(&json).unwrap();
|
||||||
|
let dashboard = DashboardYml::new(yaml).unwrap();
|
||||||
|
|
||||||
|
// Verify the explicit ID was preserved
|
||||||
|
assert_eq!(dashboard.rows[0].id, Some(42));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -25,6 +25,26 @@ pub struct UpdateMetricRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handler to update a metric by ID
|
/// Handler to update a metric by ID
|
||||||
|
///
|
||||||
|
/// This handler updates a metric file in the database and increments its version number.
|
||||||
|
/// Each time a metric is updated, the previous version is saved in the version history.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `metric_id` - The UUID of the metric to update
|
||||||
|
/// * `user_id` - The UUID of the user making the update
|
||||||
|
/// * `request` - The update request containing the fields to modify
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// * `Result<BusterMetric>` - The updated metric on success, or an error
|
||||||
|
///
|
||||||
|
/// # Versioning
|
||||||
|
/// The function automatically handles versioning:
|
||||||
|
/// 1. Retrieves the current metric and extracts its content
|
||||||
|
/// 2. Updates the content based on the request parameters
|
||||||
|
/// 3. Increments the version number (based on the number of existing versions)
|
||||||
|
/// 4. Adds the updated content to the version history with the new version number
|
||||||
|
/// 5. Saves both the updated content and version history to the database
|
||||||
|
///
|
||||||
pub async fn update_metric_handler(
|
pub async fn update_metric_handler(
|
||||||
metric_id: &Uuid,
|
metric_id: &Uuid,
|
||||||
user_id: &Uuid,
|
user_id: &Uuid,
|
||||||
|
|
|
@ -85,6 +85,18 @@ async fn test_update_metric_integration() -> Result<()> {
|
||||||
assert!(db_metric.version_history.0.contains_key(&"1".to_string()));
|
assert!(db_metric.version_history.0.contains_key(&"1".to_string()));
|
||||||
assert!(db_metric.version_history.0.contains_key(&"2".to_string()));
|
assert!(db_metric.version_history.0.contains_key(&"2".to_string()));
|
||||||
|
|
||||||
|
// Get the latest version from the version history
|
||||||
|
let latest_version = db_metric.version_history.get_latest_version().unwrap();
|
||||||
|
assert_eq!(latest_version.version_number, 2);
|
||||||
|
|
||||||
|
// Verify the latest version's content matches the current content
|
||||||
|
if let database::types::VersionContent::MetricYml(latest_content) = &latest_version.content {
|
||||||
|
assert_eq!(latest_content.time_frame, "weekly");
|
||||||
|
assert_eq!(latest_content.description, Some("Updated test description".to_string()));
|
||||||
|
} else {
|
||||||
|
panic!("Expected MetricYml content in version history");
|
||||||
|
}
|
||||||
|
|
||||||
println!("Update metric test passed with ID: {}", metric_id);
|
println!("Update metric test passed with ID: {}", metric_id);
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -133,7 +145,7 @@ async fn test_update_nonexistent_metric() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test updating specific metric fields one at a time
|
/// Test updating specific metric fields one at a time and verify version increments properly
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_update_specific_metric_fields() -> Result<()> {
|
async fn test_update_specific_metric_fields() -> Result<()> {
|
||||||
// Setup test environment
|
// Setup test environment
|
||||||
|
@ -174,6 +186,25 @@ async fn test_update_specific_metric_fields() -> Result<()> {
|
||||||
// Verify other fields were not changed
|
// Verify other fields were not changed
|
||||||
assert_eq!(metric.time_frame, "daily");
|
assert_eq!(metric.time_frame, "daily");
|
||||||
assert_eq!(metric.status, Verification::NotRequested);
|
assert_eq!(metric.status, Verification::NotRequested);
|
||||||
|
|
||||||
|
// Verify version number was incremented
|
||||||
|
assert_eq!(metric.version_number, 2);
|
||||||
|
|
||||||
|
// Verify in the database
|
||||||
|
let mut conn = get_pg_pool().get().await?;
|
||||||
|
let db_metric = metric_files::table
|
||||||
|
.filter(metric_files::id.eq(metric_id))
|
||||||
|
.first::<database::models::MetricFile>(&mut conn)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Check version history has exactly 2 versions
|
||||||
|
assert_eq!(db_metric.version_history.0.len(), 2);
|
||||||
|
assert!(db_metric.version_history.0.contains_key(&"1".to_string()));
|
||||||
|
assert!(db_metric.version_history.0.contains_key(&"2".to_string()));
|
||||||
|
|
||||||
|
// Get the latest version
|
||||||
|
let latest_version = db_metric.version_history.get_latest_version().unwrap();
|
||||||
|
assert_eq!(latest_version.version_number, 2);
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
cleanup_test_data(Some(metric_id), None).await?;
|
cleanup_test_data(Some(metric_id), None).await?;
|
||||||
|
@ -198,6 +229,26 @@ async fn test_update_specific_metric_fields() -> Result<()> {
|
||||||
|
|
||||||
// Verify title remains from previous update
|
// Verify title remains from previous update
|
||||||
assert_eq!(metric.title, "Title Only Update");
|
assert_eq!(metric.title, "Title Only Update");
|
||||||
|
|
||||||
|
// Verify version number increased again
|
||||||
|
assert_eq!(metric.version_number, 3);
|
||||||
|
|
||||||
|
// Verify in the database
|
||||||
|
let mut conn = get_pg_pool().get().await?;
|
||||||
|
let db_metric = metric_files::table
|
||||||
|
.filter(metric_files::id.eq(metric_id))
|
||||||
|
.first::<database::models::MetricFile>(&mut conn)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Check version history has exactly 3 versions
|
||||||
|
assert_eq!(db_metric.version_history.0.len(), 3);
|
||||||
|
assert!(db_metric.version_history.0.contains_key(&"1".to_string()));
|
||||||
|
assert!(db_metric.version_history.0.contains_key(&"2".to_string()));
|
||||||
|
assert!(db_metric.version_history.0.contains_key(&"3".to_string()));
|
||||||
|
|
||||||
|
// Get the latest version
|
||||||
|
let latest_version = db_metric.version_history.get_latest_version().unwrap();
|
||||||
|
assert_eq!(latest_version.version_number, 3);
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
cleanup_test_data(Some(metric_id), None).await?;
|
cleanup_test_data(Some(metric_id), None).await?;
|
||||||
|
@ -223,6 +274,40 @@ async fn test_update_specific_metric_fields() -> Result<()> {
|
||||||
// Verify other fields remain from previous updates
|
// Verify other fields remain from previous updates
|
||||||
assert_eq!(metric.title, "Title Only Update");
|
assert_eq!(metric.title, "Title Only Update");
|
||||||
assert_eq!(metric.status, Verification::Verified);
|
assert_eq!(metric.status, Verification::Verified);
|
||||||
|
|
||||||
|
// Verify version number increased to 4
|
||||||
|
assert_eq!(metric.version_number, 4);
|
||||||
|
|
||||||
|
// Verify in the database
|
||||||
|
let mut conn = get_pg_pool().get().await?;
|
||||||
|
let db_metric = metric_files::table
|
||||||
|
.filter(metric_files::id.eq(metric_id))
|
||||||
|
.first::<database::models::MetricFile>(&mut conn)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Check version history now has 4 versions
|
||||||
|
assert_eq!(db_metric.version_history.0.len(), 4);
|
||||||
|
|
||||||
|
// Verify all version numbers are present
|
||||||
|
for i in 1..=4 {
|
||||||
|
assert!(db_metric.version_history.0.contains_key(&i.to_string()),
|
||||||
|
"Version {} missing from history", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the latest version
|
||||||
|
let latest_version = db_metric.version_history.get_latest_version().unwrap();
|
||||||
|
assert_eq!(latest_version.version_number, 4);
|
||||||
|
|
||||||
|
// Check the content of the latest version
|
||||||
|
if let database::types::VersionContent::MetricYml(latest_content) = &latest_version.content {
|
||||||
|
assert_eq!(latest_content.time_frame, "monthly");
|
||||||
|
|
||||||
|
// The title should be preserved from earlier updates
|
||||||
|
let yaml = serde_yaml::to_string(latest_content).unwrap();
|
||||||
|
assert!(yaml.contains("Title Only Update"));
|
||||||
|
} else {
|
||||||
|
panic!("Expected MetricYml content in version history");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
cleanup_test_data(Some(metric_id), None).await?;
|
cleanup_test_data(Some(metric_id), None).await?;
|
||||||
|
|
Loading…
Reference in New Issue