test: refusing fakes, failure-path tests, shared panic stubs
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
mod insights;
|
||||
mod noops;
|
||||
mod panics;
|
||||
mod refusing;
|
||||
mod store;
|
||||
mod store_goal;
|
||||
mod store_import;
|
||||
@@ -19,6 +20,7 @@ mod wrapup;
|
||||
pub use insights::*;
|
||||
pub use noops::*;
|
||||
pub use panics::*;
|
||||
pub use refusing::*;
|
||||
pub use store::{
|
||||
InMemoryStore, MOVIE_CASCADE_TABLES, USER_CASCADE_TABLES, USER_CASCADE_TABLES_OUTSIDE_STORE,
|
||||
};
|
||||
|
||||
@@ -4,8 +4,9 @@ use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
errors::DomainError,
|
||||
events::DomainEvent,
|
||||
ports::{EventPublisher, ObjectStorage},
|
||||
events::{AckHandle, DomainEvent},
|
||||
models::{Person, PersonEnrichmentData, PersonId},
|
||||
ports::{EventPublisher, ObjectStorage, PersonCommand},
|
||||
value_objects::UserId,
|
||||
};
|
||||
|
||||
@@ -145,3 +146,38 @@ impl crate::ports::RemoteGoalRepository for NoopRemoteGoalRepository {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
// ── NoopAckHandle ─────────────────────────────────────────────────────────────
|
||||
|
||||
pub struct NoopAckHandle;
|
||||
|
||||
#[async_trait]
|
||||
impl AckHandle for NoopAckHandle {
|
||||
async fn ack(&self) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
async fn nack(&self) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ── NoopPersonCommand ─────────────────────────────────────────────────────────
|
||||
|
||||
pub struct NoopPersonCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl PersonCommand for NoopPersonCommand {
|
||||
async fn upsert_batch(&self, _: &[Person]) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
async fn backfill_from_credits_batch(&self, _: u32) -> Result<(u64, bool), DomainError> {
|
||||
Ok((0, false))
|
||||
}
|
||||
async fn update_enrichment(
|
||||
&self,
|
||||
_: &PersonId,
|
||||
_: &PersonEnrichmentData,
|
||||
) -> Result<(), DomainError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,16 @@ use crate::{
|
||||
value_objects::{ImportProfileId, ImportSessionId, MovieId, PosterUrl, UserId},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
events::DomainEvent,
|
||||
models::{GeneratedToken, Movie, Review},
|
||||
ports::{
|
||||
AuthService, EventPublisher, MetadataClient, MovieCommand, MovieQuery, ObjectStorage,
|
||||
PasswordHasher, ReviewRepository, UserRepository,
|
||||
},
|
||||
value_objects::{Email, ExternalMetadataId, MovieTitle, PasswordHash, ReleaseYear, ReviewId},
|
||||
};
|
||||
|
||||
// ── PanicDiaryQuery ───────────────────────────────────────────────────────────
|
||||
|
||||
pub struct PanicDiaryQuery;
|
||||
@@ -467,3 +477,273 @@ impl crate::ports::WebhookTokenRepository for PanicWebhookTokenRepository {
|
||||
panic!("PanicWebhookTokenRepository called")
|
||||
}
|
||||
}
|
||||
pub struct PanicMovieCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl MovieCommand for PanicMovieCommand {
|
||||
async fn upsert_movie(&self, _: &Movie) -> Result<(), DomainError> {
|
||||
panic!("PanicMovieCommand called")
|
||||
}
|
||||
async fn delete_movie(&self, _: &MovieId) -> Result<(), DomainError> {
|
||||
panic!("PanicMovieCommand called")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PanicMovieQuery;
|
||||
|
||||
#[async_trait]
|
||||
impl MovieQuery for PanicMovieQuery {
|
||||
async fn get_movie_by_external_id(
|
||||
&self,
|
||||
_: &ExternalMetadataId,
|
||||
) -> Result<Option<Movie>, DomainError> {
|
||||
panic!("PanicMovieQuery called")
|
||||
}
|
||||
async fn get_movie_by_id(&self, _: &MovieId) -> Result<Option<Movie>, DomainError> {
|
||||
panic!("PanicMovieQuery called")
|
||||
}
|
||||
async fn get_movies_by_title_and_year(
|
||||
&self,
|
||||
_: &MovieTitle,
|
||||
_: &ReleaseYear,
|
||||
) -> Result<Vec<Movie>, DomainError> {
|
||||
panic!("PanicMovieQuery called")
|
||||
}
|
||||
async fn existing_external_ids(
|
||||
&self,
|
||||
_: &[ExternalMetadataId],
|
||||
) -> Result<std::collections::HashSet<String>, DomainError> {
|
||||
panic!("PanicMovieQuery called")
|
||||
}
|
||||
async fn existing_title_year_pairs(
|
||||
&self,
|
||||
_: &[(MovieTitle, ReleaseYear)],
|
||||
) -> Result<std::collections::HashSet<(String, u16)>, DomainError> {
|
||||
panic!("PanicMovieQuery called")
|
||||
}
|
||||
async fn list_movies(
|
||||
&self,
|
||||
_: &crate::models::collections::PageParams,
|
||||
_: &crate::models::MovieFilter,
|
||||
) -> Result<crate::models::collections::Paginated<crate::models::MovieSummary>, DomainError>
|
||||
{
|
||||
panic!("PanicMovieQuery called")
|
||||
}
|
||||
async fn list_movies_with_external_id(&self) -> Result<Vec<crate::models::Movie>, DomainError> {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PanicReviewRepository;
|
||||
|
||||
#[async_trait]
|
||||
impl ReviewRepository for PanicReviewRepository {
|
||||
async fn save_review(&self, _: &Review) -> Result<(), DomainError> {
|
||||
panic!("PanicReviewRepository called")
|
||||
}
|
||||
async fn get_review_by_id(&self, _: &ReviewId) -> Result<Option<Review>, DomainError> {
|
||||
panic!("PanicReviewRepository called")
|
||||
}
|
||||
async fn update_review(&self, _: &Review) -> Result<(), DomainError> {
|
||||
panic!("PanicReviewRepository called")
|
||||
}
|
||||
async fn delete_review(&self, _: &ReviewId) -> Result<(), DomainError> {
|
||||
panic!("PanicReviewRepository called")
|
||||
}
|
||||
async fn get_all_reviews_for_user(&self, _: &UserId) -> Result<Vec<Review>, DomainError> {
|
||||
panic!("PanicReviewRepository called")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PanicMetadataClient;
|
||||
|
||||
#[async_trait]
|
||||
impl MetadataClient for PanicMetadataClient {
|
||||
async fn fetch_movie_metadata(
|
||||
&self,
|
||||
_: &crate::models::MetadataSearchCriteria,
|
||||
) -> Result<Movie, DomainError> {
|
||||
panic!("PanicMetadataClient called")
|
||||
}
|
||||
async fn get_poster_url(
|
||||
&self,
|
||||
_: &ExternalMetadataId,
|
||||
) -> Result<Option<PosterUrl>, DomainError> {
|
||||
panic!("PanicMetadataClient called")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PanicObjectStorage;
|
||||
|
||||
#[async_trait]
|
||||
impl ObjectStorage for PanicObjectStorage {
|
||||
async fn store(&self, _: &str, _: &[u8]) -> Result<String, DomainError> {
|
||||
panic!("PanicObjectStorage called")
|
||||
}
|
||||
async fn get(&self, _: &str) -> Result<Vec<u8>, DomainError> {
|
||||
panic!("PanicObjectStorage called")
|
||||
}
|
||||
async fn get_stream(
|
||||
&self,
|
||||
_: &str,
|
||||
) -> Result<futures::stream::BoxStream<'static, Result<bytes::Bytes, DomainError>>, DomainError>
|
||||
{
|
||||
panic!("PanicObjectStorage called")
|
||||
}
|
||||
async fn delete(&self, _: &str) -> Result<(), DomainError> {
|
||||
panic!("PanicObjectStorage called")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PanicAuthService;
|
||||
|
||||
#[async_trait]
|
||||
impl AuthService for PanicAuthService {
|
||||
async fn generate_token(&self, _: &UserId) -> Result<GeneratedToken, DomainError> {
|
||||
panic!("PanicAuthService called")
|
||||
}
|
||||
async fn validate_token(&self, _: &str) -> Result<UserId, DomainError> {
|
||||
panic!("PanicAuthService called")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PanicPasswordHasher;
|
||||
|
||||
#[async_trait]
|
||||
impl PasswordHasher for PanicPasswordHasher {
|
||||
async fn hash(&self, _: &str) -> Result<PasswordHash, DomainError> {
|
||||
panic!("PanicPasswordHasher called")
|
||||
}
|
||||
async fn verify(&self, _: &str, _: &PasswordHash) -> Result<bool, DomainError> {
|
||||
panic!("PanicPasswordHasher called")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PanicUserRepository;
|
||||
|
||||
#[async_trait]
|
||||
impl UserRepository for PanicUserRepository {
|
||||
async fn find_by_email(&self, _: &Email) -> Result<Option<crate::models::User>, DomainError> {
|
||||
panic!("PanicUserRepository called")
|
||||
}
|
||||
async fn save(&self, _: &crate::models::User) -> Result<(), DomainError> {
|
||||
panic!("PanicUserRepository called")
|
||||
}
|
||||
async fn find_by_id(&self, _: &UserId) -> Result<Option<crate::models::User>, DomainError> {
|
||||
panic!("PanicUserRepository called")
|
||||
}
|
||||
async fn find_by_username(
|
||||
&self,
|
||||
_: &crate::value_objects::Username,
|
||||
) -> Result<Option<crate::models::User>, DomainError> {
|
||||
panic!("PanicUserRepository called")
|
||||
}
|
||||
async fn list_with_stats(&self) -> Result<Vec<crate::models::UserSummary>, DomainError> {
|
||||
panic!("PanicUserRepository called")
|
||||
}
|
||||
async fn update_profile(
|
||||
&self,
|
||||
_: &UserId,
|
||||
_: &crate::models::UserProfile,
|
||||
) -> Result<(), DomainError> {
|
||||
panic!("PanicUserRepository called")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PanicEventPublisher;
|
||||
|
||||
#[async_trait]
|
||||
impl EventPublisher for PanicEventPublisher {
|
||||
async fn publish(&self, _: &DomainEvent) -> Result<(), DomainError> {
|
||||
panic!("PanicEventPublisher called")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PanicRssFeedRenderer;
|
||||
|
||||
impl crate::ports::RssFeedRenderer for PanicRssFeedRenderer {
|
||||
fn render_feed(
|
||||
&self,
|
||||
_: &[DiaryEntry],
|
||||
_: &crate::ports::FeedChannel<'_>,
|
||||
) -> Result<String, DomainError> {
|
||||
panic!("PanicRssFeedRenderer called")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PanicGoalCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl crate::ports::GoalCommand for PanicGoalCommand {
|
||||
async fn save(&self, _: &crate::models::Goal) -> Result<(), DomainError> {
|
||||
panic!("PanicGoalCommand called")
|
||||
}
|
||||
async fn update(&self, _: &crate::models::Goal) -> Result<(), DomainError> {
|
||||
panic!("PanicGoalCommand called")
|
||||
}
|
||||
async fn delete(
|
||||
&self,
|
||||
_: &crate::value_objects::GoalId,
|
||||
_: &crate::value_objects::UserId,
|
||||
) -> Result<(), DomainError> {
|
||||
panic!("PanicGoalCommand called")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PanicGoalQuery;
|
||||
|
||||
#[async_trait]
|
||||
impl crate::ports::GoalQuery for PanicGoalQuery {
|
||||
async fn find_by_user_and_year(
|
||||
&self,
|
||||
_: &crate::value_objects::UserId,
|
||||
_: u16,
|
||||
) -> Result<Option<crate::models::Goal>, DomainError> {
|
||||
panic!("PanicGoalQuery called")
|
||||
}
|
||||
async fn list_for_user(
|
||||
&self,
|
||||
_: &crate::value_objects::UserId,
|
||||
) -> Result<Vec<crate::models::Goal>, DomainError> {
|
||||
panic!("PanicGoalQuery called")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PanicUserSettingsRepository;
|
||||
|
||||
#[async_trait]
|
||||
impl crate::ports::UserSettingsRepository for PanicUserSettingsRepository {
|
||||
async fn get(
|
||||
&self,
|
||||
_: &crate::value_objects::UserId,
|
||||
) -> Result<crate::models::UserSettings, DomainError> {
|
||||
panic!("PanicUserSettingsRepository called")
|
||||
}
|
||||
async fn save(&self, _: &crate::models::UserSettings) -> Result<(), DomainError> {
|
||||
panic!("PanicUserSettingsRepository called")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PanicRemoteGoalRepository;
|
||||
|
||||
#[async_trait]
|
||||
impl crate::ports::RemoteGoalRepository for PanicRemoteGoalRepository {
|
||||
async fn save(&self, _: crate::models::RemoteGoalEntry) -> Result<(), DomainError> {
|
||||
panic!("PanicRemoteGoalRepository called")
|
||||
}
|
||||
async fn update_by_ap_id(&self, _: &str, _: u32, _: u32) -> Result<(), DomainError> {
|
||||
panic!("PanicRemoteGoalRepository called")
|
||||
}
|
||||
async fn remove_by_ap_id(&self, _: &str, _: &str) -> Result<(), DomainError> {
|
||||
panic!("PanicRemoteGoalRepository called")
|
||||
}
|
||||
async fn remove_all_by_actor(&self, _: &str) -> Result<(), DomainError> {
|
||||
panic!("PanicRemoteGoalRepository called")
|
||||
}
|
||||
async fn get_by_actor_url(
|
||||
&self,
|
||||
_: &str,
|
||||
) -> Result<Vec<crate::models::RemoteGoalEntry>, DomainError> {
|
||||
panic!("PanicRemoteGoalRepository called")
|
||||
}
|
||||
}
|
||||
|
||||
85
crates/domain/src/testing/fakes/refusing.rs
Normal file
85
crates/domain/src/testing/fakes/refusing.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
//! Fakes whose every call fails with an infrastructure error, for failure-path tests.
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
errors::DomainError,
|
||||
events::DomainEvent,
|
||||
models::{
|
||||
Movie, Review, WatchlistEntry, WatchlistWithMovie,
|
||||
collections::{PageParams, Paginated},
|
||||
},
|
||||
ports::{EventPublisher, MovieCommand, ReviewRepository, WatchlistRepository},
|
||||
value_objects::{MovieId, ReviewId, UserId},
|
||||
};
|
||||
|
||||
fn refused() -> DomainError {
|
||||
DomainError::InfrastructureError("refused".into())
|
||||
}
|
||||
|
||||
pub struct RefusingReviewRepository;
|
||||
|
||||
#[async_trait]
|
||||
impl ReviewRepository for RefusingReviewRepository {
|
||||
async fn save_review(&self, _: &Review) -> Result<(), DomainError> {
|
||||
Err(refused())
|
||||
}
|
||||
async fn get_review_by_id(&self, _: &ReviewId) -> Result<Option<Review>, DomainError> {
|
||||
Err(refused())
|
||||
}
|
||||
async fn update_review(&self, _: &Review) -> Result<(), DomainError> {
|
||||
Err(refused())
|
||||
}
|
||||
async fn delete_review(&self, _: &ReviewId) -> Result<(), DomainError> {
|
||||
Err(refused())
|
||||
}
|
||||
async fn get_all_reviews_for_user(&self, _: &UserId) -> Result<Vec<Review>, DomainError> {
|
||||
Err(refused())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RefusingMovieCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl MovieCommand for RefusingMovieCommand {
|
||||
async fn upsert_movie(&self, _: &Movie) -> Result<(), DomainError> {
|
||||
Err(refused())
|
||||
}
|
||||
async fn delete_movie(&self, _: &MovieId) -> Result<(), DomainError> {
|
||||
Err(refused())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RefusingWatchlistRepository;
|
||||
|
||||
#[async_trait]
|
||||
impl WatchlistRepository for RefusingWatchlistRepository {
|
||||
async fn add(&self, _: &WatchlistEntry) -> Result<(), DomainError> {
|
||||
Err(refused())
|
||||
}
|
||||
async fn remove(&self, _: &UserId, _: &MovieId) -> Result<(), DomainError> {
|
||||
Err(refused())
|
||||
}
|
||||
async fn remove_if_present(&self, _: &UserId, _: &MovieId) -> Result<bool, DomainError> {
|
||||
Err(refused())
|
||||
}
|
||||
async fn get_for_user(
|
||||
&self,
|
||||
_: &UserId,
|
||||
_: &PageParams,
|
||||
) -> Result<Paginated<WatchlistWithMovie>, DomainError> {
|
||||
Err(refused())
|
||||
}
|
||||
async fn contains(&self, _: &UserId, _: &MovieId) -> Result<bool, DomainError> {
|
||||
Err(refused())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RefusingEventPublisher;
|
||||
|
||||
#[async_trait]
|
||||
impl EventPublisher for RefusingEventPublisher {
|
||||
async fn publish(&self, _: &DomainEvent) -> Result<(), DomainError> {
|
||||
Err(refused())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user