Ride writes: WrapUpRequested/Completed (wrapup create/set_complete), MovieDiscovered (resolve upsert, new+ext id only), PosterSynced (poster handler upsert), MovieDeleted (delete_movie), WatchEventIngested (save). Deps drop event_publisher: wrapup, ingest, delete_review, watchlist add, review logger. Remaining .publish( sites: announce-only: users/delete_account UserDeleted, person/get + get_credits PersonEnrichmentRequested, jobs/enrichment_staleness, http-axum search, movies/sync_poster + poster_sync_handler + image-converter backfill + users/update_profile ImageStored (object storage), activitypub federation_event_bridge (reacting to events). rides write, deferred: social/execute (see report), activitypub review_handler MovieEnrichmentRequested (after save_remote_review).
257 lines
8.0 KiB
Rust
257 lines
8.0 KiB
Rust
use domain::testing::{test_movie_with_year, test_review};
|
|
use std::sync::Arc;
|
|
|
|
use domain::{
|
|
ports::{MovieCommand, MovieQuery, ReviewRepository},
|
|
testing::{FakeDiaryQuery, InMemoryStore, RefusingMovieCommand},
|
|
value_objects::{MovieId, UserId},
|
|
};
|
|
|
|
use application::{
|
|
diary::commands::DeleteReviewCommand, diary::delete_review, diary::deps::DeleteReviewDeps,
|
|
};
|
|
|
|
#[tokio::test]
|
|
async fn deleting_a_review_removes_it() {
|
|
let movies = InMemoryStore::new();
|
|
let reviews = Arc::clone(&movies);
|
|
let diary = FakeDiaryQuery::new();
|
|
|
|
let movie = test_movie_with_year("Terminator", 1984);
|
|
let user_id = UserId::from_uuid(uuid::Uuid::new_v4());
|
|
let review = test_review(movie.id(), &user_id, 4);
|
|
|
|
movies.upsert_movie(&movie, &[]).await.unwrap();
|
|
reviews.save_review(&review, &[]).await.unwrap();
|
|
diary.seed_history(movie.clone(), vec![]);
|
|
|
|
let deps = DeleteReviewDeps {
|
|
review: Arc::clone(&reviews) as _,
|
|
diary: diary.clone() as _,
|
|
movie_command: Arc::clone(&movies) as _,
|
|
};
|
|
|
|
delete_review::execute(
|
|
&deps,
|
|
DeleteReviewCommand {
|
|
review_id: review.id().value(),
|
|
requesting_user_id: user_id.value(),
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(reviews.review_count(), 0, "review should be deleted");
|
|
assert!(
|
|
movies.get_movie_by_id(movie.id()).await.unwrap().is_none(),
|
|
"movie should be deleted when no reviews remain"
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn deleting_another_users_review_is_unauthorized() {
|
|
let reviews = InMemoryStore::new();
|
|
let diary = FakeDiaryQuery::new();
|
|
let movies = Arc::clone(&reviews);
|
|
|
|
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 = test_review(&movie_id, &owner_id, 4);
|
|
|
|
reviews.save_review(&review, &[]).await.unwrap();
|
|
|
|
let deps = DeleteReviewDeps {
|
|
review: Arc::clone(&reviews) as _,
|
|
diary: diary as _,
|
|
movie_command: movies as _,
|
|
};
|
|
|
|
let result = delete_review::execute(
|
|
&deps,
|
|
DeleteReviewCommand {
|
|
review_id: review.id().value(),
|
|
requesting_user_id: other_id,
|
|
},
|
|
)
|
|
.await;
|
|
|
|
assert!(result.is_err(), "wrong user should not be able to delete");
|
|
assert_eq!(reviews.review_count(), 1, "review should still exist");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn deleting_a_review_removes_it_and_records_its_event_together() {
|
|
let movies = InMemoryStore::new();
|
|
let diary = FakeDiaryQuery::new();
|
|
let movie = test_movie_with_year("Terminator", 1984);
|
|
let user_id = UserId::from_uuid(uuid::Uuid::new_v4());
|
|
let review = test_review(movie.id(), &user_id, 4);
|
|
movies.upsert_movie(&movie, &[]).await.unwrap();
|
|
movies.save_review(&review, &[]).await.unwrap();
|
|
diary.seed_history(movie.clone(), vec![review.clone()]);
|
|
let deps = DeleteReviewDeps {
|
|
review: Arc::clone(&movies) as _,
|
|
diary: diary.clone() as _,
|
|
movie_command: Arc::clone(&movies) as _,
|
|
};
|
|
|
|
delete_review::execute(
|
|
&deps,
|
|
DeleteReviewCommand {
|
|
review_id: review.id().value(),
|
|
requesting_user_id: user_id.value(),
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(movies.review_count(), 0);
|
|
let recorded = movies.recorded_events();
|
|
assert_eq!(recorded.len(), 1);
|
|
assert!(matches!(
|
|
recorded[0],
|
|
domain::events::DomainEvent::ReviewDeleted { .. }
|
|
));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn a_refused_delete_propagates_and_records_nothing() {
|
|
let movies = InMemoryStore::new();
|
|
let diary = FakeDiaryQuery::new();
|
|
let user_id = UserId::from_uuid(uuid::Uuid::new_v4());
|
|
let review = test_review(&MovieId::generate(), &user_id, 4);
|
|
movies.save_review(&review, &[]).await.unwrap();
|
|
let deps = DeleteReviewDeps {
|
|
review: Arc::new(crate::edit_review_test::WriteRefusingReviews(Arc::clone(
|
|
&movies,
|
|
))),
|
|
diary: diary as _,
|
|
movie_command: Arc::clone(&movies) as _,
|
|
};
|
|
|
|
let result = delete_review::execute(
|
|
&deps,
|
|
DeleteReviewCommand {
|
|
review_id: review.id().value(),
|
|
requesting_user_id: user_id.value(),
|
|
},
|
|
)
|
|
.await;
|
|
|
|
assert!(result.is_err());
|
|
assert_eq!(movies.review_count(), 1);
|
|
assert!(movies.recorded_events().is_empty());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn deleting_the_last_review_records_movie_deleted_with_the_movie_delete() {
|
|
let movies = InMemoryStore::new();
|
|
let diary = FakeDiaryQuery::new();
|
|
let movie = test_movie_with_year("Terminator", 1984);
|
|
let user_id = UserId::from_uuid(uuid::Uuid::new_v4());
|
|
let review = test_review(movie.id(), &user_id, 4);
|
|
movies.upsert_movie(&movie, &[]).await.unwrap();
|
|
movies.save_review(&review, &[]).await.unwrap();
|
|
diary.seed_history(movie.clone(), vec![]);
|
|
let deps = DeleteReviewDeps {
|
|
review: Arc::clone(&movies) as _,
|
|
diary: diary as _,
|
|
movie_command: Arc::clone(&movies) as _,
|
|
};
|
|
|
|
delete_review::execute(
|
|
&deps,
|
|
DeleteReviewCommand {
|
|
review_id: review.id().value(),
|
|
requesting_user_id: user_id.value(),
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert!(movies.get_movie_by_id(movie.id()).await.unwrap().is_none());
|
|
let recorded = movies.recorded_events();
|
|
assert!(matches!(
|
|
recorded[0],
|
|
domain::events::DomainEvent::ReviewDeleted { .. }
|
|
));
|
|
assert!(matches!(
|
|
&recorded[1],
|
|
domain::events::DomainEvent::MovieDeleted { movie_id, .. } if movie_id == movie.id()
|
|
));
|
|
assert_eq!(recorded.len(), 2);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn deleting_a_review_while_viewings_remain_keeps_the_movie_and_records_no_movie_deleted() {
|
|
let movies = InMemoryStore::new();
|
|
let diary = FakeDiaryQuery::new();
|
|
let movie = test_movie_with_year("Terminator", 1984);
|
|
let user_id = UserId::from_uuid(uuid::Uuid::new_v4());
|
|
let review = test_review(movie.id(), &user_id, 4);
|
|
let other = test_review(movie.id(), &UserId::from_uuid(uuid::Uuid::new_v4()), 3);
|
|
movies.upsert_movie(&movie, &[]).await.unwrap();
|
|
movies.save_review(&review, &[]).await.unwrap();
|
|
movies.save_review(&other, &[]).await.unwrap();
|
|
diary.seed_history(movie.clone(), vec![other.clone()]);
|
|
let deps = DeleteReviewDeps {
|
|
review: Arc::clone(&movies) as _,
|
|
diary: diary as _,
|
|
movie_command: Arc::clone(&movies) as _,
|
|
};
|
|
|
|
delete_review::execute(
|
|
&deps,
|
|
DeleteReviewCommand {
|
|
review_id: review.id().value(),
|
|
requesting_user_id: user_id.value(),
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert!(movies.get_movie_by_id(movie.id()).await.unwrap().is_some());
|
|
assert!(
|
|
!movies
|
|
.recorded_events()
|
|
.iter()
|
|
.any(|e| matches!(e, domain::events::DomainEvent::MovieDeleted { .. }))
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn a_refused_movie_delete_propagates_and_records_no_movie_deleted() {
|
|
let movies = InMemoryStore::new();
|
|
let diary = FakeDiaryQuery::new();
|
|
let movie = test_movie_with_year("Terminator", 1984);
|
|
let user_id = UserId::from_uuid(uuid::Uuid::new_v4());
|
|
let review = test_review(movie.id(), &user_id, 4);
|
|
movies.upsert_movie(&movie, &[]).await.unwrap();
|
|
movies.save_review(&review, &[]).await.unwrap();
|
|
diary.seed_history(movie.clone(), vec![]);
|
|
let deps = DeleteReviewDeps {
|
|
review: Arc::clone(&movies) as _,
|
|
diary: diary as _,
|
|
movie_command: Arc::new(RefusingMovieCommand),
|
|
};
|
|
|
|
let result = delete_review::execute(
|
|
&deps,
|
|
DeleteReviewCommand {
|
|
review_id: review.id().value(),
|
|
requesting_user_id: user_id.value(),
|
|
},
|
|
)
|
|
.await;
|
|
|
|
assert!(result.is_err());
|
|
assert!(movies.get_movie_by_id(movie.id()).await.unwrap().is_some());
|
|
assert!(
|
|
!movies
|
|
.recorded_events()
|
|
.iter()
|
|
.any(|e| matches!(e, domain::events::DomainEvent::MovieDeleted { .. }))
|
|
);
|
|
}
|