#![allow(warnings)]
#[macro_use]
extern crate lazy_static;
use std::cell::Cell;
use std::net::{SocketAddr, ToSocketAddrs};
use std::rc::Rc;
use std::sync::Arc;
use std::thread;
use async_object_pool::Pool;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tokio::task::LocalSet;
use tokio::time::Duration;
pub use crate::api::{Method, Mock, MockRef, MockRefExt, Regex};
pub use data::{HttpMockRequest, MockMatcherFunction};
use crate::api::{LocalMockServerAdapter, RemoteMockServerAdapter};
use crate::data::Pattern;
use crate::server::{start_server, MockServerState};
use crate::util::{read_env, with_retry};
use api::MockServerAdapter;
use util::Join;
mod api;
pub(crate) mod data;
mod server;
pub mod standalone;
pub(crate) mod util;
pub struct MockServer {
pub(crate) server_adapter: Option<Arc<dyn MockServerAdapter + Send + Sync>>,
pool: Arc<Pool<Arc<dyn MockServerAdapter + Send + Sync>>>,
}
impl MockServer {
async fn from(
server_adapter: Arc<dyn MockServerAdapter + Send + Sync>,
pool: Arc<Pool<Arc<dyn MockServerAdapter + Send + Sync>>>,
) -> Self {
with_retry(5, || server_adapter.ping())
.await
.expect("Cannot ping mock server.");
with_retry(5, || server_adapter.delete_all_mocks())
.await
.expect("Cannot reset mock server (task: delete mocks).");
with_retry(5, || server_adapter.delete_history())
.await
.expect("Cannot reset mock server (task: delete request history).");
Self {
server_adapter: Some(server_adapter),
pool,
}
}
pub async fn connect_async(address: &str) -> Self {
let addr = address
.to_socket_addrs()
.expect("Cannot parse address")
.find(|addr| addr.is_ipv4())
.expect("Not able to resolve the provided host name to an IPv4 address");
let adapter = REMOTE_SERVER_POOL_REF
.take_or_create(|| Arc::new(RemoteMockServerAdapter::new(addr)))
.await;
Self::from(adapter, REMOTE_SERVER_POOL_REF.clone()).await
}
pub fn connect(address: &str) -> Self {
Self::connect_async(address).join()
}
pub async fn connect_from_env_async() -> Self {
let host = read_env("HTTPMOCK_HOST", "127.0.0.1");
let port = read_env("HTTPMOCK_PORT", "5000")
.parse::<u16>()
.expect("Cannot parse environment variable HTTPMOCK_PORT to an integer");
Self::connect_async(&format!("{}:{}", host, port)).await
}
pub fn connect_from_env() -> Self {
Self::connect_from_env_async().join()
}
pub async fn start_async() -> Self {
let adapter = LOCAL_SERVER_POOL_REF
.take_or_create(LOCAL_SERVER_ADAPTER_GENERATOR)
.await;
Self::from(adapter, LOCAL_SERVER_POOL_REF.clone()).await
}
pub fn start() -> MockServer {
Self::start_async().join()
}
pub fn host(&self) -> String {
self.server_adapter.as_ref().unwrap().host()
}
pub fn port(&self) -> u16 {
self.server_adapter.as_ref().unwrap().port()
}
pub fn address(&self) -> &SocketAddr {
self.server_adapter.as_ref().unwrap().address()
}
pub fn url<S: Into<String>>(&self, path: S) -> String {
format!("http://{}{}", self.address(), path.into())
}
pub fn base_url(&self) -> String {
self.url("")
}
pub fn mock<F>(&self, config_fn: F) -> MockRef
where
F: FnOnce(When, Then),
{
self.mock_async(config_fn).join()
}
pub async fn mock_async<'a, F>(&'a self, config_fn: F) -> MockRef<'a>
where
F: FnOnce(When, Then),
{
let mock = Rc::new(Cell::new(Mock::new()));
config_fn(When { mock: mock.clone() }, Then { mock: mock.clone() });
mock.take().create_on_async(self).await
}
}
pub struct When {
pub(crate) mock: Rc<Cell<Mock>>,
}
impl When {
pub fn any_request(self) -> Self {
self
}
pub fn method<M: Into<Method>>(self, method: M) -> Self {
self.mock.set(self.mock.take().expect_method(method));
self
}
pub fn path<S: Into<String>>(self, path: S) -> Self {
self.mock.set(self.mock.take().expect_path(path));
self
}
pub fn path_contains<S: Into<String>>(self, substring: S) -> Self {
self.mock
.set(self.mock.take().expect_path_contains(substring));
self
}
pub fn path_matches<R: Into<Regex>>(self, regex: R) -> Self {
self.mock.set(self.mock.take().expect_path_matches(regex));
self
}
pub fn query_param<S: Into<String>>(self, name: S, value: S) -> Self {
self.mock
.set(self.mock.take().expect_query_param(name, value));
self
}
pub fn query_param_exists<S: Into<String>>(self, name: S) -> Self {
self.mock
.set(self.mock.take().expect_query_param_exists(name));
self
}
pub fn body<S: Into<String>>(self, body: S) -> Self {
self.mock.set(self.mock.take().expect_body(body));
self
}
pub fn body_matches<R: Into<Regex>>(self, regex: R) -> Self {
self.mock.set(self.mock.take().expect_body_matches(regex));
self
}
pub fn body_contains<S: Into<String>>(self, substring: S) -> Self {
self.mock
.set(self.mock.take().expect_body_contains(substring));
self
}
pub fn json_body<V: Into<serde_json::Value>>(self, value: V) -> Self {
self.mock.set(self.mock.take().expect_json_body(value));
self
}
pub fn json_body_obj<'a, T>(self, body: &T) -> Self
where
T: Serialize + Deserialize<'a>,
{
self.mock.set(self.mock.take().expect_json_body_obj(body));
self
}
pub fn json_body_partial<S: Into<String>>(self, partial: S) -> Self {
self.mock
.set(self.mock.take().expect_json_body_partial(partial));
self
}
pub fn header<S: Into<String>>(self, name: S, value: S) -> Self {
self.mock.set(self.mock.take().expect_header(name, value));
self
}
pub fn header_exists<S: Into<String>>(self, name: S) -> Self {
self.mock.set(self.mock.take().expect_header_exists(name));
self
}
pub fn cookie<S: Into<String>>(self, name: S, value: S) -> Self {
self.mock.set(self.mock.take().expect_cookie(name, value));
self
}
pub fn cookie_exists<S: Into<String>>(self, name: S) -> Self {
self.mock.set(self.mock.take().expect_cookie_exists(name));
self
}
pub fn matches(self, matcher: MockMatcherFunction) -> Self {
self.mock.set(self.mock.take().expect_match(matcher));
self
}
}
pub struct Then {
pub(crate) mock: Rc<Cell<Mock>>,
}
impl Then {
pub fn status(self, status: u16) -> Self {
self.mock.set(self.mock.take().return_status(status));
self
}
pub fn body(self, body: impl AsRef<[u8]>) -> Self {
self.mock.set(self.mock.take().return_body(body));
self
}
pub fn body_from_file<S: Into<String>>(self, body: S) -> Self {
self.mock.set(self.mock.take().return_body_from_file(body));
self
}
pub fn json_body<V: Into<Value>>(self, value: V) -> Self {
self.mock.set(self.mock.take().return_json_body(value));
self
}
pub fn json_body_obj<T>(self, body: &T) -> Self
where
T: Serialize,
{
self.mock.set(self.mock.take().return_json_body_obj(body));
self
}
pub fn header<S: Into<String>>(self, name: S, value: S) -> Self {
self.mock.set(self.mock.take().return_header(name, value));
self
}
#[deprecated(
since = "0.5.6",
note = "Please use desired response code and headers instead"
)]
pub fn temporary_redirect<S: Into<String>>(mut self, redirect_url: S) -> Self {
self.mock
.set(self.mock.take().return_temporary_redirect(redirect_url));
self
}
pub fn permanent_redirect<S: Into<String>>(mut self, redirect_url: S) -> Self {
self.mock
.set(self.mock.take().return_permanent_redirect(redirect_url));
self
}
pub fn delay<D: Into<Duration>>(self, duration: D) -> Self {
self.mock.set(self.mock.take().return_with_delay(duration));
self
}
}
impl Drop for MockServer {
fn drop(&mut self) {
let adapter = self.server_adapter.take().unwrap();
self.pool.put(adapter).join();
}
}
const LOCAL_SERVER_ADAPTER_GENERATOR: fn() -> Arc<dyn MockServerAdapter + Send + Sync> = || {
let (addr_sender, addr_receiver) = tokio::sync::oneshot::channel::<SocketAddr>();
let state = Arc::new(MockServerState::new());
let server_state = state.clone();
thread::spawn(move || {
let server_state = server_state.clone();
let srv = start_server(0, false, &server_state, Some(addr_sender), false);
let mut runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("Cannot build local tokio runtime");
LocalSet::new().block_on(&mut runtime, srv)
});
let addr = addr_receiver.join().expect("Cannot get server address");
Arc::new(LocalMockServerAdapter::new(addr, state))
};
lazy_static! {
static ref LOCAL_SERVER_POOL_REF: Arc<Pool<Arc<dyn MockServerAdapter + Send + Sync>>> = {
let max_servers = read_env("HTTPMOCK_MAX_SERVERS", "25")
.parse::<usize>()
.expect("Cannot parse environment variable HTTPMOCK_MAX_SERVERS to an integer");
Arc::new(Pool::new(max_servers))
};
static ref REMOTE_SERVER_POOL_REF: Arc<Pool<Arc<dyn MockServerAdapter + Send + Sync>>> =
Arc::new(Pool::new(1));
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct NameValuePair {
name: String,
value: String,
}
#[derive(Debug, Serialize, Deserialize)]
struct YAMLRequestRequirements {
pub path: Option<String>,
pub path_contains: Option<Vec<String>>,
pub path_matches: Option<Vec<String>>,
pub method: Option<Method>,
pub header: Option<Vec<NameValuePair>>,
pub header_exists: Option<Vec<String>>,
pub cookie: Option<Vec<NameValuePair>>,
pub cookie_exists: Option<Vec<String>>,
pub body: Option<String>,
pub json_body: Option<Value>,
pub json_body_partial: Option<Vec<Value>>,
pub body_contains: Option<Vec<String>>,
pub body_matches: Option<Vec<String>>,
pub query_param_exists: Option<Vec<String>>,
pub query_param: Option<Vec<NameValuePair>>,
}
#[derive(Debug, Serialize, Deserialize)]
struct YAMLHTTPResponse {
pub status: Option<u16>,
pub header: Option<Vec<NameValuePair>>,
pub body: Option<String>,
pub delay: Option<u64>,
}
#[derive(Debug, Serialize, Deserialize)]
struct YAMLMockDefinition {
when: YAMLRequestRequirements,
then: YAMLHTTPResponse,
}