Files
movies-diary/crates/application/src/insights/tests/get.rs
Gabriel Kaszewski 5cb6fd5fbb
All checks were successful
CI / Check / Test (push) Successful in 1h12m41s
CI / Check / Test (pull_request) Successful in 1h6m20s
Refactor insights handling and error encoding in presentation layer
- Updated insights analyzer tests to use Utc for date handling.
- Added timezone support for insights retrieval, allowing insights to be bucketed by the user's local time.
- Introduced `encode_domain_error` for better error handling in API responses.
- Modified various handlers to utilize the new error encoding function.
- Enhanced insights API to reject unknown time zones and return appropriate errors.
- Updated frontend components to handle insights data with timezone awareness.
- Improved localization for insights sample counts.
2026-10-12 20:44:54 +02:00

100 lines
2.7 KiB
Rust

use std::sync::Arc;
use chrono::NaiveDate;
use uuid::Uuid;
use domain::{models::insights::WatchRow, testing::InMemoryInsightsQuery, value_objects::UserId};
use super::*;
fn watch(genre: &str, rating: u8, day: u32, hour: u32) -> WatchRow {
WatchRow {
movie_id: Uuid::new_v4(),
rating,
watched_at: NaiveDate::from_ymd_opt(2024, 1, day)
.unwrap()
.and_hms_opt(hour, 0, 0)
.unwrap(),
watch_medium: Some("streaming".to_string()),
genres: vec![genre.to_string()],
}
}
#[tokio::test]
async fn returns_analyzer_output_for_the_user() {
let user = Uuid::new_v4();
let rows: Vec<WatchRow> = (0..12)
.map(|i| watch("Thriller", 5, 1 + i, 22))
.chain((0..12).map(|i| watch("Comedy", 2, 13 + i, 22)))
.collect();
let deps = GetInsightsDeps {
insights: InMemoryInsightsQuery::with_rows(user, rows),
};
let insights = execute(&deps, UserId::from_uuid(user), &chrono::Utc)
.await
.unwrap();
assert!(insights.iter().any(
|i| matches!(i, domain::models::Insight::RatingLift { genre, .. } if genre == "Thriller")
));
}
#[tokio::test]
async fn other_users_rows_are_not_seen() {
let user = Uuid::new_v4();
let other = Uuid::new_v4();
let rows: Vec<WatchRow> = (0..12)
.map(|i| watch("Thriller", 5, 1 + i, 22))
.chain((0..12).map(|i| watch("Comedy", 2, 13 + i, 22)))
.collect();
let deps = GetInsightsDeps {
insights: InMemoryInsightsQuery::with_rows(other, rows),
};
let insights = execute(&deps, UserId::from_uuid(user), &chrono::Utc)
.await
.unwrap();
assert!(insights.is_empty());
}
#[tokio::test]
async fn empty_diary_returns_empty_list() {
let user = Uuid::new_v4();
let deps = GetInsightsDeps {
insights: InMemoryInsightsQuery::new(),
};
let insights = execute(&deps, UserId::from_uuid(user), &chrono::Utc)
.await
.unwrap();
assert!(insights.is_empty());
}
#[tokio::test]
async fn port_errors_propagate() {
struct Failing;
#[async_trait::async_trait]
impl domain::ports::InsightsQuery for Failing {
async fn get_watch_rows(
&self,
_: &UserId,
) -> Result<Vec<WatchRow>, domain::errors::DomainError> {
Err(domain::errors::DomainError::InfrastructureError(
"db down".into(),
))
}
}
let deps = GetInsightsDeps {
insights: Arc::new(Failing),
};
let err = execute(&deps, UserId::from_uuid(Uuid::new_v4()), &chrono::Utc)
.await
.unwrap_err();
assert!(matches!(
err,
domain::errors::DomainError::InfrastructureError(_)
));
}