use crate::{EventPublisherAdapter, Transport}; use async_trait::async_trait; use domain::{ errors::DomainError, events::DomainEvent, ports::EventPublisher, value_objects::SystemId, }; use std::sync::{Arc, Mutex}; struct RecordingTransport { messages: Arc)>>>, } #[async_trait] impl Transport for RecordingTransport { async fn publish_bytes(&self, subject: &str, bytes: &[u8]) -> Result<(), DomainError> { self.messages .lock() .unwrap() .push((subject.to_string(), bytes.to_vec())); Ok(()) } } #[tokio::test] async fn adapter_publishes_with_correct_subject() { let messages = Arc::new(Mutex::new(Vec::new())); let adapter = EventPublisherAdapter::new(RecordingTransport { messages: messages.clone(), }); let event = DomainEvent::JobCompleted { job_id: SystemId::new(), timestamp: domain::value_objects::DateTimeStamp::now(), }; adapter.publish(&event).await.unwrap(); let recorded = messages.lock().unwrap(); assert_eq!(recorded.len(), 1); assert_eq!(recorded[0].0, "jobs.completed"); } #[tokio::test] async fn published_bytes_are_valid_json() { let messages = Arc::new(Mutex::new(Vec::new())); let adapter = EventPublisherAdapter::new(RecordingTransport { messages: messages.clone(), }); let event = DomainEvent::AssetIngested { asset_id: SystemId::new(), owner_user_id: SystemId::new(), timestamp: domain::value_objects::DateTimeStamp::now(), }; adapter.publish(&event).await.unwrap(); let recorded = messages.lock().unwrap(); let payload: event_payload::EventPayload = serde_json::from_slice(&recorded[0].1).expect("should be valid JSON"); assert_eq!(payload.subject(), "assets.ingested"); }