Files
movies-diary/crates/application/src/diary/tests/log_review.rs
Gabriel Kaszewski d867a14b28
Some checks failed
CI / Check / Test (push) Has been cancelled
add 400+ unit tests for domain and application layers
Extract ReviewLogger trait to decouple import/integrations
from diary::log_review (cross-module coupling smell).

Add in-memory fakes for all repository ports, enabling
isolated testing of every use case module without a database.

Coverage: domain+application 22% → 80%, 427 tests.
2026-06-09 02:07:26 +02:00

130 lines
3.8 KiB
Rust

use std::sync::Arc;
use chrono::Utc;
use domain::{
models::Movie,
value_objects::{MovieTitle, ReleaseYear},
};
use domain::ports::MovieRepository;
use domain::testing::{InMemoryMovieRepository, InMemoryReviewRepository, NoopEventPublisher};
use crate::{
diary::commands::{LogReviewCommand, MovieInput},
diary::log_review,
diary::review_logger::DefaultReviewLogger,
test_helpers::TestContextBuilder,
};
fn build_ctx_with_real_logger(
movies: &Arc<InMemoryMovieRepository>,
reviews: &Arc<InMemoryReviewRepository>,
events: &Arc<NoopEventPublisher>,
) -> crate::context::AppContext {
let logger = Arc::new(DefaultReviewLogger::new(
Arc::clone(movies) as _,
Arc::clone(reviews) as _,
crate::test_helpers::TestContextBuilder::new().watchlist_repo,
Arc::new(domain::testing::FakeMetadataClient) as _,
Arc::clone(events) as _,
));
TestContextBuilder::new()
.with_movies(Arc::clone(movies) as _)
.with_reviews(Arc::clone(reviews) as _)
.with_event_publisher(Arc::clone(events) as _)
.with_review_logger(logger)
.build()
}
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();
let reviews = InMemoryReviewRepository::new();
let events = NoopEventPublisher::new();
let ctx = build_ctx_with_real_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(),
};
log_review::execute(&ctx, cmd).await.unwrap();
assert_eq!(reviews.count(), 1, "review should be saved");
assert!(!events.published().is_empty(), "events should be published");
}
#[tokio::test]
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 movie_uuid = existing_movie.id().value();
movies.upsert_movie(&existing_movie).await.unwrap();
let events = NoopEventPublisher::new();
let ctx = build_ctx_with_real_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(),
};
log_review::execute(&ctx, cmd).await.unwrap();
assert_eq!(movies.count(), 1, "no duplicate movie");
assert_eq!(reviews.count(), 1);
}
#[tokio::test]
async fn test_log_review_with_invalid_rating_fails() {
let movies = InMemoryMovieRepository::new();
let reviews = InMemoryReviewRepository::new();
let events = NoopEventPublisher::new();
let ctx = build_ctx_with_real_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(),
};
let result = log_review::execute(&ctx, cmd).await;
assert!(result.is_err(), "rating > 5 should fail");
}