From 4e750420bf8baa4930db0a7bad84d0fecc1dc1b2 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Thu, 28 May 2026 03:43:35 +0200 Subject: [PATCH] feat(application): add get_local_friends use case --- .../application/src/use_cases/social/mod.rs | 14 +++++- .../application/src/use_cases/social/tests.rs | 48 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/crates/application/src/use_cases/social/mod.rs b/crates/application/src/use_cases/social/mod.rs index a02a64a..fd6b31e 100644 --- a/crates/application/src/use_cases/social/mod.rs +++ b/crates/application/src/use_cases/social/mod.rs @@ -2,7 +2,11 @@ use chrono::Utc; use domain::{ errors::DomainError, events::DomainEvent, - models::social::{Block, Boost, Follow, FollowState, Like}, + models::{ + feed::{PageParams, Paginated}, + social::{Block, Boost, Follow, FollowState, Like}, + user::User, + }, ports::{ BlockRepository, BoostRepository, EventPublisher, FederationFollowPort, FollowRepository, LikeRepository, UserReader, @@ -280,5 +284,13 @@ pub async fn unblock_user( Ok(()) } +pub async fn get_local_friends( + follows: &dyn FollowRepository, + user_id: &UserId, + page: &PageParams, +) -> Result, DomainError> { + follows.list_mutual(user_id, page).await +} + #[cfg(test)] mod tests; diff --git a/crates/application/src/use_cases/social/tests.rs b/crates/application/src/use_cases/social/tests.rs index 53bc32b..a5561b2 100644 --- a/crates/application/src/use_cases/social/tests.rs +++ b/crates/application/src/use_cases/social/tests.rs @@ -204,3 +204,51 @@ async fn boost_and_unboost() { .iter() .any(|e| matches!(e, DomainEvent::BoostRemoved { .. }))); } + +#[tokio::test] +async fn get_local_friends_returns_mutual_follows() { + use domain::models::feed::PageParams; + let store = TestStore::default(); + let alice = user("alice"); + let bob = user("bob"); + let carol = user("carol"); + + store + .users + .lock() + .unwrap() + .extend([alice.clone(), bob.clone(), carol.clone()]); + + // alice ↔ bob = friends; alice → carol but not back + store.follows.lock().unwrap().extend([ + domain::models::social::Follow { + follower_id: alice.id.clone(), + following_id: bob.id.clone(), + state: domain::models::social::FollowState::Accepted, + ap_id: None, + created_at: chrono::Utc::now(), + }, + domain::models::social::Follow { + follower_id: bob.id.clone(), + following_id: alice.id.clone(), + state: domain::models::social::FollowState::Accepted, + ap_id: None, + created_at: chrono::Utc::now(), + }, + domain::models::social::Follow { + follower_id: alice.id.clone(), + following_id: carol.id.clone(), + state: domain::models::social::FollowState::Accepted, + ap_id: None, + created_at: chrono::Utc::now(), + }, + ]); + + let page = PageParams { + page: 1, + per_page: 20, + }; + let result = get_local_friends(&store, &alice.id, &page).await.unwrap(); + assert_eq!(result.total, 1); + assert_eq!(result.items[0].id, bob.id); +}