#![deny(missing_docs)]
use std::fs::File;
use std::io::Read;
use std::sync::Mutex;
use std::time::Duration;
use async_trait::async_trait;
use futures::FutureExt;
use http::{header::HeaderName, HeaderMap, StatusCode};
use rusoto_core::credential::{AwsCredentials, ProvideAwsCredentials};
use rusoto_core::request::HttpResponse;
use rusoto_core::signature::SignedRequest;
use rusoto_core::{ByteStream, DispatchSignedRequest, HttpDispatchError};
use serde::Serialize;
pub struct MockCredentialsProvider;
#[async_trait]
impl ProvideAwsCredentials for MockCredentialsProvider {
async fn credentials(
&self,
) -> Result<rusoto_core::credential::AwsCredentials, rusoto_core::credential::CredentialsError>
{
Ok(AwsCredentials::new("mock_key", "mock_secret", None, None))
}
}
#[derive(Default)]
pub struct MockRequestDispatcher {
outcome: RequestOutcome,
body: Vec<u8>,
headers: HeaderMap<String>,
request_checker: Option<Box<dyn Fn(&SignedRequest) + Send + Sync>>,
}
enum RequestOutcome {
Performed(StatusCode),
Failed(HttpDispatchError),
}
impl Default for RequestOutcome {
fn default() -> RequestOutcome {
RequestOutcome::Performed(StatusCode::default())
}
}
impl MockRequestDispatcher {
pub fn with_status(status: u16) -> MockRequestDispatcher {
MockRequestDispatcher {
outcome: RequestOutcome::Performed(StatusCode::from_u16(status).unwrap()),
..MockRequestDispatcher::default()
}
}
pub fn with_dispatch_error(error: HttpDispatchError) -> MockRequestDispatcher {
MockRequestDispatcher {
outcome: RequestOutcome::Failed(error),
..MockRequestDispatcher::default()
}
}
pub fn with_body(mut self, body: &str) -> MockRequestDispatcher {
self.body = body.as_bytes().to_vec();
self
}
pub fn with_json_body<B>(mut self, body: B) -> MockRequestDispatcher
where
B: Serialize,
{
self.body = serde_json::to_vec(&body).expect("failed to deserialize into json");
self
}
pub fn with_request_checker<F>(mut self, checker: F) -> MockRequestDispatcher
where
F: Fn(&SignedRequest) + Send + Sync + 'static,
{
self.request_checker = Some(Box::new(checker));
self
}
pub fn with_header(mut self, key: &str, value: &str) -> MockRequestDispatcher {
self.headers
.insert(key.parse::<HeaderName>().unwrap(), value.into());
self
}
}
impl DispatchSignedRequest for MockRequestDispatcher {
fn dispatch(
&self,
request: SignedRequest,
_timeout: Option<Duration>,
) -> rusoto_core::request::DispatchSignedRequestFuture {
if self.request_checker.is_some() {
self.request_checker.as_ref().unwrap()(&request);
}
match self.outcome {
RequestOutcome::Performed(ref status) => futures::future::ready(Ok(HttpResponse {
status: *status,
body: ByteStream::from(self.body.clone()),
headers: self.headers.clone(),
}))
.boxed(),
RequestOutcome::Failed(ref error) => futures::future::ready(Err(error.clone())).boxed(),
}
}
}
pub trait ReadMockResponse {
fn read_response(dir_name: &str, file_name: &str) -> String;
}
pub struct MockResponseReader;
impl ReadMockResponse for MockResponseReader {
fn read_response(dir_name: &str, response_name: &str) -> String {
let file_name = format!("{}/{}", dir_name, response_name);
let mut input_file = File::open(&file_name).expect("couldn't find file");
let mut mock_response = String::new();
input_file
.read_to_string(&mut mock_response)
.unwrap_or_else(|_| panic!("Failed to read {:?}", file_name));
mock_response
}
}
pub struct MultipleMockRequestDispatcher<I>
where
I: Iterator<Item = MockRequestDispatcher>,
{
iterator: Mutex<I>,
}
impl<I> MultipleMockRequestDispatcher<I>
where
I: Iterator<Item = MockRequestDispatcher>,
{
pub fn new<C>(collection: C) -> MultipleMockRequestDispatcher<I>
where
C: IntoIterator<Item = MockRequestDispatcher>,
C: IntoIterator<IntoIter = I>,
{
MultipleMockRequestDispatcher {
iterator: Mutex::new(collection.into_iter()),
}
}
}
impl<I> DispatchSignedRequest for MultipleMockRequestDispatcher<I>
where
I: Iterator<Item = MockRequestDispatcher>,
{
fn dispatch(
&self,
request: SignedRequest,
_timeout: Option<Duration>,
) -> rusoto_core::request::DispatchSignedRequestFuture {
self.iterator
.lock()
.unwrap()
.next()
.expect("Ran out of mock responses to return from MultipleMockRequestDispatcher")
.dispatch(request, _timeout)
}
}