Authors can flag a review's comment as containing spoilers; the comment is blurred behind a reveal control for every viewer except the author, omitted from RSS, and federates as containsSpoilers plus an AP summary content warning (ADR-0011) so Mastodon-class clients also collapse it. Flag follows the general-edit path (ADR-0001) across domain, both DB backends, REST, HTML templates, SPA (shadcn), TUI fixtures, and exports.
132 lines
3.7 KiB
Rust
132 lines
3.7 KiB
Rust
use std::sync::Arc;
|
|
|
|
use chrono::Utc;
|
|
|
|
use domain::{
|
|
models::Movie,
|
|
ports::MovieCommand,
|
|
value_objects::{MovieTitle, ReleaseYear},
|
|
};
|
|
|
|
use domain::testing::{InMemoryMovieRepository, InMemoryReviewRepository, NoopEventPublisher};
|
|
|
|
use crate::{
|
|
diary::commands::{LogReviewCommand, MovieInput},
|
|
diary::log_review,
|
|
diary::review_logger::DefaultReviewLogger,
|
|
test_helpers::TestContextBuilder,
|
|
};
|
|
|
|
fn build_logger(
|
|
movies: &Arc<InMemoryMovieRepository>,
|
|
reviews: &Arc<InMemoryReviewRepository>,
|
|
events: &Arc<NoopEventPublisher>,
|
|
) -> Arc<dyn crate::ports::ReviewLogger> {
|
|
Arc::new(DefaultReviewLogger::new(
|
|
Arc::clone(movies) as _,
|
|
Arc::clone(movies) as _,
|
|
Arc::clone(reviews) as _,
|
|
TestContextBuilder::new().watchlist_repo,
|
|
Arc::new(domain::testing::FakeMetadataClient) as _,
|
|
Arc::clone(events) as _,
|
|
))
|
|
}
|
|
|
|
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 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,
|
|
};
|
|
|
|
log_review::execute(&logger, 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 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::execute(&logger, 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 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,
|
|
};
|
|
let result = log_review::execute(&logger, cmd).await;
|
|
assert!(result.is_err(), "rating > 5 should fail");
|
|
}
|