use crate::common::{ fixtures::{collections::create_collection, dashboards::create_dashboard, metrics::create_metric, users::create_user}, http::test_app::create_test_app, matchers::json::json_eq, }; use axum::{body::Body, http::Request}; use database::{ enums::{AssetPermissionRole, AssetType, IdentityType}, pool::get_pg_pool, schema::{asset_permissions, collections, collections_to_assets}, }; use diesel::{ExpressionMethods, QueryDsl}; use diesel_async::RunQueryDsl; use http::StatusCode; use serde_json::{json, Value}; use sharing::create_asset_permission::create_asset_permission; use uuid::Uuid; #[tokio::test] async fn test_add_assets_to_collection() { // Create a test app and database connection let app = create_test_app().await; let pool = get_pg_pool(); let mut conn = pool.get().await.unwrap(); // Create test user, collection, dashboard, and metric let user = create_user(None).await; let collection = create_collection(user.id, None).await; let dashboard = create_dashboard(user.id, None).await; let metric = create_metric(user.id, None).await; // Give the user permission to the collection as owner create_asset_permission( user.id, IdentityType::User, collection.id, AssetType::Collection, AssetPermissionRole::Owner, user.id, ) .await .unwrap(); // Give the user permission to the dashboard and metric create_asset_permission( user.id, IdentityType::User, dashboard.id, AssetType::DashboardFile, AssetPermissionRole::CanView, user.id, ) .await .unwrap(); create_asset_permission( user.id, IdentityType::User, metric.id, AssetType::MetricFile, AssetPermissionRole::CanView, user.id, ) .await .unwrap(); // Build request to add assets to collection let request = Request::builder() .uri(format!("/collections/{}/assets", collection.id)) .method("POST") .header("Content-Type", "application/json") .header("Authorization", format!("Bearer {}", user.id)) .body(Body::from( json!({ "assets": [ { "id": dashboard.id, "type": "dashboard" }, { "id": metric.id, "type": "metric" } ] }) .to_string(), )) .unwrap(); // Send the request let response = app.oneshot(request).await.unwrap(); // Check the response assert_eq!(response.status(), StatusCode::OK); // Parse the response body let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); let response_json: Value = serde_json::from_slice(&body).unwrap(); // Verify the response structure assert!(json_eq( &response_json, &json!({ "message": "Assets processed", "added_count": 2, "failed_count": 0, "failed_assets": [] }) )); // Verify that the assets were added to the database let dashboard_asset = collections_to_assets::table .filter(collections_to_assets::collection_id.eq(collection.id)) .filter(collections_to_assets::asset_id.eq(dashboard.id)) .filter(collections_to_assets::asset_type.eq(AssetType::DashboardFile)) .filter(collections_to_assets::deleted_at.is_null()) .first::(&mut conn) .await; let metric_asset = collections_to_assets::table .filter(collections_to_assets::collection_id.eq(collection.id)) .filter(collections_to_assets::asset_id.eq(metric.id)) .filter(collections_to_assets::asset_type.eq(AssetType::MetricFile)) .filter(collections_to_assets::deleted_at.is_null()) .first::(&mut conn) .await; // Assert that both assets exist in the collection assert!(dashboard_asset.is_ok()); assert!(metric_asset.is_ok()); } #[tokio::test] async fn test_add_assets_to_collection_permission_denied() { // Create a test app let app = create_test_app().await; // Create test user, collection, dashboard, and metric let user = create_user(None).await; let other_user = create_user(None).await; let collection = create_collection(other_user.id, None).await; let dashboard = create_dashboard(user.id, None).await; // No permissions are given to the user for the collection // Build request to add assets to collection let request = Request::builder() .uri(format!("/collections/{}/assets", collection.id)) .method("POST") .header("Content-Type", "application/json") .header("Authorization", format!("Bearer {}", user.id)) .body(Body::from( json!({ "assets": [ { "id": dashboard.id, "type": "dashboard" } ] }) .to_string(), )) .unwrap(); // Send the request let response = app.oneshot(request).await.unwrap(); // Check that permission is denied assert_eq!(response.status(), StatusCode::FORBIDDEN); } #[tokio::test] async fn test_add_assets_to_collection_not_found() { // Create a test app let app = create_test_app().await; // Create test user and dashboard let user = create_user(None).await; let dashboard = create_dashboard(user.id, None).await; // Create a non-existent collection ID let non_existent_collection_id = Uuid::new_v4(); // Build request to add assets to a non-existent collection let request = Request::builder() .uri(format!("/collections/{}/assets", non_existent_collection_id)) .method("POST") .header("Content-Type", "application/json") .header("Authorization", format!("Bearer {}", user.id)) .body(Body::from( json!({ "assets": [ { "id": dashboard.id, "type": "dashboard" } ] }) .to_string(), )) .unwrap(); // Send the request let response = app.oneshot(request).await.unwrap(); // Check that collection not found assert_eq!(response.status(), StatusCode::NOT_FOUND); } #[tokio::test] async fn test_add_nonexistent_assets_to_collection() { // Create a test app let app = create_test_app().await; // Create test user and collection let user = create_user(None).await; let collection = create_collection(user.id, None).await; // Give the user permission to the collection create_asset_permission( user.id, IdentityType::User, collection.id, AssetType::Collection, AssetPermissionRole::Owner, user.id, ) .await .unwrap(); // Create a non-existent dashboard ID let non_existent_dashboard_id = Uuid::new_v4(); // Build request to add non-existent assets to collection let request = Request::builder() .uri(format!("/collections/{}/assets", collection.id)) .method("POST") .header("Content-Type", "application/json") .header("Authorization", format!("Bearer {}", user.id)) .body(Body::from( json!({ "assets": [ { "id": non_existent_dashboard_id, "type": "dashboard" } ] }) .to_string(), )) .unwrap(); // Send the request let response = app.oneshot(request).await.unwrap(); // Check the response - should return 200 but with failed assets assert_eq!(response.status(), StatusCode::OK); // Parse the response body let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); let response_json: Value = serde_json::from_slice(&body).unwrap(); // Verify the response contains the failed asset assert!(json_eq( &response_json, &json!({ "message": "Assets processed", "added_count": 0, "failed_count": 1, "failed_assets": [ { "id": non_existent_dashboard_id, "type": "dashboard", "error": "Dashboard not found" } ] }) )); } #[tokio::test] async fn test_add_duplicate_assets_to_collection() { // Create a test app and database connection let app = create_test_app().await; let pool = get_pg_pool(); let mut conn = pool.get().await.unwrap(); // Create test user, collection, and dashboard let user = create_user(None).await; let collection = create_collection(user.id, None).await; let dashboard = create_dashboard(user.id, None).await; // Give the user permission to the collection and dashboard create_asset_permission( user.id, IdentityType::User, collection.id, AssetType::Collection, AssetPermissionRole::Owner, user.id, ) .await .unwrap(); create_asset_permission( user.id, IdentityType::User, dashboard.id, AssetType::DashboardFile, AssetPermissionRole::CanView, user.id, ) .await .unwrap(); // First, add the dashboard to the collection diesel::insert_into(collections_to_assets::table) .values(( collections_to_assets::collection_id.eq(collection.id), collections_to_assets::asset_id.eq(dashboard.id), collections_to_assets::asset_type.eq(AssetType::DashboardFile), collections_to_assets::created_at.eq(chrono::Utc::now()), collections_to_assets::created_by.eq(user.id), collections_to_assets::updated_at.eq(chrono::Utc::now()), collections_to_assets::updated_by.eq(user.id), )) .execute(&mut conn) .await .unwrap(); // Build request to add the same dashboard again let request = Request::builder() .uri(format!("/collections/{}/assets", collection.id)) .method("POST") .header("Content-Type", "application/json") .header("Authorization", format!("Bearer {}", user.id)) .body(Body::from( json!({ "assets": [ { "id": dashboard.id, "type": "dashboard" } ] }) .to_string(), )) .unwrap(); // Send the request let response = app.oneshot(request).await.unwrap(); // Check the response - should return 200 as the asset is already in the collection assert_eq!(response.status(), StatusCode::OK); // Parse the response body let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); let response_json: Value = serde_json::from_slice(&body).unwrap(); // Verify the response structure (added_count is 1 because we consider existing assets as "added") assert!(json_eq( &response_json, &json!({ "message": "Assets processed", "added_count": 1, "failed_count": 0, "failed_assets": [] }) )); }