ruststream_lapin/testing/
broker.rs1use std::sync::{Arc, OnceLock};
4
5use bytes::Bytes;
6use ruststream::testing::{Coordinator, TestableBroker};
7use ruststream::{Broker, DescribeServer, OutgoingMessage, RawMessage, ServerSpec, Subscribe};
8
9use super::publisher::LapinTestPublisher;
10use super::router::KeyRouter;
11use super::subscriber::LapinTestSubscriber;
12use crate::error::AmqpError;
13
14pub(crate) struct TestBrokerState {
15 pub(crate) router: KeyRouter,
16 coordinator: OnceLock<Coordinator>,
17}
18
19impl TestBrokerState {
20 pub(crate) fn install(&self, coordinator: Coordinator) {
21 let _ = self.coordinator.set(coordinator);
24 }
25
26 pub(crate) fn coordinator(&self) -> Option<Coordinator> {
27 self.coordinator.get().cloned()
28 }
29}
30
31impl Default for TestBrokerState {
32 fn default() -> Self {
33 Self {
34 router: KeyRouter::default(),
35 coordinator: OnceLock::new(),
36 }
37 }
38}
39
40impl std::fmt::Debug for TestBrokerState {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 f.debug_struct("TestBrokerState")
43 .field("router", &self.router)
44 .finish_non_exhaustive()
45 }
46}
47
48#[derive(Debug, Clone, Default)]
67pub struct LapinTestBroker {
68 state: Arc<TestBrokerState>,
69}
70
71impl LapinTestBroker {
72 #[must_use]
74 pub fn new() -> Self {
75 Self::default()
76 }
77
78 #[allow(clippy::unused_async)]
86 pub async fn subscribe(
87 &self,
88 queue: impl Into<String>,
89 ) -> Result<LapinTestSubscriber, AmqpError> {
90 let queue = queue.into();
91 if queue.is_empty() {
92 return Err(AmqpError::InvalidOptions(
93 "queue name must not be empty; subscribe with the queue the handler consumes"
94 .to_owned(),
95 ));
96 }
97 Ok(LapinTestSubscriber::open(&self.state, queue))
98 }
99
100 #[must_use]
102 pub fn publisher(&self) -> LapinTestPublisher {
103 LapinTestPublisher::new(Arc::clone(&self.state))
104 }
105}
106
107impl Broker for LapinTestBroker {
108 type Error = AmqpError;
109
110 async fn connect(&self) -> Result<(), Self::Error> {
111 Ok(())
112 }
113
114 async fn shutdown(&self) -> Result<(), Self::Error> {
115 self.state.router.clear();
116 Ok(())
117 }
118}
119
120#[allow(clippy::use_self)]
123impl Subscribe for LapinTestBroker {
124 type Subscriber = LapinTestSubscriber;
125
126 async fn subscribe(&self, name: &str) -> Result<Self::Subscriber, Self::Error> {
127 LapinTestBroker::subscribe(self, name).await
128 }
129}
130
131impl DescribeServer for LapinTestBroker {
132 fn describe_server(&self) -> ServerSpec {
133 ServerSpec::in_process("amqp")
134 }
135}
136
137impl TestableBroker for LapinTestBroker {
139 fn install_coordinator(&self, coordinator: Coordinator) {
140 self.state.install(coordinator);
141 }
142
143 fn inject(&self, message: OutgoingMessage<'_>) {
144 self.state.router.publish(
145 message.name(),
146 &Bytes::copy_from_slice(message.payload()),
147 message.headers(),
148 self.state.coordinator().as_ref(),
149 );
150 }
151
152 fn published(&self, name: &str) -> Vec<RawMessage> {
153 self.state.router.published(name)
154 }
155}
156
157ruststream::register_testable_broker!(LapinTestBroker);
158