buster/apps/api/libs/database/tests/helpers/metric_files_test.rs

514 lines
19 KiB
Rust

use anyhow::Result;
use chrono::Utc;
use database::enums::{AssetPermissionRole, AssetType};
use database::helpers::metric_files::{
fetch_metric_file_with_permissions, fetch_metric_files_with_permissions,
};
use database::models::{Collection, CollectionToAsset, MetricFileToDashboardFile};
use diesel::prelude::*;
use diesel_async::RunQueryDsl;
use tokio;
use uuid::Uuid;
use database::test_utils::{TestDb, insert_test_metric_file, insert_test_dashboard_file, insert_test_permission, cleanup_test_data};
/// Tests the fetch_metric_file_with_permissions function with direct permission
#[tokio::test]
async fn test_metric_file_direct_permission() -> Result<()> {
// Initialize test environment
let test_db = TestDb::new().await?;
// Create test user and file
let user = test_db.create_test_user().await?;
let owner_id = user.id;
let metric_file = test_db.create_test_metric_file(&owner_id).await?;
let metric_id = metric_file.id;
// Insert the test metric file
insert_test_metric_file(&metric_file).await?;
// Test cases with different permission roles
for role in [
AssetPermissionRole::Owner,
AssetPermissionRole::FullAccess,
AssetPermissionRole::CanEdit,
AssetPermissionRole::CanFilter,
AssetPermissionRole::CanView,
] {
// Create and insert permission
let permission = test_db
.create_asset_permission(&metric_id, AssetType::MetricFile, &owner_id, role)
.await?;
insert_test_permission(&permission).await?;
// Fetch file with permissions
let result = fetch_metric_file_with_permissions(&metric_id, &owner_id).await?;
// Assert file is found and has correct permission
assert!(result.is_some(), "Metric file should be found");
let file_with_permission = result.unwrap();
assert_eq!(file_with_permission.metric_file.id, metric_id);
assert_eq!(file_with_permission.permission, Some(role));
}
// Clean up
cleanup_test_data(&[metric_id]).await?;
Ok(())
}
/// Tests the fetch_metric_file_with_permissions function when no permission exists
#[tokio::test]
async fn test_metric_file_no_permission() -> Result<()> {
// Initialize test environment
let test_db = TestDb::new().await?;
// Create test user and file
let user = test_db.create_test_user().await?;
let owner_id = user.id;
let metric_file = test_db.create_test_metric_file(&owner_id).await?;
let metric_id = metric_file.id;
// Insert the test metric file
insert_test_metric_file(&metric_file).await?;
// Fetch file with permissions (no permission exists)
let result = fetch_metric_file_with_permissions(&metric_id, &Uuid::new_v4()).await?;
// Assert file is found but has no permission
assert!(result.is_some(), "Metric file should be found");
let file_with_permission = result.unwrap();
assert_eq!(file_with_permission.metric_file.id, metric_id);
assert_eq!(file_with_permission.permission, None);
// Clean up
cleanup_test_data(&[metric_id]).await?;
Ok(())
}
/// Tests the fetch_metric_file_with_permissions function with public accessibility
#[tokio::test]
async fn test_metric_file_public_access() -> Result<()> {
// Initialize test environment
let test_db = TestDb::new().await?;
// Create test user and file
let user = test_db.create_test_user().await?;
let owner_id = user.id;
let mut metric_file = test_db.create_test_metric_file(&owner_id).await?;
// Make the file publicly accessible
metric_file.publicly_accessible = true;
metric_file.publicly_enabled_by = Some(owner_id);
metric_file.public_expiry_date = Some(Utc::now() + chrono::Duration::days(1));
let metric_id = metric_file.id;
// Insert the test metric file
insert_test_metric_file(&metric_file).await?;
// Fetch file with permissions for a random user (no direct permission)
let random_user_id = Uuid::new_v4();
let result = fetch_metric_file_with_permissions(&metric_id, &random_user_id).await?;
// Assert file is found and has CanView permission due to public access
assert!(result.is_some(), "Metric file should be found");
let file_with_permission = result.unwrap();
assert_eq!(file_with_permission.metric_file.id, metric_id);
assert_eq!(file_with_permission.permission, Some(AssetPermissionRole::CanView));
// Clean up
cleanup_test_data(&[metric_id]).await?;
Ok(())
}
/// Tests the fetch_metric_file_with_permissions function with expired public accessibility
#[tokio::test]
async fn test_metric_file_expired_public_access() -> Result<()> {
// Initialize test environment
let test_db = TestDb::new().await?;
// Create test user and file
let user = test_db.create_test_user().await?;
let owner_id = user.id;
let mut metric_file = test_db.create_test_metric_file(&owner_id).await?;
// Make the file publicly accessible but expired
metric_file.publicly_accessible = true;
metric_file.publicly_enabled_by = Some(owner_id);
metric_file.public_expiry_date = Some(Utc::now() - chrono::Duration::days(1)); // Expired
let metric_id = metric_file.id;
// Insert the test metric file
insert_test_metric_file(&metric_file).await?;
// Fetch file with permissions for a random user (no direct permission)
let random_user_id = Uuid::new_v4();
let result = fetch_metric_file_with_permissions(&metric_id, &random_user_id).await?;
// Assert file is found but has no permission (public access expired)
assert!(result.is_some(), "Metric file should be found");
let file_with_permission = result.unwrap();
assert_eq!(file_with_permission.metric_file.id, metric_id);
assert_eq!(file_with_permission.permission, None);
// Clean up
cleanup_test_data(&[metric_id]).await?;
Ok(())
}
/// Tests the fetch_metric_files_with_permissions function for multiple files
#[tokio::test]
async fn test_fetch_multiple_metric_files() -> Result<()> {
// Initialize test environment
let test_db = TestDb::new().await?;
// Create test user and files
let user = test_db.create_test_user().await?;
let owner_id = user.id;
// Create and insert three test metric files with different permissions
let metric_file1 = test_db.create_test_metric_file(&owner_id).await?;
let metric_id1 = metric_file1.id;
insert_test_metric_file(&metric_file1).await?;
let mut metric_file2 = test_db.create_test_metric_file(&owner_id).await?;
metric_file2.publicly_accessible = true;
metric_file2.public_expiry_date = Some(Utc::now() + chrono::Duration::days(1));
let metric_id2 = metric_file2.id;
insert_test_metric_file(&metric_file2).await?;
let metric_file3 = test_db.create_test_metric_file(&owner_id).await?;
let metric_id3 = metric_file3.id;
insert_test_metric_file(&metric_file3).await?;
// Create and insert permissions
let permission1 = test_db
.create_asset_permission(&metric_id1, AssetType::MetricFile, &owner_id, AssetPermissionRole::CanEdit)
.await?;
insert_test_permission(&permission1).await?;
let permission3 = test_db
.create_asset_permission(&metric_id3, AssetType::MetricFile, &owner_id, AssetPermissionRole::CanView)
.await?;
insert_test_permission(&permission3).await?;
// Fetch multiple files with permissions
let ids = vec![metric_id1, metric_id2, metric_id3];
let results = fetch_metric_files_with_permissions(&ids, &owner_id).await?;
// Assert correct permissions for each file
assert_eq!(results.len(), 3, "Should return 3 files");
// Find each file in results and check its permission
for result in &results {
if result.metric_file.id == metric_id1 {
assert_eq!(result.permission, Some(AssetPermissionRole::CanEdit));
} else if result.metric_file.id == metric_id2 {
assert_eq!(result.permission, Some(AssetPermissionRole::CanView)); // Public file
} else if result.metric_file.id == metric_id3 {
assert_eq!(result.permission, Some(AssetPermissionRole::CanView));
} else {
panic!("Unexpected file ID in results");
}
}
// Clean up
cleanup_test_data(&ids).await?;
Ok(())
}
/// Tests access to a metric file through dashboard permissions
/// This test specifically checks that a user with access to a dashboard can view
/// metrics associated with that dashboard
#[tokio::test]
async fn test_metric_file_dashboard_access() -> Result<()> {
// Initialize test environment
let test_db = TestDb::new().await?;
let mut conn = test_db.get_conn().await?;
// Create test users and files
let user = test_db.create_test_user().await?;
let owner_id = user.id;
let viewer_id = Uuid::new_v4(); // Another user who will have access to dashboard but not directly to metric
// Create metric and dashboard files
let metric_file = test_db.create_test_metric_file(&owner_id).await?;
let metric_id = metric_file.id;
insert_test_metric_file(&metric_file).await?;
let dashboard_file = test_db.create_test_dashboard_file(&owner_id).await?;
let dashboard_id = dashboard_file.id;
insert_test_dashboard_file(&dashboard_file).await?;
// Create association between metric and dashboard
let metric_to_dashboard = MetricFileToDashboardFile {
metric_file_id: metric_id,
dashboard_file_id: dashboard_id,
created_at: Utc::now(),
updated_at: Utc::now(),
deleted_at: None,
created_by: owner_id,
};
// Insert association
diesel::insert_into(database::schema::metric_files_to_dashboard_files::table)
.values(&metric_to_dashboard)
.execute(&mut conn)
.await?;
// Give viewer permission to dashboard but not to metric
let dashboard_permission = test_db
.create_asset_permission(&dashboard_id, AssetType::DashboardFile, &viewer_id, AssetPermissionRole::CanView)
.await?;
insert_test_permission(&dashboard_permission).await?;
// Fetch metric file with permissions as viewer
let result = fetch_metric_file_with_permissions(&metric_id, &viewer_id).await?;
// Assert viewer can view metric through dashboard permission
assert!(result.is_some(), "Metric file should be found");
let file_with_permission = result.unwrap();
assert_eq!(file_with_permission.metric_file.id, metric_id);
assert_eq!(file_with_permission.permission, Some(AssetPermissionRole::CanView));
// Clean up
cleanup_test_data(&[metric_id, dashboard_id]).await?;
// Delete metric-to-dashboard association
diesel::delete(database::schema::metric_files_to_dashboard_files::table)
.filter(database::schema::metric_files_to_dashboard_files::metric_file_id.eq(metric_id))
.execute(&mut conn)
.await?;
Ok(())
}
/// Tests access to a metric file through collection permissions
#[tokio::test]
async fn test_metric_file_collection_access() -> Result<()> {
// Initialize test environment
let test_db = TestDb::new().await?;
let mut conn = test_db.get_conn().await?;
// Create test users and files
let user = test_db.create_test_user().await?;
let owner_id = user.id;
let viewer_id = Uuid::new_v4(); // Another user who will have access to collection but not directly to metric
// Create metric file
let metric_file = test_db.create_test_metric_file(&owner_id).await?;
let metric_id = metric_file.id;
insert_test_metric_file(&metric_file).await?;
// Create collection
let collection_id = Uuid::new_v4();
let collection = Collection {
id: collection_id,
name: format!("Test Collection {}", test_db.test_id),
description: Some("Test collection description".to_string()),
created_by: owner_id,
updated_by: owner_id,
created_at: Utc::now(),
updated_at: Utc::now(),
deleted_at: None,
organization_id: test_db.organization_id,
};
// Insert collection
diesel::insert_into(database::schema::collections::table)
.values(&collection)
.execute(&mut conn)
.await?;
// Create association between metric and collection
let collection_to_asset = CollectionToAsset {
collection_id,
asset_id: metric_id,
asset_type: AssetType::MetricFile,
created_at: Utc::now(),
updated_at: Utc::now(),
deleted_at: None,
created_by: owner_id,
updated_by: owner_id,
};
// Insert association
diesel::insert_into(database::schema::collections_to_assets::table)
.values(&collection_to_asset)
.execute(&mut conn)
.await?;
// Give viewer permission to collection but not to metric
let collection_permission = test_db
.create_asset_permission(&collection_id, AssetType::Collection, &viewer_id, AssetPermissionRole::CanEdit)
.await?;
insert_test_permission(&collection_permission).await?;
// Fetch metric file with permissions as viewer
let result = fetch_metric_file_with_permissions(&metric_id, &viewer_id).await?;
// Assert viewer can view metric through collection permission
assert!(result.is_some(), "Metric file should be found");
let file_with_permission = result.unwrap();
assert_eq!(file_with_permission.metric_file.id, metric_id);
assert_eq!(file_with_permission.permission, Some(AssetPermissionRole::CanEdit));
// Clean up
cleanup_test_data(&[metric_id]).await?;
// Delete collection and associations
diesel::delete(database::schema::collections_to_assets::table)
.filter(database::schema::collections_to_assets::collection_id.eq(collection_id))
.execute(&mut conn)
.await?;
diesel::delete(database::schema::collections::table)
.filter(database::schema::collections::id.eq(collection_id))
.execute(&mut conn)
.await?;
diesel::delete(database::schema::asset_permissions::table)
.filter(database::schema::asset_permissions::asset_id.eq(collection_id))
.execute(&mut conn)
.await?;
Ok(())
}
/// Tests permission hierarchy when multiple permissions exist
#[tokio::test]
async fn test_metric_file_permission_hierarchy() -> Result<()> {
// Initialize test environment
let test_db = TestDb::new().await?;
let mut conn = test_db.get_conn().await?;
// Create test users and files
let user = test_db.create_test_user().await?;
let owner_id = user.id;
// Create metric file
let metric_file = test_db.create_test_metric_file(&owner_id).await?;
let metric_id = metric_file.id;
insert_test_metric_file(&metric_file).await?;
// Create collection
let collection_id = Uuid::new_v4();
let collection = Collection {
id: collection_id,
name: format!("Test Collection {}", test_db.test_id),
description: Some("Test collection description".to_string()),
created_by: owner_id,
updated_by: owner_id,
created_at: Utc::now(),
updated_at: Utc::now(),
deleted_at: None,
organization_id: test_db.organization_id,
};
// Insert collection
diesel::insert_into(database::schema::collections::table)
.values(&collection)
.execute(&mut conn)
.await?;
// Create association between metric and collection
let collection_to_asset = CollectionToAsset {
collection_id,
asset_id: metric_id,
asset_type: AssetType::MetricFile,
created_at: Utc::now(),
updated_at: Utc::now(),
deleted_at: None,
created_by: owner_id,
updated_by: owner_id,
};
// Insert association
diesel::insert_into(database::schema::collections_to_assets::table)
.values(&collection_to_asset)
.execute(&mut conn)
.await?;
// Create dashboard file
let dashboard_file = test_db.create_test_dashboard_file(&owner_id).await?;
let dashboard_id = dashboard_file.id;
insert_test_dashboard_file(&dashboard_file).await?;
// Create association between metric and dashboard
let metric_to_dashboard = MetricFileToDashboardFile {
metric_file_id: metric_id,
dashboard_file_id: dashboard_id,
created_at: Utc::now(),
updated_at: Utc::now(),
deleted_at: None,
created_by: owner_id,
};
// Insert association
diesel::insert_into(database::schema::metric_files_to_dashboard_files::table)
.values(&metric_to_dashboard)
.execute(&mut conn)
.await?;
// Give user multiple permissions to test hierarchy
// Direct permission - CanFilter
let direct_permission = test_db
.create_asset_permission(&metric_id, AssetType::MetricFile, &owner_id, AssetPermissionRole::CanFilter)
.await?;
insert_test_permission(&direct_permission).await?;
// Collection permission - CanEdit (higher than direct)
let collection_permission = test_db
.create_asset_permission(&collection_id, AssetType::Collection, &owner_id, AssetPermissionRole::CanEdit)
.await?;
insert_test_permission(&collection_permission).await?;
// Dashboard permission - CanView (lower than others)
let dashboard_permission = test_db
.create_asset_permission(&dashboard_id, AssetType::DashboardFile, &owner_id, AssetPermissionRole::CanView)
.await?;
insert_test_permission(&dashboard_permission).await?;
// Fetch metric file with permissions
let result = fetch_metric_file_with_permissions(&metric_id, &owner_id).await?;
// Assert the highest permission level is used (collection's CanEdit)
assert!(result.is_some(), "Metric file should be found");
let file_with_permission = result.unwrap();
assert_eq!(file_with_permission.metric_file.id, metric_id);
assert_eq!(file_with_permission.permission, Some(AssetPermissionRole::CanEdit));
// Clean up
cleanup_test_data(&[metric_id, dashboard_id]).await?;
// Delete collections and associations
diesel::delete(database::schema::collections_to_assets::table)
.filter(database::schema::collections_to_assets::collection_id.eq(collection_id))
.execute(&mut conn)
.await?;
diesel::delete(database::schema::collections::table)
.filter(database::schema::collections::id.eq(collection_id))
.execute(&mut conn)
.await?;
diesel::delete(database::schema::asset_permissions::table)
.filter(database::schema::asset_permissions::asset_id.eq(collection_id))
.execute(&mut conn)
.await?;
// Delete metric-to-dashboard association
diesel::delete(database::schema::metric_files_to_dashboard_files::table)
.filter(database::schema::metric_files_to_dashboard_files::metric_file_id.eq(metric_id))
.execute(&mut conn)
.await?;
Ok(())
}