test: shared factories and command builders

This commit is contained in:
2026-10-25 22:08:17 +01:00
parent 2ac1d4e0cf
commit 51d92e08a7
58 changed files with 784 additions and 1344 deletions

View File

@@ -1,15 +1,13 @@
use crate::test_support::{login_command, register_command};
use std::sync::Arc;
use domain::models::UserRole;
use domain::testing::InMemoryUserRepository;
use crate::{
auth::{
commands::RegisterCommand,
deps::{LoginDeps, RegisterDeps},
login,
queries::LoginCommand,
register,
login, register,
},
test_helpers::TestContextBuilder,
};
@@ -23,10 +21,8 @@ async fn setup_user(b: &TestContextBuilder, email: &str, password: &str) {
register::execute(
&deps,
RegisterCommand {
email: email.to_string(),
username: "testuser".to_string(),
password: password.to_string(),
role: UserRole::Standard,
..register_command(email, "testuser")
},
)
.await
@@ -46,15 +42,9 @@ async fn test_login_valid_credentials_returns_token() {
refresh_session: b.refresh_session_repo.clone(),
config: b.config.clone(),
};
let result = login::execute(
&deps,
LoginCommand {
email: "carol@example.com".into(),
password: "secret123".into(),
},
)
.await
.unwrap();
let result = login::execute(&deps, login_command("carol@example.com", "secret123"))
.await
.unwrap();
assert!(!result.token.is_empty());
assert!(!result.refresh_token.is_empty());
@@ -74,14 +64,7 @@ async fn test_login_wrong_password_fails() {
refresh_session: b.refresh_session_repo.clone(),
config: b.config.clone(),
};
let result = login::execute(
&deps,
LoginCommand {
email: "dave@example.com".into(),
password: "wrong_password".into(),
},
)
.await;
let result = login::execute(&deps, login_command("dave@example.com", "wrong_password")).await;
assert!(result.is_err());
}
@@ -96,14 +79,7 @@ async fn test_login_unknown_email_fails() {
refresh_session: b.refresh_session_repo.clone(),
config: b.config.clone(),
};
let result = login::execute(
&deps,
LoginCommand {
email: "nobody@example.com".into(),
password: "anything".into(),
},
)
.await;
let result = login::execute(&deps, login_command("nobody@example.com", "anything")).await;
assert!(result.is_err());
}

View File

@@ -1,15 +1,12 @@
use crate::test_support::{login_command, register_command};
use std::sync::Arc;
use domain::models::UserRole;
use domain::testing::InMemoryUserRepository;
use crate::{
auth::{
commands::RegisterCommand,
deps::{LoginDeps, LogoutDeps, RefreshDeps, RegisterDeps},
login, logout,
queries::LoginCommand,
refresh, register,
login, logout, refresh, register,
},
test_helpers::TestContextBuilder,
};
@@ -24,17 +21,9 @@ async fn logout_revokes_refresh_token() {
password_hasher: b.password_hasher.clone(),
config: b.config.clone(),
};
register::execute(
&reg_deps,
RegisterCommand {
email: "bob@example.com".to_string(),
username: "bob".to_string(),
password: "password123".to_string(),
role: UserRole::Standard,
},
)
.await
.unwrap();
register::execute(&reg_deps, register_command("bob@example.com", "bob"))
.await
.unwrap();
let login_deps = LoginDeps {
user: b.user_repo.clone(),
@@ -43,15 +32,9 @@ async fn logout_revokes_refresh_token() {
refresh_session: b.refresh_session_repo.clone(),
config: b.config.clone(),
};
let login_result = login::execute(
&login_deps,
LoginCommand {
email: "bob@example.com".into(),
password: "password123".into(),
},
)
.await
.unwrap();
let login_result = login::execute(&login_deps, login_command("bob@example.com", "password123"))
.await
.unwrap();
let logout_deps = LogoutDeps {
refresh_session: b.refresh_session_repo.clone(),

View File

@@ -1,15 +1,12 @@
use crate::test_support::{login_command, register_command};
use std::sync::Arc;
use domain::models::UserRole;
use domain::testing::InMemoryUserRepository;
use crate::{
auth::{
commands::RegisterCommand,
deps::{LoginDeps, RefreshDeps, RegisterDeps},
login,
queries::LoginCommand,
refresh, register,
login, refresh, register,
},
test_helpers::TestContextBuilder,
};
@@ -20,17 +17,9 @@ async fn login_user(b: &TestContextBuilder) -> login::LoginResult {
password_hasher: b.password_hasher.clone(),
config: b.config.clone(),
};
register::execute(
&reg_deps,
RegisterCommand {
email: "alice@example.com".to_string(),
username: "alice".to_string(),
password: "password123".to_string(),
role: UserRole::Standard,
},
)
.await
.unwrap();
register::execute(&reg_deps, register_command("alice@example.com", "alice"))
.await
.unwrap();
let login_deps = LoginDeps {
user: b.user_repo.clone(),
@@ -41,10 +30,7 @@ async fn login_user(b: &TestContextBuilder) -> login::LoginResult {
};
login::execute(
&login_deps,
LoginCommand {
email: "alice@example.com".into(),
password: "password123".into(),
},
login_command("alice@example.com", "password123"),
)
.await
.unwrap()

View File

@@ -1,6 +1,6 @@
use crate::test_support::register_command;
use std::sync::Arc;
use domain::models::UserRole;
use domain::ports::UserRepository;
use domain::testing::InMemoryUserRepository;
use domain::value_objects::Email;
@@ -11,12 +11,7 @@ use crate::{
};
fn cmd(email: &str) -> RegisterCommand {
RegisterCommand {
email: email.to_string(),
username: "alice".to_string(),
password: "password123".to_string(),
role: UserRole::Standard,
}
register_command(email, "alice")
}
#[tokio::test]
@@ -67,10 +62,8 @@ async fn test_register_short_password_fails() {
let result = register::execute(
&deps,
RegisterCommand {
email: "x@y.com".to_string(),
username: "testuser".to_string(),
password: "short".to_string(),
role: UserRole::Standard,
..register_command("x@y.com", "testuser")
},
)
.await;

View File

@@ -1,43 +1,18 @@
use domain::testing::{test_movie_with_year, test_review};
use std::sync::Arc;
use chrono::Utc;
use domain::{
models::{Movie, Review},
ports::{MovieCommand, MovieQuery, ReviewRepository},
testing::{
FakeDiaryQuery, InMemoryMovieRepository, InMemoryReviewRepository, NoopEventPublisher,
},
value_objects::{MovieId, MovieTitle, Rating, ReleaseYear, UserId},
value_objects::{MovieId, UserId},
};
use crate::{
diary::commands::DeleteReviewCommand, diary::delete_review, diary::deps::DeleteReviewDeps,
};
fn make_movie() -> Movie {
Movie::new(
None,
MovieTitle::new("Terminator".into()).unwrap(),
ReleaseYear::new(1984).unwrap(),
None,
None,
)
}
fn make_review(movie_id: MovieId, user_id: UserId) -> Review {
Review::new(
movie_id,
user_id,
Rating::new(4).unwrap(),
None,
Utc::now().naive_utc(),
None,
false,
)
.unwrap()
}
#[tokio::test]
async fn test_delete_review_removes_it() {
let movies = InMemoryMovieRepository::new();
@@ -45,9 +20,9 @@ async fn test_delete_review_removes_it() {
let diary = FakeDiaryQuery::new();
let events = NoopEventPublisher::new();
let movie = make_movie();
let movie = test_movie_with_year("Terminator", 1984);
let user_id = UserId::from_uuid(uuid::Uuid::new_v4());
let review = make_review(movie.id().clone(), user_id.clone());
let review = test_review(movie.id(), &user_id, 4);
movies.upsert_movie(&movie).await.unwrap();
reviews.save_review(&review).await.unwrap();
@@ -87,7 +62,7 @@ async fn test_delete_review_wrong_user_is_unauthorized() {
let movie_id = MovieId::from_uuid(uuid::Uuid::new_v4());
let owner_id = UserId::from_uuid(uuid::Uuid::new_v4());
let other_id = uuid::Uuid::new_v4();
let review = make_review(movie_id, owner_id);
let review = test_review(&movie_id, &owner_id, 4);
reviews.save_review(&review).await.unwrap();

View File

@@ -1,30 +1,15 @@
use crate::test_support::edit_review_command;
use std::sync::Arc;
use chrono::Utc;
use uuid::Uuid;
use domain::{
models::Review,
models::ReviewEdit,
ports::ReviewRepository,
testing::{InMemoryReviewRepository, NoopEventPublisher},
value_objects::{Comment, MovieId, Rating, ReviewId, UserId, WatchMedium},
testing::{InMemoryReviewRepository, NoopEventPublisher, test_review},
value_objects::{Comment, MovieId, ReviewId, UserId, WatchMedium},
};
use crate::diary::{commands::EditReviewCommand, deps::EditReviewDeps, edit_review};
fn make_review(user_id: UserId) -> Review {
Review::new(
MovieId::generate(),
user_id,
Rating::new(3).unwrap(),
Some(Comment::new("original comment".into()).unwrap()),
Utc::now().naive_utc(),
Some(WatchMedium::Streaming),
false,
)
.unwrap()
}
async fn setup() -> (
Arc<InMemoryReviewRepository>,
Arc<NoopEventPublisher>,
@@ -34,7 +19,11 @@ async fn setup() -> (
let reviews = InMemoryReviewRepository::new();
let events = NoopEventPublisher::new();
let user_id = UserId::generate();
let review = make_review(user_id.clone());
let review = test_review(&MovieId::generate(), &user_id, 3).apply_edit(ReviewEdit {
comment: Some(Some(Comment::new("original comment".into()).unwrap())),
watch_medium: Some(Some(WatchMedium::Streaming)),
..Default::default()
});
let review_id = review.id().clone();
reviews.save_review(&review).await.unwrap();
(reviews, events, review_id, user_id)
@@ -59,11 +48,7 @@ async fn edit_own_review_updates_rating() {
EditReviewCommand {
review_id: review_id.value(),
requesting_user_id: user_id.value(),
rating: Some(5),
comment: None,
watched_at: None,
watch_medium: None,
spoilers: None,
..edit_review_command(5)
},
)
.await
@@ -80,20 +65,9 @@ async fn edit_nonexistent_review_returns_not_found() {
let reviews = InMemoryReviewRepository::new();
let events = NoopEventPublisher::new();
let err = edit_review::execute(
&deps(&reviews, &events),
EditReviewCommand {
review_id: Uuid::new_v4(),
requesting_user_id: Uuid::new_v4(),
rating: Some(5),
comment: None,
watched_at: None,
watch_medium: None,
spoilers: None,
},
)
.await
.unwrap_err();
let err = edit_review::execute(&deps(&reviews, &events), edit_review_command(5))
.await
.unwrap_err();
assert!(matches!(err, domain::errors::DomainError::NotFound(_)));
}
@@ -106,12 +80,7 @@ async fn edit_other_users_review_returns_forbidden() {
&deps(&reviews, &events),
EditReviewCommand {
review_id: review_id.value(),
requesting_user_id: Uuid::new_v4(),
rating: Some(5),
comment: None,
watched_at: None,
watch_medium: None,
spoilers: None,
..edit_review_command(5)
},
)
.await
@@ -129,11 +98,7 @@ async fn edit_publishes_review_updated_event() {
EditReviewCommand {
review_id: review_id.value(),
requesting_user_id: user_id.value(),
rating: Some(1),
comment: None,
watched_at: None,
watch_medium: None,
spoilers: None,
..edit_review_command(1)
},
)
.await
@@ -154,13 +119,11 @@ async fn edit_sets_watch_medium() {
edit_review::execute(
&deps(&reviews, &events),
EditReviewCommand {
rating: None,
review_id: review_id.value(),
requesting_user_id: user_id.value(),
rating: None,
comment: None,
watched_at: None,
watch_medium: Some(Some(WatchMedium::Cinema)),
spoilers: None,
..edit_review_command(1)
},
)
.await

View File

@@ -1,12 +1,11 @@
use domain::testing::test_movie_with_year;
use std::sync::Arc;
use uuid::Uuid;
use domain::{
models::Movie,
ports::MovieCommand,
testing::{FakeDiaryQuery, InMemoryMovieProfileRepository, InMemoryMovieRepository},
value_objects::{MovieTitle, ReleaseYear},
};
use crate::{
@@ -39,13 +38,7 @@ async fn fails_when_movie_not_found() {
async fn returns_movie_social_page() {
let movies = InMemoryMovieRepository::new();
let movie = Movie::new(
None,
MovieTitle::new("Social Movie".into()).unwrap(),
ReleaseYear::new(2024).unwrap(),
None,
None,
);
let movie = test_movie_with_year("Social Movie", 2024);
let movie_uuid = movie.id().value();
movies.upsert_movie(&movie).await.unwrap();

View File

@@ -1,11 +1,7 @@
use domain::testing::test_movie_with_year;
use std::sync::Arc;
use domain::{
models::Movie,
ports::DiaryQuery,
services::review_history::Trend,
value_objects::{MovieTitle, ReleaseYear},
};
use domain::{ports::DiaryQuery, services::review_history::Trend};
use crate::{
diary::deps::GetReviewHistoryDeps, diary::get_review_history,
@@ -14,13 +10,7 @@ use crate::{
#[tokio::test]
async fn returns_empty_history() {
let movie = Movie::new(
None,
MovieTitle::new("Test".into()).unwrap(),
ReleaseYear::new(2024).unwrap(),
None,
None,
);
let movie = test_movie_with_year("Test", 2024);
let movie_id = movie.id().value();
let diary = domain::testing::FakeDiaryQuery::new();

View File

@@ -1,13 +1,14 @@
use crate::test_support::register_command;
use domain::testing::{test_movie_with_year, test_review};
use std::sync::Arc;
use uuid::Uuid;
use domain::errors::DomainError;
use domain::models::{DiaryEntry, Movie, Review, UserRole, collections::Paginated};
use domain::models::{DiaryEntry, collections::Paginated};
use domain::testing::FakeDiaryQuery;
use domain::value_objects::{Email, MovieTitle, Rating, ReleaseYear, UserId};
use domain::value_objects::{Email, UserId};
use crate::auth::commands::RegisterCommand;
use crate::auth::deps::RegisterDeps;
use crate::auth::register;
use crate::diary::deps::GetUserFeedDeps;
@@ -20,17 +21,9 @@ async fn setup_user(b: &TestContextBuilder, email: &str, username: &str) {
password_hasher: b.password_hasher.clone(),
config: b.config.clone(),
};
register::execute(
&deps,
RegisterCommand {
email: email.into(),
username: username.into(),
password: "password123".into(),
role: UserRole::Standard,
},
)
.await
.unwrap();
register::execute(&deps, register_command(email, username))
.await
.unwrap();
}
#[tokio::test]
@@ -43,23 +36,8 @@ async fn user_feed_carries_author_and_entries() {
let uid = user.id().value();
let diary = FakeDiaryQuery::new();
let movie = Movie::new(
None,
MovieTitle::new("Feed Movie".into()).unwrap(),
ReleaseYear::new(2020).unwrap(),
None,
None,
);
let review = Review::new(
movie.id().clone(),
UserId::from_uuid(uid),
Rating::new(5).unwrap(),
None,
chrono::Utc::now().naive_utc(),
None,
false,
)
.unwrap();
let movie = test_movie_with_year("Feed Movie", 2020);
let review = test_review(movie.id(), &UserId::from_uuid(uid), 5);
diary.set_diary_page(Paginated {
items: vec![DiaryEntry::new(movie, review)],
total_count: 1,

View File

@@ -1,20 +1,14 @@
use crate::test_support::{log_review_command, log_review_command_manual};
use domain::testing::test_movie_with_year;
use std::sync::Arc;
use chrono::Utc;
use domain::{
models::Movie,
ports::MovieCommand,
value_objects::{MovieTitle, ReleaseYear},
};
use domain::ports::MovieCommand;
use domain::testing::{InMemoryMovieRepository, InMemoryReviewRepository, NoopEventPublisher};
use crate::{
diary::commands::{LogReviewCommand, MovieInput},
diary::log_review,
diary::review_logger::DefaultReviewLogger,
test_helpers::TestContextBuilder,
diary::commands::LogReviewCommand, diary::log_review,
diary::review_logger::DefaultReviewLogger, test_helpers::TestContextBuilder,
};
fn build_logger(
@@ -32,26 +26,6 @@ fn build_logger(
))
}
fn movie_input_manual(title: &str, year: u16) -> MovieInput {
MovieInput {
movie_id: None,
external_metadata_id: None,
manual_title: Some(title.to_string()),
manual_release_year: Some(year),
manual_director: None,
}
}
fn movie_input_by_id(id: uuid::Uuid) -> MovieInput {
MovieInput {
movie_id: Some(id),
external_metadata_id: None,
manual_title: None,
manual_release_year: None,
manual_director: None,
}
}
#[tokio::test]
async fn test_log_review_creates_movie_and_review() {
let movies = InMemoryMovieRepository::new();
@@ -60,15 +34,7 @@ async fn test_log_review_creates_movie_and_review() {
let logger = build_logger(&movies, &reviews, &events);
let user_id = uuid::Uuid::new_v4();
let cmd = LogReviewCommand {
user_id,
input: movie_input_manual("Blade Runner", 1982),
rating: 4,
comment: None,
watched_at: Utc::now().naive_utc(),
watch_medium: None,
spoilers: false,
};
let cmd = log_review_command_manual(user_id, "Blade Runner", 1982);
log_review::execute(&logger, cmd).await.unwrap();
@@ -81,13 +47,7 @@ async fn test_log_review_reuses_existing_movie() {
let movies = InMemoryMovieRepository::new();
let reviews = InMemoryReviewRepository::new();
let existing_movie = Movie::new(
None,
MovieTitle::new("Alien".into()).unwrap(),
ReleaseYear::new(1979).unwrap(),
None,
None,
);
let existing_movie = test_movie_with_year("Alien", 1979);
let movie_uuid = existing_movie.id().value();
movies.upsert_movie(&existing_movie).await.unwrap();
@@ -95,13 +55,8 @@ async fn test_log_review_reuses_existing_movie() {
let logger = build_logger(&movies, &reviews, &events);
let cmd = LogReviewCommand {
user_id: uuid::Uuid::new_v4(),
input: movie_input_by_id(movie_uuid),
rating: 5,
comment: None,
watched_at: Utc::now().naive_utc(),
watch_medium: None,
spoilers: false,
..log_review_command(uuid::Uuid::new_v4(), movie_uuid)
};
log_review::execute(&logger, cmd).await.unwrap();
@@ -118,13 +73,8 @@ async fn test_log_review_with_invalid_rating_fails() {
let logger = build_logger(&movies, &reviews, &events);
let cmd = LogReviewCommand {
user_id: uuid::Uuid::new_v4(),
input: movie_input_manual("Some Film", 2000),
rating: 6,
comment: None,
watched_at: Utc::now().naive_utc(),
watch_medium: None,
spoilers: false,
..log_review_command_manual(uuid::Uuid::new_v4(), "Some Film", 2000)
};
let result = log_review::execute(&logger, cmd).await;
assert!(result.is_err(), "rating > 5 should fail");

View File

@@ -1,5 +1,6 @@
use super::*;
use crate::diary::commands::MovieInput;
use domain::testing::test_movie_with_year;
use domain::{
errors::DomainError,
models::{MetadataSearchCriteria, Movie},
@@ -17,16 +18,6 @@ fn make_input(ext_id: Option<&str>, title: Option<&str>, year: Option<u16>) -> M
}
}
fn make_movie() -> Movie {
Movie::new(
None,
MovieTitle::new("Inception".to_string()).unwrap(),
ReleaseYear::new(2010).unwrap(),
None,
None,
)
}
struct RepoWithExternalMovie(Movie);
struct RepoEmpty;
struct RepoWithTitleMatch(Movie);
@@ -213,7 +204,7 @@ fn external_id_strategy_cannot_handle_cmd_without_id() {
#[tokio::test]
async fn external_id_strategy_returns_cached_movie() {
let movie = make_movie();
let movie = test_movie_with_year("Inception", 2010);
let repo = RepoWithExternalMovie(movie.clone());
let meta = MetaErrors;
let deps = MovieResolverDeps {
@@ -227,7 +218,7 @@ async fn external_id_strategy_returns_cached_movie() {
#[tokio::test]
async fn external_id_strategy_fetches_from_metadata_when_not_cached() {
let movie = make_movie();
let movie = test_movie_with_year("Inception", 2010);
let repo = RepoEmpty;
let meta = MetaReturnsMovie(movie);
let deps = MovieResolverDeps {
@@ -268,7 +259,7 @@ fn title_strategy_cannot_handle_cmd_without_title() {
#[tokio::test]
async fn title_strategy_fetches_from_metadata() {
let movie = make_movie();
let movie = test_movie_with_year("Inception", 2010);
let repo = RepoEmpty;
let meta = MetaReturnsMovie(movie);
let deps = MovieResolverDeps {
@@ -309,7 +300,7 @@ fn manual_strategy_cannot_handle_cmd_without_title() {
#[tokio::test]
async fn manual_strategy_returns_existing_movie() {
let movie = make_movie();
let movie = test_movie_with_year("Inception", 2010);
let repo = RepoWithTitleMatch(movie.clone());
let meta = MetaErrors;
let deps = MovieResolverDeps {
@@ -365,7 +356,7 @@ async fn resolver_returns_error_when_no_strategy_matches() {
#[tokio::test]
async fn resolver_uses_cached_movie_when_external_id_matches() {
let movie = make_movie();
let movie = test_movie_with_year("Inception", 2010);
let repo = RepoWithExternalMovie(movie.clone());
let meta = MetaErrors;
let deps = MovieResolverDeps {

View File

@@ -1,7 +1,8 @@
use crate::test_support::{log_review_command, log_review_command_manual};
use domain::testing::{test_movie_with_external_id, test_movie_with_year};
use std::sync::Arc;
use async_trait::async_trait;
use chrono::Utc;
use domain::{
errors::DomainError,
models::WatchlistEntry,
@@ -11,11 +12,11 @@ use domain::{
FakeMetadataClient, InMemoryMovieRepository, InMemoryReviewRepository,
InMemoryWatchlistRepository, NoopEventPublisher,
},
value_objects::{ExternalMetadataId, MovieId, MovieTitle, PosterUrl, ReleaseYear, UserId},
value_objects::{ExternalMetadataId, MovieId, PosterUrl, UserId},
};
use uuid::Uuid;
use crate::diary::commands::{LogReviewCommand, MovieInput};
use crate::diary::commands::LogReviewCommand;
use crate::diary::review_logger::DefaultReviewLogger;
use crate::ports::ReviewLogger;
@@ -44,21 +45,7 @@ async fn logs_review_with_manual_movie() {
let logger = make_logger(&movies, &reviews, &watchlist, &events);
let uid = Uuid::new_v4();
let cmd = LogReviewCommand {
user_id: uid,
input: MovieInput {
movie_id: None,
external_metadata_id: None,
manual_title: Some("Test Film".into()),
manual_release_year: Some(2024),
manual_director: None,
},
rating: 4,
comment: None,
watched_at: Utc::now().naive_utc(),
watch_medium: None,
spoilers: false,
};
let cmd = log_review_command_manual(uid, "Test Film", 2024);
logger.log_review(cmd).await.unwrap();
@@ -78,13 +65,7 @@ async fn removes_from_watchlist_on_review() {
let user_id = UserId::from_uuid(uid);
// Create and store movie
let movie = Movie::new(
None,
MovieTitle::new("Watchlisted Film".into()).unwrap(),
ReleaseYear::new(2024).unwrap(),
None,
None,
);
let movie = test_movie_with_year("Watchlisted Film", 2024);
let movie_id = movie.id().value();
movies.upsert_movie(&movie).await.unwrap();
@@ -95,19 +76,8 @@ async fn removes_from_watchlist_on_review() {
// Log review for same movie
let cmd = LogReviewCommand {
user_id: uid,
input: MovieInput {
movie_id: Some(movie_id),
external_metadata_id: None,
manual_title: None,
manual_release_year: None,
manual_director: None,
},
rating: 5,
comment: None,
watched_at: Utc::now().naive_utc(),
watch_medium: None,
spoilers: false,
..log_review_command(uid, movie_id)
};
logger.log_review(cmd).await.unwrap();
@@ -123,30 +93,13 @@ async fn logs_review_with_existing_movie_by_id() {
let events = NoopEventPublisher::new();
let logger = make_logger(&movies, &reviews, &watchlist, &events);
let movie = Movie::new(
None,
MovieTitle::new("Existing Film".into()).unwrap(),
ReleaseYear::new(2020).unwrap(),
None,
None,
);
let movie = test_movie_with_year("Existing Film", 2020);
let movie_uuid = movie.id().value();
movies.upsert_movie(&movie).await.unwrap();
let cmd = LogReviewCommand {
user_id: Uuid::new_v4(),
input: MovieInput {
movie_id: Some(movie_uuid),
external_metadata_id: None,
manual_title: None,
manual_release_year: None,
manual_director: None,
},
rating: 3,
comment: None,
watched_at: Utc::now().naive_utc(),
watch_medium: None,
spoilers: false,
..log_review_command(Uuid::new_v4(), movie_uuid)
};
logger.log_review(cmd).await.unwrap();
@@ -163,21 +116,7 @@ async fn existing_movie_not_found_returns_error() {
let events = NoopEventPublisher::new();
let logger = make_logger(&movies, &reviews, &watchlist, &events);
let cmd = LogReviewCommand {
user_id: Uuid::new_v4(),
input: MovieInput {
movie_id: Some(Uuid::new_v4()),
external_metadata_id: None,
manual_title: None,
manual_release_year: None,
manual_director: None,
},
rating: 4,
comment: None,
watched_at: Utc::now().naive_utc(),
watch_medium: None,
spoilers: false,
};
let cmd = log_review_command(Uuid::new_v4(), Uuid::new_v4());
assert!(logger.log_review(cmd).await.is_err());
}
@@ -191,19 +130,8 @@ async fn invalid_rating_returns_error() {
let logger = make_logger(&movies, &reviews, &watchlist, &events);
let cmd = LogReviewCommand {
user_id: Uuid::new_v4(),
input: MovieInput {
movie_id: None,
external_metadata_id: None,
manual_title: Some("Film".into()),
manual_release_year: Some(2024),
manual_director: None,
},
rating: 6,
comment: None,
watched_at: Utc::now().naive_utc(),
watch_medium: None,
spoilers: false,
..log_review_command_manual(Uuid::new_v4(), "Film", 2024)
};
let result = logger.log_review(cmd).await;
@@ -221,21 +149,7 @@ async fn watchlist_not_present_does_not_publish_removed() {
let events = NoopEventPublisher::new();
let logger = make_logger(&movies, &reviews, &watchlist, &events);
let cmd = LogReviewCommand {
user_id: Uuid::new_v4(),
input: MovieInput {
movie_id: None,
external_metadata_id: None,
manual_title: Some("No Watchlist Film".into()),
manual_release_year: Some(2024),
manual_director: None,
},
rating: 4,
comment: None,
watched_at: Utc::now().naive_utc(),
watch_medium: None,
spoilers: false,
};
let cmd = log_review_command_manual(Uuid::new_v4(), "No Watchlist Film", 2024);
logger.log_review(cmd).await.unwrap();
@@ -256,12 +170,10 @@ impl MetadataClient for MetadataClientWithExternalId {
&self,
_criteria: &MetadataSearchCriteria,
) -> Result<Movie, DomainError> {
Ok(Movie::new(
Some(ExternalMetadataId::new("tmdb:99999".into()).unwrap()),
MovieTitle::new("Discovered Film".into()).unwrap(),
ReleaseYear::new(2024).unwrap(),
None,
None,
Ok(test_movie_with_external_id(
"Discovered Film",
2024,
ExternalMetadataId::new("tmdb:99999".into()).unwrap(),
))
}
@@ -290,19 +202,8 @@ async fn publishes_movie_discovered_for_new_movie_with_external_id() {
);
let cmd = LogReviewCommand {
user_id: Uuid::new_v4(),
input: MovieInput {
movie_id: None,
external_metadata_id: None,
manual_title: Some("Discovered Film".into()),
manual_release_year: Some(2024),
manual_director: None,
},
rating: 5,
comment: None,
watched_at: Utc::now().naive_utc(),
watch_medium: None,
spoilers: false,
..log_review_command_manual(Uuid::new_v4(), "Discovered Film", 2024)
};
logger.log_review(cmd).await.unwrap();

View File

@@ -1,11 +1,12 @@
use crate::test_support::create_goal_command;
use std::sync::Arc;
use domain::events::DomainEvent;
use domain::testing::{FakeStatsRepository, InMemoryGoalRepository, NoopEventPublisher};
use uuid::Uuid;
use crate::goals::create;
use crate::goals::deps::GoalCommandDeps;
use crate::goals::{commands::CreateGoalCommand, create};
use crate::test_helpers::TestContextBuilder;
#[tokio::test]
@@ -20,16 +21,9 @@ async fn creates_goal_and_returns_progress() {
event_publisher: Arc::clone(&events) as _,
};
let result = create::execute(
&deps,
CreateGoalCommand {
user_id: Uuid::nil(),
year: 2025,
target_count: 50,
},
)
.await
.unwrap();
let result = create::execute(&deps, create_goal_command(2025, 50))
.await
.unwrap();
assert_eq!(result.goal.year(), 2025);
assert_eq!(result.goal.target_count(), 50);
@@ -50,16 +44,9 @@ async fn creates_goal_with_review_count() {
event_publisher: Arc::clone(&events) as _,
};
let result = create::execute(
&deps,
CreateGoalCommand {
user_id: Uuid::nil(),
year: 2025,
target_count: 50,
},
)
.await
.unwrap();
let result = create::execute(&deps, create_goal_command(2025, 50))
.await
.unwrap();
assert_eq!(result.current_count, 5);
assert_eq!(goals.count(), 1);
@@ -76,16 +63,9 @@ async fn emits_goal_created_event() {
event_publisher: Arc::clone(&events) as _,
};
create::execute(
&deps,
CreateGoalCommand {
user_id: Uuid::nil(),
year: 2025,
target_count: 10,
},
)
.await
.unwrap();
create::execute(&deps, create_goal_command(2025, 10))
.await
.unwrap();
let published = events.published();
assert!(
@@ -104,23 +84,11 @@ async fn rejects_duplicate_year() {
stats: b.stats_repo.clone(),
event_publisher: b.event_publisher.clone(),
};
let cmd = CreateGoalCommand {
user_id: Uuid::nil(),
year: 2025,
target_count: 10,
};
let cmd = create_goal_command(2025, 10);
create::execute(&deps, cmd).await.unwrap();
let result = create::execute(
&deps,
CreateGoalCommand {
user_id: Uuid::nil(),
year: 2025,
target_count: 20,
},
)
.await;
let result = create::execute(&deps, create_goal_command(2025, 20)).await;
assert!(result.is_err());
}
@@ -134,15 +102,7 @@ async fn rejects_year_before_2020() {
stats: b.stats_repo.clone(),
event_publisher: b.event_publisher.clone(),
};
let result = create::execute(
&deps,
CreateGoalCommand {
user_id: Uuid::nil(),
year: 2019,
target_count: 10,
},
)
.await;
let result = create::execute(&deps, create_goal_command(2019, 10)).await;
assert!(result.is_err());
}
@@ -156,15 +116,7 @@ async fn rejects_zero_target() {
stats: b.stats_repo.clone(),
event_publisher: b.event_publisher.clone(),
};
let result = create::execute(
&deps,
CreateGoalCommand {
user_id: Uuid::nil(),
year: 2025,
target_count: 0,
},
)
.await;
let result = create::execute(&deps, create_goal_command(2025, 0)).await;
assert!(result.is_err());
}

View File

@@ -1,13 +1,11 @@
use crate::test_support::create_goal_command;
use std::sync::Arc;
use domain::testing::{FakeStatsRepository, InMemoryGoalRepository, NoopEventPublisher};
use uuid::Uuid;
use crate::goals::deps::GoalCommandDeps;
use crate::goals::{
commands::{CreateGoalCommand, DeleteGoalCommand},
create, delete,
};
use crate::goals::{commands::DeleteGoalCommand, create, delete};
use crate::test_helpers::TestContextBuilder;
#[tokio::test]
@@ -22,16 +20,9 @@ async fn deletes_existing_goal() {
event_publisher: Arc::clone(&events) as _,
};
create::execute(
&deps,
CreateGoalCommand {
user_id: Uuid::nil(),
year: 2025,
target_count: 10,
},
)
.await
.unwrap();
create::execute(&deps, create_goal_command(2025, 10))
.await
.unwrap();
assert_eq!(goals.count(), 1);
delete::execute(

View File

@@ -1,7 +1,8 @@
use crate::test_support::create_goal_command;
use uuid::Uuid;
use crate::goals::deps::{GoalCommandDeps, GoalQueryDeps};
use crate::goals::{commands::CreateGoalCommand, create, get, queries::GetGoalQuery};
use crate::goals::{create, get, queries::GetGoalQuery};
use crate::test_helpers::TestContextBuilder;
#[tokio::test]
@@ -18,16 +19,9 @@ async fn returns_goal_when_exists() {
stats: b.stats_repo.clone(),
};
create::execute(
&cmd_deps,
CreateGoalCommand {
user_id: Uuid::nil(),
year: 2025,
target_count: 50,
},
)
.await
.unwrap();
create::execute(&cmd_deps, create_goal_command(2025, 50))
.await
.unwrap();
let result = get::execute(
&query_deps,

View File

@@ -1,7 +1,8 @@
use crate::test_support::create_goal_command;
use uuid::Uuid;
use crate::goals::deps::{GoalCommandDeps, GoalQueryDeps};
use crate::goals::{commands::CreateGoalCommand, create, list, queries::ListGoalsQuery};
use crate::goals::{create, list, queries::ListGoalsQuery};
use crate::test_helpers::TestContextBuilder;
#[tokio::test]
@@ -38,16 +39,9 @@ async fn returns_all_goals_for_user() {
};
for year in [2023, 2024, 2025] {
create::execute(
&cmd_deps,
CreateGoalCommand {
user_id: Uuid::nil(),
year,
target_count: 10,
},
)
.await
.unwrap();
create::execute(&cmd_deps, create_goal_command(year, 10))
.await
.unwrap();
}
let result = list::execute(

View File

@@ -1,10 +1,7 @@
use uuid::Uuid;
use crate::test_support::{create_goal_command, update_goal_command};
use crate::goals::deps::GoalCommandDeps;
use crate::goals::{
commands::{CreateGoalCommand, UpdateGoalCommand},
create, update,
};
use crate::goals::{create, update};
use crate::test_helpers::TestContextBuilder;
#[tokio::test]
@@ -17,27 +14,13 @@ async fn updates_target_count() {
event_publisher: b.event_publisher.clone(),
};
create::execute(
&deps,
CreateGoalCommand {
user_id: Uuid::nil(),
year: 2025,
target_count: 10,
},
)
.await
.unwrap();
create::execute(&deps, create_goal_command(2025, 10))
.await
.unwrap();
let result = update::execute(
&deps,
UpdateGoalCommand {
user_id: Uuid::nil(),
year: 2025,
target_count: 100,
},
)
.await
.unwrap();
let result = update::execute(&deps, update_goal_command(2025, 100))
.await
.unwrap();
assert_eq!(result.goal.target_count(), 100);
}
@@ -51,15 +34,7 @@ async fn fails_when_goal_not_found() {
stats: b.stats_repo.clone(),
event_publisher: b.event_publisher.clone(),
};
let result = update::execute(
&deps,
UpdateGoalCommand {
user_id: Uuid::nil(),
year: 2025,
target_count: 10,
},
)
.await;
let result = update::execute(&deps, update_goal_command(2025, 10)).await;
assert!(result.is_err());
}
@@ -74,26 +49,11 @@ async fn rejects_zero_target() {
event_publisher: b.event_publisher.clone(),
};
create::execute(
&deps,
CreateGoalCommand {
user_id: Uuid::nil(),
year: 2025,
target_count: 10,
},
)
.await
.unwrap();
create::execute(&deps, create_goal_command(2025, 10))
.await
.unwrap();
let result = update::execute(
&deps,
UpdateGoalCommand {
user_id: Uuid::nil(),
year: 2025,
target_count: 0,
},
)
.await;
let result = update::execute(&deps, update_goal_command(2025, 0)).await;
assert!(result.is_err());
}

View File

@@ -1,23 +1,21 @@
use crate::test_support::{apply_import_mapping_command, create_import_session_command};
use domain::testing::{test_movie_with_external_id, test_movie_with_year};
use std::sync::Arc;
use uuid::Uuid;
use domain::{
models::{
AnnotatedRow, Movie,
AnnotatedRow,
import::{ImportRow, ParsedFile, RowResult},
},
ports::{DocumentParser, MovieCommand},
testing::{InMemoryImportSessionRepository, InMemoryMovieRepository},
value_objects::{ExternalMetadataId, MovieTitle, ReleaseYear},
value_objects::ExternalMetadataId,
};
use crate::import::deps::{ApplyMappingDeps, CreateSessionDeps};
use crate::import::{
apply_mapping,
commands::{ApplyImportMappingCommand, CreateImportSessionCommand},
create_session,
};
use crate::import::{apply_mapping, create_session};
use crate::test_helpers::TestContextBuilder;
#[tokio::test]
@@ -33,11 +31,7 @@ async fn applies_mapping_to_session() {
let session = create_session::execute(
&create_deps,
CreateImportSessionCommand {
user_id,
bytes: b"title\nTest".to_vec(),
format: domain::models::FileFormat::Csv,
},
create_import_session_command(user_id, b"title\nTest"),
)
.await
.unwrap();
@@ -50,11 +44,7 @@ async fn applies_mapping_to_session() {
let rows = apply_mapping::execute(
&mapping_deps,
ApplyImportMappingCommand {
user_id,
session_id: session.session_id.value(),
mappings: vec![],
},
apply_import_mapping_command(user_id, session.session_id.value()),
)
.await
.unwrap();
@@ -75,11 +65,7 @@ async fn fails_when_session_not_found() {
let result = apply_mapping::execute(
&deps,
ApplyImportMappingCommand {
user_id: Uuid::new_v4(),
session_id: Uuid::new_v4(),
mappings: vec![],
},
apply_import_mapping_command(Uuid::new_v4(), Uuid::new_v4()),
)
.await;
@@ -123,13 +109,7 @@ async fn marks_duplicate_by_external_id() {
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,
);
let movie = test_movie_with_external_id("Known Movie", 2020, ext_id);
movies.upsert_movie(&movie).await.unwrap();
let parser = Arc::new(DuplicateTestParser {
@@ -150,11 +130,7 @@ async fn marks_duplicate_by_external_id() {
let session = create_session::execute(
&create_deps,
CreateImportSessionCommand {
user_id,
bytes: b"title\nKnown Movie".to_vec(),
format: domain::models::FileFormat::Csv,
},
create_import_session_command(user_id, b"title\nKnown Movie"),
)
.await
.unwrap();
@@ -167,11 +143,7 @@ async fn marks_duplicate_by_external_id() {
let rows = apply_mapping::execute(
&mapping_deps,
ApplyImportMappingCommand {
user_id,
session_id: session.session_id.value(),
mappings: vec![],
},
apply_import_mapping_command(user_id, session.session_id.value()),
)
.await
.unwrap();
@@ -185,13 +157,7 @@ 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,
);
let movie = test_movie_with_year("Duplicate Film", 2022);
movies.upsert_movie(&movie).await.unwrap();
let parser = Arc::new(DuplicateTestParser {
@@ -211,11 +177,7 @@ async fn marks_duplicate_by_title_and_year() {
let session = create_session::execute(
&create_deps,
CreateImportSessionCommand {
user_id,
bytes: b"title\nDuplicate Film".to_vec(),
format: domain::models::FileFormat::Csv,
},
create_import_session_command(user_id, b"title\nDuplicate Film"),
)
.await
.unwrap();
@@ -228,11 +190,7 @@ async fn marks_duplicate_by_title_and_year() {
let rows = apply_mapping::execute(
&mapping_deps,
ApplyImportMappingCommand {
user_id,
session_id: session.session_id.value(),
mappings: vec![],
},
apply_import_mapping_command(user_id, session.session_id.value()),
)
.await
.unwrap();

View File

@@ -1,3 +1,4 @@
use crate::test_support::apply_import_profile_command;
use std::sync::Arc;
use chrono::Utc;
@@ -7,8 +8,8 @@ use domain::testing::{InMemoryImportProfileRepository, InMemoryImportSessionRepo
use domain::value_objects::{ImportProfileId, UserId};
use uuid::Uuid;
use crate::import::apply_profile;
use crate::import::deps::ApplyProfileDeps;
use crate::import::{apply_profile, commands::ApplyImportProfileCommand};
#[tokio::test]
async fn fails_when_profile_not_found() {
@@ -22,11 +23,7 @@ async fn fails_when_profile_not_found() {
let result = apply_profile::execute(
&deps,
ApplyImportProfileCommand {
user_id: Uuid::new_v4(),
session_id: Uuid::new_v4(),
profile_id: Uuid::new_v4(),
},
apply_import_profile_command(Uuid::new_v4(), Uuid::new_v4(), Uuid::new_v4()),
)
.await;
@@ -56,11 +53,7 @@ async fn fails_when_session_not_found() {
let result = apply_profile::execute(
&deps,
ApplyImportProfileCommand {
user_id,
session_id: Uuid::new_v4(),
profile_id: profile_id.value(),
},
apply_import_profile_command(user_id, Uuid::new_v4(), profile_id.value()),
)
.await;
@@ -98,11 +91,7 @@ async fn applies_profile_mappings_to_session() {
apply_profile::execute(
&deps,
ApplyImportProfileCommand {
user_id,
session_id: session_id.value(),
profile_id: profile_id.value(),
},
apply_import_profile_command(user_id, session_id.value(), profile_id.value()),
)
.await
.unwrap();

View File

@@ -1,19 +1,17 @@
use crate::test_support::create_import_session_command;
use std::sync::Arc;
use chrono::Utc;
use uuid::Uuid;
use domain::models::import::{DomainField, Transform};
use domain::models::{FieldMapping, FileFormat, ImportProfile};
use domain::models::{FieldMapping, 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::import::{apply_profile_and_map, commands::ApplyProfileAndMapCommand, create_session};
use crate::test_helpers::TestContextBuilder;
#[tokio::test]
@@ -69,11 +67,7 @@ async fn applies_profile_then_regenerates_mapping() {
};
let created = create_session::execute(
&create_deps,
CreateImportSessionCommand {
user_id,
bytes: b"title\nTest".to_vec(),
format: FileFormat::Csv,
},
create_import_session_command(user_id, b"title\nTest"),
)
.await
.unwrap();

View File

@@ -1,11 +1,12 @@
use crate::test_support::create_import_session_command;
use std::sync::Arc;
use uuid::Uuid;
use domain::testing::InMemoryImportSessionRepository;
use crate::import::create_session;
use crate::import::deps::CreateSessionDeps;
use crate::import::{commands::CreateImportSessionCommand, create_session};
use crate::test_helpers::TestContextBuilder;
#[tokio::test]
@@ -20,11 +21,7 @@ async fn creates_session_with_parsed_file() {
let result = create_session::execute(
&deps,
CreateImportSessionCommand {
user_id: Uuid::new_v4(),
bytes: b"col1\nval1".to_vec(),
format: domain::models::FileFormat::Csv,
},
create_import_session_command(Uuid::new_v4(), b"col1\nval1"),
)
.await
.unwrap();

View File

@@ -1,3 +1,4 @@
use crate::test_support::execute_import_command;
use std::sync::Arc;
use domain::models::{AnnotatedRow, ImportSession, import::RowResult};
@@ -59,9 +60,8 @@ async fn imports_confirmed_rows() {
let result = execute::execute(
&deps,
ExecuteImportCommand {
user_id: uid,
session_id: sid.value(),
confirmed_indices: vec![0, 1],
..execute_import_command(uid, sid.value())
},
)
.await
@@ -86,16 +86,9 @@ async fn skips_unconfirmed_rows() {
review_logger: Arc::new(NoopReviewLogger),
};
let result = execute::execute(
&deps,
ExecuteImportCommand {
user_id: uid,
session_id: sid.value(),
confirmed_indices: vec![0],
},
)
.await
.unwrap();
let result = execute::execute(&deps, execute_import_command(uid, sid.value()))
.await
.unwrap();
assert_eq!(result.imported, 1);
assert_eq!(result.skipped_duplicates, 1);
@@ -113,9 +106,8 @@ async fn fails_when_session_not_found() {
let result = execute::execute(
&deps,
ExecuteImportCommand {
user_id: Uuid::new_v4(),
session_id: Uuid::new_v4(),
confirmed_indices: vec![],
..execute_import_command(Uuid::new_v4(), Uuid::new_v4())
},
)
.await;
@@ -149,16 +141,9 @@ async fn handles_datetime_format() {
review_logger: Arc::new(NoopReviewLogger),
};
let result = execute::execute(
&deps,
ExecuteImportCommand {
user_id: uid,
session_id: sid.value(),
confirmed_indices: vec![0],
},
)
.await
.unwrap();
let result = execute::execute(&deps, execute_import_command(uid, sid.value()))
.await
.unwrap();
assert_eq!(result.imported, 1);
assert!(result.failed.is_empty());
@@ -190,16 +175,9 @@ async fn fails_on_invalid_rating() {
review_logger: Arc::new(NoopReviewLogger),
};
let result = execute::execute(
&deps,
ExecuteImportCommand {
user_id: uid,
session_id: sid.value(),
confirmed_indices: vec![0],
},
)
.await
.unwrap();
let result = execute::execute(&deps, execute_import_command(uid, sid.value()))
.await
.unwrap();
assert_eq!(result.imported, 0);
assert_eq!(result.failed.len(), 1);
@@ -231,16 +209,9 @@ async fn fails_on_missing_watched_at() {
review_logger: Arc::new(NoopReviewLogger),
};
let result = execute::execute(
&deps,
ExecuteImportCommand {
user_id: uid,
session_id: sid.value(),
confirmed_indices: vec![0],
},
)
.await
.unwrap();
let result = execute::execute(&deps, execute_import_command(uid, sid.value()))
.await
.unwrap();
assert_eq!(result.imported, 0);
assert_eq!(result.failed.len(), 1);
@@ -272,16 +243,9 @@ async fn imports_row_with_external_metadata_id() {
review_logger: Arc::new(NoopReviewLogger),
};
let result = execute::execute(
&deps,
ExecuteImportCommand {
user_id: uid,
session_id: sid.value(),
confirmed_indices: vec![0],
},
)
.await
.unwrap();
let result = execute::execute(&deps, execute_import_command(uid, sid.value()))
.await
.unwrap();
assert_eq!(result.imported, 1);
assert!(result.failed.is_empty());
@@ -313,16 +277,9 @@ async fn imports_row_with_director_and_comment() {
review_logger: Arc::new(NoopReviewLogger),
};
let result = execute::execute(
&deps,
ExecuteImportCommand {
user_id: uid,
session_id: sid.value(),
confirmed_indices: vec![0],
},
)
.await
.unwrap();
let result = execute::execute(&deps, execute_import_command(uid, sid.value()))
.await
.unwrap();
assert_eq!(result.imported, 1);
assert!(result.failed.is_empty());
@@ -354,16 +311,9 @@ async fn handles_space_separated_datetime_format() {
review_logger: Arc::new(NoopReviewLogger),
};
let result = execute::execute(
&deps,
ExecuteImportCommand {
user_id: uid,
session_id: sid.value(),
confirmed_indices: vec![0],
},
)
.await
.unwrap();
let result = execute::execute(&deps, execute_import_command(uid, sid.value()))
.await
.unwrap();
assert_eq!(result.imported, 1);
assert!(result.failed.is_empty());
@@ -390,16 +340,9 @@ async fn reports_invalid_row_result_errors() {
review_logger: Arc::new(NoopReviewLogger),
};
let result = execute::execute(
&deps,
ExecuteImportCommand {
user_id: uid,
session_id: sid.value(),
confirmed_indices: vec![0],
},
)
.await
.unwrap();
let result = execute::execute(&deps, execute_import_command(uid, sid.value()))
.await
.unwrap();
assert_eq!(result.imported, 0);
assert_eq!(result.failed.len(), 1);
@@ -433,16 +376,9 @@ async fn fails_on_missing_rating() {
review_logger: Arc::new(NoopReviewLogger),
};
let result = execute::execute(
&deps,
ExecuteImportCommand {
user_id: uid,
session_id: sid.value(),
confirmed_indices: vec![0],
},
)
.await
.unwrap();
let result = execute::execute(&deps, execute_import_command(uid, sid.value()))
.await
.unwrap();
assert_eq!(result.imported, 0);
assert_eq!(result.failed.len(), 1);
@@ -475,16 +411,9 @@ async fn fails_on_unparseable_date() {
review_logger: Arc::new(NoopReviewLogger),
};
let result = execute::execute(
&deps,
ExecuteImportCommand {
user_id: uid,
session_id: sid.value(),
confirmed_indices: vec![0],
},
)
.await
.unwrap();
let result = execute::execute(&deps, execute_import_command(uid, sid.value()))
.await
.unwrap();
assert_eq!(result.imported, 0);
assert_eq!(result.failed.len(), 1);
@@ -517,16 +446,9 @@ async fn imports_row_without_release_year() {
review_logger: Arc::new(NoopReviewLogger),
};
let result = execute::execute(
&deps,
ExecuteImportCommand {
user_id: uid,
session_id: sid.value(),
confirmed_indices: vec![0],
},
)
.await
.unwrap();
let result = execute::execute(&deps, execute_import_command(uid, sid.value()))
.await
.unwrap();
assert_eq!(result.imported, 1);
assert!(result.failed.is_empty());
@@ -547,16 +469,9 @@ async fn deletes_session_after_import() {
review_logger: Arc::new(NoopReviewLogger),
};
execute::execute(
&deps,
ExecuteImportCommand {
user_id: uid,
session_id: sid.value(),
confirmed_indices: vec![0],
},
)
.await
.unwrap();
execute::execute(&deps, execute_import_command(uid, sid.value()))
.await
.unwrap();
assert_eq!(
sessions.count(),
@@ -599,9 +514,8 @@ async fn imports_more_rows_than_concurrency_limit() {
let result = execute::execute(
&deps,
ExecuteImportCommand {
user_id: uid,
session_id: sid.value(),
confirmed_indices,
..execute_import_command(uid, sid.value())
},
)
.await

View File

@@ -1,3 +1,4 @@
use crate::test_support::confirm_watch_events_command;
use std::sync::Arc;
use domain::models::{WatchEvent, WatchEventSource};
@@ -45,14 +46,7 @@ async fn confirms_watch_event_via_review_logger() {
let result = confirm::execute(
&deps(&watch_events, noop_logger()),
ConfirmWatchEventsCommand {
user_id: uid,
confirmations: vec![WatchEventConfirmation {
watch_event_id: event_id,
rating: 4,
comment: None,
}],
},
confirm_watch_events_command(uid, event_id, 4),
)
.await
.unwrap();
@@ -131,14 +125,7 @@ async fn rejects_other_users_event() {
let result = confirm::execute(
&deps(&watch_events, noop_logger()),
ConfirmWatchEventsCommand {
user_id: intruder,
confirmations: vec![WatchEventConfirmation {
watch_event_id: event_id,
rating: 3,
comment: None,
}],
},
confirm_watch_events_command(intruder, event_id, 3),
)
.await;
@@ -151,14 +138,7 @@ async fn fails_when_event_not_found() {
let result = confirm::execute(
&deps(&watch_events, noop_logger()),
ConfirmWatchEventsCommand {
user_id: Uuid::new_v4(),
confirmations: vec![WatchEventConfirmation {
watch_event_id: Uuid::new_v4(),
rating: 4,
comment: None,
}],
},
confirm_watch_events_command(Uuid::new_v4(), Uuid::new_v4(), 4),
)
.await;
@@ -211,14 +191,7 @@ async fn confirms_event_with_movie_id() {
let result = confirm::execute(
&deps(&watch_events, review_logger),
ConfirmWatchEventsCommand {
user_id: uid,
confirmations: vec![WatchEventConfirmation {
watch_event_id: event_id,
rating: 4,
comment: None,
}],
},
confirm_watch_events_command(uid, event_id, 4),
)
.await
.unwrap();
@@ -293,7 +266,6 @@ async fn confirms_multiple_events() {
let result = confirm::execute(
&deps(&watch_events, noop_logger()),
ConfirmWatchEventsCommand {
user_id: uid,
confirmations: vec![
WatchEventConfirmation {
watch_event_id: id1,
@@ -306,6 +278,7 @@ async fn confirms_multiple_events() {
comment: None,
},
],
..confirm_watch_events_command(uid, id1, 3)
},
)
.await
@@ -333,14 +306,7 @@ async fn confirms_event_without_year() {
let result = confirm::execute(
&deps(&watch_events, noop_logger()),
ConfirmWatchEventsCommand {
user_id: uid,
confirmations: vec![WatchEventConfirmation {
watch_event_id: event_id,
rating: 3,
comment: None,
}],
},
confirm_watch_events_command(uid, event_id, 3),
)
.await
.unwrap();

View File

@@ -1,3 +1,4 @@
use crate::test_support::dismiss_watch_events_command;
use std::sync::Arc;
use domain::models::{WatchEvent, WatchEventSource};
@@ -7,7 +8,7 @@ use domain::value_objects::UserId;
use uuid::Uuid;
use crate::integrations::deps::DismissWatchEventsDeps;
use crate::integrations::{commands::DismissWatchEventsCommand, dismiss};
use crate::integrations::dismiss;
fn deps(watch_events: &Arc<InMemoryWatchEventRepository>) -> DismissWatchEventsDeps {
DismissWatchEventsDeps {
@@ -22,10 +23,7 @@ async fn dismisses_empty_list_returns_zero() {
let result = dismiss::execute(
&deps(&events),
DismissWatchEventsCommand {
user_id: Uuid::new_v4(),
event_ids: vec![],
},
dismiss_watch_events_command(Uuid::new_v4(), vec![]),
)
.await
.unwrap();
@@ -39,10 +37,7 @@ async fn fails_when_event_not_found() {
let result = dismiss::execute(
&deps(&events),
DismissWatchEventsCommand {
user_id: Uuid::new_v4(),
event_ids: vec![Uuid::new_v4()],
},
dismiss_watch_events_command(Uuid::new_v4(), vec![Uuid::new_v4()]),
)
.await;
@@ -80,10 +75,7 @@ async fn dismisses_existing_events() {
let result = dismiss::execute(
&deps(&watch_events),
DismissWatchEventsCommand {
user_id: uid,
event_ids: vec![id1, id2],
},
dismiss_watch_events_command(uid, vec![id1, id2]),
)
.await
.unwrap();

View File

@@ -1,12 +1,12 @@
use crate::test_support::generate_webhook_token_command;
use std::sync::Arc;
use domain::models::WatchEventSource;
use domain::ports::WebhookTokenRepository;
use domain::testing::InMemoryWebhookTokenRepository;
use uuid::Uuid;
use crate::integrations::deps::GenerateWebhookTokenDeps;
use crate::integrations::{commands::GenerateWebhookTokenCommand, generate_token};
use crate::integrations::generate_token;
#[tokio::test]
async fn generates_token_and_saves() {
@@ -17,11 +17,7 @@ async fn generates_token_and_saves() {
&GenerateWebhookTokenDeps {
webhook_token: Arc::clone(&tokens),
},
GenerateWebhookTokenCommand {
user_id,
provider: WatchEventSource::Jellyfin,
label: None,
},
generate_webhook_token_command(user_id),
)
.await
.unwrap();

View File

@@ -1,3 +1,4 @@
use crate::test_support::generate_webhook_token_command;
use std::sync::Arc;
use domain::models::WatchEventSource;
@@ -47,11 +48,7 @@ async fn returns_tokens_after_generate() {
generate_token::execute(
&generate_deps(&tokens),
GenerateWebhookTokenCommand {
user_id,
provider: WatchEventSource::Jellyfin,
label: None,
},
generate_webhook_token_command(user_id),
)
.await
.unwrap();
@@ -59,9 +56,9 @@ async fn returns_tokens_after_generate() {
generate_token::execute(
&generate_deps(&tokens),
GenerateWebhookTokenCommand {
user_id,
provider: WatchEventSource::Plex,
label: Some("living room".into()),
..generate_webhook_token_command(user_id)
},
)
.await

View File

@@ -1,3 +1,4 @@
use crate::test_support::generate_webhook_token_command;
use std::sync::Arc;
use domain::models::WatchEventSource;
@@ -7,7 +8,7 @@ use domain::testing::{
};
use uuid::Uuid;
use crate::integrations::commands::{GenerateWebhookTokenCommand, IngestWatchEventCommand};
use crate::integrations::commands::IngestWatchEventCommand;
use crate::integrations::deps::{GenerateWebhookTokenDeps, IngestWatchEventDeps};
use crate::integrations::{generate_token, ingest};
@@ -38,11 +39,7 @@ async fn ingests_watch_event() {
&GenerateWebhookTokenDeps {
webhook_token: Arc::clone(&tokens),
},
GenerateWebhookTokenCommand {
user_id,
provider: WatchEventSource::Jellyfin,
label: None,
},
generate_webhook_token_command(user_id),
)
.await
.unwrap();

View File

@@ -1,6 +1,6 @@
use crate::test_support::generate_webhook_token_command;
use std::sync::Arc;
use domain::models::WatchEventSource;
use domain::ports::WebhookTokenRepository;
use domain::testing::InMemoryWebhookTokenRepository;
use uuid::Uuid;
@@ -9,10 +9,8 @@ use crate::integrations::deps::{
GenerateWebhookTokenDeps, GetWebhookTokensDeps, RevokeWebhookTokenDeps,
};
use crate::integrations::{
commands::{GenerateWebhookTokenCommand, RevokeWebhookTokenCommand},
generate_token, get_tokens,
queries::GetWebhookTokensQuery,
revoke_token,
commands::RevokeWebhookTokenCommand, generate_token, get_tokens,
queries::GetWebhookTokensQuery, revoke_token,
};
#[tokio::test]
@@ -25,11 +23,7 @@ async fn revokes_existing_token() {
&GenerateWebhookTokenDeps {
webhook_token: Arc::clone(&tokens),
},
GenerateWebhookTokenCommand {
user_id,
provider: WatchEventSource::Jellyfin,
label: None,
},
generate_webhook_token_command(user_id),
)
.await
.unwrap();

View File

@@ -22,6 +22,9 @@ pub mod wrapup;
#[cfg(test)]
pub mod test_helpers;
#[cfg(test)]
mod test_support;
#[cfg(test)]
#[path = "tests/services.rs"]
mod services_tests;

View File

@@ -1,14 +1,15 @@
use domain::testing::test_movie_with_year;
use std::sync::Arc;
use chrono::Utc;
use domain::{
models::{Movie, MovieProfile},
models::MovieProfile,
ports::MovieCommand,
testing::{
FakeSearchCommand, InMemoryMovieProfileRepository, InMemoryMovieRepository,
PanicPersonCommand,
},
value_objects::{MovieId, MovieTitle, ReleaseYear},
value_objects::MovieId,
};
use crate::movies::{commands::EnrichMovieCommand, deps::EnrichMovieDeps, enrich_movie};
@@ -19,13 +20,7 @@ async fn stores_profile_and_indexes() {
let profile_repo = InMemoryMovieProfileRepository::new();
// PanicPersonCommand is safe here — empty cast/crew means upsert_batch is never called
let movie = Movie::new(
None,
MovieTitle::new("Test".into()).unwrap(),
ReleaseYear::new(2024).unwrap(),
None,
None,
);
let movie = test_movie_with_year("Test", 2024);
let movie_id = MovieId::from_uuid(movie.id().value());
movie_repo.upsert_movie(&movie).await.unwrap();
@@ -99,13 +94,7 @@ async fn extracts_and_indexes_persons() {
let movie_repo = InMemoryMovieRepository::new();
let profile_repo = InMemoryMovieProfileRepository::new();
let movie = Movie::new(
None,
MovieTitle::new("Cast Movie".into()).unwrap(),
ReleaseYear::new(2024).unwrap(),
None,
None,
);
let movie = test_movie_with_year("Cast Movie", 2024);
let movie_id = MovieId::from_uuid(movie.id().value());
movie_repo.upsert_movie(&movie).await.unwrap();

View File

@@ -1,3 +1,5 @@
use crate::test_support::sync_poster_command;
use domain::testing::{test_movie_with_external_id, test_movie_with_year};
use std::sync::Arc;
use uuid::Uuid;
@@ -10,13 +12,10 @@ use domain::{
FakeSearchCommand, InMemoryMovieProfileRepository, InMemoryMovieRepository,
NoopEventPublisher, NoopObjectStorage,
},
value_objects::{ExternalMetadataId, MovieTitle, PosterUrl, ReleaseYear},
value_objects::{ExternalMetadataId, PosterUrl},
};
use crate::{
diary::commands::SyncPosterCommand,
movies::{deps::SyncPosterDeps, sync_poster},
};
use crate::movies::{deps::SyncPosterDeps, sync_poster};
fn default_deps() -> SyncPosterDeps {
SyncPosterDeps {
@@ -35,13 +34,7 @@ fn default_deps() -> SyncPosterDeps {
async fn fails_when_movie_not_found() {
let deps = default_deps();
let result = sync_poster::execute(
&deps,
SyncPosterCommand {
movie_id: Uuid::new_v4(),
},
)
.await;
let result = sync_poster::execute(&deps, sync_poster_command(Uuid::new_v4())).await;
assert!(result.is_err());
}
@@ -49,13 +42,7 @@ async fn fails_when_movie_not_found() {
#[tokio::test]
async fn fails_when_no_external_id() {
let movies = InMemoryMovieRepository::new();
let movie = Movie::new(
None,
MovieTitle::new("Test".into()).unwrap(),
ReleaseYear::new(2024).unwrap(),
None,
None,
);
let movie = test_movie_with_year("Test", 2024);
let movie_id = movie.id().value();
movies.upsert_movie(&movie).await.unwrap();
@@ -65,7 +52,7 @@ async fn fails_when_no_external_id() {
..default_deps()
};
let result = sync_poster::execute(&deps, SyncPosterCommand { movie_id }).await;
let result = sync_poster::execute(&deps, sync_poster_command(movie_id)).await;
assert!(result.is_err());
}
@@ -94,13 +81,7 @@ impl MetadataClient for FakeMetaWithPoster {
async fn syncs_poster_for_movie_with_external_id() {
let movies = InMemoryMovieRepository::new();
let ext_id = ExternalMetadataId::new("tmdb:999".into()).unwrap();
let movie = Movie::new(
Some(ext_id),
MovieTitle::new("Poster Movie".into()).unwrap(),
ReleaseYear::new(2024).unwrap(),
None,
None,
);
let movie = test_movie_with_external_id("Poster Movie", 2024, ext_id);
let movie_id = movie.id().value();
movies.upsert_movie(&movie).await.unwrap();
@@ -111,7 +92,7 @@ async fn syncs_poster_for_movie_with_external_id() {
..default_deps()
};
sync_poster::execute(&deps, SyncPosterCommand { movie_id })
sync_poster::execute(&deps, sync_poster_command(movie_id))
.await
.unwrap();

View File

@@ -1,16 +1,16 @@
use domain::testing::test_user;
use std::sync::{Arc, Mutex};
use async_trait::async_trait;
use domain::{
errors::DomainError,
models::{User, UserRole},
ports::{
BlockQuery, FollowCommand, FollowGraphQuery, FollowQuery, SocialCommand, UserRepository,
},
testing::InMemoryUserRepository,
value_objects::{
Email, FollowRelation, FollowStatus, FollowTarget, InstanceIdentity, PasswordHash,
SocialActor, SocialIdentity, UserId, Username,
FollowRelation, FollowStatus, FollowTarget, InstanceIdentity, SocialActor, SocialIdentity,
UserId,
},
};
use uuid::Uuid;
@@ -247,12 +247,7 @@ fn instance() -> InstanceIdentity {
}
async fn register_user(repo: &Arc<InMemoryUserRepository>, username: &str) -> UserId {
let user = User::new(
Email::new(format!("{username}@example.com")).unwrap(),
Username::new(username.to_string()).unwrap(),
PasswordHash::new("hashed-password".to_string()).unwrap(),
UserRole::Standard,
);
let user = test_user(username);
let id = user.id().clone();
repo.save(&user).await.unwrap();
id

View File

@@ -0,0 +1,240 @@
//! Command builders for tests. Each builder returns a command with sensible
//! defaults; tests override individual fields with struct-update syntax:
//! `LogReviewCommand { rating: 6, ..log_review_command(user, movie) }`.
//! Moves to `tests/support/mod.rs` when application tests become integration tests.
use chrono::{NaiveDate, NaiveDateTime};
use uuid::Uuid;
use domain::models::{FileFormat, UserRole, WatchEventSource};
use crate::auth::commands::RegisterCommand;
use crate::auth::queries::LoginCommand;
use crate::diary::commands::{EditReviewCommand, LogReviewCommand, MovieInput, SyncPosterCommand};
use crate::goals::commands::{CreateGoalCommand, UpdateGoalCommand};
use crate::import::commands::{
ApplyImportMappingCommand, ApplyImportProfileCommand, CreateImportSessionCommand,
ExecuteImportCommand,
};
use crate::integrations::commands::{
ConfirmWatchEventsCommand, DismissWatchEventsCommand, GenerateWebhookTokenCommand,
WatchEventConfirmation,
};
use crate::users::commands::UpdateProfileCommand;
use crate::users::update_settings::UpdateUserSettingsCommand;
use crate::watchlist::commands::AddToWatchlistCommand;
use crate::wrapup::commands::RequestWrapUpCommand;
fn watched_at() -> NaiveDateTime {
NaiveDate::from_ymd_opt(2024, 6, 15)
.unwrap()
.and_hms_opt(20, 0, 0)
.unwrap()
}
pub(crate) fn movie_input_by_id(movie_id: Uuid) -> MovieInput {
MovieInput {
movie_id: Some(movie_id),
external_metadata_id: None,
manual_title: None,
manual_release_year: None,
manual_director: None,
}
}
pub(crate) fn movie_input_manual(title: &str, year: u16) -> MovieInput {
MovieInput {
movie_id: None,
external_metadata_id: None,
manual_title: Some(title.to_string()),
manual_release_year: Some(year),
manual_director: None,
}
}
pub(crate) fn log_review_command(user_id: Uuid, movie_id: Uuid) -> LogReviewCommand {
LogReviewCommand {
user_id,
input: movie_input_by_id(movie_id),
rating: 4,
comment: None,
watched_at: watched_at(),
watch_medium: None,
spoilers: false,
}
}
pub(crate) fn log_review_command_manual(user_id: Uuid, title: &str, year: u16) -> LogReviewCommand {
LogReviewCommand {
input: movie_input_manual(title, year),
..log_review_command(user_id, Uuid::nil())
}
}
/// Edits only the rating; `review_id` and `requesting_user_id` are random.
pub(crate) fn edit_review_command(rating: u8) -> EditReviewCommand {
EditReviewCommand {
review_id: Uuid::new_v4(),
requesting_user_id: Uuid::new_v4(),
rating: Some(rating),
comment: None,
watched_at: None,
watch_medium: None,
spoilers: None,
}
}
pub(crate) fn sync_poster_command(movie_id: Uuid) -> SyncPosterCommand {
SyncPosterCommand { movie_id }
}
pub(crate) fn register_command(
email: impl Into<String>,
username: impl Into<String>,
) -> RegisterCommand {
RegisterCommand {
email: email.into(),
username: username.into(),
password: "password123".to_string(),
role: UserRole::Standard,
}
}
pub(crate) fn login_command(email: impl Into<String>, password: impl Into<String>) -> LoginCommand {
LoginCommand {
email: email.into(),
password: password.into(),
}
}
pub(crate) fn create_goal_command(year: u16, target_count: u32) -> CreateGoalCommand {
CreateGoalCommand {
user_id: Uuid::nil(),
year,
target_count,
}
}
pub(crate) fn update_goal_command(year: u16, target_count: u32) -> UpdateGoalCommand {
UpdateGoalCommand {
user_id: Uuid::nil(),
year,
target_count,
}
}
/// Confirms indices `[0]`.
pub(crate) fn execute_import_command(user_id: Uuid, session_id: Uuid) -> ExecuteImportCommand {
ExecuteImportCommand {
user_id,
session_id,
confirmed_indices: vec![0],
}
}
pub(crate) fn create_import_session_command(
user_id: Uuid,
csv: &[u8],
) -> CreateImportSessionCommand {
CreateImportSessionCommand {
user_id,
bytes: csv.to_vec(),
format: FileFormat::Csv,
}
}
/// No field mappings.
pub(crate) fn apply_import_mapping_command(
user_id: Uuid,
session_id: Uuid,
) -> ApplyImportMappingCommand {
ApplyImportMappingCommand {
user_id,
session_id,
mappings: vec![],
}
}
pub(crate) fn apply_import_profile_command(
user_id: Uuid,
session_id: Uuid,
profile_id: Uuid,
) -> ApplyImportProfileCommand {
ApplyImportProfileCommand {
user_id,
session_id,
profile_id,
}
}
/// One confirmation, rating as given, no comment.
pub(crate) fn confirm_watch_events_command(
user_id: Uuid,
watch_event_id: Uuid,
rating: u8,
) -> ConfirmWatchEventsCommand {
ConfirmWatchEventsCommand {
user_id,
confirmations: vec![WatchEventConfirmation {
watch_event_id,
rating,
comment: None,
}],
}
}
pub(crate) fn dismiss_watch_events_command(
user_id: Uuid,
event_ids: Vec<Uuid>,
) -> DismissWatchEventsCommand {
DismissWatchEventsCommand { user_id, event_ids }
}
/// Jellyfin provider, no label.
pub(crate) fn generate_webhook_token_command(user_id: Uuid) -> GenerateWebhookTokenCommand {
GenerateWebhookTokenCommand {
user_id,
provider: WatchEventSource::Jellyfin,
label: None,
}
}
/// Every field unset; override what the test cares about.
pub(crate) fn update_profile_command(user_id: Uuid) -> UpdateProfileCommand {
UpdateProfileCommand {
user_id,
display_name: None,
bio: None,
avatar_bytes: None,
avatar_content_type: None,
banner_bytes: None,
banner_content_type: None,
also_known_as: None,
}
}
/// All federation toggles on.
pub(crate) fn update_user_settings_command(user_id: Uuid) -> UpdateUserSettingsCommand {
UpdateUserSettingsCommand {
user_id,
federate_goals: true,
federate_reviews: true,
federate_watchlist: true,
}
}
pub(crate) fn add_to_watchlist_command(user_id: Uuid, movie_id: Uuid) -> AddToWatchlistCommand {
AddToWatchlistCommand {
user_id,
input: movie_input_by_id(movie_id),
}
}
/// Window is 1 Jan of `year` to 1 Jan of `year + 1`.
pub(crate) fn request_wrap_up_command(user_id: Option<Uuid>, year: i32) -> RequestWrapUpCommand {
RequestWrapUpCommand {
user_id,
start_date: NaiveDate::from_ymd_opt(year, 1, 1).unwrap(),
end_date: NaiveDate::from_ymd_opt(year + 1, 1, 1).unwrap(),
}
}

View File

@@ -1,3 +1,4 @@
use crate::test_support::register_command;
use std::sync::Arc;
use domain::models::UserRole;
@@ -23,10 +24,8 @@ async fn register_user(
register::execute(
&reg_deps,
RegisterCommand {
email: email.into(),
username: username.into(),
password: "password123".into(),
role,
..register_command(email, username)
},
)
.await

View File

@@ -1,3 +1,4 @@
use crate::test_support::register_command;
use std::sync::Arc;
use domain::models::{ProfileField, User, UserProfile, UserRole};
@@ -7,7 +8,7 @@ use domain::value_objects::{Email, PasswordHash, UserId, Username};
use uuid::Uuid;
use crate::{
auth::{commands::RegisterCommand, deps::RegisterDeps, register},
auth::{deps::RegisterDeps, register},
test_helpers::TestContextBuilder,
users::{deps::GetCurrentProfileDeps, get_current_profile, queries::GetCurrentProfileQuery},
};
@@ -23,17 +24,9 @@ async fn returns_profile_for_existing_user() {
config: b.config.clone(),
};
register::execute(
&reg_deps,
RegisterCommand {
email: "alice@example.com".into(),
username: "alice".into(),
password: "password123".into(),
role: UserRole::Standard,
},
)
.await
.unwrap();
register::execute(&reg_deps, register_command("alice@example.com", "alice"))
.await
.unwrap();
let user = users
.find_by_email(&domain::value_objects::Email::new("alice@example.com".into()).unwrap())

View File

@@ -1,8 +1,7 @@
use crate::test_support::{register_command, update_profile_command};
use domain::errors::DomainError;
use domain::models::UserRole;
use domain::value_objects::Email;
use crate::auth::commands::RegisterCommand;
use crate::auth::deps::RegisterDeps;
use crate::auth::register;
use crate::test_helpers::TestContextBuilder;
@@ -18,17 +17,9 @@ async fn setup_user(b: &TestContextBuilder, email: &str, username: &str) {
password_hasher: b.password_hasher.clone(),
config: b.config.clone(),
};
register::execute(
&deps,
RegisterCommand {
email: email.into(),
username: username.into(),
password: "password123".into(),
role: UserRole::Standard,
},
)
.await
.unwrap();
register::execute(&deps, register_command(email, username))
.await
.unwrap();
}
fn deps(b: &TestContextBuilder) -> GetLocalProfileDeps {
@@ -276,14 +267,9 @@ async fn populates_handle_and_actor_url_for_a_local_user() {
update_profile::execute(
&update_deps,
UpdateProfileCommand {
user_id: uid,
display_name: Some("Gabriel K".into()),
bio: Some("Movies and diaries.".into()),
avatar_bytes: None,
avatar_content_type: None,
banner_bytes: None,
banner_content_type: None,
also_known_as: None,
..update_profile_command(uid)
},
)
.await

View File

@@ -1,7 +1,6 @@
use domain::models::UserRole;
use crate::test_support::register_command;
use domain::value_objects::{Email, FollowTarget, SocialIdentity, UserId};
use crate::auth::commands::RegisterCommand;
use crate::auth::deps::RegisterDeps;
use crate::auth::register;
use crate::test_helpers::TestContextBuilder;
@@ -14,17 +13,9 @@ async fn setup_user(b: &TestContextBuilder, email: &str, username: &str) -> uuid
password_hasher: b.password_hasher.clone(),
config: b.config.clone(),
};
register::execute(
&deps,
RegisterCommand {
email: email.into(),
username: username.into(),
password: "password123".into(),
role: UserRole::Standard,
},
)
.await
.unwrap();
register::execute(&deps, register_command(email, username))
.await
.unwrap();
let user = b
.user_repo
.find_by_email(&Email::new(email.into()).unwrap())

View File

@@ -1,7 +1,7 @@
use domain::models::{ProfileField, UserRole};
use crate::test_support::register_command;
use domain::models::ProfileField;
use domain::value_objects::{Email, UserId};
use crate::auth::commands::RegisterCommand;
use crate::auth::deps::RegisterDeps;
use crate::auth::register;
use crate::test_helpers::TestContextBuilder;
@@ -18,12 +18,7 @@ async fn get_profile_settings_includes_profile_fields() {
};
register::execute(
&register_deps,
RegisterCommand {
email: "settings@test.com".into(),
username: "settingsuser".into(),
password: "password123".into(),
role: UserRole::Standard,
},
register_command("settings@test.com", "settingsuser"),
)
.await
.unwrap();

View File

@@ -1,13 +1,13 @@
use crate::test_support::{register_command, update_profile_command};
use std::sync::Arc;
use domain::events::DomainEvent;
use domain::models::UserRole;
use domain::ports::UserRepository;
use domain::testing::{InMemoryUserRepository, NoopEventPublisher};
use uuid::Uuid;
use crate::{
auth::{commands::RegisterCommand, deps::RegisterDeps, register},
auth::{deps::RegisterDeps, register},
test_helpers::TestContextBuilder,
users::{commands::UpdateProfileCommand, deps::UpdateProfileDeps, update_profile},
};
@@ -18,17 +18,9 @@ async fn register_user(b: &TestContextBuilder, users: &Arc<InMemoryUserRepositor
password_hasher: b.password_hasher.clone(),
config: b.config.clone(),
};
register::execute(
&reg_deps,
RegisterCommand {
email: "alice@example.com".into(),
username: "alice".into(),
password: "password123".into(),
role: UserRole::Standard,
},
)
.await
.unwrap();
register::execute(&reg_deps, register_command("alice@example.com", "alice"))
.await
.unwrap();
let user = users
.find_by_email(&domain::value_objects::Email::new("alice@example.com".into()).unwrap())
@@ -55,14 +47,8 @@ async fn updates_display_name() {
update_profile::execute(
&deps,
UpdateProfileCommand {
user_id: uid,
display_name: Some("Alice W.".into()),
bio: None,
avatar_bytes: None,
avatar_content_type: None,
banner_bytes: None,
banner_content_type: None,
also_known_as: None,
..update_profile_command(uid)
},
)
.await
@@ -90,14 +76,9 @@ async fn rejects_invalid_avatar_content_type() {
let result = update_profile::execute(
&deps,
UpdateProfileCommand {
user_id: uid,
display_name: None,
bio: None,
avatar_bytes: Some(vec![0u8; 10]),
avatar_content_type: Some("image/gif".into()),
banner_bytes: None,
banner_content_type: None,
also_known_as: None,
..update_profile_command(uid)
},
)
.await;
@@ -122,14 +103,9 @@ async fn uploads_avatar() {
update_profile::execute(
&deps,
UpdateProfileCommand {
user_id: uid,
display_name: None,
bio: None,
avatar_bytes: Some(vec![0xFFu8, 0xD8, 0xFF]),
avatar_content_type: Some("image/jpeg".into()),
banner_bytes: None,
banner_content_type: None,
also_known_as: None,
..update_profile_command(uid)
},
)
.await
@@ -165,14 +141,9 @@ async fn uploads_banner() {
update_profile::execute(
&deps,
UpdateProfileCommand {
user_id: uid,
display_name: None,
bio: None,
avatar_bytes: None,
avatar_content_type: None,
banner_bytes: Some(vec![0x89, 0x50, 0x4E]),
banner_content_type: Some("image/png".into()),
also_known_as: None,
..update_profile_command(uid)
},
)
.await
@@ -203,14 +174,8 @@ async fn fails_for_nonexistent_user() {
let result = update_profile::execute(
&deps,
UpdateProfileCommand {
user_id: Uuid::new_v4(),
display_name: Some("Ghost".into()),
bio: None,
avatar_bytes: None,
avatar_content_type: None,
banner_bytes: None,
banner_content_type: None,
also_known_as: None,
..update_profile_command(Uuid::new_v4())
},
)
.await;
@@ -232,14 +197,9 @@ async fn rejects_invalid_banner_content_type() {
let result = update_profile::execute(
&deps,
UpdateProfileCommand {
user_id: uid,
display_name: None,
bio: None,
avatar_bytes: None,
avatar_content_type: None,
banner_bytes: Some(vec![0u8; 10]),
banner_content_type: Some("text/plain".into()),
also_known_as: None,
..update_profile_command(uid)
},
)
.await;
@@ -264,14 +224,9 @@ async fn text_only_update_emits_user_updated_no_image_stored() {
update_profile::execute(
&deps,
UpdateProfileCommand {
user_id: uid,
display_name: Some("Alice Updated".into()),
bio: Some("Hello world".into()),
avatar_bytes: None,
avatar_content_type: None,
banner_bytes: None,
banner_content_type: None,
also_known_as: None,
..update_profile_command(uid)
},
)
.await

View File

@@ -1,3 +1,4 @@
use crate::test_support::update_user_settings_command;
use std::sync::Arc;
use domain::testing::InMemoryUserSettingsRepository;
@@ -24,10 +25,8 @@ async fn updates_federate_goals() {
user_settings: user_settings.clone(),
},
UpdateUserSettingsCommand {
user_id: uid,
federate_goals: false,
federate_reviews: true,
federate_watchlist: true,
..update_user_settings_command(uid)
},
)
.await
@@ -53,10 +52,8 @@ async fn updates_federate_reviews() {
user_settings: user_settings.clone(),
},
UpdateUserSettingsCommand {
user_id: uid,
federate_goals: true,
federate_reviews: false,
federate_watchlist: true,
..update_user_settings_command(uid)
},
)
.await
@@ -82,10 +79,8 @@ async fn updates_federate_watchlist() {
user_settings: user_settings.clone(),
},
UpdateUserSettingsCommand {
user_id: uid,
federate_goals: true,
federate_reviews: true,
federate_watchlist: false,
..update_user_settings_command(uid)
},
)
.await

View File

@@ -1,16 +1,13 @@
use crate::test_support::{add_to_watchlist_command, movie_input_manual};
use domain::testing::test_movie_with_year;
use std::sync::Arc;
use domain::{
models::Movie,
ports::MovieCommand,
testing::{InMemoryMovieRepository, InMemoryWatchlistRepository, NoopEventPublisher},
value_objects::{MovieTitle, ReleaseYear},
};
use crate::{
diary::commands::MovieInput,
watchlist::{add, commands::AddToWatchlistCommand, deps::WatchlistAddDeps},
};
use crate::watchlist::{add, commands::AddToWatchlistCommand, deps::WatchlistAddDeps};
fn make_deps(
movies: Arc<InMemoryMovieRepository>,
@@ -30,28 +27,13 @@ async fn test_add_to_watchlist_resolves_and_saves() {
let movies = InMemoryMovieRepository::new();
let watchlist = InMemoryWatchlistRepository::new();
let movie = Movie::new(
None,
MovieTitle::new("The Thing".into()).unwrap(),
ReleaseYear::new(1982).unwrap(),
None,
None,
);
let movie = test_movie_with_year("The Thing", 1982);
let movie_uuid = movie.id().value();
movies.upsert_movie(&movie).await.unwrap();
let deps = make_deps(Arc::clone(&movies), Arc::clone(&watchlist));
let cmd = AddToWatchlistCommand {
user_id: uuid::Uuid::new_v4(),
input: MovieInput {
movie_id: Some(movie_uuid),
external_metadata_id: None,
manual_title: None,
manual_release_year: None,
manual_director: None,
},
};
let cmd = add_to_watchlist_command(uuid::Uuid::new_v4(), movie_uuid);
add::execute(&deps, cmd).await.unwrap();
@@ -63,29 +45,14 @@ async fn test_add_to_watchlist_already_present_is_idempotent() {
let movies = InMemoryMovieRepository::new();
let watchlist = InMemoryWatchlistRepository::new();
let movie = Movie::new(
None,
MovieTitle::new("RoboCop".into()).unwrap(),
ReleaseYear::new(1987).unwrap(),
None,
None,
);
let movie = test_movie_with_year("RoboCop", 1987);
let movie_uuid = movie.id().value();
let user_id = uuid::Uuid::new_v4();
movies.upsert_movie(&movie).await.unwrap();
let deps = make_deps(Arc::clone(&movies), Arc::clone(&watchlist));
let make_cmd = || AddToWatchlistCommand {
user_id,
input: MovieInput {
movie_id: Some(movie_uuid),
external_metadata_id: None,
manual_title: None,
manual_release_year: None,
manual_director: None,
},
};
let make_cmd = || add_to_watchlist_command(user_id, movie_uuid);
add::execute(&deps, make_cmd()).await.unwrap();
add::execute(&deps, make_cmd()).await.unwrap();
@@ -102,13 +69,7 @@ async fn test_add_to_watchlist_with_manual_movie() {
let cmd = AddToWatchlistCommand {
user_id: uuid::Uuid::new_v4(),
input: MovieInput {
movie_id: None,
external_metadata_id: None,
manual_title: Some("New Manual Movie".into()),
manual_release_year: Some(2024),
manual_director: None,
},
input: movie_input_manual("New Manual Movie", 2024),
};
add::execute(&deps, cmd).await.unwrap();
@@ -124,16 +85,7 @@ async fn test_add_to_watchlist_movie_not_found_by_id() {
let deps = make_deps(Arc::clone(&movies), Arc::clone(&watchlist));
let cmd = AddToWatchlistCommand {
user_id: uuid::Uuid::new_v4(),
input: MovieInput {
movie_id: Some(uuid::Uuid::new_v4()),
external_metadata_id: None,
manual_title: None,
manual_release_year: None,
manual_director: None,
},
};
let cmd = add_to_watchlist_command(uuid::Uuid::new_v4(), uuid::Uuid::new_v4());
assert!(add::execute(&deps, cmd).await.is_err());
}

View File

@@ -1,15 +1,14 @@
use crate::test_support::register_command;
use std::sync::Arc;
use uuid::Uuid;
use domain::models::UserRole;
use domain::testing::{
InMemoryRemoteWatchlistRepository, InMemoryUserRepository, InMemoryWatchlistRepository,
PanicRemoteWatchlistRepository, PanicWatchlistRepository,
};
use domain::value_objects::Email;
use crate::auth::commands::RegisterCommand;
use crate::auth::deps::RegisterDeps;
use crate::auth::register;
use crate::test_helpers::TestContextBuilder;
@@ -22,17 +21,9 @@ async fn setup_user(b: &TestContextBuilder, email: &str, username: &str) {
password_hasher: b.password_hasher.clone(),
config: b.config.clone(),
};
register::execute(
&deps,
RegisterCommand {
email: email.into(),
username: username.into(),
password: "password123".into(),
role: UserRole::Standard,
},
)
.await
.unwrap();
register::execute(&deps, register_command(email, username))
.await
.unwrap();
}
#[tokio::test]

View File

@@ -1,3 +1,4 @@
use crate::test_support::request_wrap_up_command;
use chrono::NaiveDate;
use domain::events::DomainEvent;
use domain::models::wrapup::{WrapUpRecord, WrapUpStatus};
@@ -21,11 +22,7 @@ fn deps(
}
fn past_cmd() -> RequestWrapUpCommand {
RequestWrapUpCommand {
user_id: Some(Uuid::nil()),
start_date: NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(),
end_date: NaiveDate::from_ymd_opt(2025, 1, 1).unwrap(),
}
request_wrap_up_command(Some(Uuid::nil()), 2024)
}
#[tokio::test]
@@ -105,16 +102,9 @@ async fn replaces_failed_wrapup() {
async fn rejects_future_end_date() {
let repo = InMemoryWrapUpRepository::new();
let events = NoopEventPublisher::new();
let err = generate::execute(
&deps(&repo, &events),
RequestWrapUpCommand {
user_id: None,
start_date: NaiveDate::from_ymd_opt(2030, 1, 1).unwrap(),
end_date: NaiveDate::from_ymd_opt(2031, 1, 1).unwrap(),
},
)
.await
.unwrap_err();
let err = generate::execute(&deps(&repo, &events), request_wrap_up_command(None, 2030))
.await
.unwrap_err();
assert!(err.to_string().contains("future"));
}