use std::sync::Arc; use uuid::Uuid; use domain::{ models::{ AnnotatedRow, Movie, import::{ImportRow, ParsedFile, RowResult}, }, ports::{DocumentParser, MovieRepository}, testing::{InMemoryImportSessionRepository, InMemoryMovieRepository}, value_objects::{ExternalMetadataId, MovieTitle, ReleaseYear}, }; use crate::import::{ apply_mapping, commands::{ApplyImportMappingCommand, CreateImportSessionCommand}, create_session, }; use crate::test_helpers::TestContextBuilder; #[tokio::test] async fn applies_mapping_to_session() { let sessions = InMemoryImportSessionRepository::new(); let ctx = TestContextBuilder::new().build(); let user_id = Uuid::new_v4(); let session = create_session::execute( Arc::clone(&sessions) as _, ctx.services.document_parser.clone(), CreateImportSessionCommand { user_id, bytes: b"title\nTest".to_vec(), format: domain::models::FileFormat::Csv, }, ) .await .unwrap(); let rows = apply_mapping::execute( Arc::clone(&sessions) as _, ctx.services.document_parser.clone(), ctx.repos.movie.clone(), ApplyImportMappingCommand { user_id, session_id: session.session_id.value(), mappings: vec![], }, ) .await .unwrap(); assert!(!rows.is_empty()); } #[tokio::test] async fn fails_when_session_not_found() { let sessions = InMemoryImportSessionRepository::new(); let ctx = TestContextBuilder::new().build(); let result = apply_mapping::execute( Arc::clone(&sessions) as _, ctx.services.document_parser.clone(), ctx.repos.movie.clone(), ApplyImportMappingCommand { user_id: Uuid::new_v4(), session_id: Uuid::new_v4(), mappings: vec![], }, ) .await; assert!(result.is_err()); } /// A document parser that returns rows with specific field values for testing /// the mark_duplicates logic. struct DuplicateTestParser { rows: Vec, } impl DocumentParser for DuplicateTestParser { fn parse( &self, _: &[u8], _: domain::models::FileFormat, ) -> Result { Ok(ParsedFile { columns: vec!["title".into()], rows: vec![vec!["x".into()]], }) } fn apply_mapping( &self, _: &ParsedFile, _: &[domain::models::FieldMapping], ) -> Vec { self.rows .iter() .map(|r| AnnotatedRow { result: RowResult::Valid(r.clone()), is_duplicate: false, }) .collect() } } #[tokio::test] async fn marks_duplicate_by_external_id() { let movies = InMemoryMovieRepository::new(); let sessions = InMemoryImportSessionRepository::new(); let ext_id = ExternalMetadataId::new("tt1234567".into()).unwrap(); let movie = Movie::new( Some(ext_id), MovieTitle::new("Known Movie".into()).unwrap(), ReleaseYear::new(2020).unwrap(), None, None, ); movies.upsert_movie(&movie).await.unwrap(); let parser = Arc::new(DuplicateTestParser { rows: vec![ImportRow { title: Some("Known Movie".into()), release_year: Some("2020".into()), external_metadata_id: Some("tt1234567".into()), ..ImportRow::default() }], }); let user_id = Uuid::new_v4(); let session = create_session::execute( Arc::clone(&sessions) as _, Arc::clone(&parser) as _, CreateImportSessionCommand { user_id, bytes: b"title\nKnown Movie".to_vec(), format: domain::models::FileFormat::Csv, }, ) .await .unwrap(); let rows = apply_mapping::execute( Arc::clone(&sessions) as _, Arc::clone(&parser) as _, Arc::clone(&movies) as _, ApplyImportMappingCommand { user_id, session_id: session.session_id.value(), mappings: vec![], }, ) .await .unwrap(); let has_dup = rows.iter().any(|r| r.is_duplicate); assert!(has_dup, "row with matching external_id should be duplicate"); } #[tokio::test] async fn marks_duplicate_by_title_and_year() { let movies = InMemoryMovieRepository::new(); let sessions = InMemoryImportSessionRepository::new(); let movie = Movie::new( None, MovieTitle::new("Duplicate Film".into()).unwrap(), ReleaseYear::new(2022).unwrap(), None, None, ); movies.upsert_movie(&movie).await.unwrap(); let parser = Arc::new(DuplicateTestParser { rows: vec![ImportRow { title: Some("Duplicate Film".into()), release_year: Some("2022".into()), ..ImportRow::default() }], }); let user_id = Uuid::new_v4(); let session = create_session::execute( Arc::clone(&sessions) as _, Arc::clone(&parser) as _, CreateImportSessionCommand { user_id, bytes: b"title\nDuplicate Film".to_vec(), format: domain::models::FileFormat::Csv, }, ) .await .unwrap(); let rows = apply_mapping::execute( Arc::clone(&sessions) as _, Arc::clone(&parser) as _, Arc::clone(&movies) as _, ApplyImportMappingCommand { user_id, session_id: session.session_id.value(), mappings: vec![], }, ) .await .unwrap(); let has_dup = rows.iter().any(|r| r.is_duplicate); assert!(has_dup, "row with matching title+year should be duplicate"); }