arminius_lib_message_bus/mock/
command.rs1use std::{
9 ops::Deref,
10 sync::{Arc, Mutex},
11};
12
13use crate::{Command, Error, ReceivedCommand};
14
15pub struct MockReceivedCommand<C: Command> {
22 cmd: C,
24 context: opentelemetry::Context,
26 reply_tx: Arc<Mutex<Option<futures::channel::oneshot::Sender<Vec<u8>>>>>,
28}
29
30impl<C: Command + std::fmt::Debug> std::fmt::Debug for MockReceivedCommand<C> {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 f.debug_struct("MockReceivedCommand").field("cmd", &self.cmd).finish_non_exhaustive()
33 }
34}
35
36impl<C: Command> MockReceivedCommand<C> {
37 pub fn new(cmd: C) -> (Self, ReplyChecker<C::Reply>) {
41 let (tx, rx) = futures::channel::oneshot::channel();
42 let mock = Self {
43 cmd,
44 context: opentelemetry::Context::new(),
45 reply_tx: Arc::new(Mutex::new(Some(tx))),
46 };
47 (mock, ReplyChecker { rx, _phantom: std::marker::PhantomData })
48 }
49
50 pub(crate) fn with_reply_channel(
57 cmd: C,
58 reply_tx: futures::channel::oneshot::Sender<Vec<u8>>,
59 ) -> Self {
60 Self {
61 cmd,
62 context: opentelemetry::Context::new(),
63 reply_tx: Arc::new(Mutex::new(Some(reply_tx))),
64 }
65 }
66}
67
68impl<C: Command> Deref for MockReceivedCommand<C> {
69 type Target = C;
70
71 fn deref(&self) -> &C {
72 &self.cmd
73 }
74}
75
76impl<C: Command> ReceivedCommand<C> for MockReceivedCommand<C> {
77 async fn ack(&self) -> Result<(), Error> {
80 Ok(())
81 }
82
83 #[expect(clippy::expect_used, reason = "panicking is acceptable in test-only code")]
84 async fn reply(&self, response: C::Reply) -> Result<(), Error> {
85 let bytes = serde_json::to_vec(&response)
86 .map_err(|error| Error::SendFailed { error: error.to_string() })?;
87 if let Some(tx) = self
88 .reply_tx
89 .lock()
90 .expect("lock should not be poisoned: no holder should panic while the mutex is held")
91 .take()
92 {
93 let _ = tx.send(bytes);
94 }
95 Ok(())
96 }
97
98 fn context(&self) -> &opentelemetry::Context {
99 &self.context
100 }
101}
102
103pub struct ReplyChecker<R> {
109 rx: futures::channel::oneshot::Receiver<Vec<u8>>,
111 _phantom: std::marker::PhantomData<R>,
113}
114
115impl<R> std::fmt::Debug for ReplyChecker<R> {
116 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117 f.debug_struct("ReplyChecker").finish_non_exhaustive()
118 }
119}
120
121impl<R: serde::de::DeserializeOwned> ReplyChecker<R> {
122 pub async fn into_reply(self) -> Option<R> {
127 self.rx.await.ok().and_then(|bytes| serde_json::from_slice(&bytes).ok())
128 }
129}