Files
movies-diary/crates/domain/tests/ports/social_test.rs
Gabriel Kaszewski 9a4d0c1714 feat: atomic social local writes, remote review enrichment
SocialCommand methods take events. Local follow ops = one FollowCommand::apply
tx (rows + events). k_ap path (remote, block/unblock): best-effort publish in
CompositeSocialAdapter after k_ap write; not atomic, k_ap owns tx.
save_remote_review takes events, enqueued in same tx via
sqlite::persistence::enqueue_events. Deps drop event_publisher: social
command, ReviewObjectHandler.

Remaining .publish( sites:
announce-only: users/delete_account UserDeleted, person/get + get_credits
PersonEnrichmentRequested, jobs/enrichment_staleness, http-axum search,
ImageStored (sync_poster, poster_sync_handler, image-converter backfill,
update_profile; object storage), federation_event_bridge (handler reaction),
nats relay (transport).
non-atomic by necessity: CompositeSocialAdapter::announce (k_ap-owned write).
Everything else rides its write.
2026-10-26 01:12:58 +01:00

171 lines
4.7 KiB
Rust

use async_trait::async_trait;
use domain::errors::DomainError;
use domain::ports::{
BlockQuery, FollowGraphQuery, FollowTargetResolver, LocalSocial, ResolvedFollow, SocialCommand,
};
use domain::value_objects::SocialIdentity;
use std::sync::Arc;
struct Stub;
#[async_trait]
impl SocialCommand for Stub {
async fn follow(
&self,
_: &domain::value_objects::UserId,
_: &domain::value_objects::FollowTarget,
_: &[domain::events::DomainEvent],
) -> Result<(), DomainError> {
Ok(())
}
async fn unfollow(
&self,
_: &domain::value_objects::UserId,
_: &domain::value_objects::SocialIdentity,
_: &[domain::events::DomainEvent],
) -> Result<(), DomainError> {
Ok(())
}
async fn accept_follow(
&self,
_: &domain::value_objects::UserId,
_: &domain::value_objects::SocialIdentity,
_: &[domain::events::DomainEvent],
) -> Result<(), DomainError> {
Ok(())
}
async fn reject_follow(
&self,
_: &domain::value_objects::UserId,
_: &domain::value_objects::SocialIdentity,
_: &[domain::events::DomainEvent],
) -> Result<(), DomainError> {
Ok(())
}
async fn remove_follower(
&self,
_: &domain::value_objects::UserId,
_: &domain::value_objects::SocialIdentity,
_: &[domain::events::DomainEvent],
) -> Result<(), DomainError> {
Ok(())
}
async fn block(
&self,
_: &domain::value_objects::UserId,
_: &domain::value_objects::SocialIdentity,
_: &[domain::events::DomainEvent],
) -> Result<(), DomainError> {
Ok(())
}
async fn unblock(
&self,
_: &domain::value_objects::UserId,
_: &domain::value_objects::SocialIdentity,
_: &[domain::events::DomainEvent],
) -> Result<(), DomainError> {
Ok(())
}
}
#[async_trait]
impl FollowGraphQuery for Stub {
async fn get_following(
&self,
_: &domain::value_objects::UserId,
) -> Result<Vec<domain::value_objects::SocialActor>, DomainError> {
Ok(vec![])
}
async fn get_followers(
&self,
_: &domain::value_objects::UserId,
) -> Result<Vec<domain::value_objects::SocialActor>, DomainError> {
Ok(vec![])
}
async fn get_pending_followers(
&self,
_: &domain::value_objects::UserId,
) -> Result<Vec<domain::value_objects::SocialActor>, DomainError> {
Ok(vec![])
}
async fn get_pending_following(
&self,
_: &domain::value_objects::UserId,
) -> Result<Vec<domain::value_objects::SocialActor>, DomainError> {
Ok(vec![])
}
async fn count_following(
&self,
_: &domain::value_objects::UserId,
) -> Result<usize, DomainError> {
Ok(0)
}
async fn count_followers(
&self,
_: &domain::value_objects::UserId,
) -> Result<usize, DomainError> {
Ok(0)
}
async fn count_pending_followers(
&self,
_: &domain::value_objects::UserId,
) -> Result<usize, DomainError> {
Ok(0)
}
async fn get_relation(
&self,
_: &domain::value_objects::UserId,
_: &domain::value_objects::SocialIdentity,
) -> Result<domain::value_objects::FollowRelation, DomainError> {
Err(DomainError::NotFound("stub".into()))
}
}
#[async_trait]
impl BlockQuery for Stub {
async fn get_blocked(
&self,
_: &domain::value_objects::UserId,
) -> Result<Vec<domain::value_objects::SocialActor>, DomainError> {
Ok(vec![])
}
}
#[async_trait]
impl FollowTargetResolver for Stub {
async fn resolve_target(
&self,
target: &domain::value_objects::FollowTarget,
) -> Result<SocialIdentity, DomainError> {
match target {
domain::value_objects::FollowTarget::Identity(id) => Ok(id.clone()),
domain::value_objects::FollowTarget::Handle(handle) => Ok(SocialIdentity::Remote {
actor_url: handle.clone(),
}),
}
}
}
#[async_trait]
impl ResolvedFollow for Stub {
async fn follow_resolved(
&self,
_: &domain::value_objects::UserId,
_: &SocialIdentity,
_: &[domain::events::DomainEvent],
) -> Result<(), DomainError> {
Ok(())
}
}
#[test]
fn blanket_impl_yields_a_trait_object() {
let local: Arc<dyn LocalSocial> = Arc::new(Stub);
// Reachable through each supertrait without a separate Arc.
let _: &dyn SocialCommand = local.as_ref();
let _: &dyn FollowGraphQuery = local.as_ref();
let _: &dyn BlockQuery = local.as_ref();
let _: &dyn FollowTargetResolver = local.as_ref();
let _: &dyn ResolvedFollow = local.as_ref();
}