use application::catalog::{GetAssetHandler, GetAssetQuery}; use application::testing::{InMemoryAssetMetadataRepository, InMemoryAssetRepository}; use domain::catalog::entities::{Asset, AssetMetadata, AssetType, MetadataSource, SourceReference}; use domain::errors::DomainError; use domain::ports::{AssetMetadataRepository, AssetRepository}; use domain::value_objects::{Checksum, MetadataValue, StructuredData, SystemId}; use std::sync::Arc; #[tokio::test] async fn returns_asset_with_resolved_metadata() { let asset_repo = Arc::new(InMemoryAssetRepository::new()); let meta_repo = Arc::new(InMemoryAssetMetadataRepository::new()); let source = SourceReference { volume_id: SystemId::new(), relative_path: "photos/img.jpg".into(), checksum: Checksum::new("a".repeat(64)).unwrap(), }; let owner = SystemId::new(); let asset = Asset::new( source, AssetType::Image, "image/jpeg", 1024, owner, ); asset_repo.save(&asset).await.unwrap(); // Add exif layer let mut exif_data = StructuredData::new(); exif_data.insert("camera", MetadataValue::String("Nikon".into())); exif_data.insert("title", MetadataValue::String("EXIF title".into())); let exif = AssetMetadata::new(asset.asset_id, MetadataSource::ExifExtracted, exif_data); meta_repo.save(&exif).await.unwrap(); // Add user layer (overrides title) let mut user_data = StructuredData::new(); user_data.insert("title", MetadataValue::String("My Photo".into())); let user_meta = AssetMetadata::new(asset.asset_id, MetadataSource::UserEdited, user_data); meta_repo.save(&user_meta).await.unwrap(); let handler = GetAssetHandler::new(asset_repo, meta_repo); let (returned, resolved) = handler .execute(GetAssetQuery { asset_id: asset.asset_id, user_id: owner, }) .await .unwrap(); assert_eq!(returned.asset_id, asset.asset_id); // UserEdited overrides ExifExtracted assert_eq!(resolved.get_string("title"), Some("My Photo")); // ExifExtracted field preserved assert_eq!(resolved.get_string("camera"), Some("Nikon")); } #[tokio::test] async fn rejects_nonexistent() { let asset_repo = Arc::new(InMemoryAssetRepository::new()); let meta_repo = Arc::new(InMemoryAssetMetadataRepository::new()); let handler = GetAssetHandler::new(asset_repo, meta_repo); let result = handler .execute(GetAssetQuery { asset_id: SystemId::new(), user_id: SystemId::new(), }) .await; assert!(matches!(result, Err(DomainError::NotFound(_)))); } #[tokio::test] async fn rejects_forbidden_access() { let asset_repo = Arc::new(InMemoryAssetRepository::new()); let meta_repo = Arc::new(InMemoryAssetMetadataRepository::new()); let source = SourceReference { volume_id: SystemId::new(), relative_path: "photos/img.jpg".into(), checksum: Checksum::new("a".repeat(64)).unwrap(), }; let owner = SystemId::new(); let asset = Asset::new(source, AssetType::Image, "image/jpeg", 1024, owner); asset_repo.save(&asset).await.unwrap(); let handler = GetAssetHandler::new(asset_repo, meta_repo); let other_user = SystemId::new(); let result = handler .execute(GetAssetQuery { asset_id: asset.asset_id, user_id: other_user, }) .await; assert!(matches!(result, Err(DomainError::Forbidden(_)))); }