use std::collections::VecDeque;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use crate::client::{Client, Config};
use crate::http::{Request, Response, Url};
use crate::transport::{Timeouts, Transport, TransportError};
#[derive(Debug, Clone)]
pub struct Seen {
pub origin: String,
pub request: Request,
pub timeouts: Timeouts,
}
impl Seen {
pub fn header(&self, name: &str) -> Option<&str> {
self.request
.headers
.iter()
.find(|(k, _)| k.eq_ignore_ascii_case(name))
.map(|(_, v)| v.as_str())
}
pub fn body_text(&self) -> String {
String::from_utf8_lossy(&self.request.body).into_owned()
}
}
#[derive(Default)]
pub struct MockTransport {
replies: Mutex<VecDeque<Result<Response, TransportError>>>,
seen: Mutex<Vec<Seen>>,
}
impl MockTransport {
pub fn new() -> Arc<MockTransport> {
Arc::new(MockTransport::default())
}
pub fn reply(&self, status: u16, headers: &[(&str, &str)], body: &str) {
self.replies.lock().unwrap().push_back(Ok(Response {
status,
headers: headers
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect(),
body: body.as_bytes().to_vec(),
}));
}
pub fn fail(&self, error: TransportError) {
self.replies.lock().unwrap().push_back(Err(error));
}
pub fn seen(&self) -> Vec<Seen> {
self.seen.lock().unwrap().clone()
}
pub fn pending(&self) -> usize {
self.replies.lock().unwrap().len()
}
}
impl Transport for Arc<MockTransport> {
fn send(
&self,
url: &Url,
request: &Request,
timeouts: &Timeouts,
) -> Result<Response, TransportError> {
self.seen.lock().unwrap().push(Seen {
origin: url.origin(),
request: request.clone(),
timeouts: *timeouts,
});
self.replies
.lock()
.unwrap()
.pop_front()
.unwrap_or(Err(TransportError::Connect(
"no agreed response in the test".into(),
)))
}
}
pub fn client(config: Config, mock: &Arc<MockTransport>) -> Client {
Client::with_transport(config, Box::new(mock.clone()), Arc::new(|_: Duration| {}))
.expect("invalid test address")
}