arminius_lib_message_bus/mock/
event.rs1use std::{
10 ops::Deref,
11 sync::{Arc, Mutex},
12};
13
14use futures::Stream;
15
16use crate::{Error, Event, Publisher, ReceivedEvent, subject_matches};
17
18#[derive(Clone, Debug, Default)]
49pub struct MockPublisher<E> {
50 published: Arc<Mutex<Vec<E>>>,
52}
53
54impl<E: Clone> MockPublisher<E> {
55 #[must_use]
57 pub fn new() -> Self {
58 Self { published: Arc::new(Mutex::new(Vec::new())) }
59 }
60
61 #[expect(clippy::expect_used, reason = "panicking is acceptable in test-only code")]
63 #[must_use]
64 pub fn published_messages(&self) -> Vec<E> {
65 self.published
66 .lock()
67 .expect("lock should not be poisoned: no holder should panic while the mutex is held")
68 .to_vec()
69 }
70}
71
72impl<T> Publisher for MockPublisher<T>
73where
74 T: Event + Clone + 'static,
75{
76 #[expect(clippy::expect_used, reason = "panicking is acceptable in test-only code")]
77 async fn publish<E>(&self, message: E) -> Result<(), Error>
78 where
79 E: Event + Clone + 'static,
80 {
81 let any_message: &dyn std::any::Any = &message;
82 if let Some(mock_event) = any_message.downcast_ref::<T>() {
83 let subject = mock_event.subject();
84 debug_assert!(
86 subject_matches(E::SUBJECT, &subject),
87 "concrete subject {subject:?} is not covered by the subject pattern {:?}",
88 E::SUBJECT,
89 );
90
91 self.published
92 .lock()
93 .expect(
94 "lock should not be poisoned: no holder should panic while the mutex is held",
95 )
96 .push(mock_event.clone());
97 } else {
98 return Err(Error::SendFailed {
99 error: format!(
100 "Type mismatch: MockPublisher<{}> cannot publish events of type {}.",
101 std::any::type_name::<T>(),
102 std::any::type_name::<E>(),
103 ),
104 });
105 }
106
107 Ok(())
108 }
109}
110
111#[derive(Debug, Clone)]
116pub struct MockReceivedEvent<E> {
117 event: E,
119 acked: Arc<Mutex<bool>>,
121 context: opentelemetry::Context,
123}
124
125impl<E> MockReceivedEvent<E> {
126 pub fn new(event: E) -> Self {
129 Self { event, acked: Arc::new(Mutex::new(false)), context: opentelemetry::Context::new() }
130 }
131}
132
133impl<E: PartialEq> PartialEq for MockReceivedEvent<E> {
134 #[expect(clippy::expect_used, reason = "panicking is acceptable in test-only code")]
135 fn eq(&self, other: &Self) -> bool {
136 let self_ack = *self.acked.lock().expect("lock poisoned");
137 let other_ack = *other.acked.lock().expect("lock poisoned");
138 self.event == other.event && self_ack == other_ack
139 }
140}
141
142impl<E: Event + Clone> From<&E> for MockReceivedEvent<E> {
143 fn from(value: &E) -> Self {
144 Self {
145 event: value.clone(),
146 acked: Arc::new(Mutex::new(false)),
147 context: opentelemetry::Context::new(),
148 }
149 }
150}
151
152impl<E: Event> From<E> for MockReceivedEvent<E> {
153 fn from(value: E) -> Self {
154 Self {
155 event: value,
156 acked: Arc::new(Mutex::new(false)),
157 context: opentelemetry::Context::new(),
158 }
159 }
160}
161
162impl<E: Event> Deref for MockReceivedEvent<E> {
163 type Target = E;
164
165 fn deref(&self) -> &Self::Target {
166 &self.event
167 }
168}
169
170impl<E> MockReceivedEvent<E> {
171 #[expect(clippy::expect_used, reason = "panicking is acceptable in test-only code")]
174 pub fn was_acked(&self) -> bool {
175 *self
176 .acked
177 .lock()
178 .expect("lock should not be poisoned: no holder should panic while the mutex is held")
179 }
180}
181
182impl<E: Event> ReceivedEvent<E> for MockReceivedEvent<E> {
183 #[expect(clippy::expect_used, reason = "panicking is acceptable in test-only code")]
184 async fn ack(&self) -> Result<(), Error> {
185 *self.acked.lock().expect(
186 "lock should not be poisoned: no holder should panic while the mutex is held",
187 ) = true;
188 Ok(())
189 }
190
191 fn context(&self) -> &opentelemetry::Context {
192 &self.context
193 }
194}
195
196#[derive(Debug, Default)]
226pub struct MockSubscriber<E> {
227 _phantom: std::marker::PhantomData<E>,
229}
230
231impl<E: Event + Clone> MockSubscriber<E> {
232 #[must_use]
234 pub fn new() -> Self {
235 Self { _phantom: std::marker::PhantomData }
236 }
237
238 pub fn create_stream(
243 &self,
244 events: Vec<E>,
245 ) -> (impl Stream<Item = Result<MockReceivedEvent<E>, Error>> + use<E>, Vec<MockReceivedEvent<E>>)
246 {
247 let mocks: Vec<MockReceivedEvent<E>> = events.into_iter().map(Into::into).collect();
248 let checkers = mocks.clone();
249 (futures::stream::iter(mocks.into_iter().map(Ok)), checkers)
250 }
251}