From ddf100cfc25aabc217a38e61a9b6e96dd2310764 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Thu, 11 Jun 2026 22:29:30 +0200 Subject: [PATCH] =?UTF-8?q?refactor(wrapup):=20scoped=20deps=20=E2=80=94?= =?UTF-8?q?=20HandleWrapUpRequestedDeps,=20flat-Arc=20jobs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/application/src/jobs/wrapup.rs | 59 ++++++++++++------- crates/application/src/wrapup/compute.rs | 10 ++-- crates/application/src/wrapup/delete.rs | 15 +++-- crates/application/src/wrapup/deps.rs | 9 +++ .../application/src/wrapup/event_handler.rs | 20 +++++-- crates/application/src/wrapup/generate.rs | 21 ++++--- crates/application/src/wrapup/get_wrapup.rs | 12 ++-- .../src/wrapup/handle_requested.rs | 21 +++---- crates/application/src/wrapup/list_wrapups.rs | 11 ++-- crates/application/src/wrapup/mod.rs | 1 + .../application/src/wrapup/tests/compute.rs | 13 ++-- crates/application/src/wrapup/tests/delete.rs | 18 +----- .../application/src/wrapup/tests/generate.rs | 57 +++++------------- .../src/wrapup/tests/get_wrapup.rs | 18 +----- .../src/wrapup/tests/handle_requested.rs | 43 +++++++------- .../src/wrapup/tests/list_wrapups.rs | 25 +------- crates/presentation/src/handlers/wrapup.rs | 15 +++-- crates/worker/src/main.rs | 22 +++++-- 18 files changed, 185 insertions(+), 205 deletions(-) create mode 100644 crates/application/src/wrapup/deps.rs diff --git a/crates/application/src/jobs/wrapup.rs b/crates/application/src/jobs/wrapup.rs index 12e3e86..c4adc50 100644 --- a/crates/application/src/jobs/wrapup.rs +++ b/crates/application/src/jobs/wrapup.rs @@ -1,18 +1,30 @@ +use std::sync::Arc; use std::time::Duration; use async_trait::async_trait; use chrono::Datelike; -use domain::{errors::DomainError, ports::PeriodicJob}; - -use crate::context::AppContext; +use domain::{ + errors::DomainError, + ports::{EventPublisher, PeriodicJob, UserRepository, WrapUpRepository}, +}; pub struct WrapUpAutoGenerateJob { - ctx: AppContext, + user: Arc, + wrapup_repo: Arc, + event_publisher: Arc, } impl WrapUpAutoGenerateJob { - pub fn new(ctx: AppContext) -> Self { - Self { ctx } + pub fn new( + user: Arc, + wrapup_repo: Arc, + event_publisher: Arc, + ) -> Self { + Self { + user, + wrapup_repo, + event_publisher, + } } } @@ -33,12 +45,10 @@ impl PeriodicJob for WrapUpAutoGenerateJob { let end = chrono::NaiveDate::from_ymd_opt(year + 1, 1, 1) .ok_or_else(|| DomainError::ValidationError("invalid date".into()))?; - let users = self.ctx.repos.user.list_with_stats().await?; + let users = self.user.list_with_stats().await?; for user in &users { if user.total_movies > 0 { let existing = self - .ctx - .repos .wrapup_repo .find_existing(Some(user.user_id.value()), start, end) .await?; @@ -48,7 +58,13 @@ impl PeriodicJob for WrapUpAutoGenerateJob { start_date: start, end_date: end, }; - if let Err(e) = crate::wrapup::generate::execute(&self.ctx, cmd).await { + if let Err(e) = crate::wrapup::generate::execute( + self.wrapup_repo.clone(), + self.event_publisher.clone(), + cmd, + ) + .await + { tracing::warn!( "auto-generate wrapup for user {} failed: {e}", user.user_id.value() @@ -58,19 +74,20 @@ impl PeriodicJob for WrapUpAutoGenerateJob { } } - let existing = self - .ctx - .repos - .wrapup_repo - .find_existing(None, start, end) - .await?; + let existing = self.wrapup_repo.find_existing(None, start, end).await?; if existing.is_none() { let cmd = crate::wrapup::commands::RequestWrapUpCommand { user_id: None, start_date: start, end_date: end, }; - if let Err(e) = crate::wrapup::generate::execute(&self.ctx, cmd).await { + if let Err(e) = crate::wrapup::generate::execute( + self.wrapup_repo.clone(), + self.event_publisher.clone(), + cmd, + ) + .await + { tracing::warn!("auto-generate global wrapup failed: {e}"); } } @@ -80,12 +97,12 @@ impl PeriodicJob for WrapUpAutoGenerateJob { } pub struct WrapUpCleanupJob { - ctx: AppContext, + wrapup_repo: Arc, } impl WrapUpCleanupJob { - pub fn new(ctx: AppContext) -> Self { - Self { ctx } + pub fn new(wrapup_repo: Arc) -> Self { + Self { wrapup_repo } } } @@ -98,8 +115,6 @@ impl PeriodicJob for WrapUpCleanupJob { async fn run(&self) -> Result<(), DomainError> { let cutoff = chrono::Utc::now().naive_utc() - chrono::Duration::days(7); let n = self - .ctx - .repos .wrapup_repo .delete_failed_older_than(cutoff) .await?; diff --git a/crates/application/src/wrapup/compute.rs b/crates/application/src/wrapup/compute.rs index 4f62d42..5a4b56e 100644 --- a/crates/application/src/wrapup/compute.rs +++ b/crates/application/src/wrapup/compute.rs @@ -1,16 +1,16 @@ -use crate::context::AppContext; +use std::sync::Arc; + use crate::wrapup::queries::ComputeWrapUpQuery; use domain::errors::DomainError; use domain::models::wrapup::WrapUpReport; +use domain::ports::WrapUpStatsQuery; use domain::services::wrapup_analyzer; pub async fn execute( - ctx: &AppContext, + wrapup_stats: Arc, query: ComputeWrapUpQuery, ) -> Result { - let rows = ctx - .repos - .wrapup_stats + let rows = wrapup_stats .get_reviews_with_profiles(&query.scope, &query.date_range) .await?; Ok(wrapup_analyzer::build_report( diff --git a/crates/application/src/wrapup/delete.rs b/crates/application/src/wrapup/delete.rs index 7631f8c..1729ea7 100644 --- a/crates/application/src/wrapup/delete.rs +++ b/crates/application/src/wrapup/delete.rs @@ -1,16 +1,19 @@ +use std::sync::Arc; + use domain::errors::DomainError; +use domain::ports::WrapUpRepository; use domain::value_objects::WrapUpId; -use crate::context::AppContext; - -pub async fn execute(ctx: &AppContext, id: WrapUpId) -> Result<(), DomainError> { - ctx.repos - .wrapup_repo +pub async fn execute( + wrapup_repo: Arc, + id: WrapUpId, +) -> Result<(), DomainError> { + wrapup_repo .get_by_id(&id) .await? .ok_or_else(|| DomainError::NotFound("wrap-up not found".into()))?; - ctx.repos.wrapup_repo.delete(&id).await + wrapup_repo.delete(&id).await } #[cfg(test)] diff --git a/crates/application/src/wrapup/deps.rs b/crates/application/src/wrapup/deps.rs new file mode 100644 index 0000000..b57f806 --- /dev/null +++ b/crates/application/src/wrapup/deps.rs @@ -0,0 +1,9 @@ +use std::sync::Arc; + +use domain::ports::{EventPublisher, WrapUpRepository, WrapUpStatsQuery}; + +pub struct HandleWrapUpRequestedDeps { + pub wrapup_repo: Arc, + pub event_publisher: Arc, + pub wrapup_stats: Arc, +} diff --git a/crates/application/src/wrapup/event_handler.rs b/crates/application/src/wrapup/event_handler.rs index f0d2b9e..351b769 100644 --- a/crates/application/src/wrapup/event_handler.rs +++ b/crates/application/src/wrapup/event_handler.rs @@ -3,20 +3,28 @@ use std::sync::Arc; use async_trait::async_trait; use domain::errors::DomainError; use domain::events::DomainEvent; -use domain::ports::EventHandler; +use domain::ports::{EventHandler, EventPublisher, WrapUpRepository, WrapUpStatsQuery}; use tokio::sync::Semaphore; -use crate::context::AppContext; +use super::deps::HandleWrapUpRequestedDeps; pub struct WrapUpEventHandler { - ctx: AppContext, + deps: HandleWrapUpRequestedDeps, semaphore: Arc, } impl WrapUpEventHandler { - pub fn new(ctx: AppContext) -> Self { + pub fn new( + wrapup_repo: Arc, + event_publisher: Arc, + wrapup_stats: Arc, + ) -> Self { Self { - ctx, + deps: HandleWrapUpRequestedDeps { + wrapup_repo, + event_publisher, + wrapup_stats, + }, semaphore: Arc::new(Semaphore::new(2)), } } @@ -36,7 +44,7 @@ impl EventHandler for WrapUpEventHandler { DomainError::InfrastructureError("render semaphore closed".into()) })?; super::handle_requested::execute( - &self.ctx, + &self.deps, wrapup_id.clone(), user_id.as_ref().map(|u| u.value()), *start_date, diff --git a/crates/application/src/wrapup/generate.rs b/crates/application/src/wrapup/generate.rs index 329e834..fd3e05e 100644 --- a/crates/application/src/wrapup/generate.rs +++ b/crates/application/src/wrapup/generate.rs @@ -1,13 +1,19 @@ +use std::sync::Arc; + use chrono::Utc; use domain::errors::DomainError; use domain::events::DomainEvent; use domain::models::wrapup::{DateRange, WrapUpStatus}; +use domain::ports::{EventPublisher, WrapUpRepository}; use domain::value_objects::{UserId, WrapUpId}; -use crate::context::AppContext; use crate::wrapup::commands::RequestWrapUpCommand; -pub async fn execute(ctx: &AppContext, cmd: RequestWrapUpCommand) -> Result { +pub async fn execute( + wrapup_repo: Arc, + event_publisher: Arc, + cmd: RequestWrapUpCommand, +) -> Result { let date_range = DateRange::new(cmd.start_date, cmd.end_date)?; if cmd.end_date > Utc::now().date_naive() { @@ -16,9 +22,7 @@ pub async fn execute(ctx: &AppContext, cmd: RequestWrapUpCommand) -> Result Result return Ok(rec.id.clone()), WrapUpStatus::Failed => { - ctx.repos.wrapup_repo.delete(&rec.id).await?; + wrapup_repo.delete(&rec.id).await?; } WrapUpStatus::Pending => return Ok(rec.id.clone()), } @@ -44,10 +48,9 @@ pub async fn execute(ctx: &AppContext, cmd: RequestWrapUpCommand) -> Result Result, DomainError> { - ctx.repos.wrapup_repo.get_by_id(&id).await +pub async fn execute( + wrapup_repo: Arc, + id: WrapUpId, +) -> Result, DomainError> { + wrapup_repo.get_by_id(&id).await } #[cfg(test)] diff --git a/crates/application/src/wrapup/handle_requested.rs b/crates/application/src/wrapup/handle_requested.rs index fa6a0be..25faf38 100644 --- a/crates/application/src/wrapup/handle_requested.rs +++ b/crates/application/src/wrapup/handle_requested.rs @@ -1,18 +1,17 @@ -use crate::context::AppContext; -use crate::wrapup::{compute, queries::ComputeWrapUpQuery}; +use crate::wrapup::{compute, deps::HandleWrapUpRequestedDeps, queries::ComputeWrapUpQuery}; use domain::errors::DomainError; use domain::events::DomainEvent; use domain::models::wrapup::{DateRange, WrapUpScope, WrapUpStatus}; use domain::value_objects::WrapUpId; pub async fn execute( - ctx: &AppContext, + deps: &HandleWrapUpRequestedDeps, wrapup_id: WrapUpId, user_id: Option, start_date: chrono::NaiveDate, end_date: chrono::NaiveDate, ) -> Result<(), DomainError> { - if let Ok(Some(rec)) = ctx.repos.wrapup_repo.get_by_id(&wrapup_id).await + if let Ok(Some(rec)) = deps.wrapup_repo.get_by_id(&wrapup_id).await && (rec.status == WrapUpStatus::Ready || rec.status == WrapUpStatus::Generating) { tracing::debug!( @@ -23,8 +22,7 @@ pub async fn execute( return Ok(()); } - ctx.repos - .wrapup_repo + deps.wrapup_repo .update_status(&wrapup_id, &WrapUpStatus::Generating, None) .await?; @@ -37,22 +35,19 @@ pub async fn execute( date_range: DateRange::new(start_date, end_date)?, }; - match compute::execute(ctx, query).await { + match compute::execute(deps.wrapup_stats.clone(), query).await { Ok(report) => { - ctx.repos - .wrapup_repo + deps.wrapup_repo .set_complete(&wrapup_id, &report) .await?; - ctx.services - .event_publisher + deps.event_publisher .publish(&DomainEvent::WrapUpCompleted { wrapup_id }) .await?; Ok(()) } Err(e) => { - ctx.repos - .wrapup_repo + deps.wrapup_repo .update_status(&wrapup_id, &WrapUpStatus::Failed, Some(&e.to_string())) .await?; Err(e) diff --git a/crates/application/src/wrapup/list_wrapups.rs b/crates/application/src/wrapup/list_wrapups.rs index 1f42d94..ad4d9e3 100644 --- a/crates/application/src/wrapup/list_wrapups.rs +++ b/crates/application/src/wrapup/list_wrapups.rs @@ -1,21 +1,22 @@ +use std::sync::Arc; + use uuid::Uuid; use domain::errors::DomainError; use domain::models::wrapup::WrapUpRecord; - -use crate::context::AppContext; +use domain::ports::WrapUpRepository; pub struct ListWrapUpsQuery { pub user_id: Option, } pub async fn execute( - ctx: &AppContext, + wrapup_repo: Arc, query: ListWrapUpsQuery, ) -> Result, DomainError> { match query.user_id { - Some(uid) => ctx.repos.wrapup_repo.list_for_user(uid).await, - None => ctx.repos.wrapup_repo.list_global().await, + Some(uid) => wrapup_repo.list_for_user(uid).await, + None => wrapup_repo.list_global().await, } } diff --git a/crates/application/src/wrapup/mod.rs b/crates/application/src/wrapup/mod.rs index d2be4dd..cf9cae8 100644 --- a/crates/application/src/wrapup/mod.rs +++ b/crates/application/src/wrapup/mod.rs @@ -1,6 +1,7 @@ pub mod commands; pub mod compute; pub mod delete; +pub mod deps; pub mod event_handler; pub mod generate; pub mod get_wrapup; diff --git a/crates/application/src/wrapup/tests/compute.rs b/crates/application/src/wrapup/tests/compute.rs index 57c98b0..2768481 100644 --- a/crates/application/src/wrapup/tests/compute.rs +++ b/crates/application/src/wrapup/tests/compute.rs @@ -4,7 +4,6 @@ use domain::ports::WrapUpMovieRow; use domain::testing::InMemoryWrapUpStatsQuery; use uuid::Uuid; -use crate::test_helpers::TestContextBuilder; use crate::wrapup::queries::ComputeWrapUpQuery; fn make_row(title: &str, rating: u8, watched_at: &str) -> WrapUpMovieRow { @@ -42,11 +41,10 @@ fn year_2024_range() -> DateRange { #[tokio::test] async fn empty_report() { let stats = InMemoryWrapUpStatsQuery::new(); - let ctx = TestContextBuilder::new().wrapup_stats(stats).build(); let user_id = Uuid::new_v4(); let report = super::execute( - &ctx, + stats, ComputeWrapUpQuery { scope: WrapUpScope::User(user_id), date_range: year_2024_range(), @@ -74,10 +72,9 @@ async fn basic_stats() { r2.genres = vec!["Comedy".to_string()]; let stats = InMemoryWrapUpStatsQuery::with_rows(vec![r1, r2]); - let ctx = TestContextBuilder::new().wrapup_stats(stats).build(); let report = super::execute( - &ctx, + stats, ComputeWrapUpQuery { scope: WrapUpScope::User(user_id), date_range: year_2024_range(), @@ -109,10 +106,9 @@ async fn rewatch_detection() { r2.movie_id = movie_id; let stats = InMemoryWrapUpStatsQuery::with_rows(vec![r1, r2]); - let ctx = TestContextBuilder::new().wrapup_stats(stats).build(); let report = super::execute( - &ctx, + stats, ComputeWrapUpQuery { scope: WrapUpScope::User(user_id), date_range: year_2024_range(), @@ -141,10 +137,9 @@ async fn global_scope() { r2.user_id = user_b; let stats = InMemoryWrapUpStatsQuery::with_rows(vec![r1, r2]); - let ctx = TestContextBuilder::new().wrapup_stats(stats).build(); let report = super::execute( - &ctx, + stats, ComputeWrapUpQuery { scope: WrapUpScope::Global, date_range: year_2024_range(), diff --git a/crates/application/src/wrapup/tests/delete.rs b/crates/application/src/wrapup/tests/delete.rs index 359f503..6a0ffe9 100644 --- a/crates/application/src/wrapup/tests/delete.rs +++ b/crates/application/src/wrapup/tests/delete.rs @@ -1,11 +1,8 @@ -use std::sync::Arc; - use chrono::NaiveDate; use domain::models::wrapup::{WrapUpRecord, WrapUpStatus}; use domain::testing::InMemoryWrapUpRepository; use domain::value_objects::WrapUpId; -use crate::test_helpers::TestContextBuilder; use crate::wrapup::delete; #[tokio::test] @@ -24,22 +21,13 @@ async fn deletes_existing_wrapup() { completed_at: None, }); - let ctx = TestContextBuilder::new().build(); - let ctx = crate::context::AppContext { - repos: crate::context::Repositories { - wrapup_repo: Arc::clone(&repo) as _, - ..ctx.repos - }, - ..ctx - }; - - delete::execute(&ctx, id).await.unwrap(); + delete::execute(repo.clone(), id).await.unwrap(); assert_eq!(repo.store.lock().unwrap().len(), 0); } #[tokio::test] async fn fails_when_not_found() { - let ctx = TestContextBuilder::new().build(); - let result = delete::execute(&ctx, WrapUpId::generate()).await; + let repo = InMemoryWrapUpRepository::new(); + let result = delete::execute(repo.clone(), WrapUpId::generate()).await; assert!(result.is_err()); } diff --git a/crates/application/src/wrapup/tests/generate.rs b/crates/application/src/wrapup/tests/generate.rs index 008b3b4..4168c19 100644 --- a/crates/application/src/wrapup/tests/generate.rs +++ b/crates/application/src/wrapup/tests/generate.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - use chrono::NaiveDate; use domain::events::DomainEvent; use domain::models::wrapup::{WrapUpRecord, WrapUpStatus}; @@ -7,7 +5,6 @@ use domain::testing::{InMemoryWrapUpRepository, NoopEventPublisher}; use domain::value_objects::WrapUpId; use uuid::Uuid; -use crate::test_helpers::TestContextBuilder; use crate::wrapup::{commands::RequestWrapUpCommand, generate}; fn past_cmd() -> RequestWrapUpCommand { @@ -22,22 +19,10 @@ fn past_cmd() -> RequestWrapUpCommand { async fn creates_pending_record_and_emits_event() { let repo = InMemoryWrapUpRepository::new(); let events = NoopEventPublisher::new(); - let ctx = TestContextBuilder::new() - .wrapup_stats(domain::testing::InMemoryWrapUpStatsQuery::new()) - .build(); - let ctx = crate::context::AppContext { - repos: crate::context::Repositories { - wrapup_repo: Arc::clone(&repo) as _, - ..ctx.repos - }, - services: crate::context::Services { - event_publisher: Arc::clone(&events) as _, - ..ctx.services - }, - config: ctx.config, - }; - let id = generate::execute(&ctx, past_cmd()).await.unwrap(); + let id = generate::execute(repo.clone(), events.clone(), past_cmd()) + .await + .unwrap(); let stored = repo.store.lock().unwrap(); assert_eq!(stored.len(), 1); @@ -67,17 +52,11 @@ async fn reuses_existing_ready_wrapup() { created_at: chrono::Utc::now().naive_utc(), completed_at: None, }); + let events = NoopEventPublisher::new(); - let ctx = TestContextBuilder::new().build(); - let ctx = crate::context::AppContext { - repos: crate::context::Repositories { - wrapup_repo: Arc::clone(&repo) as _, - ..ctx.repos - }, - ..ctx - }; - - let id = generate::execute(&ctx, past_cmd()).await.unwrap(); + let id = generate::execute(repo.clone(), events.clone(), past_cmd()) + .await + .unwrap(); assert_eq!(id, existing_id); assert_eq!(repo.store.lock().unwrap().len(), 1); } @@ -98,20 +77,10 @@ async fn replaces_failed_wrapup() { }); let events = NoopEventPublisher::new(); - let ctx = TestContextBuilder::new().build(); - let ctx = crate::context::AppContext { - repos: crate::context::Repositories { - wrapup_repo: Arc::clone(&repo) as _, - ..ctx.repos - }, - services: crate::context::Services { - event_publisher: Arc::clone(&events) as _, - ..ctx.services - }, - config: ctx.config, - }; - let id = generate::execute(&ctx, past_cmd()).await.unwrap(); + let id = generate::execute(repo.clone(), events.clone(), past_cmd()) + .await + .unwrap(); let stored = repo.store.lock().unwrap(); assert_eq!(stored.len(), 1); @@ -121,9 +90,11 @@ async fn replaces_failed_wrapup() { #[tokio::test] async fn rejects_future_end_date() { - let ctx = TestContextBuilder::new().build(); + let repo = InMemoryWrapUpRepository::new(); + let events = NoopEventPublisher::new(); let err = generate::execute( - &ctx, + repo.clone(), + events.clone(), RequestWrapUpCommand { user_id: None, start_date: NaiveDate::from_ymd_opt(2030, 1, 1).unwrap(), diff --git a/crates/application/src/wrapup/tests/get_wrapup.rs b/crates/application/src/wrapup/tests/get_wrapup.rs index 719be72..791bcde 100644 --- a/crates/application/src/wrapup/tests/get_wrapup.rs +++ b/crates/application/src/wrapup/tests/get_wrapup.rs @@ -1,11 +1,8 @@ -use std::sync::Arc; - use chrono::NaiveDate; use domain::models::wrapup::{WrapUpRecord, WrapUpStatus}; use domain::testing::InMemoryWrapUpRepository; use domain::value_objects::WrapUpId; -use crate::test_helpers::TestContextBuilder; use crate::wrapup::get_wrapup; #[tokio::test] @@ -24,24 +21,15 @@ async fn returns_record_when_exists() { completed_at: None, }); - let ctx = TestContextBuilder::new().build(); - let ctx = crate::context::AppContext { - repos: crate::context::Repositories { - wrapup_repo: Arc::clone(&repo) as _, - ..ctx.repos - }, - ..ctx - }; - - let result = get_wrapup::execute(&ctx, id).await.unwrap(); + let result = get_wrapup::execute(repo.clone(), id).await.unwrap(); assert!(result.is_some()); assert_eq!(result.unwrap().status, WrapUpStatus::Pending); } #[tokio::test] async fn returns_none_when_missing() { - let ctx = TestContextBuilder::new().build(); - let result = get_wrapup::execute(&ctx, WrapUpId::generate()) + let repo = InMemoryWrapUpRepository::new(); + let result = get_wrapup::execute(repo.clone(), WrapUpId::generate()) .await .unwrap(); assert!(result.is_none()); diff --git a/crates/application/src/wrapup/tests/handle_requested.rs b/crates/application/src/wrapup/tests/handle_requested.rs index b0bd6aa..e436b96 100644 --- a/crates/application/src/wrapup/tests/handle_requested.rs +++ b/crates/application/src/wrapup/tests/handle_requested.rs @@ -1,13 +1,10 @@ -use std::sync::Arc; - use chrono::{NaiveDate, Utc}; use domain::models::wrapup::{WrapUpRecord, WrapUpStatus}; use domain::ports::WrapUpRepository; -use domain::testing::InMemoryWrapUpRepository; +use domain::testing::{InMemoryWrapUpRepository, InMemoryWrapUpStatsQuery, NoopEventPublisher}; use domain::value_objects::WrapUpId; -use crate::test_helpers::TestContextBuilder; -use crate::wrapup::handle_requested; +use crate::wrapup::{deps::HandleWrapUpRequestedDeps, handle_requested}; #[tokio::test] async fn skips_if_already_ready() { @@ -27,12 +24,14 @@ async fn skips_if_already_ready() { }; repo.create(&record).await.unwrap(); - let ctx = TestContextBuilder::new() - .with_wrapup_repo(Arc::clone(&repo) as _) - .build(); + let deps = HandleWrapUpRequestedDeps { + wrapup_repo: repo.clone(), + event_publisher: NoopEventPublisher::new(), + wrapup_stats: InMemoryWrapUpStatsQuery::new(), + }; let result = handle_requested::execute( - &ctx, + &deps, wrapup_id, None, NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(), @@ -46,8 +45,8 @@ async fn skips_if_already_ready() { #[tokio::test] async fn generates_wrapup_and_marks_complete() { let repo = InMemoryWrapUpRepository::new(); - let stats = domain::testing::InMemoryWrapUpStatsQuery::new(); - let events = domain::testing::NoopEventPublisher::new(); + let stats = InMemoryWrapUpStatsQuery::new(); + let events = NoopEventPublisher::new(); let wrapup_id = WrapUpId::generate(); let uid = uuid::Uuid::new_v4(); @@ -64,14 +63,14 @@ async fn generates_wrapup_and_marks_complete() { }; repo.create(&record).await.unwrap(); - let ctx = TestContextBuilder::new() - .with_wrapup_repo(Arc::clone(&repo) as _) - .wrapup_stats(Arc::clone(&stats) as _) - .with_event_publisher(Arc::clone(&events) as _) - .build(); + let deps = HandleWrapUpRequestedDeps { + wrapup_repo: repo.clone(), + event_publisher: events.clone(), + wrapup_stats: stats.clone(), + }; let result = handle_requested::execute( - &ctx, + &deps, wrapup_id.clone(), Some(uid), NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(), @@ -113,12 +112,14 @@ async fn skips_if_already_generating() { }; repo.create(&record).await.unwrap(); - let ctx = TestContextBuilder::new() - .with_wrapup_repo(Arc::clone(&repo) as _) - .build(); + let deps = HandleWrapUpRequestedDeps { + wrapup_repo: repo.clone(), + event_publisher: NoopEventPublisher::new(), + wrapup_stats: InMemoryWrapUpStatsQuery::new(), + }; let result = handle_requested::execute( - &ctx, + &deps, wrapup_id.clone(), None, NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(), diff --git a/crates/application/src/wrapup/tests/list_wrapups.rs b/crates/application/src/wrapup/tests/list_wrapups.rs index 76f6e47..27ee1d0 100644 --- a/crates/application/src/wrapup/tests/list_wrapups.rs +++ b/crates/application/src/wrapup/tests/list_wrapups.rs @@ -1,12 +1,9 @@ -use std::sync::Arc; - use chrono::NaiveDate; use domain::models::wrapup::{WrapUpRecord, WrapUpStatus}; use domain::testing::InMemoryWrapUpRepository; use domain::value_objects::WrapUpId; use uuid::Uuid; -use crate::test_helpers::TestContextBuilder; use crate::wrapup::list_wrapups::{self, ListWrapUpsQuery}; fn make_record(user_id: Option) -> WrapUpRecord { @@ -34,16 +31,7 @@ async fn filters_by_user() { store.push(make_record(None)); } - let ctx = TestContextBuilder::new().build(); - let ctx = crate::context::AppContext { - repos: crate::context::Repositories { - wrapup_repo: Arc::clone(&repo) as _, - ..ctx.repos - }, - ..ctx - }; - - let result = list_wrapups::execute(&ctx, ListWrapUpsQuery { user_id: Some(uid) }) + let result = list_wrapups::execute(repo.clone(), ListWrapUpsQuery { user_id: Some(uid) }) .await .unwrap(); assert_eq!(result.len(), 1); @@ -60,16 +48,7 @@ async fn returns_global_when_no_user() { store.push(make_record(Some(Uuid::new_v4()))); } - let ctx = TestContextBuilder::new().build(); - let ctx = crate::context::AppContext { - repos: crate::context::Repositories { - wrapup_repo: Arc::clone(&repo) as _, - ..ctx.repos - }, - ..ctx - }; - - let result = list_wrapups::execute(&ctx, ListWrapUpsQuery { user_id: None }) + let result = list_wrapups::execute(repo.clone(), ListWrapUpsQuery { user_id: None }) .await .unwrap(); assert_eq!(result.len(), 2); diff --git a/crates/presentation/src/handlers/wrapup.rs b/crates/presentation/src/handlers/wrapup.rs index 18f5f8e..86b1328 100644 --- a/crates/presentation/src/handlers/wrapup.rs +++ b/crates/presentation/src/handlers/wrapup.rs @@ -66,7 +66,12 @@ pub async fn post_generate( start_date: start, end_date: end, }; - let id = generate::execute(&state.app_ctx, cmd).await?; + let id = generate::execute( + state.app_ctx.repos.wrapup_repo.clone(), + state.app_ctx.services.event_publisher.clone(), + cmd, + ) + .await?; Ok(Json(WrapUpGeneratedResponse { id: id.value().to_string(), })) @@ -85,7 +90,7 @@ pub async fn get_list( user: AuthenticatedUser, ) -> Result, ApiError> { let records = list_wrapups::execute( - &state.app_ctx, + state.app_ctx.repos.wrapup_repo.clone(), ListWrapUpsQuery { user_id: Some(user.0.value()), }, @@ -111,7 +116,7 @@ pub async fn get_status( _user: AuthenticatedUser, Path(id): Path, ) -> Result, ApiError> { - let record = get_wrapup::execute(&state.app_ctx, WrapUpId::from_uuid(id)) + let record = get_wrapup::execute(state.app_ctx.repos.wrapup_repo.clone(), WrapUpId::from_uuid(id)) .await? .ok_or_else(|| DomainError::NotFound("wrap-up not found".into()))?; Ok(Json(record_to_dto(&record))) @@ -133,7 +138,7 @@ pub async fn get_report( _user: AuthenticatedUser, Path(id): Path, ) -> impl IntoResponse { - match get_wrapup::execute(&state.app_ctx, WrapUpId::from_uuid(id)).await { + match get_wrapup::execute(state.app_ctx.repos.wrapup_repo.clone(), WrapUpId::from_uuid(id)).await { Ok(Some(record)) if record.status == WrapUpStatus::Ready => match record.report { Some(ref report) => { let json = serde_json::to_string(report).unwrap_or_default(); @@ -163,7 +168,7 @@ pub async fn delete_wrapup_handler( _admin: AdminApiUser, Path(id): Path, ) -> Result { - delete_wrapup::execute(&state.app_ctx, WrapUpId::from_uuid(id)).await?; + delete_wrapup::execute(state.app_ctx.repos.wrapup_repo.clone(), WrapUpId::from_uuid(id)).await?; Ok(StatusCode::NO_CONTENT) } diff --git a/crates/worker/src/main.rs b/crates/worker/src/main.rs index 006aa44..c3dd86c 100644 --- a/crates/worker/src/main.rs +++ b/crates/worker/src/main.rs @@ -182,8 +182,14 @@ async fn main() -> anyhow::Result<()> { let mut periodic_jobs: Vec> = vec![ Arc::new(application::jobs::ImportSessionCleanupJob::new(ctx.repos.import_session.clone())), Arc::new(application::jobs::WatchEventCleanupJob::new(ctx.repos.watch_event.clone())), - Arc::new(application::jobs::WrapUpAutoGenerateJob::new(ctx.clone())), - Arc::new(application::jobs::WrapUpCleanupJob::new(ctx.clone())), + Arc::new(application::jobs::WrapUpAutoGenerateJob::new( + Arc::clone(&ctx.repos.user), + Arc::clone(&ctx.repos.wrapup_repo), + Arc::clone(&ctx.services.event_publisher), + )), + Arc::new(application::jobs::WrapUpCleanupJob::new( + Arc::clone(&ctx.repos.wrapup_repo), + )), Arc::new(application::jobs::RefreshSessionCleanupJob::new( ctx.clone(), )), @@ -234,7 +240,11 @@ async fn main() -> anyhow::Result<()> { Arc::clone(&ctx.repos.search_command), )) as Arc; let wrapup_handler = Arc::new( - application::wrapup::event_handler::WrapUpEventHandler::new(ctx.clone()), + application::wrapup::event_handler::WrapUpEventHandler::new( + Arc::clone(&ctx.repos.wrapup_repo), + Arc::clone(&ctx.services.event_publisher), + Arc::clone(&ctx.repos.wrapup_stats), + ), ) as Arc; let reindex_handler = Arc::new(SearchReindexHandler::new(ReindexSearchDeps { @@ -297,7 +307,11 @@ async fn main() -> anyhow::Result<()> { )) as Arc; tracing::info!("federation event handler registered"); let wrapup_handler = Arc::new( - application::wrapup::event_handler::WrapUpEventHandler::new(ctx.clone()), + application::wrapup::event_handler::WrapUpEventHandler::new( + Arc::clone(&ctx.repos.wrapup_repo), + Arc::clone(&ctx.services.event_publisher), + Arc::clone(&ctx.repos.wrapup_stats), + ), ) as Arc; let reindex_handler = Arc::new(SearchReindexHandler::new(ReindexSearchDeps {