From a3534317dee23a2601d0509d9a9eefba8c4360fe Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Thu, 14 May 2026 09:23:22 +0200 Subject: [PATCH] feat(domain): SearchPort trait with thought and user search --- crates/domain/src/ports.rs | 18 ++++++++++++++++++ crates/domain/src/testing.rs | 29 +++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/crates/domain/src/ports.rs b/crates/domain/src/ports.rs index 1010659..111241e 100644 --- a/crates/domain/src/ports.rs +++ b/crates/domain/src/ports.rs @@ -136,3 +136,21 @@ pub trait FeedRepository: Send + Sync { async fn public_feed(&self, page: &PageParams, viewer_id: Option<&UserId>) -> Result, DomainError>; async fn search(&self, query: &str, page: &PageParams, viewer_id: Option<&UserId>) -> Result, DomainError>; } + +#[async_trait] +pub trait SearchPort: Send + Sync { + /// Full-text search over public thoughts, ranked by trigram similarity. + async fn search_thoughts( + &self, + query: &str, + page: &PageParams, + viewer_id: Option<&UserId>, + ) -> Result, DomainError>; + + /// Search users by username or display_name, ranked by trigram similarity. + async fn search_users( + &self, + query: &str, + page: &PageParams, + ) -> Result, DomainError>; +} diff --git a/crates/domain/src/testing.rs b/crates/domain/src/testing.rs index ba5a23b..d06fc10 100644 --- a/crates/domain/src/testing.rs +++ b/crates/domain/src/testing.rs @@ -282,6 +282,15 @@ pub struct TestStore { } } +#[async_trait] impl SearchPort for TestStore { + async fn search_thoughts(&self, _q: &str, _p: &PageParams, _v: Option<&UserId>) -> Result, DomainError> { + Ok(Paginated { items: vec![], total: 0, page: 1, per_page: 20 }) + } + async fn search_users(&self, _q: &str, _p: &PageParams) -> Result, DomainError> { + Ok(Paginated { items: vec![], total: 0, page: 1, per_page: 20 }) + } +} + #[async_trait] impl EventPublisher for TestStore { async fn publish(&self, event: &DomainEvent) -> Result<(), DomainError> { self.events.lock().unwrap().push(event.clone()); @@ -293,3 +302,23 @@ pub struct NoOpEventPublisher; #[async_trait] impl EventPublisher for NoOpEventPublisher { async fn publish(&self, _e: &DomainEvent) -> Result<(), DomainError> { Ok(()) } } + +#[cfg(test)] +mod search_tests { + use super::*; + use crate::models::feed::PageParams; + + #[tokio::test] + async fn test_store_search_thoughts_returns_empty() { + let store = TestStore::default(); + let result = store.search_thoughts("hello", &PageParams { page: 1, per_page: 20 }, None).await.unwrap(); + assert_eq!(result.total, 0); + } + + #[tokio::test] + async fn test_store_search_users_returns_empty() { + let store = TestStore::default(); + let result = store.search_users("alice", &PageParams { page: 1, per_page: 20 }).await.unwrap(); + assert_eq!(result.total, 0); + } +}