1use std::collections::VecDeque;
2use std::fmt::Debug;
3use std::marker::PhantomData;
4use std::sync::{Arc, Mutex};
5use std::time::Duration;
6
7use async_trait::async_trait;
8
9use derive_builder::Builder;
10
11use crate::connector::Connector;
12use crate::muxed::Muxed;
13
14#[derive(Debug, Clone, PartialEq, Builder)]
16pub struct MockRequest<Addr, Req, Resp, Ctx, E> {
17 to: Addr,
18 req: Req,
19 ctx: Option<Ctx>,
20
21 resp: Result<(Resp, Ctx), E>,
22 delay: Option<Duration>,
23}
24
25impl<Addr, Req, Resp, Ctx, E> MockRequest<Addr, Req, Resp, Ctx, E> {
26 pub fn new(to: Addr, req: Req, resp: Result<(Resp, Ctx), E>) -> Self {
29 MockRequest {
30 to,
31 req,
32 resp,
33 ctx: None,
34 delay: None,
35 }
36 }
37
38 pub fn with_context(mut self, ctx: Ctx) -> Self {
39 self.ctx = Some(ctx);
40 self
41 }
42}
43
44#[derive(Debug, Clone, PartialEq, Builder)]
46pub struct MockResponse<Addr, Resp, Ctx, E> {
47 to: Addr,
48 resp: Resp,
49 err: Option<E>,
50 ctx: Option<Ctx>,
51}
52
53impl<Addr, Resp, Ctx, E> MockResponse<Addr, Resp, Ctx, E> {
54 pub fn new(to: Addr, resp: Resp, err: Option<E>) -> Self {
57 MockResponse {
58 to,
59 resp,
60 err,
61 ctx: None,
62 }
63 }
64
65 pub fn with_error(mut self, err: E) -> Self {
66 self.err = Some(err);
67 self
68 }
69
70 pub fn with_context(mut self, ctx: Ctx) -> Self {
71 self.ctx = Some(ctx);
72 self
73 }
74}
75
76pub type MockTransaction<Addr, Req, Resp, Ctx, E> =
78 Muxed<MockRequest<Addr, Req, Resp, Ctx, E>, MockResponse<Addr, Resp, Ctx, E>>;
79
80impl<Addr, Req, Resp, Ctx, E> MockTransaction<Addr, Req, Resp, Ctx, E> {
81 pub fn request(
83 to: Addr, req: Req, resp: Result<(Resp, Ctx), E>,
84 ) -> MockTransaction<Addr, Req, Resp, Ctx, E> {
85 Muxed::Request(MockRequest::new(to, req, resp))
86 }
87
88 pub fn response(to: Addr, resp: Resp, err: Option<E>) -> MockTransaction<Addr, Req, Resp, Ctx, E> {
90 Muxed::Response(MockResponse::new(to, resp, err))
91 }
92}
93
94pub struct MockConnector<Addr, Req, Resp, E, Ctx> {
97 transactions: Arc<Mutex<VecDeque<MockTransaction<Addr, Req, Resp, Ctx, E>>>>,
98 _ctx: PhantomData<Ctx>,
99}
100
101impl<Addr, Req, Resp, E, Ctx> Clone for MockConnector<Addr, Req, Resp, E, Ctx> {
102 fn clone(&self) -> Self {
103 MockConnector {
104 transactions: self.transactions.clone(),
105 _ctx: PhantomData,
106 }
107 }
108}
109
110impl<Addr, Req, Resp, E, Ctx> MockConnector<Addr, Req, Resp, E, Ctx>
111where
112 Addr: PartialEq + Debug + Send + 'static,
113 Req: PartialEq + Debug + Send + 'static,
114 Resp: PartialEq + Debug + Send + 'static,
115 E: PartialEq + Debug + Send + 'static,
116 Ctx: Clone + PartialEq + Debug + Send + 'static,
117{
118 pub fn new() -> MockConnector<Addr, Req, Resp, E, Ctx> {
120 MockConnector {
121 transactions: Arc::new(Mutex::new(VecDeque::new())),
122 _ctx: PhantomData,
123 }
124 }
125
126 pub fn expect<T>(&mut self, transactions: T) -> Self
128 where
129 T: Into<VecDeque<MockTransaction<Addr, Req, Resp, Ctx, E>>>,
130 {
131 *self.transactions.lock().unwrap() = transactions.into();
132
133 self.clone()
134 }
135
136 pub fn finalise(&mut self) {
138 let transactions: Vec<_> = self.transactions.lock().unwrap().drain(..).collect();
139 let expectations = Vec::<MockTransaction<Addr, Req, Resp, Ctx, E>>::new();
140 assert_eq!(
141 expectations, transactions,
142 "not all transactions have been evaluated"
143 );
144 }
145}
146
147#[async_trait]
148impl<Id, Addr, Req, Resp, E, Ctx> Connector<Id, Addr, Req, Resp, E, Ctx>
149 for MockConnector<Addr, Req, Resp, E, Ctx>
150where
151 Id: PartialEq + Debug + Send + 'static,
152 Addr: PartialEq + Debug + Send + 'static,
153 Req: PartialEq + Debug + Send + 'static,
154 Resp: PartialEq + Debug + Send + 'static,
155 E: PartialEq + Debug + Send + 'static,
156 Ctx: Clone + PartialEq + Debug + Send + 'static,
157{
158 async fn request(
161 &mut self, ctx: Ctx, _id: Id, addr: Addr, req: Req,
162 ) -> Result<Resp, E> {
163 let mut transactions = self.transactions.lock().unwrap();
164
165 let transaction = transactions.pop_front().expect(&format!(
166 "request error, no more transactions available (request: {:?})",
167 req
168 ));
169 let request = transaction.req().expect("expected request");
170
171 assert_eq!(request.to, addr, "destination mismatch");
172 assert_eq!(request.req, req, "request mismatch");
173 if let Some(c) = request.ctx {
174 assert_eq!(c, ctx, "context mismatch");
175 }
176
177 match request.resp {
178 Ok(r) => Ok(r.0),
179 Err(e) => Err(e),
180 }
181 }
182
183 async fn respond(
186 &mut self, ctx: Ctx, _id: Id, addr: Addr, resp: Resp,
187 ) -> Result<(), E> {
188 let mut transactions = self.transactions.lock().unwrap();
189
190 let transaction = transactions.pop_front().expect(&format!(
191 "response error, no more transactions available (response: {:?})",
192 resp
193 ));
194 let response = transaction.resp().expect("expected response");
195
196 assert_eq!(response.to, addr, "destination mismatch");
197 assert_eq!(response.resp, resp, "request mismatch");
198 if let Some(c) = response.ctx {
199 assert_eq!(c, ctx, "context mismatch");
200 }
201
202 match response.err {
203 Some(e) => Err(e),
204 None => Ok(()),
205 }
206 }
207}