1use std::collections::VecDeque;
5use std::sync::{Arc, Mutex};
6use std::time::Duration;
7
8use crate::client::{Client, Config};
9use crate::http::{Request, Response, Url};
10use crate::transport::{Timeouts, Transport, TransportError};
11
12#[derive(Debug, Clone)]
14pub struct Seen {
15 pub origin: String,
17 pub request: Request,
18 pub timeouts: Timeouts,
19}
20
21impl Seen {
22 pub fn header(&self, name: &str) -> Option<&str> {
23 self.request
24 .headers
25 .iter()
26 .find(|(k, _)| k.eq_ignore_ascii_case(name))
27 .map(|(_, v)| v.as_str())
28 }
29
30 pub fn body_text(&self) -> String {
31 String::from_utf8_lossy(&self.request.body).into_owned()
32 }
33}
34
35#[derive(Default)]
36pub struct MockTransport {
37 replies: Mutex<VecDeque<Result<Response, TransportError>>>,
38 seen: Mutex<Vec<Seen>>,
39}
40
41impl MockTransport {
42 pub fn new() -> Arc<MockTransport> {
43 Arc::new(MockTransport::default())
44 }
45
46 pub fn reply(&self, status: u16, headers: &[(&str, &str)], body: &str) {
48 self.replies.lock().unwrap().push_back(Ok(Response {
49 status,
50 headers: headers
51 .iter()
52 .map(|(k, v)| (k.to_string(), v.to_string()))
53 .collect(),
54 body: body.as_bytes().to_vec(),
55 }));
56 }
57
58 pub fn fail(&self, error: TransportError) {
60 self.replies.lock().unwrap().push_back(Err(error));
61 }
62
63 pub fn seen(&self) -> Vec<Seen> {
64 self.seen.lock().unwrap().clone()
65 }
66
67 pub fn pending(&self) -> usize {
69 self.replies.lock().unwrap().len()
70 }
71}
72
73impl Transport for Arc<MockTransport> {
74 fn send(
75 &self,
76 url: &Url,
77 request: &Request,
78 timeouts: &Timeouts,
79 ) -> Result<Response, TransportError> {
80 self.seen.lock().unwrap().push(Seen {
81 origin: url.origin(),
82 request: request.clone(),
83 timeouts: *timeouts,
84 });
85 self.replies
86 .lock()
87 .unwrap()
88 .pop_front()
89 .unwrap_or(Err(TransportError::Connect(
90 "no agreed response in the test".into(),
91 )))
92 }
93}
94
95pub fn client(config: Config, mock: &Arc<MockTransport>) -> Client {
97 Client::with_transport(config, Box::new(mock.clone()), Arc::new(|_: Duration| {}))
98 .expect("invalid test address")
99}