domain: add Media Catalog entities (Asset, Metadata, Stack, Derivative, Duplicate)

This commit is contained in:
2026-05-31 03:27:41 +02:00
parent ed6eb0c28a
commit 147206d8a5
12 changed files with 371 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
use domain::entities::{Asset, AssetType, SourceReference};
use domain::value_objects::{Checksum, SystemId};
fn make_asset() -> Asset {
let src = SourceReference {
volume_id: SystemId::new(),
relative_path: "photos/img.jpg".to_string(),
checksum: Checksum::new("a".repeat(64)).unwrap(),
};
Asset::new(src, AssetType::Image, "image/jpeg", 1024, SystemId::new())
}
#[test]
fn new_asset_is_unprocessed() {
let a = make_asset();
assert!(!a.is_processed);
}
#[test]
fn mark_processed() {
let mut a = make_asset();
a.mark_processed();
assert!(a.is_processed);
}

View File

@@ -0,0 +1,17 @@
use domain::entities::{AssetMetadata, MetadataSource};
use domain::value_objects::{MetadataValue, StructuredData, SystemId};
#[test]
fn metadata_source_ordering() {
assert!(MetadataSource::ExifExtracted < MetadataSource::AiGenerated);
assert!(MetadataSource::AiGenerated < MetadataSource::UserEdited);
}
#[test]
fn create_metadata_layer() {
let mut data = StructuredData::new();
data.insert("camera", MetadataValue::String("Canon".to_string()));
let meta = AssetMetadata::new(SystemId::new(), MetadataSource::ExifExtracted, data);
assert_eq!(meta.metadata_source, MetadataSource::ExifExtracted);
assert_eq!(meta.data.get_string("camera"), Some("Canon"));
}

View File

@@ -0,0 +1,31 @@
use domain::entities::{AssetStack, StackMemberRole, StackType};
use domain::errors::DomainError;
use domain::value_objects::SystemId;
#[test]
fn new_stack_contains_primary() {
let primary = SystemId::new();
let stack = AssetStack::new(StackType::LivePhoto, primary.clone(), SystemId::new());
assert_eq!(stack.members.len(), 1);
assert_eq!(stack.members[0].asset_id, primary);
assert_eq!(stack.members[0].role, StackMemberRole::PrimaryDisplay);
assert_eq!(stack.members[0].sort_order, 0);
}
#[test]
fn add_motion_clip() {
let mut stack = AssetStack::new(StackType::LivePhoto, SystemId::new(), SystemId::new());
let clip_id = SystemId::new();
stack.add_member(clip_id.clone(), StackMemberRole::MotionClip).unwrap();
assert_eq!(stack.members.len(), 2);
assert_eq!(stack.members[1].asset_id, clip_id);
assert_eq!(stack.members[1].sort_order, 1);
}
#[test]
fn cannot_add_duplicate() {
let primary = SystemId::new();
let mut stack = AssetStack::new(StackType::LivePhoto, primary.clone(), SystemId::new());
let result = stack.add_member(primary, StackMemberRole::HighResSource);
assert!(matches!(result, Err(DomainError::Conflict(_))));
}

View File

@@ -0,0 +1,20 @@
use domain::entities::{DerivativeAsset, DerivativeProfile, GenerationStatus};
use domain::value_objects::SystemId;
#[test]
fn lifecycle() {
let mut d = DerivativeAsset::new_pending(
SystemId::new(),
DerivativeProfile::ThumbnailSquare,
"/thumbs/abc.webp",
);
assert_eq!(d.generation_status, GenerationStatus::Pending);
assert_eq!(d.file_size, 0);
assert_eq!(d.dimensions, (0, 0));
d.mark_ready("image/webp", 4096, (256, 256));
assert_eq!(d.generation_status, GenerationStatus::Ready);
assert_eq!(d.mime_type, "image/webp");
assert_eq!(d.file_size, 4096);
assert_eq!(d.dimensions, (256, 256));
}

View File

@@ -0,0 +1,19 @@
use domain::entities::{DetectionMethod, DuplicateGroup, DuplicateStatus};
use domain::value_objects::SystemId;
#[test]
fn exact_duplicate_group() {
let g = DuplicateGroup::new_exact(SystemId::new(), SystemId::new());
assert_eq!(g.detection_method, DetectionMethod::ExactHash);
assert_eq!(g.status, DuplicateStatus::Unresolved);
assert_eq!(g.candidates.len(), 2);
assert_eq!(g.candidates[0].similarity_score, 1.0);
assert_eq!(g.candidates[1].similarity_score, 1.0);
}
#[test]
fn resolve_group() {
let mut g = DuplicateGroup::new_exact(SystemId::new(), SystemId::new());
g.resolve();
assert_eq!(g.status, DuplicateStatus::Resolved);
}

View File

@@ -6,3 +6,8 @@ mod storage_volume;
mod library_path;
mod ingest_session;
mod quota;
mod asset;
mod asset_metadata;
mod asset_stack;
mod derivative_asset;
mod duplicate;