ruststream_lapin/testing/
broker.rs1use std::sync::atomic::{AtomicBool, Ordering};
4use std::sync::{Arc, OnceLock};
5
6use bytes::Bytes;
7use ruststream::testing::{Coordinator, TestableBroker};
8use ruststream::{
9 Broker, ConnectedBroker, DefaultPublish, DescribeServer, OutgoingMessage, RawMessage,
10 ServerSpec, Subscribe,
11};
12
13use super::publisher::{LapinTestPublish, LapinTestPublisher};
14use super::router::KeyRouter;
15use super::subscriber::LapinTestSubscriber;
16use crate::error::AmqpError;
17
18pub(crate) struct TestBrokerState {
24 pub(crate) router: KeyRouter,
25 closed: AtomicBool,
28 coordinator: OnceLock<Coordinator>,
29}
30
31impl TestBrokerState {
32 pub(crate) fn install(&self, coordinator: Coordinator) {
33 let _ = self.coordinator.set(coordinator);
36 }
37
38 pub(crate) fn coordinator(&self) -> Option<Coordinator> {
39 self.coordinator.get().cloned()
40 }
41
42 pub(crate) fn ensure_live(&self, target: &str) -> Result<(), AmqpError> {
44 if self.closed.load(Ordering::Acquire) {
45 return Err(AmqpError::closed(target));
46 }
47 Ok(())
48 }
49}
50
51impl Default for TestBrokerState {
52 fn default() -> Self {
53 Self {
54 router: KeyRouter::default(),
55 closed: AtomicBool::new(false),
56 coordinator: OnceLock::new(),
57 }
58 }
59}
60
61impl std::fmt::Debug for TestBrokerState {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 f.debug_struct("TestBrokerState")
64 .field("router", &self.router)
65 .field("closed", &self.closed.load(Ordering::Relaxed))
66 .finish_non_exhaustive()
67 }
68}
69
70#[derive(Debug, Clone, Default)]
92#[must_use]
93pub struct LapinTestBroker {
94 state: Arc<TestBrokerState>,
95}
96
97impl LapinTestBroker {
98 pub fn new() -> Self {
100 Self::default()
101 }
102}
103
104impl Broker for LapinTestBroker {
105 type Error = AmqpError;
106 type Connected = ConnectedLapinTestBroker;
107
108 async fn connect(self) -> Result<Self::Connected, Self::Error> {
109 Ok(ConnectedLapinTestBroker { state: self.state })
110 }
111}
112
113impl DescribeServer for LapinTestBroker {
114 fn describe_server(&self) -> ServerSpec {
115 ServerSpec::in_process("amqp")
116 }
117}
118
119#[derive(Debug, Clone)]
127pub struct ConnectedLapinTestBroker {
128 state: Arc<TestBrokerState>,
129}
130
131impl ConnectedLapinTestBroker {
132 pub(crate) fn state(&self) -> Arc<TestBrokerState> {
133 Arc::clone(&self.state)
134 }
135
136 #[allow(clippy::unused_async)]
145 pub async fn subscribe(
146 &self,
147 queue: impl Into<String>,
148 ) -> Result<LapinTestSubscriber, AmqpError> {
149 let queue = queue.into();
150 if queue.is_empty() {
151 return Err(AmqpError::InvalidOptions(
152 "queue name must not be empty; subscribe with the queue the handler consumes"
153 .to_owned(),
154 ));
155 }
156 self.state.ensure_live(&queue)?;
157 Ok(LapinTestSubscriber::open(&self.state, queue))
158 }
159
160 #[must_use]
164 pub fn publisher(&self, policy: LapinTestPublish) -> LapinTestPublisher {
165 policy.bind(self)
166 }
167}
168
169impl ConnectedBroker for ConnectedLapinTestBroker {
170 type Error = AmqpError;
171 type Closed = ();
172
173 async fn shutdown(self) -> Result<Self::Closed, Self::Error> {
174 self.state.closed.store(true, Ordering::Release);
175 self.state.router.clear();
176 Ok(())
177 }
178}
179
180#[allow(clippy::use_self)]
183impl Subscribe for ConnectedLapinTestBroker {
184 type Subscriber = LapinTestSubscriber;
185
186 async fn subscribe(&self, name: &str) -> Result<Self::Subscriber, Self::Error> {
187 ConnectedLapinTestBroker::subscribe(self, name).await
188 }
189}
190
191impl DefaultPublish for ConnectedLapinTestBroker {
192 type Policy = LapinTestPublish;
193}
194
195impl TestableBroker for ConnectedLapinTestBroker {
197 fn install_coordinator(&self, coordinator: Coordinator) {
198 self.state.install(coordinator);
199 }
200
201 fn inject(&self, message: OutgoingMessage<'_>) {
202 self.state.router.publish(
203 message.name(),
204 &Bytes::copy_from_slice(message.payload()),
205 message.headers(),
206 self.state.coordinator().as_ref(),
207 );
208 }
209
210 fn published(&self, name: &str) -> Vec<RawMessage> {
211 self.state.router.published(name)
212 }
213}
214
215ruststream::register_testable_broker!(ConnectedLapinTestBroker);
216