feat: atomic review writes with events

This commit is contained in:
2026-10-26 00:40:43 +01:00
parent 0236a488fa
commit c8a1e8bfe4
19 changed files with 440 additions and 117 deletions

View File

@@ -212,13 +212,6 @@ async fn publishes_movie_discovered_for_new_movie_with_external_id() {
.any(|e| matches!(e, domain::events::DomainEvent::MovieDiscovered { .. })),
"should publish MovieDiscovered for new movie with external_metadata_id"
);
assert!(
published.iter().any(|e| matches!(
e,
domain::events::DomainEvent::MovieEnrichmentRequested { .. }
)),
"should publish MovieEnrichmentRequested"
);
}
#[tokio::test]
@@ -241,8 +234,7 @@ async fn logging_a_review_fails_when_the_review_store_refuses() {
}
#[tokio::test]
async fn logging_a_review_when_event_publishing_fails_returns_the_error_but_keeps_the_saved_review()
{
async fn logging_a_review_saves_the_review_and_its_event_together() {
let movies = InMemoryStore::new();
let logger = DefaultReviewLogger::new(
Arc::clone(&movies) as _,
@@ -253,14 +245,70 @@ async fn logging_a_review_when_event_publishing_fails_returns_the_error_but_keep
Arc::new(domain::testing::RefusingEventPublisher),
);
logger
.log_review(log_review_command_manual(Uuid::new_v4(), "Test Film", 2024))
.await
.unwrap();
assert_eq!(movies.review_count(), 1);
let recorded = movies.recorded_events();
assert_eq!(recorded.len(), 1);
assert!(matches!(
recorded[0],
domain::events::DomainEvent::ReviewLogged { .. }
));
}
#[tokio::test]
async fn a_refused_save_records_neither_the_review_nor_its_event() {
let movies = InMemoryStore::new();
let logger = DefaultReviewLogger::new(
Arc::clone(&movies) as _,
Arc::clone(&movies) as _,
Arc::new(domain::testing::RefusingReviewRepository),
Arc::clone(&movies) as _,
Arc::new(FakeMetadataClient),
NoopEventPublisher::new(),
);
let result = logger
.log_review(log_review_command_manual(Uuid::new_v4(), "Test Film", 2024))
.await;
assert!(matches!(result, Err(DomainError::InfrastructureError(m)) if m == "refused"));
assert_eq!(
movies.review_count(),
1,
"review is saved before publishing"
assert!(result.is_err());
assert_eq!(movies.review_count(), 0);
assert!(movies.recorded_events().is_empty());
}
#[tokio::test]
async fn enrichment_request_rides_the_review_save() {
let movies = InMemoryStore::new();
let logger = DefaultReviewLogger::new(
Arc::clone(&movies) as _,
Arc::clone(&movies) as _,
Arc::clone(&movies) as _,
Arc::clone(&movies) as _,
Arc::new(MetadataClientWithExternalId),
NoopEventPublisher::new(),
);
logger
.log_review(log_review_command_manual(
Uuid::new_v4(),
"Discovered Film",
2024,
))
.await
.unwrap();
let recorded = movies.recorded_events();
assert!(recorded.iter().any(|e| matches!(
e,
domain::events::DomainEvent::MovieEnrichmentRequested { .. }
)));
assert!(
recorded
.iter()
.any(|e| matches!(e, domain::events::DomainEvent::ReviewLogged { .. }))
);
}