diff --git a/crates/application/src/services/federation_event.rs b/crates/application/src/services/federation_event.rs index 8254083..7c748e6 100644 --- a/crates/application/src/services/federation_event.rs +++ b/crates/application/src/services/federation_event.rs @@ -28,6 +28,8 @@ impl FederationEventService { } DomainEvent::ThoughtDeleted { thought_id, user_id } => { + // No DB lookup — thought is already deleted when this event fires. + // No locality guard: delete commands only reach local thoughts via the use case. let ap_id = format!("{}/thoughts/{}", self.base_url, thought_id); self.ap.broadcast_delete(user_id, &ap_id).await } @@ -215,6 +217,7 @@ mod tests { .unwrap(); assert_eq!(spy.updated.lock().unwrap().len(), 1); + assert_eq!(spy.updated.lock().unwrap()[0], thought.id); } #[tokio::test] @@ -278,4 +281,45 @@ mod tests { assert!(spy.updated.lock().unwrap().is_empty()); assert!(spy.announced.lock().unwrap().is_empty()); } + + #[tokio::test] + async fn thought_created_does_not_broadcast_if_user_missing() { + let store = TestStore::default(); + let alice = alice(); + let thought = local_thought(alice.id.clone()); + // Don't push alice into users — simulates user deleted before handler runs + store.thoughts.lock().unwrap().push(thought.clone()); + + let spy = Arc::new(SpyPort::default()); + svc(&store, spy.clone()) + .process(&DomainEvent::ThoughtCreated { + thought_id: thought.id.clone(), + user_id: alice.id.clone(), + in_reply_to_id: None, + }) + .await + .unwrap(); + + assert!(spy.created.lock().unwrap().is_empty()); + } + + #[tokio::test] + async fn thought_updated_does_not_broadcast_if_user_missing() { + let store = TestStore::default(); + let alice = alice(); + let thought = local_thought(alice.id.clone()); + // Don't push alice into users + store.thoughts.lock().unwrap().push(thought.clone()); + + let spy = Arc::new(SpyPort::default()); + svc(&store, spy.clone()) + .process(&DomainEvent::ThoughtUpdated { + thought_id: thought.id.clone(), + user_id: alice.id.clone(), + }) + .await + .unwrap(); + + assert!(spy.updated.lock().unwrap().is_empty()); + } }