use crate::support::TestContextBuilder; use std::sync::Arc; use async_trait::async_trait; use domain::errors::DomainError; use domain::testing::InMemoryStore; use domain::value_objects::{FollowRelation, SocialActor, SocialIdentity, UserId}; use application::{ config::AppSettings, diary::deps::GetActivityFeedDeps, diary::get_activity_feed, diary::queries::GetActivityFeedQuery, }; fn default_deps() -> GetActivityFeedDeps { GetActivityFeedDeps { diary: domain::testing::FakeDiaryQuery::new() as _, social_query: InMemoryStore::new() as _, config: TestContextBuilder::new().config, } } #[tokio::test] async fn returns_empty_feed() { let deps = default_deps(); let result = get_activity_feed::execute( &deps, GetActivityFeedQuery { limit: 10, offset: 0, sort_by: domain::models::FeedSortBy::Date, search: None, viewer_user_id: None, filter_following: false, }, ) .await .unwrap(); assert!(result.items.is_empty()); assert_eq!(result.total_count, 0); } #[tokio::test] async fn returns_feed_with_following_filter() { let deps = default_deps(); let viewer = uuid::Uuid::new_v4(); let result = get_activity_feed::execute( &deps, GetActivityFeedQuery { limit: 10, offset: 0, sort_by: domain::models::FeedSortBy::Date, search: None, viewer_user_id: Some(viewer), filter_following: true, }, ) .await .unwrap(); assert!(result.items.is_empty()); } struct FakeSocialWithFollowing(Vec); #[async_trait] impl domain::ports::FollowGraphQuery for FakeSocialWithFollowing { async fn get_following(&self, _: &UserId) -> Result, DomainError> { Ok(self.0.clone()) } async fn get_followers(&self, _: &UserId) -> Result, DomainError> { Ok(vec![]) } async fn get_pending_followers(&self, _: &UserId) -> Result, DomainError> { Ok(vec![]) } async fn get_pending_following(&self, _: &UserId) -> Result, DomainError> { Ok(vec![]) } async fn count_following(&self, _: &UserId) -> Result { Ok(0) } async fn count_followers(&self, _: &UserId) -> Result { Ok(0) } async fn count_pending_followers(&self, _: &UserId) -> Result { Ok(0) } async fn get_relation( &self, _: &UserId, _: &SocialIdentity, ) -> Result { Ok(FollowRelation::default()) } } #[tokio::test] async fn following_filter_separates_local_and_remote() { let viewer = uuid::Uuid::new_v4(); let local_friend = uuid::Uuid::new_v4(); let following = vec![ SocialActor { identity: SocialIdentity::Local(UserId::from_uuid(local_friend)), handle: "friend".into(), display_name: None, avatar_url: None, }, SocialActor { identity: SocialIdentity::Remote { actor_url: "https://remote.example/actor/1".into(), }, handle: "@alice@remote.example".into(), display_name: None, avatar_url: None, }, ]; let social = Arc::new(FakeSocialWithFollowing(following)); let deps = GetActivityFeedDeps { diary: domain::testing::FakeDiaryQuery::new() as _, social_query: social as _, config: AppSettings { 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::WrapUpSettings { font_path: None, logo_path: None, bg_dir: None, }, }, }; let result = get_activity_feed::execute( &deps, GetActivityFeedQuery { limit: 10, offset: 0, sort_by: domain::models::FeedSortBy::Date, search: None, viewer_user_id: Some(viewer), filter_following: true, }, ) .await .unwrap(); assert!(result.items.is_empty()); } #[tokio::test] async fn following_filter_without_viewer_returns_none() { let deps = default_deps(); let result = get_activity_feed::execute( &deps, GetActivityFeedQuery { limit: 10, offset: 0, sort_by: domain::models::FeedSortBy::Date, search: None, viewer_user_id: None, filter_following: true, }, ) .await .unwrap(); assert!(result.items.is_empty()); }