test: move application tests to tests/
test_support/test_helpers -> tests/support; watched_at unified w/ domain test_watched_at (2024-03-15). Kept inline: users::diary_filter tests (feed_sort_to_direction_all_variants, paged_user_filter_builds_correctly) - pub(super) helpers in private module. Dropped stale never-compiled users/tests/get_users.rs (old execute signature).
This commit is contained in:
226
crates/application/tests/support/commands.rs
Normal file
226
crates/application/tests/support/commands.rs
Normal file
@@ -0,0 +1,226 @@
|
||||
//! Command builders for tests. Each builder returns a command with sensible
|
||||
//! defaults; tests override individual fields with struct-update syntax:
|
||||
//! `LogReviewCommand { rating: 6, ..log_review_command(user, movie) }`.
|
||||
|
||||
use chrono::NaiveDate;
|
||||
use uuid::Uuid;
|
||||
|
||||
use domain::models::{FileFormat, UserRole, WatchEventSource};
|
||||
use domain::testing::test_watched_at;
|
||||
|
||||
use application::auth::commands::RegisterCommand;
|
||||
use application::auth::queries::LoginCommand;
|
||||
use application::diary::commands::{
|
||||
EditReviewCommand, LogReviewCommand, MovieInput, SyncPosterCommand,
|
||||
};
|
||||
use application::goals::commands::{CreateGoalCommand, UpdateGoalCommand};
|
||||
use application::import::commands::{
|
||||
ApplyImportMappingCommand, ApplyImportProfileCommand, CreateImportSessionCommand,
|
||||
ExecuteImportCommand,
|
||||
};
|
||||
use application::integrations::commands::{
|
||||
ConfirmWatchEventsCommand, DismissWatchEventsCommand, GenerateWebhookTokenCommand,
|
||||
WatchEventConfirmation,
|
||||
};
|
||||
use application::users::commands::UpdateProfileCommand;
|
||||
use application::users::update_settings::UpdateUserSettingsCommand;
|
||||
use application::watchlist::commands::AddToWatchlistCommand;
|
||||
use application::wrapup::commands::RequestWrapUpCommand;
|
||||
|
||||
pub fn movie_input_by_id(movie_id: Uuid) -> MovieInput {
|
||||
MovieInput {
|
||||
movie_id: Some(movie_id),
|
||||
external_metadata_id: None,
|
||||
manual_title: None,
|
||||
manual_release_year: None,
|
||||
manual_director: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn movie_input_manual(title: &str, year: u16) -> MovieInput {
|
||||
MovieInput {
|
||||
movie_id: None,
|
||||
external_metadata_id: None,
|
||||
manual_title: Some(title.to_string()),
|
||||
manual_release_year: Some(year),
|
||||
manual_director: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn log_review_command(user_id: Uuid, movie_id: Uuid) -> LogReviewCommand {
|
||||
LogReviewCommand {
|
||||
user_id,
|
||||
input: movie_input_by_id(movie_id),
|
||||
rating: 4,
|
||||
comment: None,
|
||||
watched_at: test_watched_at(),
|
||||
watch_medium: None,
|
||||
spoilers: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn log_review_command_manual(user_id: Uuid, title: &str, year: u16) -> LogReviewCommand {
|
||||
LogReviewCommand {
|
||||
input: movie_input_manual(title, year),
|
||||
..log_review_command(user_id, Uuid::nil())
|
||||
}
|
||||
}
|
||||
|
||||
/// Edits only the rating; `review_id` and `requesting_user_id` are random.
|
||||
pub fn edit_review_command(rating: u8) -> EditReviewCommand {
|
||||
EditReviewCommand {
|
||||
review_id: Uuid::new_v4(),
|
||||
requesting_user_id: Uuid::new_v4(),
|
||||
rating: Some(rating),
|
||||
comment: None,
|
||||
watched_at: None,
|
||||
watch_medium: None,
|
||||
spoilers: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sync_poster_command(movie_id: Uuid) -> SyncPosterCommand {
|
||||
SyncPosterCommand { movie_id }
|
||||
}
|
||||
|
||||
pub fn register_command(email: impl Into<String>, username: impl Into<String>) -> RegisterCommand {
|
||||
RegisterCommand {
|
||||
email: email.into(),
|
||||
username: username.into(),
|
||||
password: "password123".to_string(),
|
||||
role: UserRole::Standard,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn login_command(email: impl Into<String>, password: impl Into<String>) -> LoginCommand {
|
||||
LoginCommand {
|
||||
email: email.into(),
|
||||
password: password.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_goal_command(year: u16, target_count: u32) -> CreateGoalCommand {
|
||||
CreateGoalCommand {
|
||||
user_id: Uuid::nil(),
|
||||
year,
|
||||
target_count,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_goal_command(year: u16, target_count: u32) -> UpdateGoalCommand {
|
||||
UpdateGoalCommand {
|
||||
user_id: Uuid::nil(),
|
||||
year,
|
||||
target_count,
|
||||
}
|
||||
}
|
||||
|
||||
/// Confirms indices `[0]`.
|
||||
pub fn execute_import_command(user_id: Uuid, session_id: Uuid) -> ExecuteImportCommand {
|
||||
ExecuteImportCommand {
|
||||
user_id,
|
||||
session_id,
|
||||
confirmed_indices: vec![0],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_import_session_command(user_id: Uuid, csv: &[u8]) -> CreateImportSessionCommand {
|
||||
CreateImportSessionCommand {
|
||||
user_id,
|
||||
bytes: csv.to_vec(),
|
||||
format: FileFormat::Csv,
|
||||
}
|
||||
}
|
||||
|
||||
/// No field mappings.
|
||||
pub fn apply_import_mapping_command(user_id: Uuid, session_id: Uuid) -> ApplyImportMappingCommand {
|
||||
ApplyImportMappingCommand {
|
||||
user_id,
|
||||
session_id,
|
||||
mappings: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_import_profile_command(
|
||||
user_id: Uuid,
|
||||
session_id: Uuid,
|
||||
profile_id: Uuid,
|
||||
) -> ApplyImportProfileCommand {
|
||||
ApplyImportProfileCommand {
|
||||
user_id,
|
||||
session_id,
|
||||
profile_id,
|
||||
}
|
||||
}
|
||||
|
||||
/// One confirmation, rating as given, no comment.
|
||||
pub fn confirm_watch_events_command(
|
||||
user_id: Uuid,
|
||||
watch_event_id: Uuid,
|
||||
rating: u8,
|
||||
) -> ConfirmWatchEventsCommand {
|
||||
ConfirmWatchEventsCommand {
|
||||
user_id,
|
||||
confirmations: vec![WatchEventConfirmation {
|
||||
watch_event_id,
|
||||
rating,
|
||||
comment: None,
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dismiss_watch_events_command(
|
||||
user_id: Uuid,
|
||||
event_ids: Vec<Uuid>,
|
||||
) -> DismissWatchEventsCommand {
|
||||
DismissWatchEventsCommand { user_id, event_ids }
|
||||
}
|
||||
|
||||
/// Jellyfin provider, no label.
|
||||
pub fn generate_webhook_token_command(user_id: Uuid) -> GenerateWebhookTokenCommand {
|
||||
GenerateWebhookTokenCommand {
|
||||
user_id,
|
||||
provider: WatchEventSource::Jellyfin,
|
||||
label: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Every field unset; override what the test cares about.
|
||||
pub fn update_profile_command(user_id: Uuid) -> UpdateProfileCommand {
|
||||
UpdateProfileCommand {
|
||||
user_id,
|
||||
display_name: None,
|
||||
bio: None,
|
||||
avatar_bytes: None,
|
||||
avatar_content_type: None,
|
||||
banner_bytes: None,
|
||||
banner_content_type: None,
|
||||
also_known_as: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// All federation toggles on.
|
||||
pub fn update_user_settings_command(user_id: Uuid) -> UpdateUserSettingsCommand {
|
||||
UpdateUserSettingsCommand {
|
||||
user_id,
|
||||
federate_goals: true,
|
||||
federate_reviews: true,
|
||||
federate_watchlist: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_to_watchlist_command(user_id: Uuid, movie_id: Uuid) -> AddToWatchlistCommand {
|
||||
AddToWatchlistCommand {
|
||||
user_id,
|
||||
input: movie_input_by_id(movie_id),
|
||||
}
|
||||
}
|
||||
|
||||
/// Window is 1 Jan of `year` to 1 Jan of `year + 1`.
|
||||
pub fn request_wrap_up_command(user_id: Option<Uuid>, year: i32) -> RequestWrapUpCommand {
|
||||
RequestWrapUpCommand {
|
||||
user_id,
|
||||
start_date: NaiveDate::from_ymd_opt(year, 1, 1).unwrap(),
|
||||
end_date: NaiveDate::from_ymd_opt(year + 1, 1, 1).unwrap(),
|
||||
}
|
||||
}
|
||||
281
crates/application/tests/support/context.rs
Normal file
281
crates/application/tests/support/context.rs
Normal file
@@ -0,0 +1,281 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use domain::testing::{
|
||||
InMemoryStore, InMemoryWrapUpRepository, InMemoryWrapUpStatsQuery, NoopFederationAdminQuery,
|
||||
};
|
||||
use domain::{
|
||||
ports::{
|
||||
AuthService, DiaryExporter, DiaryQuery, DocumentParser, EventPublisher, GoalCommand,
|
||||
GoalQuery, ImportProfileRepository, ImportSessionRepository, MetadataClient, MovieCommand,
|
||||
MovieProfileRepository, MovieQuery, ObjectStorage, PasswordHasher, PersonCommand,
|
||||
PersonQuery, PosterFetcherClient, RefreshSessionRepository, ReviewRepository,
|
||||
SearchCommand, SearchPort, StatsRepository, UserProfileFieldsRepository, UserRepository,
|
||||
UserSettingsRepository, WatchEventCommand, WatchEventQuery, WatchlistRepository,
|
||||
WebhookTokenRepository, WrapUpRepository, WrapUpStatsQuery,
|
||||
},
|
||||
testing::{
|
||||
FakeAuthService, FakeDiaryQuery, FakeDocumentParser, FakeMetadataClient,
|
||||
FakePasswordHasher, FakePersonQuery, FakePosterFetcher, FakeSearchCommand, FakeSearchPort,
|
||||
FakeStatsRepository, NoopEventPublisher, NoopObjectStorage, PanicDiaryExporter,
|
||||
PanicPersonCommand,
|
||||
},
|
||||
};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use domain::errors::DomainError;
|
||||
|
||||
use application::{config::AppConfig, diary::commands::LogReviewCommand, ports::ReviewLogger};
|
||||
|
||||
pub struct NoopReviewLogger;
|
||||
|
||||
#[async_trait]
|
||||
impl ReviewLogger for NoopReviewLogger {
|
||||
async fn log_review(&self, _cmd: LogReviewCommand) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TestContextBuilder {
|
||||
pub movie_command: Arc<dyn MovieCommand>,
|
||||
pub movie_query: Arc<dyn MovieQuery>,
|
||||
pub review_repo: Arc<dyn ReviewRepository>,
|
||||
pub diary_repo: Arc<dyn DiaryQuery>,
|
||||
pub diary_exporter: Arc<dyn DiaryExporter>,
|
||||
pub document_parser: Arc<dyn DocumentParser>,
|
||||
pub stats_repo: Arc<dyn StatsRepository>,
|
||||
pub metadata_client: Arc<dyn MetadataClient>,
|
||||
pub poster_fetcher: Arc<dyn PosterFetcherClient>,
|
||||
pub object_storage: Arc<dyn ObjectStorage>,
|
||||
pub event_publisher: Arc<dyn EventPublisher>,
|
||||
pub auth_service: Arc<dyn AuthService>,
|
||||
pub password_hasher: Arc<dyn PasswordHasher>,
|
||||
pub user_repo: Arc<dyn UserRepository>,
|
||||
pub import_session_repo: Arc<dyn ImportSessionRepository>,
|
||||
pub import_profile_repo: Arc<dyn ImportProfileRepository>,
|
||||
pub movie_profile_repo: Arc<dyn MovieProfileRepository>,
|
||||
pub watchlist_repo: Arc<dyn WatchlistRepository>,
|
||||
pub watch_event_command: Arc<dyn WatchEventCommand>,
|
||||
pub watch_event_query: Arc<dyn WatchEventQuery>,
|
||||
pub webhook_token_repo: Arc<dyn WebhookTokenRepository>,
|
||||
pub profile_fields_repo: Arc<dyn UserProfileFieldsRepository>,
|
||||
pub person_command: Arc<dyn PersonCommand>,
|
||||
pub person_query: Arc<dyn PersonQuery>,
|
||||
pub search_port: Arc<dyn SearchPort>,
|
||||
pub search_command: Arc<dyn SearchCommand>,
|
||||
pub wrapup_stats: Arc<dyn WrapUpStatsQuery>,
|
||||
pub wrapup_repo: Arc<dyn WrapUpRepository>,
|
||||
pub goal_command: Arc<dyn GoalCommand>,
|
||||
pub goal_query: Arc<dyn GoalQuery>,
|
||||
pub user_settings_repo: Arc<dyn UserSettingsRepository>,
|
||||
pub review_logger: Arc<dyn ReviewLogger>,
|
||||
pub social_command: Arc<dyn domain::ports::SocialCommand>,
|
||||
pub social_query_unified: Arc<dyn domain::ports::FollowGraphQuery>,
|
||||
pub block_query: Arc<dyn domain::ports::BlockQuery>,
|
||||
pub federation_admin: Arc<dyn domain::ports::FederationAdminQuery>,
|
||||
pub refresh_session_repo: Arc<dyn RefreshSessionRepository>,
|
||||
pub config: AppConfig,
|
||||
}
|
||||
|
||||
impl Default for TestContextBuilder {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl TestContextBuilder {
|
||||
pub fn new() -> Self {
|
||||
let store = InMemoryStore::new();
|
||||
Self {
|
||||
movie_command: Arc::clone(&store) as _,
|
||||
movie_query: Arc::clone(&store) as _,
|
||||
review_repo: Arc::clone(&store) as _,
|
||||
diary_repo: FakeDiaryQuery::new(),
|
||||
diary_exporter: Arc::new(PanicDiaryExporter),
|
||||
document_parser: Arc::new(FakeDocumentParser),
|
||||
stats_repo: FakeStatsRepository::new(),
|
||||
metadata_client: Arc::new(FakeMetadataClient),
|
||||
poster_fetcher: Arc::new(FakePosterFetcher),
|
||||
object_storage: Arc::new(NoopObjectStorage),
|
||||
event_publisher: NoopEventPublisher::new(),
|
||||
auth_service: Arc::new(FakeAuthService),
|
||||
password_hasher: Arc::new(FakePasswordHasher),
|
||||
user_repo: Arc::clone(&store) as _,
|
||||
import_session_repo: Arc::clone(&store) as _,
|
||||
import_profile_repo: Arc::clone(&store) as _,
|
||||
movie_profile_repo: Arc::clone(&store) as _,
|
||||
watchlist_repo: Arc::clone(&store) as _,
|
||||
watch_event_command: Arc::clone(&store) as _,
|
||||
watch_event_query: Arc::clone(&store) as _,
|
||||
webhook_token_repo: Arc::clone(&store) as _,
|
||||
profile_fields_repo: Arc::clone(&store) as _,
|
||||
person_command: Arc::new(PanicPersonCommand),
|
||||
person_query: Arc::new(FakePersonQuery),
|
||||
search_port: Arc::new(FakeSearchPort),
|
||||
search_command: Arc::new(FakeSearchCommand),
|
||||
wrapup_stats: InMemoryWrapUpStatsQuery::new(),
|
||||
wrapup_repo: InMemoryWrapUpRepository::new(),
|
||||
goal_command: Arc::clone(&store) as _,
|
||||
goal_query: Arc::clone(&store) as _,
|
||||
user_settings_repo: Arc::clone(&store) as _,
|
||||
review_logger: Arc::new(NoopReviewLogger),
|
||||
social_command: Arc::clone(&store) as _,
|
||||
social_query_unified: Arc::clone(&store) as _,
|
||||
block_query: Arc::clone(&store) as _,
|
||||
federation_admin: Arc::new(NoopFederationAdminQuery),
|
||||
refresh_session_repo: Arc::clone(&store) as _,
|
||||
config: AppConfig {
|
||||
allow_registration: true,
|
||||
base_url: "http://localhost:3000".into(),
|
||||
rate_limit: 20,
|
||||
refresh_ttl_seconds: 2_592_000,
|
||||
secure_cookies: false,
|
||||
cors_origins: String::new(),
|
||||
wrapup: application::config::WrapUpConfig {
|
||||
font_path: None,
|
||||
logo_path: None,
|
||||
bg_dir: None,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_movie_command(mut self, r: Arc<dyn MovieCommand>) -> Self {
|
||||
self.movie_command = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_movie_query(mut self, r: Arc<dyn MovieQuery>) -> Self {
|
||||
self.movie_query = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_reviews(mut self, r: Arc<dyn ReviewRepository>) -> Self {
|
||||
self.review_repo = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_users(mut self, r: Arc<dyn UserRepository>) -> Self {
|
||||
self.user_repo = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_watchlist(mut self, r: Arc<dyn WatchlistRepository>) -> Self {
|
||||
self.watchlist_repo = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_diary(mut self, r: Arc<dyn DiaryQuery>) -> Self {
|
||||
self.diary_repo = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_event_publisher(mut self, p: Arc<dyn EventPublisher>) -> Self {
|
||||
self.event_publisher = p;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn wrapup_stats(mut self, r: Arc<dyn WrapUpStatsQuery>) -> Self {
|
||||
self.wrapup_stats = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_goal_command(mut self, r: Arc<dyn GoalCommand>) -> Self {
|
||||
self.goal_command = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_goal_query(mut self, r: Arc<dyn GoalQuery>) -> Self {
|
||||
self.goal_query = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_webhook_tokens(mut self, r: Arc<dyn WebhookTokenRepository>) -> Self {
|
||||
self.webhook_token_repo = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_watch_event_command(mut self, r: Arc<dyn WatchEventCommand>) -> Self {
|
||||
self.watch_event_command = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_watch_event_query(mut self, r: Arc<dyn WatchEventQuery>) -> Self {
|
||||
self.watch_event_query = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_import_sessions(mut self, r: Arc<dyn ImportSessionRepository>) -> Self {
|
||||
self.import_session_repo = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_import_profiles(mut self, r: Arc<dyn ImportProfileRepository>) -> Self {
|
||||
self.import_profile_repo = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_movie_profiles(mut self, r: Arc<dyn MovieProfileRepository>) -> Self {
|
||||
self.movie_profile_repo = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_user_settings(mut self, r: Arc<dyn UserSettingsRepository>) -> Self {
|
||||
self.user_settings_repo = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_profile_fields(mut self, r: Arc<dyn UserProfileFieldsRepository>) -> Self {
|
||||
self.profile_fields_repo = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_review_logger(mut self, r: Arc<dyn ReviewLogger>) -> Self {
|
||||
self.review_logger = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_stats(mut self, r: Arc<dyn StatsRepository>) -> Self {
|
||||
self.stats_repo = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_person_query(mut self, r: Arc<dyn PersonQuery>) -> Self {
|
||||
self.person_query = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_search_port(mut self, r: Arc<dyn SearchPort>) -> Self {
|
||||
self.search_port = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_search_command(mut self, r: Arc<dyn SearchCommand>) -> Self {
|
||||
self.search_command = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_document_parser(mut self, r: Arc<dyn DocumentParser>) -> Self {
|
||||
self.document_parser = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_poster_fetcher(mut self, r: Arc<dyn PosterFetcherClient>) -> Self {
|
||||
self.poster_fetcher = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_metadata_client(mut self, r: Arc<dyn MetadataClient>) -> Self {
|
||||
self.metadata_client = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_wrapup_repo(mut self, r: Arc<dyn WrapUpRepository>) -> Self {
|
||||
self.wrapup_repo = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_config(mut self, config: AppConfig) -> Self {
|
||||
self.config = config;
|
||||
self
|
||||
}
|
||||
}
|
||||
10
crates/application/tests/support/mod.rs
Normal file
10
crates/application/tests/support/mod.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
//! Shared fakes and builders for the application integration tests.
|
||||
//! Included via `mod support;` from each `tests/<module>.rs`; not every
|
||||
//! binary uses every helper.
|
||||
#![allow(dead_code, unused_imports)]
|
||||
|
||||
mod commands;
|
||||
mod context;
|
||||
|
||||
pub use commands::*;
|
||||
pub use context::*;
|
||||
Reference in New Issue
Block a user