refactor: LOW+MEDIUM cleanups — CQRS DiaryQuery/GoalCommand+GoalQuery,
test-helpers out of production, decompose get_user_profile_html, dedup secure_flag, remove vestigial social_query, drop PersistedImportSession, LoginCommand rename, DeleteAccountDeps, PersonId macro, ReviewSortBy rename, TUI Command for fs::read, generic PaginatedResponse<T>, noop ports for production fallbacks
This commit is contained in:
@@ -13,7 +13,7 @@ pub async fn execute(
|
||||
) -> Result<GoalWithProgress, DomainError> {
|
||||
let user_id = UserId::from_uuid(cmd.user_id);
|
||||
|
||||
let existing = deps.goal.find_by_user_and_year(&user_id, cmd.year).await?;
|
||||
let existing = deps.goal_query.find_by_user_and_year(&user_id, cmd.year).await?;
|
||||
if existing.is_some() {
|
||||
return Err(DomainError::ValidationError(
|
||||
"Goal already exists for this year".into(),
|
||||
@@ -26,7 +26,7 @@ pub async fn execute(
|
||||
cmd.target_count,
|
||||
GoalType::Movies,
|
||||
)?;
|
||||
deps.goal.save(&g).await?;
|
||||
deps.goal_command.save(&g).await?;
|
||||
|
||||
let current_count = deps.stats.count_reviews_in_year(&user_id, cmd.year).await?;
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@ pub async fn execute(deps: &GoalCommandDeps, cmd: DeleteGoalCommand) -> Result<(
|
||||
let user_id = UserId::from_uuid(cmd.user_id);
|
||||
|
||||
let g = deps
|
||||
.goal
|
||||
.goal_query
|
||||
.find_by_user_and_year(&user_id, cmd.year)
|
||||
.await?
|
||||
.ok_or_else(|| DomainError::NotFound(format!("Goal for year {}", cmd.year)))?;
|
||||
|
||||
deps.goal.delete(g.id(), &user_id).await?;
|
||||
deps.goal_command.delete(g.id(), &user_id).await?;
|
||||
|
||||
deps.event_publisher
|
||||
.publish(&DomainEvent::GoalDeleted {
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use domain::ports::{EventPublisher, GoalRepository, StatsRepository};
|
||||
use domain::ports::{EventPublisher, GoalCommand, GoalQuery, StatsRepository};
|
||||
|
||||
pub struct GoalCommandDeps {
|
||||
pub goal: Arc<dyn GoalRepository>,
|
||||
pub goal_command: Arc<dyn GoalCommand>,
|
||||
pub goal_query: Arc<dyn GoalQuery>,
|
||||
pub stats: Arc<dyn StatsRepository>,
|
||||
pub event_publisher: Arc<dyn EventPublisher>,
|
||||
}
|
||||
|
||||
pub struct GoalQueryDeps {
|
||||
pub goal: Arc<dyn GoalRepository>,
|
||||
pub goal_query: Arc<dyn GoalQuery>,
|
||||
pub stats: Arc<dyn StatsRepository>,
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ pub async fn execute(
|
||||
let user_id = UserId::from_uuid(query.user_id);
|
||||
|
||||
let found = deps
|
||||
.goal
|
||||
.goal_query
|
||||
.find_by_user_and_year(&user_id, query.year)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ pub async fn execute(
|
||||
query: ListGoalsQuery,
|
||||
) -> Result<Vec<GoalWithProgress>, DomainError> {
|
||||
let user_id = UserId::from_uuid(query.user_id);
|
||||
let goals = deps.goal.list_for_user(&user_id).await?;
|
||||
let goals = deps.goal_query.list_for_user(&user_id).await?;
|
||||
|
||||
let mut result = Vec::with_capacity(goals.len());
|
||||
for g in goals {
|
||||
|
||||
@@ -14,7 +14,8 @@ async fn creates_goal_and_returns_progress() {
|
||||
let stats = FakeStatsRepository::new();
|
||||
let events = NoopEventPublisher::new();
|
||||
let deps = GoalCommandDeps {
|
||||
goal: Arc::clone(&goals) as _,
|
||||
goal_command: Arc::clone(&goals) as _,
|
||||
goal_query: Arc::clone(&goals) as _,
|
||||
stats: Arc::clone(&stats) as _,
|
||||
event_publisher: Arc::clone(&events) as _,
|
||||
};
|
||||
@@ -43,7 +44,8 @@ async fn creates_goal_with_review_count() {
|
||||
stats.set_review_count(Uuid::nil(), 2025, 5);
|
||||
let events = NoopEventPublisher::new();
|
||||
let deps = GoalCommandDeps {
|
||||
goal: Arc::clone(&goals) as _,
|
||||
goal_command: Arc::clone(&goals) as _,
|
||||
goal_query: Arc::clone(&goals) as _,
|
||||
stats: Arc::clone(&stats) as _,
|
||||
event_publisher: Arc::clone(&events) as _,
|
||||
};
|
||||
@@ -68,7 +70,8 @@ async fn emits_goal_created_event() {
|
||||
let b = TestContextBuilder::new();
|
||||
let events = NoopEventPublisher::new();
|
||||
let deps = GoalCommandDeps {
|
||||
goal: b.goal_repo.clone(),
|
||||
goal_command: b.goal_command.clone(),
|
||||
goal_query: b.goal_query.clone(),
|
||||
stats: b.stats_repo.clone(),
|
||||
event_publisher: Arc::clone(&events) as _,
|
||||
};
|
||||
@@ -96,7 +99,8 @@ async fn emits_goal_created_event() {
|
||||
async fn rejects_duplicate_year() {
|
||||
let b = TestContextBuilder::new();
|
||||
let deps = GoalCommandDeps {
|
||||
goal: b.goal_repo.clone(),
|
||||
goal_command: b.goal_command.clone(),
|
||||
goal_query: b.goal_query.clone(),
|
||||
stats: b.stats_repo.clone(),
|
||||
event_publisher: b.event_publisher.clone(),
|
||||
};
|
||||
@@ -125,7 +129,8 @@ async fn rejects_duplicate_year() {
|
||||
async fn rejects_year_before_2020() {
|
||||
let b = TestContextBuilder::new();
|
||||
let deps = GoalCommandDeps {
|
||||
goal: b.goal_repo.clone(),
|
||||
goal_command: b.goal_command.clone(),
|
||||
goal_query: b.goal_query.clone(),
|
||||
stats: b.stats_repo.clone(),
|
||||
event_publisher: b.event_publisher.clone(),
|
||||
};
|
||||
@@ -146,7 +151,8 @@ async fn rejects_year_before_2020() {
|
||||
async fn rejects_zero_target() {
|
||||
let b = TestContextBuilder::new();
|
||||
let deps = GoalCommandDeps {
|
||||
goal: b.goal_repo.clone(),
|
||||
goal_command: b.goal_command.clone(),
|
||||
goal_query: b.goal_query.clone(),
|
||||
stats: b.stats_repo.clone(),
|
||||
event_publisher: b.event_publisher.clone(),
|
||||
};
|
||||
|
||||
@@ -16,7 +16,8 @@ async fn deletes_existing_goal() {
|
||||
let stats = FakeStatsRepository::new();
|
||||
let events = NoopEventPublisher::new();
|
||||
let deps = GoalCommandDeps {
|
||||
goal: Arc::clone(&goals) as _,
|
||||
goal_command: Arc::clone(&goals) as _,
|
||||
goal_query: Arc::clone(&goals) as _,
|
||||
stats: Arc::clone(&stats) as _,
|
||||
event_publisher: Arc::clone(&events) as _,
|
||||
};
|
||||
@@ -50,7 +51,8 @@ async fn deletes_existing_goal() {
|
||||
async fn fails_when_not_found() {
|
||||
let b = TestContextBuilder::new();
|
||||
let deps = GoalCommandDeps {
|
||||
goal: b.goal_repo.clone(),
|
||||
goal_command: b.goal_command.clone(),
|
||||
goal_query: b.goal_query.clone(),
|
||||
stats: b.stats_repo.clone(),
|
||||
event_publisher: b.event_publisher.clone(),
|
||||
};
|
||||
|
||||
@@ -8,12 +8,13 @@ use crate::test_helpers::TestContextBuilder;
|
||||
async fn returns_goal_when_exists() {
|
||||
let b = TestContextBuilder::new();
|
||||
let cmd_deps = GoalCommandDeps {
|
||||
goal: b.goal_repo.clone(),
|
||||
goal_command: b.goal_command.clone(),
|
||||
goal_query: b.goal_query.clone(),
|
||||
stats: b.stats_repo.clone(),
|
||||
event_publisher: b.event_publisher.clone(),
|
||||
};
|
||||
let query_deps = GoalQueryDeps {
|
||||
goal: b.goal_repo.clone(),
|
||||
goal_query: b.goal_query.clone(),
|
||||
stats: b.stats_repo.clone(),
|
||||
};
|
||||
|
||||
@@ -46,7 +47,7 @@ async fn returns_goal_when_exists() {
|
||||
async fn returns_none_when_missing() {
|
||||
let b = TestContextBuilder::new();
|
||||
let query_deps = GoalQueryDeps {
|
||||
goal: b.goal_repo.clone(),
|
||||
goal_query: b.goal_query.clone(),
|
||||
stats: b.stats_repo.clone(),
|
||||
};
|
||||
let result = get::execute(
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::test_helpers::TestContextBuilder;
|
||||
async fn returns_empty_when_no_goals() {
|
||||
let b = TestContextBuilder::new();
|
||||
let query_deps = GoalQueryDeps {
|
||||
goal: b.goal_repo.clone(),
|
||||
goal_query: b.goal_query.clone(),
|
||||
stats: b.stats_repo.clone(),
|
||||
};
|
||||
let result = list::execute(
|
||||
@@ -27,12 +27,13 @@ async fn returns_empty_when_no_goals() {
|
||||
async fn returns_all_goals_for_user() {
|
||||
let b = TestContextBuilder::new();
|
||||
let cmd_deps = GoalCommandDeps {
|
||||
goal: b.goal_repo.clone(),
|
||||
goal_command: b.goal_command.clone(),
|
||||
goal_query: b.goal_query.clone(),
|
||||
stats: b.stats_repo.clone(),
|
||||
event_publisher: b.event_publisher.clone(),
|
||||
};
|
||||
let query_deps = GoalQueryDeps {
|
||||
goal: b.goal_repo.clone(),
|
||||
goal_query: b.goal_query.clone(),
|
||||
stats: b.stats_repo.clone(),
|
||||
};
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ use crate::test_helpers::TestContextBuilder;
|
||||
async fn updates_target_count() {
|
||||
let b = TestContextBuilder::new();
|
||||
let deps = GoalCommandDeps {
|
||||
goal: b.goal_repo.clone(),
|
||||
goal_command: b.goal_command.clone(),
|
||||
goal_query: b.goal_query.clone(),
|
||||
stats: b.stats_repo.clone(),
|
||||
event_publisher: b.event_publisher.clone(),
|
||||
};
|
||||
@@ -45,7 +46,8 @@ async fn updates_target_count() {
|
||||
async fn fails_when_goal_not_found() {
|
||||
let b = TestContextBuilder::new();
|
||||
let deps = GoalCommandDeps {
|
||||
goal: b.goal_repo.clone(),
|
||||
goal_command: b.goal_command.clone(),
|
||||
goal_query: b.goal_query.clone(),
|
||||
stats: b.stats_repo.clone(),
|
||||
event_publisher: b.event_publisher.clone(),
|
||||
};
|
||||
@@ -66,7 +68,8 @@ async fn fails_when_goal_not_found() {
|
||||
async fn rejects_zero_target() {
|
||||
let b = TestContextBuilder::new();
|
||||
let deps = GoalCommandDeps {
|
||||
goal: b.goal_repo.clone(),
|
||||
goal_command: b.goal_command.clone(),
|
||||
goal_query: b.goal_query.clone(),
|
||||
stats: b.stats_repo.clone(),
|
||||
event_publisher: b.event_publisher.clone(),
|
||||
};
|
||||
|
||||
@@ -11,13 +11,13 @@ pub async fn execute(
|
||||
let user_id = UserId::from_uuid(cmd.user_id);
|
||||
|
||||
let mut g = deps
|
||||
.goal
|
||||
.goal_query
|
||||
.find_by_user_and_year(&user_id, cmd.year)
|
||||
.await?
|
||||
.ok_or_else(|| DomainError::NotFound(format!("Goal for year {}", cmd.year)))?;
|
||||
|
||||
g.update_target(cmd.target_count)?;
|
||||
deps.goal.update(&g).await?;
|
||||
deps.goal_command.update(&g).await?;
|
||||
|
||||
let current_count = deps.stats.count_reviews_in_year(&user_id, cmd.year).await?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user