feat(domain): add WrapUpStatsQuery port and in-memory fake
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
use async_trait::async_trait;
|
||||
use chrono::{DateTime, Utc};
|
||||
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
errors::DomainError,
|
||||
@@ -12,6 +13,7 @@ use crate::{
|
||||
ReviewHistory, SearchQuery, SearchResults, User, UserStats, UserSummary, UserTrends,
|
||||
WatchEvent, WatchEventStatus, WatchlistEntry, WatchlistWithMovie, WebhookToken,
|
||||
collections::{self, PageParams, Paginated},
|
||||
wrapup::{DateRange, WrapUpScope},
|
||||
},
|
||||
value_objects::{
|
||||
Email, ExternalMetadataId, ImportProfileId, ImportSessionId, MovieId, MovieTitle,
|
||||
@@ -468,3 +470,33 @@ pub trait WebhookTokenRepository: Send + Sync {
|
||||
async fn delete(&self, id: &WebhookTokenId, user_id: &UserId) -> Result<(), DomainError>;
|
||||
async fn touch_last_used(&self, id: &WebhookTokenId) -> Result<(), DomainError>;
|
||||
}
|
||||
|
||||
// ── Wrap-up / Year-in-Review ─────────────────────────────────────────────────
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct WrapUpMovieRow {
|
||||
pub movie_id: Uuid,
|
||||
pub title: String,
|
||||
pub release_year: u16,
|
||||
pub director: Option<String>,
|
||||
pub poster_path: Option<String>,
|
||||
pub rating: u8,
|
||||
pub watched_at: NaiveDateTime,
|
||||
pub user_id: Uuid,
|
||||
pub runtime_minutes: Option<u32>,
|
||||
pub budget_usd: Option<i64>,
|
||||
pub original_language: Option<String>,
|
||||
pub genres: Vec<String>,
|
||||
pub keywords: Vec<String>,
|
||||
pub cast_names: Vec<(String, u32)>,
|
||||
pub cast_profile_paths: Vec<Option<String>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait WrapUpStatsQuery: Send + Sync {
|
||||
async fn get_reviews_with_profiles(
|
||||
&self,
|
||||
scope: &WrapUpScope,
|
||||
range: &DateRange,
|
||||
) -> Result<Vec<WrapUpMovieRow>, DomainError>;
|
||||
}
|
||||
|
||||
@@ -991,3 +991,47 @@ impl crate::ports::WebhookTokenRepository for PanicWebhookTokenRepository {
|
||||
panic!("PanicWebhookTokenRepository called")
|
||||
}
|
||||
}
|
||||
|
||||
// ── InMemoryWrapUpStatsQuery ────────────────────────────────────────────────
|
||||
|
||||
pub struct InMemoryWrapUpStatsQuery {
|
||||
pub rows: Mutex<Vec<crate::ports::WrapUpMovieRow>>,
|
||||
}
|
||||
|
||||
impl InMemoryWrapUpStatsQuery {
|
||||
pub fn new() -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
rows: Mutex::new(Vec::new()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_rows(rows: Vec<crate::ports::WrapUpMovieRow>) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
rows: Mutex::new(rows),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl crate::ports::WrapUpStatsQuery for InMemoryWrapUpStatsQuery {
|
||||
async fn get_reviews_with_profiles(
|
||||
&self,
|
||||
scope: &crate::models::wrapup::WrapUpScope,
|
||||
range: &crate::models::wrapup::DateRange,
|
||||
) -> Result<Vec<crate::ports::WrapUpMovieRow>, DomainError> {
|
||||
let rows = self.rows.lock().unwrap();
|
||||
let filtered: Vec<_> = rows
|
||||
.iter()
|
||||
.filter(|r| {
|
||||
let date = r.watched_at.date();
|
||||
date >= range.start && date < range.end
|
||||
})
|
||||
.filter(|r| match scope {
|
||||
crate::models::wrapup::WrapUpScope::User(uid) => r.user_id == *uid,
|
||||
crate::models::wrapup::WrapUpScope::Global => true,
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
Ok(filtered)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user