structural refactor and codebase improvements
This commit is contained in:
108
crates/application/src/import/tests/apply_profile_and_map.rs
Normal file
108
crates/application/src/import/tests/apply_profile_and_map.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use chrono::Utc;
|
||||
use uuid::Uuid;
|
||||
|
||||
use domain::models::import::{DomainField, Transform};
|
||||
use domain::models::{FieldMapping, FileFormat, ImportProfile};
|
||||
use domain::ports::{ImportProfileRepository, ImportSessionRepository};
|
||||
use domain::testing::{InMemoryImportProfileRepository, InMemoryImportSessionRepository};
|
||||
use domain::value_objects::{ImportProfileId, UserId};
|
||||
|
||||
use crate::import::deps::{ApplyProfileAndMapDeps, CreateSessionDeps};
|
||||
use crate::import::{
|
||||
apply_profile_and_map, commands::ApplyProfileAndMapCommand,
|
||||
commands::CreateImportSessionCommand, create_session,
|
||||
};
|
||||
use crate::test_helpers::TestContextBuilder;
|
||||
|
||||
#[tokio::test]
|
||||
async fn fails_when_profile_not_found() {
|
||||
let profiles = InMemoryImportProfileRepository::new();
|
||||
let sessions = InMemoryImportSessionRepository::new();
|
||||
let b = TestContextBuilder::new();
|
||||
|
||||
let deps = ApplyProfileAndMapDeps {
|
||||
import_profile: Arc::clone(&profiles) as _,
|
||||
import_session: Arc::clone(&sessions) as _,
|
||||
document_parser: b.document_parser.clone(),
|
||||
movie_query: b.movie_query.clone(),
|
||||
};
|
||||
|
||||
let result = apply_profile_and_map::execute(
|
||||
&deps,
|
||||
ApplyProfileAndMapCommand {
|
||||
user_id: Uuid::new_v4(),
|
||||
session_id: Uuid::new_v4(),
|
||||
profile_id: Uuid::new_v4(),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn applies_profile_then_regenerates_mapping() {
|
||||
let profiles = InMemoryImportProfileRepository::new();
|
||||
let sessions = InMemoryImportSessionRepository::new();
|
||||
let b = TestContextBuilder::new();
|
||||
let user_id = Uuid::new_v4();
|
||||
|
||||
let profile = ImportProfile::new(
|
||||
ImportProfileId::generate(),
|
||||
UserId::from_uuid(user_id),
|
||||
"letterboxd".into(),
|
||||
vec![FieldMapping {
|
||||
source_column: "title".into(),
|
||||
domain_field: DomainField::Title,
|
||||
transform: Transform::Identity,
|
||||
}],
|
||||
Utc::now().naive_utc(),
|
||||
);
|
||||
let profile_id = profile.id.clone();
|
||||
profiles.save(&profile).await.unwrap();
|
||||
|
||||
let create_deps = CreateSessionDeps {
|
||||
import_session: Arc::clone(&sessions) as _,
|
||||
document_parser: b.document_parser.clone(),
|
||||
};
|
||||
let created = create_session::execute(
|
||||
&create_deps,
|
||||
CreateImportSessionCommand {
|
||||
user_id,
|
||||
bytes: b"title\nTest".to_vec(),
|
||||
format: FileFormat::Csv,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let deps = ApplyProfileAndMapDeps {
|
||||
import_profile: Arc::clone(&profiles) as _,
|
||||
import_session: Arc::clone(&sessions) as _,
|
||||
document_parser: b.document_parser.clone(),
|
||||
movie_query: b.movie_query.clone(),
|
||||
};
|
||||
|
||||
let rows = apply_profile_and_map::execute(
|
||||
&deps,
|
||||
ApplyProfileAndMapCommand {
|
||||
user_id,
|
||||
session_id: created.session_id.value(),
|
||||
profile_id: profile_id.value(),
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(!rows.is_empty());
|
||||
|
||||
let updated = sessions
|
||||
.get(&created.session_id, &UserId::from_uuid(user_id))
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert!(updated.row_results.is_some());
|
||||
assert!(updated.field_mappings.is_some());
|
||||
}
|
||||
@@ -3,14 +3,19 @@ use std::sync::Arc;
|
||||
use domain::testing::InMemoryImportProfileRepository;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::import::{commands::DeleteImportProfileCommand, delete_profile};
|
||||
use crate::import::{
|
||||
commands::DeleteImportProfileCommand, delete_profile, deps::DeleteImportProfileDeps,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn fails_when_profile_not_found() {
|
||||
let profiles = InMemoryImportProfileRepository::new();
|
||||
let deps = DeleteImportProfileDeps {
|
||||
import_profile: Arc::clone(&profiles) as _,
|
||||
};
|
||||
|
||||
let result = delete_profile::execute(
|
||||
Arc::clone(&profiles) as _,
|
||||
&deps,
|
||||
DeleteImportProfileCommand {
|
||||
user_id: Uuid::new_v4(),
|
||||
profile_id: Uuid::new_v4(),
|
||||
|
||||
68
crates/application/src/import/tests/get_mapping_stage.rs
Normal file
68
crates/application/src/import/tests/get_mapping_stage.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use domain::models::ImportSession;
|
||||
use domain::models::import::ParsedFile;
|
||||
use domain::ports::ImportSessionRepository;
|
||||
use domain::testing::InMemoryImportSessionRepository;
|
||||
use domain::value_objects::{ImportSessionId, UserId};
|
||||
|
||||
use crate::import::deps::GetMappingStageDeps;
|
||||
use crate::import::get_mapping_stage::{self, SAMPLE_ROW_LIMIT};
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_mapping_stage_is_not_found_when_file_not_parsed() {
|
||||
let sessions = InMemoryImportSessionRepository::new();
|
||||
let user_id = Uuid::new_v4();
|
||||
let session = ImportSession::new(UserId::from_uuid(user_id));
|
||||
let session_id = session.id.clone();
|
||||
sessions.create(&session).await.unwrap();
|
||||
|
||||
let deps = GetMappingStageDeps {
|
||||
import_session: Arc::clone(&sessions) as _,
|
||||
};
|
||||
|
||||
let result = get_mapping_stage::execute(&deps, session_id, user_id).await;
|
||||
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_mapping_stage_is_not_found_when_session_missing() {
|
||||
let sessions = InMemoryImportSessionRepository::new();
|
||||
let deps = GetMappingStageDeps {
|
||||
import_session: Arc::clone(&sessions) as _,
|
||||
};
|
||||
|
||||
let result =
|
||||
get_mapping_stage::execute(&deps, ImportSessionId::generate(), Uuid::new_v4()).await;
|
||||
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_mapping_stage_returns_columns_and_capped_sample_rows() {
|
||||
let sessions = InMemoryImportSessionRepository::new();
|
||||
let user_id = Uuid::new_v4();
|
||||
let mut session = ImportSession::new(UserId::from_uuid(user_id));
|
||||
session.parsed_file = Some(ParsedFile {
|
||||
columns: vec!["Name".into(), "Year".into()],
|
||||
rows: (0..7)
|
||||
.map(|i| vec![format!("row{i}"), "2020".into()])
|
||||
.collect(),
|
||||
});
|
||||
let session_id = session.id.clone();
|
||||
sessions.create(&session).await.unwrap();
|
||||
|
||||
let deps = GetMappingStageDeps {
|
||||
import_session: Arc::clone(&sessions) as _,
|
||||
};
|
||||
|
||||
let stage = get_mapping_stage::execute(&deps, session_id, user_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(stage.columns, vec!["Name".to_string(), "Year".to_string()]);
|
||||
assert_eq!(stage.sample_rows.len(), SAMPLE_ROW_LIMIT);
|
||||
}
|
||||
80
crates/application/src/import/tests/get_preview_stage.rs
Normal file
80
crates/application/src/import/tests/get_preview_stage.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use domain::models::import::{ImportRow, ParsedFile, RowResult};
|
||||
use domain::models::{AnnotatedRow, ImportSession};
|
||||
use domain::ports::ImportSessionRepository;
|
||||
use domain::testing::InMemoryImportSessionRepository;
|
||||
use domain::value_objects::{ImportSessionId, UserId};
|
||||
|
||||
use crate::import::deps::GetPreviewStageDeps;
|
||||
use crate::import::get_preview_stage::{self, PreviewStage};
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_preview_stage_reports_not_yet_mapped_when_row_results_absent() {
|
||||
let sessions = InMemoryImportSessionRepository::new();
|
||||
let user_id = Uuid::new_v4();
|
||||
let session = ImportSession::new(UserId::from_uuid(user_id));
|
||||
let session_id = session.id.clone();
|
||||
sessions.create(&session).await.unwrap();
|
||||
|
||||
let deps = GetPreviewStageDeps {
|
||||
import_session: Arc::clone(&sessions) as _,
|
||||
};
|
||||
|
||||
let stage = get_preview_stage::execute(&deps, session_id, user_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(matches!(stage, PreviewStage::NotYetMapped));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_preview_stage_returns_rows_once_mapped() {
|
||||
let sessions = InMemoryImportSessionRepository::new();
|
||||
let user_id = Uuid::new_v4();
|
||||
let mut session = ImportSession::new(UserId::from_uuid(user_id));
|
||||
session.parsed_file = Some(ParsedFile {
|
||||
columns: vec!["Name".into()],
|
||||
rows: vec![vec!["Test".into()]],
|
||||
});
|
||||
session.row_results = Some(vec![AnnotatedRow {
|
||||
result: RowResult::Valid(ImportRow {
|
||||
title: Some("Test".into()),
|
||||
..ImportRow::default()
|
||||
}),
|
||||
is_duplicate: false,
|
||||
}]);
|
||||
let session_id = session.id.clone();
|
||||
sessions.create(&session).await.unwrap();
|
||||
|
||||
let deps = GetPreviewStageDeps {
|
||||
import_session: Arc::clone(&sessions) as _,
|
||||
};
|
||||
|
||||
let stage = get_preview_stage::execute(&deps, session_id, user_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
match stage {
|
||||
PreviewStage::Ready(preview) => {
|
||||
assert_eq!(preview.columns, vec!["Name".to_string()]);
|
||||
assert_eq!(preview.rows.len(), 1);
|
||||
}
|
||||
PreviewStage::NotYetMapped => panic!("expected Ready, got NotYetMapped"),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_preview_stage_is_not_found_when_session_missing() {
|
||||
let sessions = InMemoryImportSessionRepository::new();
|
||||
let deps = GetPreviewStageDeps {
|
||||
import_session: Arc::clone(&sessions) as _,
|
||||
};
|
||||
|
||||
let result =
|
||||
get_preview_stage::execute(&deps, ImportSessionId::generate(), Uuid::new_v4()).await;
|
||||
|
||||
assert!(result.is_err());
|
||||
}
|
||||
50
crates/application/src/import/tests/get_session_state.rs
Normal file
50
crates/application/src/import/tests/get_session_state.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use domain::models::ImportSession;
|
||||
use domain::models::import::ParsedFile;
|
||||
use domain::ports::ImportSessionRepository;
|
||||
use domain::testing::InMemoryImportSessionRepository;
|
||||
use domain::value_objects::{ImportSessionId, UserId};
|
||||
|
||||
use crate::import::deps::GetSessionStateDeps;
|
||||
use crate::import::get_session_state;
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_session_state_is_not_found_when_session_missing() {
|
||||
let sessions = InMemoryImportSessionRepository::new();
|
||||
let deps = GetSessionStateDeps {
|
||||
import_session: Arc::clone(&sessions) as _,
|
||||
};
|
||||
|
||||
let result =
|
||||
get_session_state::execute(&deps, ImportSessionId::generate(), Uuid::new_v4()).await;
|
||||
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_session_state_reports_columns_row_count_and_mapping_status() {
|
||||
let sessions = InMemoryImportSessionRepository::new();
|
||||
let user_id = Uuid::new_v4();
|
||||
let mut session = ImportSession::new(UserId::from_uuid(user_id));
|
||||
session.parsed_file = Some(ParsedFile {
|
||||
columns: vec!["Name".into()],
|
||||
rows: vec![vec!["a".into()], vec!["b".into()]],
|
||||
});
|
||||
let session_id = session.id.clone();
|
||||
sessions.create(&session).await.unwrap();
|
||||
|
||||
let deps = GetSessionStateDeps {
|
||||
import_session: Arc::clone(&sessions) as _,
|
||||
};
|
||||
|
||||
let state = get_session_state::execute(&deps, session_id, user_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(state.columns, vec!["Name".to_string()]);
|
||||
assert_eq!(state.row_count, 2);
|
||||
assert!(!state.has_mappings);
|
||||
}
|
||||
@@ -4,16 +4,17 @@ use domain::testing::InMemoryImportProfileRepository;
|
||||
use domain::value_objects::UserId;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::import::list_profiles;
|
||||
use crate::import::{deps::ListImportProfilesDeps, list_profiles};
|
||||
|
||||
#[tokio::test]
|
||||
async fn returns_empty_when_no_profiles() {
|
||||
let profiles = InMemoryImportProfileRepository::new();
|
||||
let deps = ListImportProfilesDeps {
|
||||
import_profile: Arc::clone(&profiles) as _,
|
||||
};
|
||||
|
||||
let user_id = UserId::from_uuid(Uuid::new_v4());
|
||||
let result = list_profiles::execute(Arc::clone(&profiles) as _, &user_id)
|
||||
.await
|
||||
.unwrap();
|
||||
let result = list_profiles::execute(&deps, &user_id).await.unwrap();
|
||||
|
||||
assert!(result.is_empty());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user