aldrin_test/
lib.rs

1//! Utilities for Aldrin server and client tests
2//!
3//! This crate provides a simple way to quickly setup a complete Aldrin bus with a broker and
4//! multiple clients and is intended to be used in unit tests.
5//!
6//! If you are using Tokio, it is strongly recommended to enable this crate's `tokio` feature and
7//! use the types in the `tokio` module instead of the crate-level types.
8
9#![deny(missing_debug_implementations)]
10#![deny(missing_docs)]
11
12#[cfg(test)]
13mod test;
14
15#[cfg(feature = "tokio")]
16pub mod tokio;
17
18use aldrin::{Client, Handle};
19use aldrin_broker::{Broker, BrokerHandle, Connection, ConnectionHandle};
20use aldrin_core::channel::{self, Disconnected};
21use aldrin_core::transport::{AsyncTransportExt, BoxedTransport};
22use futures_util::future;
23use std::ops::{Deref, DerefMut};
24
25// For tests directly in aldrin_broker and aldrin.
26#[doc(hidden)]
27pub use {aldrin, aldrin_broker};
28
29/// Broker for use in tests.
30///
31/// This type is a simple wrapper around [`aldrin_broker::Broker`] and
32/// [`aldrin_broker::BrokerHandle`]. All method of [`BrokerHandle`] can be called on this type as
33/// well due to its [`Deref`] implementation.
34///
35/// The underlying [`Broker`] is not automatically started. Users must
36/// [take the `broker`](TestBroker::take_broker) and [run](Broker::run) it manually.
37///
38/// See the [crate-level documentation](crate) for usage examples.
39#[derive(Debug)]
40pub struct TestBroker {
41    handle: BrokerHandle,
42    broker: Option<Broker>,
43}
44
45impl TestBroker {
46    /// Creates a new broker.
47    pub fn new() -> Self {
48        let broker = Broker::new();
49
50        Self {
51            handle: broker.handle().clone(),
52            broker: Some(broker),
53        }
54    }
55
56    /// Returns a handle to the broker.
57    pub fn handle(&self) -> &BrokerHandle {
58        &self.handle
59    }
60
61    /// Takes the broker out of this struct.
62    ///
63    /// After creating a [`TestBroker`], the actual [`Broker`] must be taken out and
64    /// [run](Broker::run).
65    ///
66    /// # Panics
67    ///
68    /// This function panics when the [`Broker`] has already been taken out. It must be called only
69    /// once.
70    pub fn take_broker(&mut self) -> Broker {
71        self.broker.take().expect("broker already taken")
72    }
73
74    /// Add a new client to the broker.
75    pub async fn add_client(&mut self) -> TestClient {
76        let (t1, t2) = channel::unbounded();
77
78        let client = Client::connect(t1.boxed());
79        let conn = self.handle.connect(t2.boxed());
80
81        let (client, conn) = future::join(client, conn).await;
82        let client = client.expect("client failed to connect");
83        let handle = client.handle().clone();
84        let conn = conn.expect("connection failed to establish");
85        let connection_handle = conn.handle().clone();
86
87        TestClient::new(handle, connection_handle, client, conn)
88    }
89}
90
91impl Default for TestBroker {
92    fn default() -> Self {
93        Self::new()
94    }
95}
96
97impl Deref for TestBroker {
98    type Target = BrokerHandle;
99
100    fn deref(&self) -> &BrokerHandle {
101        &self.handle
102    }
103}
104
105impl DerefMut for TestBroker {
106    fn deref_mut(&mut self) -> &mut BrokerHandle {
107        &mut self.handle
108    }
109}
110
111/// Client for use in tests.
112///
113/// After creating a [`TestClient`], it is fully connected to the [`TestBroker`], but neither the
114/// underlying [`Client`] nor the [`Connection`] are running. This must be done manually by taking
115/// both parts out and calling their respective `run` methods (see
116/// [`take_client`](Self::take_client) and [`take_connection`](Self::take_connection).
117///
118/// [`TestClient`] dereferences to [`aldrin::Handle`] and thus all methods on [`Handle`] can
119/// be called on [`TestClient`] as well.
120#[derive(Debug)]
121pub struct TestClient {
122    handle: Handle,
123    connection_handle: ConnectionHandle,
124    client: Option<Client<BoxedTransport<'static, Disconnected>>>,
125    conn: Option<Connection<BoxedTransport<'static, Disconnected>>>,
126}
127
128impl TestClient {
129    fn new(
130        handle: Handle,
131        connection_handle: ConnectionHandle,
132        client: Client<BoxedTransport<'static, Disconnected>>,
133        conn: Connection<BoxedTransport<'static, Disconnected>>,
134    ) -> Self {
135        Self {
136            handle,
137            connection_handle,
138            client: Some(client),
139            conn: Some(conn),
140        }
141    }
142
143    /// Returns a handle to the client.
144    pub fn handle(&self) -> &Handle {
145        &self.handle
146    }
147
148    /// Returns a handle to the connection.
149    pub fn connection(&self) -> &ConnectionHandle {
150        &self.connection_handle
151    }
152
153    /// Takes the `Client` out of this struct.
154    ///
155    /// After creating a [`TestClient`], the actual [`Client`] must be taken out and
156    /// [run](Client::run).
157    ///
158    /// # Panics
159    ///
160    /// This function panics when the [`Client`] has already been taken out. It must be called only
161    /// once.
162    pub fn take_client(&mut self) -> Client<BoxedTransport<'static, Disconnected>> {
163        self.client.take().expect("client already taken")
164    }
165
166    /// Takes the `Connection` out of this struct.
167    ///
168    /// After creating a [`TestClient`], the actual [`Connection`] must be taken out and
169    /// [run](Connection::run).
170    ///
171    /// # Panics
172    ///
173    /// This function panics when the [`Connection`] has already been taken out. It must be called
174    /// only once.
175    pub fn take_connection(&mut self) -> Connection<BoxedTransport<'static, Disconnected>> {
176        self.conn.take().expect("connection already taken")
177    }
178}
179
180impl Deref for TestClient {
181    type Target = Handle;
182
183    fn deref(&self) -> &Handle {
184        &self.handle
185    }
186}