chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

3
vendor/wiremock/src/http.rs vendored Normal file
View File

@@ -0,0 +1,3 @@
//! Convenient re-exports of http types that are part of `wiremock`'s public API.
pub use http::{HeaderMap, HeaderName, HeaderValue, Method};
pub use url::Url;

164
vendor/wiremock/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,164 @@
#![allow(clippy::needless_doctest_main)]
//! `wiremock` provides HTTP mocking to perform black-box testing of Rust applications that
//! interact with third-party APIs.
//!
//! It provides mocking of HTTP responses using request matching and response templating.
//!
//! ## How to install
//!
//! Add `wiremock` to your development dependencies:
//! ```toml
//! [dev-dependencies]
//! # ...
//! wiremock = "0.5"
//! ```
//! If you are using [`cargo-edit`](https://github.com/killercup/cargo-edit), run
//! ```bash
//! cargo add wiremock --dev
//! ```
//!
//! ## Getting started
//!
//! ```rust
//! use wiremock::{MockServer, Mock, ResponseTemplate};
//! use wiremock::matchers::{method, path};
//!
//! #[async_std::main]
//! async fn main() {
//! // Start a background HTTP server on a random local port
//! let mock_server = MockServer::start().await;
//!
//! // Arrange the behaviour of the MockServer adding a Mock:
//! // when it receives a GET request on '/hello' it will respond with a 200.
//! Mock::given(method("GET"))
//! .and(path("/hello"))
//! .respond_with(ResponseTemplate::new(200))
//! // Mounting the mock on the mock server - it's now effective!
//! .mount(&mock_server)
//! .await;
//!
//! // If we probe the MockServer using any HTTP client it behaves as expected.
//! let status = reqwest::get(format!("{}/hello", &mock_server.uri()))
//! .await
//! .unwrap()
//! .status();
//! assert_eq!(status, 200);
//!
//! // If the request doesn't match any `Mock` mounted on our `MockServer` a 404 is returned.
//! let status = reqwest::get(format!("{}/missing", &mock_server.uri()))
//! .await
//! .unwrap()
//! .status();
//! assert_eq!(status, 404);
//! }
//! ```
//!
//! ## Matchers
//!
//! `wiremock` provides a set of matching strategies out of the box - check the [`matchers`] module
//! for a complete list.
//!
//! You can define your own matchers using the [`Match`] trait, as well as using `Fn` closures.
//! Check [`Match`]'s documentation for more details and examples.
//!
//! ## Spying
//!
//! `wiremock` empowers you to set expectations on the number of invocations to your [`Mock`]s -
//! check the [`expect`] method for more details.
//!
//! Expectations can be used to verify that a side-effect has (or has not) taken place!
//!
//! Expectations are automatically verified during the shutdown of each [`MockServer`] instance,
//! at the end of your test. A failed verification will trigger a panic.
//! By default, no expectations are set on your [`Mock`]s.
//!
//! ## Responses
//!
//! `wiremock` lets you specify pre-determined responses using [`ResponseTemplate`] and
//! [`respond_with`].
//!
//! You also given the option to have [`Mock`]s that return different responses based on the matched
//! [`Request`] using the [`Respond`] trait.
//! Check [`Respond`]'s documentation for more details and examples.
//!
//! ## Test isolation
//!
//! Each instance of [`MockServer`] is fully isolated: [`start`] takes care of finding a random port
//! available on your local machine which is assigned to the new [`MockServer`].
//!
//! To ensure full isolation and no cross-test interference, [`MockServer`]s shouldn't be
//! shared between tests. Instead, [`MockServer`]s should be created in the test where they are used.
//!
//! When a [`MockServer`] instance goes out of scope (e.g. the test finishes), the corresponding
//! HTTP server running in the background is shut down to free up the port it was using.
//!
//! ## Runtime compatibility
//!
//! `wiremock` can be used (and it is tested to work) with both [`async_std`] and [`tokio`] as
//! futures runtimes.
//! If you encounter any compatibility bug, please open an issue on our [GitHub repository].
//!
//! ## Efficiency
//!
//! `wiremock` maintains a pool of mock servers in the background to minimise the number of
//! connections and the time spent starting up a new [`MockServer`].
//! Pooling reduces the likelihood of you having to tune your OS configurations (e.g. ulimit).
//!
//! The pool is designed to be invisible: it makes your life easier and your tests faster. If you
//! end up having to worry about it, it's a bug: open an issue!
//!
//! ## Prior art
//!
//! [`mockito`] and [`httpmock`] provide HTTP mocking for Rust.
//!
//! Check the table below to see how `wiremock` compares to them across the following dimensions:
//! - Test execution strategy (do tests have to be executed sequentially or can they be executed in parallel?);
//! - How many APIs can I mock in a test?
//! - Out-of-the-box request matchers;
//! - Extensible request matching (i.e. you can define your own matchers);
//! - Sync/Async API;
//! - Spying (e.g. verify that a mock has/hasn't been called in a test);
//! - Standalone mode (i.e. can I launch an HTTP mock server outside of a test suite?).
//!
//! | | Test execution strategy | How many APIs can I mock? | Out-of-the-box request matchers | Extensible request matching | API | Spying | Standalone mode |
//! |-----------|-------------------------|---------------------------|---------------------------------|----------------------------|-------|----------|-----------------|
//! | mockito | ❌ Sequential | ❌ 1 | ✔ | ❌ | Sync | ✔ | ❌ |
//! | httpmock | ✔ Parallel | ✔ Unbounded | ✔ | ✔ | Async/Sync | ✔ | ✔ |
//! | wiremock | ✔ Parallel | ✔ Unbounded | ✔ | ✔ | Async | ✔ | ❌ |
//!
//!
//! ## Future evolution
//!
//! More request matchers can be added to those provided out-of-the-box to handle common usecases.
//!
//! ## Related projects
//!
//! * [`stubr`](https://github.com/beltram/stubr) for mounting [`Wiremock`](http://wiremock.org/) json stubs in a [`MockServer`]. Also works as a cli.
//!
//! [`MockServer`]: MockServer
//! [`start`]: MockServer::start
//! [`expect`]: Mock::expect
//! [`respond_with`]: MockBuilder::respond_with
//! [GitHub repository]: https://github.com/LukeMathWalker/wiremock-rs
//! [`mockito`]: https://docs.rs/mockito/
//! [`httpmock`]: https://docs.rs/httpmock/
//! [`async_std`]: https://docs.rs/async-std/
//! [`tokio`]: https://docs.rs/tokio/
pub mod http;
pub mod matchers;
mod mock;
mod mock_server;
mod mock_set;
mod mounted_mock;
mod request;
mod respond;
mod response_template;
mod verification;
pub type ErrorResponse = Box<dyn std::error::Error + Send + Sync + 'static>;
pub use mock::{Match, Mock, MockBuilder, Times};
pub use mock_server::{MockGuard, MockServer, MockServerBuilder};
pub use request::{BodyPrintLimit, Request};
pub use respond::Respond;
pub use response_template::ResponseTemplate;

1208
vendor/wiremock/src/matchers.rs vendored Normal file

File diff suppressed because it is too large Load Diff

825
vendor/wiremock/src/mock.rs vendored Normal file
View File

@@ -0,0 +1,825 @@
use crate::respond::{Respond, RespondErr};
use crate::{ErrorResponse, MockGuard, MockServer, Request, ResponseTemplate};
use std::fmt::{Debug, Formatter};
use std::ops::{
Range, RangeBounds, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
};
/// Anything that implements `Match` can be used to constrain when a [`Mock`] is activated.
///
/// `Match` can be used to extend the set of matchers provided out-of-the-box by `wiremock` to
/// cater to your specific testing needs:
/// ```rust
/// use wiremock::{Match, MockServer, Mock, Request, ResponseTemplate};
/// use wiremock::matchers::HeaderExactMatcher;
/// use std::convert::TryInto;
///
/// // Check that a header with the specified name exists and its value has an odd length.
/// pub struct OddHeaderMatcher(http::HeaderName);
///
/// impl Match for OddHeaderMatcher {
/// fn matches(&self, request: &Request) -> bool {
/// match request.headers.get(&self.0) {
/// // We are ignoring multi-valued headers for simplicity
/// Some(value) => value.to_str().unwrap_or_default().len() % 2 == 1,
/// None => false
/// }
/// }
/// }
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
///
/// Mock::given(OddHeaderMatcher("custom".try_into().unwrap()))
/// .respond_with(ResponseTemplate::new(200))
/// .mount(&mock_server)
/// .await;
///
/// let client = reqwest::Client::new();
///
/// // Even length
/// let status = client
/// .get(&mock_server.uri())
/// .header("custom", "even")
/// .send()
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 404);
///
/// // Odd length
/// let status = client
/// .get(&mock_server.uri())
/// .header("custom", "odd")
/// .send()
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 200);
/// }
/// ```
///
/// Anonymous functions that take a reference to a [`Request`] as input and return a boolean
/// as output automatically implement the `Match` trait.
///
/// The previous example could be rewritten as follows:
/// ```rust
/// use wiremock::{Match, MockServer, Mock, Request, ResponseTemplate};
/// use wiremock::matchers::HeaderExactMatcher;
/// use std::convert::TryInto;
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
///
/// let header_name = http::HeaderName::from_static("custom");
/// // Check that a header with the specified name exists and its value has an odd length.
/// let matcher = move |request: &Request| {
/// match request.headers.get(&header_name) {
/// Some(value) => value.to_str().unwrap_or_default().len() % 2 == 1,
/// None => false
/// }
/// };
///
/// Mock::given(matcher)
/// .respond_with(ResponseTemplate::new(200))
/// .mount(&mock_server)
/// .await;
///
/// let client = reqwest::Client::new();
///
/// // Even length
/// let status = client
/// .get(&mock_server.uri())
/// .header("custom", "even")
/// .send()
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 404);
///
/// // Odd length
/// let status = client
/// .get(&mock_server.uri())
/// .header("custom", "odd")
/// .send()
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 200);
/// }
/// ```
pub trait Match: Send + Sync {
/// Given a reference to a [`Request`], determine if it should match or not given
/// a specific criterion.
fn matches(&self, request: &Request) -> bool;
}
/// Wrapper around a `Match` trait object.
///
/// We need the wrapper to provide a (fake) implementation of `Debug`,
/// thus allowing us to pass this struct around as a `bastion` message.
/// This is because Rust's closures do not implement `Debug`.
///
/// We wouldn't need this if `bastion` didn't require `Debug` as a trait bound for its Message trait
/// or if Rust automatically implemented `Debug` for closures.
pub(crate) struct Matcher(Box<dyn Match>);
impl Match for Matcher {
fn matches(&self, request: &Request) -> bool {
self.0.matches(request)
}
}
impl Debug for Matcher {
fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result {
// Dummy `Debug` implementation to allow us to pass `Matcher` as a message in `bastion`.
// It's needed because closures do not implement `Debug` and we really want to enable
// closures as matchers from an API perspective.
// Might re-think this in the future.
Ok(())
}
}
/// Given a set of matchers, a `Mock` instructs an instance of [`MockServer`] to return a pre-determined response if the matching conditions are satisfied.
///
/// `Mock`s have to be mounted (or registered) with a [`MockServer`] to become effective.
/// You can use:
///
/// - [`MockServer::register`] or [`Mock::mount`] to activate a **global** `Mock`;
/// - [`MockServer::register_as_scoped`] or [`Mock::mount_as_scoped`] to activate a **scoped** `Mock`.
///
/// Check the respective documentations for more details (or look at the following examples!).
///
/// # Example (using [`register`]):
///
/// ```rust
/// use wiremock::{MockServer, Mock, ResponseTemplate};
/// use wiremock::matchers::method;
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
///
/// let response = ResponseTemplate::new(200);
///
/// let mock = Mock::given(method("GET")).respond_with(response.clone());
/// // Registering the mock with the mock server - it's now effective!
/// mock_server.register(mock).await;
///
/// // We won't register this mock instead.
/// let unregistered_mock = Mock::given(method("POST")).respond_with(response);
///
/// // Act
/// let status = reqwest::get(&mock_server.uri())
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 200);
///
/// // This would have matched `unregistered_mock`, but we haven't registered it!
/// // Hence it returns a 404, the default response when no mocks matched on the mock server.
/// let client = reqwest::Client::new();
/// let status = client.post(&mock_server.uri())
/// .send()
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 404);
/// }
/// ```
///
/// # Example (using [`mount`]):
///
/// If you prefer a fluent style, you can use the [`mount`] method on the `Mock` itself
/// instead of [`register`].
/// ```rust
/// use wiremock::{MockServer, Mock, ResponseTemplate};
/// use wiremock::matchers::method;
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
///
/// Mock::given(method("GET"))
/// .respond_with(ResponseTemplate::new(200))
/// .up_to_n_times(1)
/// // Mounting the mock on the mock server - it's now effective!
/// .mount(&mock_server)
/// .await;
///
/// // Act
/// let status = reqwest::get(&mock_server.uri())
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 200);
/// }
/// ```
///
/// # Example (using [`mount_as_scoped`]):
///
/// Sometimes you will need a `Mock` to be active within the scope of a function, but not any longer.
/// You can use [`Mock::mount_as_scoped`] to precisely control how long a `Mock` stays active.
///
/// ```rust
/// use wiremock::{MockServer, Mock, ResponseTemplate};
/// use wiremock::matchers::method;
///
/// async fn my_test_helper(mock_server: &MockServer) {
/// let mock_guard = Mock::given(method("GET"))
/// .respond_with(ResponseTemplate::new(200))
/// .expect(1)
/// .named("my_test_helper GET /")
/// .mount_as_scoped(mock_server)
/// .await;
///
/// reqwest::get(&mock_server.uri())
/// .await
/// .unwrap();
///
/// // `mock_guard` is dropped, expectations are verified!
/// }
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
/// my_test_helper(&mock_server).await;
///
/// // Act
///
/// // This would have returned 200 if the `Mock` in
/// // `my_test_helper` had not been scoped.
/// let status = reqwest::get(&mock_server.uri())
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 404);
/// }
/// ```
///
/// [`register`]: MockServer::register
/// [`mount`]: Mock::mount
/// [`mount_as_scoped`]: Mock::mount_as_scoped
#[must_use = "`Mock`s have to be mounted or registered with a `MockServer` to become effective"]
pub struct Mock {
pub(crate) matchers: Vec<Matcher>,
pub(crate) response: Result<Box<dyn Respond>, Box<dyn RespondErr>>,
/// Maximum number of times (inclusive) we should return a response from this Mock on
/// matching requests.
/// If `None`, there is no cap and we will respond to all incoming matching requests.
/// If `Some(max_n_matches)`, when `max_n_matches` matching incoming requests have been processed,
/// [`crate::mounted_mock::MountedMock::matches`] should start returning `false`, regardless of the incoming request.
pub(crate) max_n_matches: Option<u64>,
/// Allows prioritizing a Mock over another one.
/// `1` is the highest priority, `255` the lowest, default to `5`.
/// When priority is the same, it fallbacks to insertion order.
pub(crate) priority: u8,
/// The friendly mock name specified by the user.
/// Used in diagnostics and error messages if the mock expectations are not satisfied.
pub(crate) name: Option<String>,
/// The expectation is satisfied if the number of incoming requests falls within `expectation_range`.
pub(crate) expectation_range: Times,
}
/// A fluent builder to construct a [`Mock`] instance given matchers and a [`ResponseTemplate`].
#[derive(Debug)]
pub struct MockBuilder {
pub(crate) matchers: Vec<Matcher>,
}
impl Mock {
/// Start building a [`Mock`] specifying the first matcher.
///
/// It returns an instance of [`MockBuilder`].
pub fn given<M: 'static + Match>(matcher: M) -> MockBuilder {
MockBuilder {
matchers: vec![Matcher(Box::new(matcher))],
}
}
/// Specify an upper limit to the number of times you would like this [`Mock`] to respond to
/// incoming requests that satisfy the conditions imposed by your [`matchers`].
///
/// ### Example:
///
/// ```rust
/// use wiremock::{MockServer, Mock, ResponseTemplate};
/// use wiremock::matchers::method;
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
///
/// Mock::given(method("GET"))
/// .respond_with(ResponseTemplate::new(200))
/// // Default behaviour will have this Mock responding to any incoming request
/// // that satisfied our matcher (e.g. being a GET request).
/// // We can opt out of the default behaviour by setting a cap on the number of
/// // matching requests this Mock should respond to.
/// //
/// // In this case, once one matching request has been received, the mock will stop
/// // matching additional requests and you will receive a 404 if no other mock
/// // matches on those requests.
/// .up_to_n_times(1)
/// .mount(&mock_server)
/// .await;
///
/// // Act
///
/// // The first request matches, as expected.
/// let status = reqwest::get(&mock_server.uri())
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 200);
///
/// // The second request does NOT match given our `up_to_n_times(1)` setting.
/// let status = reqwest::get(&mock_server.uri())
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 404);
/// }
/// ```
///
/// [`matchers`]: crate::matchers
pub fn up_to_n_times(mut self, n: u64) -> Mock {
assert!(n > 0, "n must be strictly greater than 0!");
self.max_n_matches = Some(n);
self
}
/// Specify a priority for this [`Mock`].
/// Use this when you mount many [`Mock`] in a [`MockServer`]
/// and those mocks have interlaced request matching conditions
/// e.g. `mock A` accepts path `/abcd` and `mock B` a path regex `[a-z]{4}`
/// It is recommended to set the highest priority (1) for mocks with exact conditions (`mock A` in this case)
/// `1` is the highest priority, `255` the lowest, default to `5`
/// If two mocks have the same priority, priority is defined by insertion order (first one mounted has precedence over the others).
///
/// ### Example:
///
/// ```rust
/// use wiremock::{MockServer, Mock, ResponseTemplate};
/// use wiremock::matchers::{method, path, path_regex};
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
///
/// Mock::given(method("GET"))
/// .and(path("abcd"))
/// .respond_with(ResponseTemplate::new(200))
/// .with_priority(1) // highest priority
/// .mount(&mock_server)
/// .await;
///
/// Mock::given(method("GET"))
/// .and(path_regex("[a-z]{4}"))
/// .respond_with(ResponseTemplate::new(201))
/// .with_priority(2)
/// .mount(&mock_server)
/// .await;
///
/// // Act
///
/// // The request with highest priority, as expected.
/// let status = reqwest::get(&format!("{}/abcd", mock_server.uri()))
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 200);
/// }
/// ```
///
/// [`matchers`]: crate::matchers
pub fn with_priority(mut self, p: u8) -> Mock {
assert!(p > 0, "priority must be strictly greater than 0!");
self.priority = p;
self
}
/// Set an expectation on the number of times this [`Mock`] should match in the current
/// test case.
/// Expectations are verified when the [`MockServer`] is shutting down: if the expectation
/// is not satisfied, the [`MockServer`] will panic and the `error_message` is shown.
///
/// By default, no expectation is set for [`Mock`]s.
///
/// ### When is this useful?
///
/// `expect` can turn out handy when you'd like to verify that a certain side-effect has
/// (or has not!) taken place.
///
/// For example:
/// - check that a 3rd party notification API (e.g. email service) is called when an event
/// in your application is supposed to trigger a notification;
/// - check that a 3rd party API is NOT called when the response of a call is expected
/// to be retrieved from a cache (`.expect(0)`).
///
/// This technique is also called [spying](https://martinfowler.com/bliki/TestDouble.html).
///
/// ### Example:
///
/// ```rust
/// use wiremock::{MockServer, Mock, ResponseTemplate};
/// use wiremock::matchers::method;
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
///
/// Mock::given(method("GET"))
/// .respond_with(ResponseTemplate::new(200))
/// .up_to_n_times(2)
/// // We expect the mock to be called at least once.
/// // If that does not happen, the `MockServer` will panic on shutdown,
/// // causing the whole test to fail.
/// .expect(1..)
/// // We assign a name to the mock - it will be shown in error messages
/// // if our expectation is not verified!
/// .named("Root GET")
/// .mount(&mock_server)
/// .await;
///
/// // Act
/// let status = reqwest::get(&mock_server.uri())
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 200);
///
/// // Assert
/// // We made at least one matching request, the expectation is satisfied.
/// // The `MockServer` will shutdown peacefully, without panicking.
/// }
/// ```
pub fn expect<T: Into<Times>>(mut self, r: T) -> Self {
let range = r.into();
self.expectation_range = range;
self
}
/// Assign a name to your mock.
///
/// The mock name will be used in error messages (e.g. if the mock expectation
/// is not satisfied) and debug logs to help you identify what failed.
///
/// ### Example:
///
/// ```should_panic
/// use wiremock::{MockServer, Mock, ResponseTemplate};
/// use wiremock::matchers::method;
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
///
/// // We have two mocks in the same test - how do we find out
/// // which one failed when the test panics?
/// // Assigning a name to each mock with `named` gives us better error
/// // messages and makes it much easier to debug why a test is failing!
/// Mock::given(method("GET"))
/// .respond_with(ResponseTemplate::new(200))
/// .up_to_n_times(2)
/// .expect(1..)
/// // We assign a name to the mock - it will be shown in error messages
/// // if our expectation is not verified!
/// .named("Root GET")
/// .mount(&mock_server)
/// .await;
///
/// Mock::given(method("POST"))
/// .respond_with(ResponseTemplate::new(200))
/// .up_to_n_times(2)
/// .expect(1..)
/// // We assign a name to the mock - it will be shown in error messages
/// // if our expectation is not verified!
/// .named("Root POST")
/// .mount(&mock_server)
/// .await;
///
/// // Act
/// let status = reqwest::get(&mock_server.uri())
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 200);
///
/// // Assert
/// // We did not make a POST request, therefore the expectation on `Root POST`
/// // is not satisfied and the test will panic.
/// }
/// ```
pub fn named<T: Into<String>>(mut self, mock_name: T) -> Self {
self.name = Some(mock_name.into());
self
}
/// Mount a [`Mock`] on an instance of [`MockServer`].
/// The [`Mock`] will remain active until [`MockServer`] is shut down. If you want to control or limit how
/// long your [`Mock`] stays active, check out [`Mock::mount_as_scoped`].
///
/// Be careful! [`Mock`]s are not effective until they are [`mount`]ed or [`register`]ed on a [`MockServer`].
/// [`mount`] is an asynchronous method, make sure to `.await` it!
///
/// [`register`]: MockServer::register
/// [`mount`]: Mock::mount
pub async fn mount(self, server: &MockServer) {
server.register(self).await;
}
/// Mount a [`Mock`] as **scoped** on an instance of [`MockServer`].
///
/// When using [`mount`], your [`Mock`]s will be active until the [`MockServer`] is shut down.
/// When using `mount_as_scoped`, your [`Mock`]s will be active as long as the returned [`MockGuard`] is not dropped.
/// When the returned [`MockGuard`] is dropped, [`MockServer`] will verify that the expectations set on the scoped [`Mock`] were
/// verified - if not, it will panic.
///
/// `mount_as_scoped` is the ideal solution when you need a [`Mock`] within a test helper
/// but you do not want it to linger around after the end of the function execution.
///
/// # Limitations
///
/// When expectations of a scoped [`Mock`] are not verified, it will trigger a panic - just like a normal [`Mock`].
/// Due to [limitations](https://internals.rust-lang.org/t/should-drop-glue-use-track-caller/13682) in Rust's [`Drop`] trait,
/// the panic message will not include the filename and the line location
/// where the corresponding [`MockGuard`] was dropped - it will point into `wiremock`'s source code.
///
/// This can be an issue when you are using more than one scoped [`Mock`] in a single test - which of them panicked?
/// To improve your debugging experience it is strongly recommended to use [`Mock::named`] to assign a unique
/// identifier to your scoped [`Mock`]s, which will in turn be referenced in the panic message if their expectations are
/// not met.
///
/// # Example:
///
/// - The behaviour of the scoped mock is invisible outside of `my_test_helper`.
///
/// ```rust
/// use wiremock::{MockServer, Mock, ResponseTemplate};
/// use wiremock::matchers::method;
///
/// async fn my_test_helper(mock_server: &MockServer) {
/// let mock_guard = Mock::given(method("GET"))
/// .respond_with(ResponseTemplate::new(200))
/// .expect(1)
/// .named("my_test_helper GET /")
/// .mount_as_scoped(mock_server)
/// .await;
///
/// reqwest::get(&mock_server.uri())
/// .await
/// .unwrap();
///
/// // `mock_guard` is dropped, expectations are verified!
/// }
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
/// my_test_helper(&mock_server).await;
///
/// // Act
///
/// // This would have returned 200 if the `Mock` in
/// // `my_test_helper` had not been scoped.
/// let status = reqwest::get(&mock_server.uri())
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 404);
/// }
/// ```
///
/// - The expectations for the scoped mock are not verified, it panics at the end of `my_test_helper`.
///
/// ```rust,should_panic
/// use wiremock::{MockServer, Mock, ResponseTemplate};
/// use wiremock::matchers::method;
///
/// async fn my_test_helper(mock_server: &MockServer) {
/// let mock_guard = Mock::given(method("GET"))
/// .respond_with(ResponseTemplate::new(200))
/// .expect(1)
/// .named("my_test_helper GET /")
/// .mount_as_scoped(mock_server)
/// .await;
/// // `mock_guard` is dropped, expectations are NOT verified!
/// // Panic!
/// }
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
/// my_test_helper(&mock_server).await;
///
/// // Act
/// let status = reqwest::get(&mock_server.uri())
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 404);
/// }
/// ```
///
/// [`mount`]: Mock::mount
pub async fn mount_as_scoped(self, server: &MockServer) -> MockGuard {
server.register_as_scoped(self).await
}
/// Given a [`Request`] build an instance a [`ResponseTemplate`] using
/// the responder associated with the `Mock`.
pub(crate) fn response_template(
&self,
request: &Request,
) -> Result<ResponseTemplate, ErrorResponse> {
match &self.response {
Ok(responder) => Ok(responder.respond(request)),
Err(responder_err) => Err(responder_err.respond_err(request)),
}
}
}
impl MockBuilder {
/// Add another request matcher to the mock you are building.
///
/// **All** specified [`matchers`] must match for the overall [`Mock`] to match an incoming request.
///
/// [`matchers`]: crate::matchers
pub fn and<M: Match + 'static>(mut self, matcher: M) -> Self {
self.matchers.push(Matcher(Box::new(matcher)));
self
}
/// Establish what [`ResponseTemplate`] should be used to generate a response when an incoming
/// request matches.
///
/// `respond_with` finalises the `MockBuilder` and returns you a [`Mock`] instance, ready to
/// be [`register`]ed or [`mount`]ed on a [`MockServer`]!
///
/// [`register`]: MockServer::register
/// [`mount`]: Mock::mount
pub fn respond_with<R: Respond + 'static>(self, responder: R) -> Mock {
Mock {
matchers: self.matchers,
response: Ok(Box::new(responder)),
max_n_matches: None,
priority: 5,
name: None,
expectation_range: Times(TimesEnum::Unbounded(RangeFull)),
}
}
/// Instead of response with an HTTP reply, return a Rust error.
///
/// This can simulate lower level errors, e.g., a [`ConnectionReset`] IO Error.
///
/// [`ConnectionReset`]: std::io::ErrorKind::ConnectionReset
pub fn respond_with_err<R: RespondErr + 'static>(self, responder_err: R) -> Mock {
Mock {
matchers: self.matchers,
response: Err(Box::new(responder_err)),
max_n_matches: None,
priority: 5,
name: None,
expectation_range: Times(TimesEnum::Unbounded(RangeFull)),
}
}
}
/// Specify how many times we expect a [`Mock`] to match via [`expect`].
/// It is used to set expectations on the usage of a [`Mock`] in a test case.
///
/// You can either specify an exact value, e.g.
/// ```rust
/// use wiremock::Times;
///
/// let times: Times = 10.into();
/// ```
/// or a range
/// ```rust
/// use wiremock::Times;
///
/// // Between 10 and 15 (not included) times
/// let times: Times = (10..15).into();
/// // Between 10 and 15 (included) times
/// let times: Times = (10..=15).into();
/// // At least 10 times
/// let times: Times = (10..).into();
/// // Strictly less than 15 times
/// let times: Times = (..15).into();
/// // Strictly less than 16 times
/// let times: Times = (..=15).into();
/// ```
///
/// [`expect`]: Mock::expect
#[derive(Clone, Debug)]
pub struct Times(TimesEnum);
impl Times {
pub(crate) fn contains(&self, n_calls: u64) -> bool {
match &self.0 {
TimesEnum::Exact(e) => e == &n_calls,
TimesEnum::Unbounded(r) => r.contains(&n_calls),
TimesEnum::Range(r) => r.contains(&n_calls),
TimesEnum::RangeFrom(r) => r.contains(&n_calls),
TimesEnum::RangeTo(r) => r.contains(&n_calls),
TimesEnum::RangeToInclusive(r) => r.contains(&n_calls),
TimesEnum::RangeInclusive(r) => r.contains(&n_calls),
}
}
}
impl std::fmt::Display for Times {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.0 {
TimesEnum::Exact(e) => write!(f, "== {}", e),
TimesEnum::Unbounded(_) => write!(f, "0 <= x"),
TimesEnum::Range(r) => write!(f, "{} <= x < {}", r.start, r.end),
TimesEnum::RangeFrom(r) => write!(f, "{} <= x", r.start),
TimesEnum::RangeTo(r) => write!(f, "0 <= x < {}", r.end),
TimesEnum::RangeToInclusive(r) => write!(f, "0 <= x <= {}", r.end),
TimesEnum::RangeInclusive(r) => write!(f, "{} <= x <= {}", r.start(), r.end()),
}
}
}
// Implementation notes: this has gone through a couple of iterations before landing to
// what you see now.
//
// The original draft had Times itself as an enum with two variants (Exact and Range), with
// the Range variant generic over `R: RangeBounds<u64>`.
//
// We switched to a generic struct wrapper around a private `R: RangeBounds<u64>` when we realised
// that you would have had to specify a range type when creating the Exact variant
// (e.g. as you do for `Option` when creating a `None` variant).
//
// We achieved the same functionality with a struct wrapper, but exact values had to converted
// to ranges with a single element (e.g. 15 -> 15..16).
// Not the most expressive representation, but we would have lived with it.
//
// We changed once again when we started to update our `MockActor`: we are storing all `Mock`s
// in a vector. Being generic over `R`, the range type leaked into the overall `Mock` (and `MountedMock`)
// type, thus making those generic as well over `R`.
// To store them in a vector all mocks would have had to use the same range internally, which is
// obviously an unreasonable restrictions.
// At the same time, we can't have a Box<dyn RangeBounds<u64>> because `contains` is a generic
// method hence the requirements for object safety are not satisfied.
//
// Thus we ended up creating this master enum that wraps all range variants with the addition
// of the Exact variant.
// If you can do better, please submit a PR.
// We keep them enum private to the crate to allow for future refactoring.
#[derive(Clone, Debug)]
pub(crate) enum TimesEnum {
Exact(u64),
Unbounded(RangeFull),
Range(Range<u64>),
RangeFrom(RangeFrom<u64>),
RangeTo(RangeTo<u64>),
RangeToInclusive(RangeToInclusive<u64>),
RangeInclusive(RangeInclusive<u64>),
}
impl From<u64> for Times {
fn from(x: u64) -> Self {
Times(TimesEnum::Exact(x))
}
}
impl From<RangeFull> for Times {
fn from(x: RangeFull) -> Self {
Times(TimesEnum::Unbounded(x))
}
}
// A quick macro to help easing the implementation pain.
macro_rules! impl_from_for_range {
($type_name:ident) => {
impl From<$type_name<u64>> for Times {
fn from(r: $type_name<u64>) -> Self {
Times(TimesEnum::$type_name(r))
}
}
};
}
impl_from_for_range!(Range);
impl_from_for_range!(RangeTo);
impl_from_for_range!(RangeFrom);
impl_from_for_range!(RangeInclusive);
impl_from_for_range!(RangeToInclusive);

View File

@@ -0,0 +1,329 @@
use crate::mock_server::hyper::run_server;
use crate::mock_set::MockId;
use crate::mock_set::MountedMockSet;
use crate::request::BodyPrintLimit;
use crate::{ErrorResponse, Request, mock::Mock, verification::VerificationOutcome};
use http_body_util::Full;
use hyper::body::Bytes;
use std::fmt::{Debug, Write};
use std::net::{SocketAddr, TcpListener, TcpStream};
use std::pin::pin;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use tokio::sync::Notify;
use tokio::sync::RwLock;
/// An HTTP web-server running in the background to behave as one of your dependencies using `Mock`s
/// for testing purposes.
///
/// `BareMockServer` is the actual mock server behind the publicly-exposed `MockServer`, which
/// is instead a thin facade over a `BareMockServer` retrieved from a pool - see `get_pooled_server`
/// for more details.
pub(crate) struct BareMockServer {
state: Arc<RwLock<MockServerState>>,
server_address: SocketAddr,
// When `_shutdown_trigger` gets dropped the listening server terminates gracefully.
_shutdown_trigger: tokio::sync::watch::Sender<()>,
}
/// The elements of [`BareMockServer`] that are affected by each incoming request.
/// By bundling them together, we can expose a unified `handle_request` that ensures
/// they are kept in sync without having to leak logic across multiple corners of the `wiremock`'s codebase.
pub(super) struct MockServerState {
mock_set: MountedMockSet,
received_requests: Option<Vec<Request>>,
body_print_limit: BodyPrintLimit,
}
impl MockServerState {
pub(super) async fn handle_request(
&mut self,
request: Request,
) -> Result<(hyper::Response<Full<Bytes>>, Option<tokio::time::Sleep>), ErrorResponse> {
// If request recording is enabled, record the incoming request
// by adding it to the `received_requests` stack
if let Some(received_requests) = &mut self.received_requests {
received_requests.push(request.clone());
}
self.mock_set.handle_request(request).await
}
}
impl BareMockServer {
/// Start a new instance of a `BareMockServer` listening on the specified
/// [`TcpListener`].
pub(super) async fn start(
listener: TcpListener,
request_recording: RequestRecording,
body_print_limit: BodyPrintLimit,
) -> Self {
let (shutdown_trigger, shutdown_receiver) = tokio::sync::watch::channel(());
let received_requests = match request_recording {
RequestRecording::Enabled => Some(Vec::new()),
RequestRecording::Disabled => None,
};
let state = Arc::new(RwLock::new(MockServerState {
mock_set: MountedMockSet::new(body_print_limit),
received_requests,
body_print_limit,
}));
let server_address = listener
.local_addr()
.expect("Failed to get server address.");
let server_state = state.clone();
std::thread::spawn(move || {
let server_future = run_server(listener, server_state, shutdown_receiver);
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("Cannot build local tokio runtime");
runtime.block_on(server_future);
});
for _ in 0..40 {
if TcpStream::connect_timeout(&server_address, std::time::Duration::from_millis(25))
.is_ok()
{
break;
}
tokio::time::sleep(std::time::Duration::from_millis(25)).await;
}
Self {
state,
server_address,
_shutdown_trigger: shutdown_trigger,
}
}
/// Register a `Mock` on an instance of `BareMockServer`.
///
/// Be careful! `Mock`s are not effective until they are `mount`ed or `register`ed on a
/// `BareMockServer`.
pub(crate) async fn register(&self, mock: Mock) {
self.state.write().await.mock_set.register(mock);
}
/// Register a **scoped** `Mock` on an instance of `MockServer`.
///
/// When using `register`, your `Mock`s will be active until the `MockServer` is shut down.
/// When using `register_as_scoped`, your `Mock`s will be active as long as the returned `MockGuard` is not dropped.
/// When the returned `MockGuard` is dropped, `MockServer` will verify that the expectations set on the scoped `Mock` were
/// verified - if not, it will panic.
pub async fn register_as_scoped(&self, mock: Mock) -> MockGuard {
let (notify, mock_id) = self.state.write().await.mock_set.register(mock);
MockGuard {
notify,
mock_id,
server_state: self.state.clone(),
}
}
/// Drop all mounted `Mock`s from an instance of `BareMockServer`.
/// Delete all recorded requests.
///
/// It *must* be called if you plan to reuse a `BareMockServer` instance (i.e. in our
/// `MockServerPoolManager`).
pub(crate) async fn reset(&self) {
let mut state = self.state.write().await;
state.mock_set.reset();
if let Some(received_requests) = &mut state.received_requests {
received_requests.clear();
}
}
/// Verify that all mounted `Mock`s on this instance of `BareMockServer` have satisfied
/// their expectations on their number of invocations.
pub(crate) async fn verify(&self) -> VerificationOutcome {
let mock_set = &self.state.read().await.mock_set;
mock_set.verify_all()
}
/// Return the base uri of this running instance of `BareMockServer`, e.g. `http://127.0.0.1:4372`.
///
/// Use this method to compose uris when interacting with this instance of `BareMockServer` via
/// an HTTP client.
pub(crate) fn uri(&self) -> String {
format!("http://{}", self.server_address)
}
/// Return the socket address of this running instance of `BareMockServer`, e.g. `127.0.0.1:4372`.
///
/// Use this method to interact with the `BareMockServer` using `TcpStream`s.
pub(crate) fn address(&self) -> &SocketAddr {
&self.server_address
}
/// Return the body print limit of this running instance of `BareMockServer`.
pub(crate) async fn body_print_limit(&self) -> BodyPrintLimit {
self.state.read().await.body_print_limit
}
/// Return a vector with all the requests received by the `BareMockServer` since it started.
/// If no request has been served, it returns an empty vector.
///
/// If request recording was disabled, it returns `None`.
pub(crate) async fn received_requests(&self) -> Option<Vec<Request>> {
let state = self.state.read().await;
state.received_requests.clone()
}
}
impl Debug for BareMockServer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "BareMockServer {{ address: {} }}", self.address())
}
}
pub(super) enum RequestRecording {
Enabled,
Disabled,
}
/// You get a `MockGuard` when registering a **scoped** [`Mock`] using [`MockServer::register_as_scoped`](crate::MockServer::register_as_scoped)
/// or [`Mock::mount_as_scoped`].
///
/// When the [`MockGuard`] is dropped, the [`MockServer`](crate::MockServer) verifies that the expectations set on the
/// scoped [`Mock`] were verified - if not, it will panic.
///
/// # Limitations
///
/// When expectations of a scoped [`Mock`] are not verified, it will trigger a panic - just like a normal [`Mock`].
/// Due to [limitations](https://internals.rust-lang.org/t/should-drop-glue-use-track-caller/13682) in Rust's `Drop` trait,
/// the panic message will not include the filename and the line location
/// where the corresponding `MockGuard` was dropped - it will point into `wiremock`'s source code.
///
/// This can be an issue when you are using more than one scoped [`Mock`] in a single test - which of them panicked?
/// To improve your debugging experience it is strongly recommended to use [`Mock::named`] to assign a unique
/// identifier to your scoped [`Mock`]s, which will in turn be referenced in the panic message if their expectations are
/// not met.
#[must_use = "All *_scoped methods return a `MockGuard`.
This guard MUST be bound to a variable (e.g. _mock_guard), \
otherwise the mock will immediately be unmounted (and its expectations checked).
Check `wiremock`'s documentation on scoped mocks for more details."]
pub struct MockGuard {
mock_id: MockId,
server_state: Arc<RwLock<MockServerState>>,
notify: Arc<(Notify, AtomicBool)>,
}
impl MockGuard {
/// Return all the requests that have been matched by the corresponding
/// scoped [`Mock`] since it was mounted.
/// The requests are returned in the order they were received.
///
/// It returns an empty vector if no request has been matched.
pub async fn received_requests(&self) -> Vec<crate::Request> {
let state = self.server_state.read().await;
let (mounted_mock, _) = &state.mock_set[self.mock_id];
mounted_mock.received_requests()
}
/// This method doesn't return until the expectations set on the
/// corresponding scoped [`Mock`] are satisfied.
///
/// It can be useful when you are testing asynchronous flows (e.g. a
/// message queue consumer) and you don't have a good event that can be used
/// to trigger the verification of the expectations set on the scoped [`Mock`].
///
/// # Timeouts
///
/// There is no default timeout for this method, so it will end up waiting
/// **forever** if your expectations are never met. Probably not what you
/// want.
///
/// It is strongly recommended that you set your own timeout using the
/// appropriate timers from your chosen async runtime.
/// Since `wiremock` is runtime-agnostic, it cannot provide a default
/// timeout mechanism that would work for all users.
///
/// ```rust
/// use std::time::Duration;
/// use wiremock::{Mock, MockServer, ResponseTemplate};
/// use wiremock::matchers::method;
///
/// #[tokio::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
///
/// let response = ResponseTemplate::new(200);
/// let mock = Mock::given(method("GET")).respond_with(response);
/// let mock_guard = mock_server.register_as_scoped(mock).await;
///
/// // Act
/// let waiter = mock_guard.wait_until_satisfied();
/// // Here we wrap the waiter in a tokio timeout
/// let outcome = tokio::time::timeout(Duration::from_millis(10), waiter).await;
///
/// // Assert
/// assert!(outcome.is_err());
/// }
/// ```
pub async fn wait_until_satisfied(&self) {
let (notify, flag) = &*self.notify;
let mut notification = pin!(notify.notified());
// listen for events of satisfaction.
notification.as_mut().enable();
// check if satisfaction has previously been recorded
if flag.load(std::sync::atomic::Ordering::Acquire) {
return;
}
// await event
notification.await;
}
}
impl Drop for MockGuard {
fn drop(&mut self) {
let future = async move {
let MockGuard {
mock_id,
server_state,
..
} = self;
let mut state = server_state.write().await;
let report = state.mock_set.verify(*mock_id);
if !report.is_satisfied() {
let received_requests_message = if let Some(received_requests) =
&state.received_requests
{
if received_requests.is_empty() {
"The server did not receive any request.".into()
} else {
received_requests.iter().enumerate().fold(
"Received requests:\n".to_string(),
|mut message, (index, request)| {
_ = write!(message, "- Request #{}\n\t", index + 1,);
_ = request.print_with_limit(&mut message, state.body_print_limit);
message
},
)
}
} else {
"Enable request recording on the mock server to get the list of incoming requests as part of the panic message.".into()
};
let verifications_error = format!("- {}\n", report.error_message());
let error_message = format!(
"Verification failed for a scoped mock:\n{}\n{}",
verifications_error, received_requests_message
);
if std::thread::panicking() {
log::debug!("{}", &error_message);
} else {
panic!("{}", &error_message);
}
} else {
state.mock_set.deactivate(*mock_id);
}
};
futures::executor::block_on(future);
}
}

View File

@@ -0,0 +1,121 @@
use crate::MockServer;
use crate::mock_server::bare_server::{BareMockServer, RequestRecording};
use crate::mock_server::exposed_server::InnerServer;
use crate::request::{BODY_PRINT_LIMIT, BodyPrintLimit};
use std::env;
use std::net::TcpListener;
/// A builder providing a fluent API to assemble a [`MockServer`] step-by-step.
/// Use [`MockServer::builder`] to get started.
pub struct MockServerBuilder {
listener: Option<TcpListener>,
record_incoming_requests: bool,
body_print_limit: BodyPrintLimit,
}
impl MockServerBuilder {
pub(super) fn new() -> Self {
let body_print_limit = match env::var("WIREMOCK_BODY_PRINT_LIMIT")
.ok()
.and_then(|x| x.parse::<usize>().ok())
{
Some(limit) => BodyPrintLimit::Limited(limit),
None => BodyPrintLimit::Limited(BODY_PRINT_LIMIT),
};
Self {
listener: None,
record_incoming_requests: true,
body_print_limit,
}
}
/// Each instance of [`MockServer`] is, by default, running on a random
/// port available on your local machine.
/// With `MockServerBuilder::listener` you can choose to start the `MockServer`
/// instance on a specific port you have already bound.
///
/// ### Example:
/// ```rust
/// use wiremock::MockServer;
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
/// let expected_server_address = listener
/// .local_addr()
/// .expect("Failed to get server address.");
///
/// // Act
/// let mock_server = MockServer::builder().listener(listener).start().await;
///
/// // Assert
/// assert_eq!(&expected_server_address, mock_server.address());
/// }
/// ```
pub fn listener(mut self, listener: TcpListener) -> Self {
self.listener = Some(listener);
self
}
/// By default, [`MockServer`] will record all incoming requests to display
/// more meaningful error messages when your expectations are not verified.
///
/// This can sometimes be undesirable (e.g. a long-lived server serving
/// high volumes of traffic) - you can disable request recording using
/// `MockServerBuilder::disable_request_recording`.
///
/// ### Example (Request recording disabled):
///
/// ```rust
/// use wiremock::MockServer;
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::builder().disable_request_recording().start().await;
///
/// // Act
/// let received_requests = mock_server.received_requests().await;
///
/// // Assert
/// assert!(received_requests.is_none());
/// }
/// ```
pub fn disable_request_recording(mut self) -> Self {
self.record_incoming_requests = false;
self
}
/// The mock server prints the requests it received when one or more mocks have expectations that have not been satisfied.
/// By default, the size of the printed body is limited.
///
/// You may want to change this if you're working with services with very large
/// bodies, or when printing wiremock output to a file where size matters
/// less than in a terminal window. You can configure this limit with
/// `MockServerBuilder::body_print_limit`.
pub fn body_print_limit(mut self, limit: BodyPrintLimit) -> Self {
self.body_print_limit = limit;
self
}
/// Finalise the builder to get an instance of a [`BareMockServer`].
pub(super) async fn build_bare(self) -> BareMockServer {
let listener = if let Some(listener) = self.listener {
listener
} else {
TcpListener::bind("127.0.0.1:0").expect("Failed to bind an OS port for a mock server.")
};
let recording = if self.record_incoming_requests {
RequestRecording::Enabled
} else {
RequestRecording::Disabled
};
BareMockServer::start(listener, recording, self.body_print_limit).await
}
/// Finalise the builder and launch the [`MockServer`] instance!
pub async fn start(self) -> MockServer {
MockServer::new(InnerServer::Bare(self.build_bare().await))
}
}

View File

@@ -0,0 +1,496 @@
use crate::mock_server::MockServerBuilder;
use crate::mock_server::bare_server::BareMockServer;
use crate::mock_server::pool::{PooledMockServer, get_pooled_mock_server};
use crate::{MockGuard, Request, mock::Mock, verification::VerificationOutcome};
use log::debug;
use std::fmt::{Debug, Write};
use std::net::SocketAddr;
use std::ops::Deref;
/// An HTTP web-server running in the background to behave as one of your dependencies using [`Mock`]s
/// for testing purposes.
///
/// Each instance of `MockServer` is fully isolated: [`MockServer::start`] takes care of finding a random port
/// available on your local machine which is assigned to the new `MockServer`.
///
/// You can use [`MockServer::builder`] if you need to specify custom configuration - e.g.
/// run on a specific port or disable request recording.
///
/// ## Best practices
///
/// You should use one instance of `MockServer` for each REST API that your application interacts
/// with and needs mocking for testing purposes.
///
/// To ensure full isolation and no cross-test interference, `MockServer`s shouldn't be
/// shared between tests. Instead, `MockServer`s should be created in the test where they are used.
///
/// When using a [`Mock`] within a test helper function, consider using [`MockServer::register_as_scoped`]
/// instead of [`MockServer::register`].
///
/// You can register as many [`Mock`]s as your scenario requires on a `MockServer`.
#[derive(Debug)]
pub struct MockServer(InnerServer);
/// `MockServer` is either a wrapper around a `BareMockServer` retrieved from an
/// object pool or a wrapper around an exclusive `BareMockServer`.
/// We use the pool when the user does not care about the port the mock server listens to, while
/// we provision a dedicated one if they specify their own `TcpListener` with `start_on`.
///
/// `InnerServer` implements `Deref<Target=BareMockServer>`, so we never actually have to match
/// on `InnerServer` in `MockServer` - the compiler does all the boring heavy-lifting for us.
#[derive(Debug)]
pub(super) enum InnerServer {
Bare(BareMockServer),
Pooled(PooledMockServer),
}
impl Deref for InnerServer {
type Target = BareMockServer;
fn deref(&self) -> &Self::Target {
match self {
InnerServer::Bare(b) => b,
InnerServer::Pooled(p) => p,
}
}
}
impl MockServer {
pub(super) fn new(server: InnerServer) -> Self {
Self(server)
}
/// You can use `MockServer::builder` if you need to specify custom configuration - e.g.
/// run on a specific port or disable request recording.
///
/// If this is not your case, use [`MockServer::start`].
pub fn builder() -> MockServerBuilder {
MockServerBuilder::new()
}
/// Start a new instance of a `MockServer` listening on a random port.
///
/// Each instance of `MockServer` is fully isolated: `start` takes care of finding a random port
/// available on your local machine which is assigned to the new `MockServer`.
///
/// You should use one instance of `MockServer` for each REST API that your application interacts
/// with and needs mocking for testing purposes.
///
/// ### Example:
/// ```rust
/// use wiremock::{MockServer, Mock, ResponseTemplate};
/// use wiremock::matchers::method;
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server_one = MockServer::start().await;
/// let mock_server_two = MockServer::start().await;
///
/// assert!(mock_server_one.address() != mock_server_two.address());
///
/// let mock = Mock::given(method("GET")).respond_with(ResponseTemplate::new(200));
/// // Registering the mock with the first mock server - it's now effective!
/// // But it *won't* be used by the second mock server!
/// mock_server_one.register(mock).await;
///
/// // Act
///
/// let status = reqwest::get(&mock_server_one.uri())
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 200);
///
/// // This would have matched our mock, but we haven't registered it for `mock_server_two`!
/// // Hence it returns a 404, the default response when no mocks matched on the mock server.
/// let status = reqwest::get(&mock_server_two.uri())
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 404);
/// }
/// ```
pub async fn start() -> Self {
Self(InnerServer::Pooled(get_pooled_mock_server().await))
}
/// Register a [`Mock`] on an instance of `MockServer`.
/// The [`Mock`] will remain active until `MockServer` is shut down. If you want to control or limit how
/// long your [`Mock`] stays active, check out [`MockServer::register_as_scoped`].
///
/// Be careful! [`Mock`]s are not effective until they are [`mount`]ed or `register`ed on a `MockServer`.
/// `register` is an asynchronous method, make sure to `.await` it!
///
/// ### Example:
/// ```rust
/// use wiremock::{MockServer, Mock, ResponseTemplate};
/// use wiremock::matchers::method;
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
///
/// let response = ResponseTemplate::new(200);
///
/// let mock = Mock::given(method("GET")).respond_with(response.clone());
/// // Registering the mock with the mock server - it's now effective!
/// mock_server.register(mock).await;
///
/// // We won't register this mock instead.
/// let unregistered_mock = Mock::given(method("GET")).respond_with(response);
///
/// // Act
/// let status = reqwest::get(&mock_server.uri())
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 200);
///
/// // This would have matched `unregistered_mock`, but we haven't registered it!
/// // Hence it returns a 404, the default response when no mocks matched on the mock server.
/// let client = reqwest::Client::new();
/// let status = client.post(&mock_server.uri())
/// .send()
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 404);
/// }
/// ```
///
/// [`mount`]: Mock::mount
pub async fn register(&self, mock: Mock) {
self.0.register(mock).await;
}
/// Register a **scoped** [`Mock`] on an instance of `MockServer`.
///
/// When using `register`, your [`Mock`]s will be active until the `MockServer` is shut down.
/// When using `register_as_scoped`, your [`Mock`]s will be active as long as the returned [`MockGuard`] is not dropped.
/// When the returned [`MockGuard`] is dropped, `MockServer` will verify that the expectations set on the scoped [`Mock`] were
/// verified - if not, it will panic.
///
/// `register_as_scoped` is the ideal solution when you need a [`Mock`] within a test helper
/// but you do not want it to linger around after the end of the function execution.
///
/// # Limitations
///
/// When expectations of a scoped [`Mock`] are not verified, it will trigger a panic - just like a normal [`Mock`].
/// Due to [limitations](https://internals.rust-lang.org/t/should-drop-glue-use-track-caller/13682) in Rust's `Drop` trait,
/// the panic message will not include the filename and the line location
/// where the corresponding [`MockGuard`] was dropped - it will point into `wiremock`'s source code.
///
/// This can be an issue when you are using more than one scoped [`Mock`] in a single test - which of them panicked?
/// To improve your debugging experience it is strongly recommended to use [`Mock::named`] to assign a unique
/// identifier to your scoped [`Mock`]s, which will in turn be referenced in the panic message if their expectations are
/// not met.
///
/// # Example:
///
/// - The behaviour of the scoped mock is invisible outside of `my_test_helper`.
///
/// ```rust
/// use wiremock::{MockServer, Mock, ResponseTemplate};
/// use wiremock::matchers::method;
///
/// async fn my_test_helper(mock_server: &MockServer) {
/// let mock = Mock::given(method("GET"))
/// .respond_with(ResponseTemplate::new(200))
/// .expect(1)
/// .named("my_test_helper GET /");
/// let mock_guard = mock_server.register_as_scoped(mock).await;
///
/// reqwest::get(&mock_server.uri())
/// .await
/// .unwrap();
///
/// // `mock_guard` is dropped, expectations are verified!
/// }
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
/// my_test_helper(&mock_server).await;
///
/// // Act
///
/// // This would have returned 200 if the `Mock` in
/// // `my_test_helper` had not been scoped.
/// let status = reqwest::get(&mock_server.uri())
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 404);
/// }
/// ```
///
/// - The expectations for the scoped mock are not verified, it panics at the end of `my_test_helper`.
///
/// ```rust,should_panic
/// use wiremock::{MockServer, Mock, ResponseTemplate};
/// use wiremock::matchers::method;
///
/// async fn my_test_helper(mock_server: &MockServer) {
/// let mock = Mock::given(method("GET"))
/// .respond_with(ResponseTemplate::new(200))
/// .expect(1)
/// .named("my_test_helper GET /");
/// let mock_guard = mock_server.register_as_scoped(mock).await;
/// // `mock_guard` is dropped, expectations are NOT verified!
/// // Panic!
/// }
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
/// my_test_helper(&mock_server).await;
///
/// // Act
/// let status = reqwest::get(&mock_server.uri())
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 404);
/// }
/// ```
pub async fn register_as_scoped(&self, mock: Mock) -> MockGuard {
self.0.register_as_scoped(mock).await
}
/// Drop all mounted [`Mock`]s from an instance of [`MockServer`].
/// It also deletes all recorded requests.
///
/// ### Example
///
/// ```rust
/// use wiremock::{MockServer, Mock, ResponseTemplate};
/// use wiremock::matchers::method;
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
///
/// let response = ResponseTemplate::new(200);
/// Mock::given(method("GET")).respond_with(response).mount(&mock_server).await;
///
/// // Act
/// let status = reqwest::get(&mock_server.uri())
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 200);
///
/// // Reset the server
/// mock_server.reset().await;
///
/// // This would have matched our mock, but we have dropped it resetting the server!
/// let client = reqwest::Client::new();
/// let status = client.post(&mock_server.uri())
/// .send()
/// .await
/// .unwrap()
/// .status();
/// assert_eq!(status, 404);
/// }
/// ```
///
/// ### Example (Recorded requests are reset)
///
/// ```rust
/// use wiremock::{MockServer, Mock, ResponseTemplate};
/// use wiremock::matchers::method;
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
///
/// // Act
/// reqwest::get(&mock_server.uri()).await.unwrap();
///
/// // We have recorded the incoming request
/// let received_requests = mock_server.received_requests().await.unwrap();
/// assert!(!received_requests.is_empty());
///
/// // Reset the server
/// mock_server.reset().await;
///
/// // All received requests have been forgotten after the call to `.reset`
/// let received_requests = mock_server.received_requests().await.unwrap();
/// assert!(received_requests.is_empty())
/// }
/// ```
pub async fn reset(&self) {
self.0.reset().await;
}
/// Verify that all mounted [`Mock`]s on this instance of `MockServer` have satisfied
/// their expectations on their number of invocations. Panics otherwise.
pub async fn verify(&self) {
debug!("Verify mock expectations.");
let body_print_limit = self.0.body_print_limit().await;
if let VerificationOutcome::Failure(failed_verifications) = self.0.verify().await {
let received_requests_message = if let Some(received_requests) =
self.0.received_requests().await
{
if received_requests.is_empty() {
"The server did not receive any request.".into()
} else {
received_requests.iter().enumerate().fold(
"Received requests:\n".to_string(),
|mut message, (index, request)| {
_ = write!(message, "- Request #{}\n\t", index + 1,);
_ = request.print_with_limit(&mut message, body_print_limit);
message
},
)
}
} else {
"Enable request recording on the mock server to get the list of incoming requests as part of the panic message.".into()
};
let verifications_errors: String =
failed_verifications.iter().fold(String::new(), |mut s, m| {
_ = writeln!(s, "- {}", m.error_message());
s
});
let error_message = format!(
"Verifications failed:\n{verifications_errors}\n{received_requests_message}",
);
if std::thread::panicking() {
debug!("{}", &error_message);
} else {
panic!("{}", &error_message);
}
}
}
/// Return the base uri of this running instance of `MockServer`, e.g. `http://127.0.0.1:4372`.
///
/// Use this method to compose uris when interacting with this instance of `MockServer` via
/// an HTTP client.
///
/// ### Example:
/// ```rust
/// use wiremock::MockServer;
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange - no mocks mounted
///
/// let mock_server = MockServer::start().await;
/// // Act
/// let uri = format!("{}/health_check", &mock_server.uri());
/// let status = reqwest::get(uri).await.unwrap().status();
///
/// // Assert - default response
/// assert_eq!(status, 404);
/// }
/// ```
pub fn uri(&self) -> String {
self.0.uri()
}
/// Return the socket address of this running instance of `MockServer`, e.g. `127.0.0.1:4372`.
///
/// Use this method to interact with the `MockServer` using [`TcpStream`]s.
///
/// ### Example:
/// ```rust
/// use wiremock::MockServer;
/// use std::net::TcpStream;
///
/// #[async_std::main]
/// async fn main() {
/// // Act - the server is started
/// let mock_server = MockServer::start().await;
///
/// // Assert - we can connect to it
/// assert!(TcpStream::connect(mock_server.address()).is_ok());
/// }
/// ```
///
/// [`TcpStream`]: std::net::TcpStream
pub fn address(&self) -> &SocketAddr {
self.0.address()
}
/// Return a vector with all the requests received by the `MockServer` since it started.
/// If no request has been served, it returns an empty vector.
///
/// If request recording has been disabled using [`MockServerBuilder::disable_request_recording`],
/// it returns `None`.
///
/// ### Example:
///
/// ```rust
/// use wiremock::MockServer;
/// use http::Method;
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
///
/// // Act
/// reqwest::get(&mock_server.uri()).await.unwrap();
///
/// // Assert
/// let received_requests = mock_server.received_requests().await.unwrap();
/// assert_eq!(received_requests.len(), 1);
///
/// let received_request = &received_requests[0];
/// assert_eq!(received_request.method, Method::GET);
/// assert_eq!(received_request.url.path(), "/");
/// assert!(received_request.body.is_empty());
/// }
/// ```
///
/// ### Example (No request served):
///
/// ```rust
/// use wiremock::MockServer;
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
///
/// // Assert
/// let received_requests = mock_server.received_requests().await.unwrap();
/// assert_eq!(received_requests.len(), 0);
/// }
/// ```
///
/// ### Example (Request recording disabled):
///
/// ```rust
/// use wiremock::MockServer;
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::builder().disable_request_recording().start().await;
///
/// // Assert
/// let received_requests = mock_server.received_requests().await;
/// assert!(received_requests.is_none());
/// }
/// ```
pub async fn received_requests(&self) -> Option<Vec<Request>> {
self.0.received_requests().await
}
}
impl Drop for MockServer {
// Clean up when the `MockServer` instance goes out of scope.
fn drop(&mut self) {
futures::executor::block_on(self.verify());
// The sender half of the channel, `shutdown_trigger`, gets dropped here
// Triggering the graceful shutdown of the server itself.
}
}

View File

@@ -0,0 +1,88 @@
use crate::mock_server::bare_server::MockServerState;
use hyper::service::service_fn;
use hyper_util::rt::TokioIo;
use std::sync::Arc;
use tokio::net::TcpListener;
use tokio::sync::RwLock;
/// Work around a lifetime error where, for some reason,
/// `Box<dyn std::error::Error + Send + Sync + 'static>` can't be converted to a
/// `Box<dyn std::error::Error + Send + Sync>`
struct ErrorLifetimeCast(Box<dyn std::error::Error + Send + Sync + 'static>);
impl From<ErrorLifetimeCast> for Box<dyn std::error::Error + Send + Sync> {
fn from(value: ErrorLifetimeCast) -> Self {
value.0
}
}
/// The actual HTTP server responding to incoming requests according to the specified mocks.
pub(super) async fn run_server(
listener: std::net::TcpListener,
server_state: Arc<RwLock<MockServerState>>,
mut shutdown_signal: tokio::sync::watch::Receiver<()>,
) {
listener
.set_nonblocking(true)
.expect("Cannot set non-blocking mode on TcpListener");
let listener = TcpListener::from_std(listener).expect("Cannot upgrade TcpListener");
let request_handler = move |request| {
let server_state = server_state.clone();
async move {
let wiremock_request = crate::Request::from_hyper(request).await;
let (response, delay) = server_state
.write()
.await
.handle_request(wiremock_request)
.await
.map_err(ErrorLifetimeCast)?;
// We do not wait for the delay within the handler otherwise we would be
// holding on to the write-side of the `RwLock` on `mock_set`.
// Holding on the lock while waiting prevents us from handling other requests until
// we have waited the whole duration specified in the delay.
// In particular, we cannot perform even perform read-only operation -
// e.g. check that mock assumptions have been verified.
// Using long delays in tests without handling the delay as we are doing here
// caused tests to hang (see https://github.com/seanmonstar/reqwest/issues/1147)
if let Some(delay) = delay {
delay.await;
}
Ok::<_, ErrorLifetimeCast>(response)
}
};
loop {
let (stream, _) = tokio::select! { biased;
accepted = listener.accept() => {
match accepted {
Ok(accepted) => accepted,
Err(_) => break,
}
},
_ = shutdown_signal.changed() => {
log::info!("Mock server shutting down");
break;
}
};
let io = TokioIo::new(stream);
let request_handler = request_handler.clone();
let mut shutdown_signal = shutdown_signal.clone();
tokio::task::spawn(async move {
let http_server =
hyper_util::server::conn::auto::Builder::new(hyper_util::rt::TokioExecutor::new());
let conn = http_server.serve_connection_with_upgrades(io, service_fn(request_handler));
tokio::pin!(conn);
loop {
tokio::select! {
_ = conn.as_mut() => break,
_ = shutdown_signal.changed() => conn.as_mut().graceful_shutdown(),
}
}
});
}
}

21
vendor/wiremock/src/mock_server/mod.rs vendored Normal file
View File

@@ -0,0 +1,21 @@
//! All bits and pieces concerning the HTTP mock server are in this module.
//!
//! `bare_server::BareMockServer` is the "front-end" to drive behaviour for the `hyper` HTTP
//! server running in the background, defined in the `hyper` sub-module.
//!
//! `bare_server::BareMockServer` is not exposed directly: crate users only get to interact with
//! `exposed_server::MockServer`.
//! `exposed_server::MockServer` is either a wrapper around a `BareMockServer` retrieved from an
//! object pool or a wrapper around an exclusive `BareMockServer`.
//! We use the pool when the user does not care about the port the mock server listens to, while
//! we provision a dedicated one if they specify their own `TcpListener` with `start_on`.
//! Check the `pool` submodule for more details on our pooling strategy.
mod bare_server;
mod builder;
mod exposed_server;
mod hyper;
mod pool;
pub use bare_server::MockGuard;
pub use builder::MockServerBuilder;
pub use exposed_server::MockServer;

73
vendor/wiremock/src/mock_server/pool.rs vendored Normal file
View File

@@ -0,0 +1,73 @@
use crate::MockServer;
use crate::mock_server::bare_server::BareMockServer;
use deadpool::managed::{Metrics, Object, Pool};
use once_cell::sync::Lazy;
use std::convert::Infallible;
/// A pool of `BareMockServer`s.
///
/// ## Design constraints
///
/// `wiremock`'s pooling is designed to be an invisible optimisation: users of the crate, if
/// we are successful, should never have to reason about it.
///
/// ## Motivation
///
/// Why are we pooling `BareMockServer`s?
/// Mostly to reduce the number of `TcpListener`s that are being opened and closed, therefore
/// mitigating risk of our users having to fight/raise OS limits for the maximum number of open
/// connections (e.g. ulimit on Linux).
///
/// It is also marginally faster to get a pooled `BareMockServer` than to create a new one, but
/// the absolute time is so small (<1 ms) that it does not make a material difference in a real
/// world test suite.
static MOCK_SERVER_POOL: Lazy<Pool<MockServerPoolManager>> = Lazy::new(|| {
// We are choosing an arbitrarily high max_size because we never want a test to "wait" for
// a `BareMockServer` instance to become available.
//
// We might expose in the future a way for a crate user to tune this value.
Pool::builder(MockServerPoolManager)
.max_size(1000)
.build()
.expect("Building a server pool is not expected to fail. Please report an issue")
});
pub(crate) type PooledMockServer = Object<MockServerPoolManager>;
/// Retrieve a `BareMockServer` from the pool.
/// The operation should never fail.
pub(crate) async fn get_pooled_mock_server() -> PooledMockServer {
MOCK_SERVER_POOL
.get()
.await
.expect("Failed to get a MockServer from the pool")
}
/// The `BareMockServer` pool manager.
///
/// It:
/// - creates a new `BareMockServer` if there is none to borrow from the pool;
/// - "cleans up" used `BareMockServer`s before making them available again for other tests to use.
#[derive(Debug)]
pub(crate) struct MockServerPoolManager;
impl deadpool::managed::Manager for MockServerPoolManager {
type Error = Infallible;
type Type = BareMockServer;
async fn create(&self) -> Result<BareMockServer, Infallible> {
// All servers in the pool use the default configuration
Ok(MockServer::builder().build_bare().await)
}
async fn recycle(
&self,
mock_server: &mut BareMockServer,
_metrics: &Metrics,
) -> deadpool::managed::RecycleResult<Infallible> {
// Remove all existing settings - we want to start clean when the mock server
// is picked up again from the pool.
mock_server.reset().await;
Ok(())
}
}

244
vendor/wiremock/src/mock_set.rs vendored Normal file
View File

@@ -0,0 +1,244 @@
use crate::request::BodyPrintLimit;
use crate::{
ErrorResponse,
mounted_mock::MountedMock,
verification::{VerificationOutcome, VerificationReport},
};
use crate::{Mock, Request};
use http_body_util::Full;
use hyper::body::Bytes;
use log::debug;
use std::{
ops::{Index, IndexMut},
sync::{Arc, atomic::AtomicBool},
};
use tokio::sync::Notify;
use tokio::time::{Sleep, sleep};
/// The collection of mocks used by a `MockServer` instance to match against
/// incoming requests.
///
/// New mocks are added to `MountedMockSet` every time [`MockServer::register`](crate::MockServer::register),
/// [`MockServer::register_as_scoped`](crate::MockServer::register_as_scoped) or
/// [`Mock::mount`](crate::Mock::mount) are called.
pub(crate) struct MountedMockSet {
mocks: Vec<(MountedMock, MountedMockState)>,
/// A counter that keeps track of how many times [`MountedMockSet::reset`] has been called.
/// It starts at `0` and gets incremented for each invocation.
///
/// We need `generation` to know if a [`MockId`] points to an [`MountedMock`] that has been
/// removed via [`MountedMockSet::reset`].
generation: u16,
body_print_limit: BodyPrintLimit,
}
/// A `MockId` is an opaque index that uniquely identifies an [`MountedMock`] inside an [`MountedMockSet`].
///
/// The only way to create a `MockId` is calling [`MountedMockSet::register`].
#[derive(Copy, Clone)]
pub(crate) struct MockId {
index: usize,
/// The generation of [`MountedMockSet`] when [`MountedMockSet::register`] was called.
/// It allows [`MountedMockSet`] to check that the [`MountedMock`] our [`MockId`] points to is still in
/// the set (i.e. the set has not been wiped by a [`MountedMockSet::reset`] call).
generation: u16,
}
impl MountedMockSet {
/// Create a new instance of `MountedMockSet`.
pub(crate) fn new(body_print_limit: BodyPrintLimit) -> MountedMockSet {
MountedMockSet {
mocks: vec![],
generation: 0,
body_print_limit,
}
}
pub(crate) async fn handle_request(
&mut self,
request: Request,
) -> Result<(hyper::Response<Full<Bytes>>, Option<Sleep>), ErrorResponse> {
debug!("Handling request.");
let mut response_template: Option<_> = None;
self.mocks.sort_by_key(|(m, _)| m.specification.priority);
for (mock, mock_state) in &mut self.mocks {
if *mock_state == MountedMockState::OutOfScope {
continue;
}
if mock.matches(&request) {
response_template = Some(mock.response_template(&request));
break;
}
}
if let Some(response_template) = response_template {
match response_template {
Ok(response_template) => {
let delay = response_template.delay().map(sleep);
Ok((response_template.generate_response(), delay))
}
Err(err) => Err(err),
}
} else {
let mut msg = "Got unexpected request:\n".to_string();
_ = request.print_with_limit(&mut msg, self.body_print_limit);
debug!("{}", msg);
let not_found_response = hyper::Response::builder()
.status(hyper::StatusCode::NOT_FOUND)
.body(Full::default())
.unwrap();
Ok((not_found_response, None))
}
}
pub(crate) fn register(&mut self, mock: Mock) -> (Arc<(Notify, AtomicBool)>, MockId) {
let n_registered_mocks = self.mocks.len();
let active_mock = MountedMock::new(mock, n_registered_mocks);
let notify = active_mock.notify();
self.mocks.push((active_mock, MountedMockState::InScope));
(
notify,
MockId {
index: self.mocks.len() - 1,
generation: self.generation,
},
)
}
pub(crate) fn reset(&mut self) {
self.mocks = vec![];
self.generation += 1;
}
/// Mark one of the mocks in the set as out of scope.
///
/// It will stop matching against incoming requests, regardless of its specification.
pub(crate) fn deactivate(&mut self, mock_id: MockId) {
let mock = &mut self[mock_id];
mock.1 = MountedMockState::OutOfScope;
}
/// Verify that expectations have been met for **all** [`MountedMock`]s in the set.
pub(crate) fn verify_all(&self) -> VerificationOutcome {
let failed_verifications: Vec<VerificationReport> = self
.mocks
.iter()
.filter(|(_, state)| *state == MountedMockState::InScope)
.map(|(m, _)| m.verify())
.filter(|verification_report| !verification_report.is_satisfied())
.collect();
if failed_verifications.is_empty() {
VerificationOutcome::Success
} else {
VerificationOutcome::Failure(failed_verifications)
}
}
/// Verify that expectations have been met for the [`MountedMock`] corresponding to the specified [`MockId`].
pub(crate) fn verify(&self, mock_id: MockId) -> VerificationReport {
let (mock, _) = &self[mock_id];
mock.verify()
}
}
impl IndexMut<MockId> for MountedMockSet {
fn index_mut(&mut self, index: MockId) -> &mut Self::Output {
if index.generation != self.generation {
panic!(
"The mock you are trying to access is no longer active. It has been deleted from the active set via `reset` - you should not hold on to a `MockId` after you call `reset`!."
)
}
&mut self.mocks[index.index]
}
}
impl Index<MockId> for MountedMockSet {
type Output = (MountedMock, MountedMockState);
fn index(&self, index: MockId) -> &Self::Output {
if index.generation != self.generation {
panic!(
"The mock you are trying to access is no longer active. It has been deleted from the active set via `reset` - you should not hold on to a `MockId` after you call `reset`!."
)
}
&self.mocks[index.index]
}
}
/// A [`MountedMock`] can either be global (i.e. registered using [`crate::MockServer::register`]) or
/// scoped (i.e. registered using [`crate::MockServer::register_as_scoped`]).
///
/// [`MountedMock`]s must currently be in scope to be matched against incoming requests.
/// Out of scope [`MountedMock`]s are skipped when trying to match an incoming request.
///
/// # Implementation Rationale
///
/// An alternative approach would be removing a [`MountedMock`] from the [`MountedMockSet`] when it goes
/// out of scope.
/// This would create an issue for the stability of [`MockId`]s: removing an element from the vector
/// of [`MountedMock`]s in [`MountedMockSet`] would invalidate the ids of all mocks registered after
/// the removed one.
///
/// Attaching a state to the mocks in the vector, instead, allows us to ensure id stability while
/// achieving the desired behaviour.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub(crate) enum MountedMockState {
InScope,
OutOfScope,
}
#[cfg(test)]
mod tests {
use crate::matchers::path;
use crate::mock_set::{MountedMockSet, MountedMockState};
use crate::request::BodyPrintLimit;
use crate::{Mock, ResponseTemplate};
fn test_mock_set() -> MountedMockSet {
MountedMockSet::new(BodyPrintLimit::Unlimited)
}
#[test]
fn generation_is_incremented_for_every_reset() {
let mut set = test_mock_set();
assert_eq!(set.generation, 0);
for i in 1..10 {
set.reset();
assert_eq!(set.generation, i);
}
}
#[test]
#[should_panic]
fn accessing_a_mock_id_after_a_reset_triggers_a_panic() {
// Assert
let mut set = test_mock_set();
let mock = Mock::given(path("/")).respond_with(ResponseTemplate::new(200));
let (_, mock_id) = set.register(mock);
// Act
set.reset();
// Assert
let _ = &set[mock_id];
}
#[test]
fn deactivating_a_mock_does_not_invalidate_other_ids() {
// Assert
let mut set = test_mock_set();
let first_mock = Mock::given(path("/")).respond_with(ResponseTemplate::new(200));
let second_mock = Mock::given(path("/hello")).respond_with(ResponseTemplate::new(500));
let (_, first_mock_id) = set.register(first_mock);
let (_, second_mock_id) = set.register(second_mock);
// Act
set.deactivate(first_mock_id);
// Assert
let first_mock = &set[first_mock_id];
assert_eq!(first_mock.1, MountedMockState::OutOfScope);
let second_mock = &set[second_mock_id];
assert_eq!(second_mock.1, MountedMockState::InScope);
}
}

99
vendor/wiremock/src/mounted_mock.rs vendored Normal file
View File

@@ -0,0 +1,99 @@
use std::sync::{Arc, atomic::AtomicBool};
use tokio::sync::Notify;
use crate::{
ErrorResponse, Match, Mock, Request, ResponseTemplate, verification::VerificationReport,
};
/// Given the behaviour specification as a [`Mock`], keep track of runtime information
/// concerning this mock - e.g. how many times it matched on a incoming request.
pub(crate) struct MountedMock {
pub(crate) specification: Mock,
n_matched_requests: u64,
/// The position occupied by this mock within the parent [`MountedMockSet`](crate::mock_set::MountedMockSet)
/// collection of `MountedMock`s.
///
/// E.g. `0` if this is the first mock that we try to match against an incoming request, `1`
/// if it is the second, etc.
position_in_set: usize,
// matched requests:
matched_requests: Vec<crate::Request>,
notify: Arc<(Notify, AtomicBool)>,
}
impl MountedMock {
pub(crate) fn new(specification: Mock, position_in_set: usize) -> Self {
Self {
specification,
n_matched_requests: 0,
position_in_set,
matched_requests: Vec::new(),
notify: Arc::new((Notify::new(), AtomicBool::new(false))),
}
}
/// This is NOT the same of `matches` from the `Match` trait!
/// Key difference: we are talking a mutable reference to `self` in order to capture
/// additional information (e.g. how many requests we matched so far) or change behaviour
/// after a certain threshold has been crossed (e.g. start returning `false` for all requests
/// once enough requests have been matched according to `max_n_matches`).
pub(crate) fn matches(&mut self, request: &Request) -> bool {
if Some(self.n_matched_requests) == self.specification.max_n_matches {
// Skip the actual check if we are already at our maximum of matched requests.
false
} else {
let matched = self
.specification
.matchers
.iter()
.all(|matcher| matcher.matches(request));
if matched {
// Increase match count
self.n_matched_requests += 1;
// Keep track of request
self.matched_requests.push(request.clone());
// notification of satisfaction
if self.verify().is_satisfied() {
// always set the satisfaction flag **before** raising the event
self.notify
.1
.store(true, std::sync::atomic::Ordering::Release);
self.notify.0.notify_waiters();
}
}
matched
}
}
/// Verify if this mock has verified the expectations set at creation time
/// over the number of invocations.
pub(crate) fn verify(&self) -> VerificationReport {
VerificationReport {
mock_name: self.specification.name.clone(),
n_matched_requests: self.n_matched_requests,
expectation_range: self.specification.expectation_range.clone(),
position_in_set: self.position_in_set,
}
}
pub(crate) fn response_template(
&self,
request: &Request,
) -> Result<ResponseTemplate, ErrorResponse> {
self.specification.response_template(request)
}
pub(crate) fn received_requests(&self) -> Vec<crate::Request> {
self.matched_requests.clone()
}
pub(crate) fn notify(&self) -> Arc<(Notify, AtomicBool)> {
self.notify.clone()
}
}

136
vendor/wiremock/src/request.rs vendored Normal file
View File

@@ -0,0 +1,136 @@
use std::fmt;
use http::{HeaderMap, Method};
use http_body_util::BodyExt;
use serde::de::DeserializeOwned;
use url::Url;
pub const BODY_PRINT_LIMIT: usize = 10_000;
/// Specifies limitations on printing request bodies when logging requests. For some mock servers
/// the bodies may be too large to reasonably print and it may be desirable to limit them.
#[derive(Debug, Copy, Clone)]
pub enum BodyPrintLimit {
/// Maximum length of a body to print in bytes.
Limited(usize),
/// There is no limit to the size of a body that may be printed.
Unlimited,
}
/// An incoming request to an instance of [`MockServer`].
///
/// Each matcher gets an immutable reference to a `Request` instance in the [`matches`] method
/// defined in the [`Match`] trait.
///
/// [`MockServer`]: crate::MockServer
/// [`matches`]: crate::Match::matches
/// [`Match`]: crate::Match
///
/// ### Implementation notes:
/// We can't use `http_types::Request` directly in our `Match::matches` signature:
/// it requires having mutable access to the request to extract the body (which gets
/// consumed when read!).
/// It would also require `matches` to be async, which is cumbersome due to the lack of async traits.
///
/// We introduce our `Request` type to perform this extraction once when the request
/// arrives in the mock serve, store the result and pass an immutable reference to it
/// to all our matchers.
#[derive(Debug, Clone)]
pub struct Request {
pub url: Url,
pub method: Method,
pub headers: HeaderMap,
pub body: Vec<u8>,
}
impl Request {
pub fn body_json<T: DeserializeOwned>(&self) -> Result<T, serde_json::Error> {
serde_json::from_slice(&self.body)
}
pub(crate) async fn from_hyper(request: hyper::Request<hyper::body::Incoming>) -> Request {
let (parts, body) = request.into_parts();
let url = match parts.uri.authority() {
Some(_) => parts.uri.to_string(),
None => format!("http://localhost{}", parts.uri),
}
.parse()
.unwrap();
let body = body
.collect()
.await
.expect("Failed to read request body.")
.to_bytes();
Self {
url,
method: parts.method,
headers: parts.headers,
body: body.to_vec(),
}
}
pub(crate) fn print_with_limit(
&self,
mut buffer: impl fmt::Write,
body_print_limit: BodyPrintLimit,
) -> fmt::Result {
writeln!(buffer, "{} {}", self.method, self.url)?;
for name in self.headers.keys() {
let values = self
.headers
.get_all(name)
.iter()
.map(|value| String::from_utf8_lossy(value.as_bytes()))
.collect::<Vec<_>>();
let values = values.join(",");
writeln!(buffer, "{}: {}", name, values)?;
}
match body_print_limit {
BodyPrintLimit::Limited(limit) if self.body.len() > limit => {
let mut written = false;
for end_byte in limit..(limit + 4).max(self.body.len()) {
if let Ok(truncated) = std::str::from_utf8(&self.body[..end_byte]) {
written = true;
writeln!(buffer, "{}", truncated)?;
if end_byte < self.body.len() {
writeln!(
buffer,
"We truncated the body because it was too large: {} bytes (limit: {} bytes)",
self.body.len(),
limit
)?;
writeln!(
buffer,
"Increase this limit by setting `WIREMOCK_BODY_PRINT_LIMIT`, or calling `MockServerBuilder::body_print_limit` when building your MockServer instance"
)?;
}
break;
}
}
if !written {
writeln!(
buffer,
"Body is likely binary (invalid utf-8) size is {} bytes",
self.body.len()
)
} else {
Ok(())
}
}
_ => {
if let Ok(body) = std::str::from_utf8(&self.body) {
writeln!(buffer, "{}", body)
} else {
writeln!(
buffer,
"Body is likely binary (invalid utf-8) size is {} bytes",
self.body.len()
)
}
}
}
}
}

169
vendor/wiremock/src/respond.rs vendored Normal file
View File

@@ -0,0 +1,169 @@
use crate::{ErrorResponse, Request, ResponseTemplate};
/// Anything that implements `Respond` can be used to reply to an incoming request when a
/// [`Mock`] is activated.
///
/// ## Fixed responses
///
/// The simplest `Respond` is [`ResponseTemplate`]: no matter the request, it will
/// always return itself.
///
/// ```rust
/// use wiremock::{MockServer, Mock, ResponseTemplate};
/// use wiremock::matchers::method;
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
/// let correlation_id = "1311db4f-fe65-4cb2-b514-1bb47f781aa7";
/// let template = ResponseTemplate::new(200).insert_header(
/// "X-Correlation-ID",
/// correlation_id
/// );
/// Mock::given(method("GET"))
/// .respond_with(template)
/// .mount(&mock_server)
/// .await;
///
/// // Act
/// let response = reqwest::get(&mock_server.uri())
/// .await
/// .unwrap();
///
/// // Assert
/// assert_eq!(response.status(), 200);
/// assert_eq!(response.headers().get("X-Correlation-ID").unwrap().to_str().unwrap(), correlation_id);
/// }
/// ```
///
/// ## Dynamic responses
///
/// You can use `Respond`, though, to implement responses that depend on the data in
/// the request matched by a [`Mock`].
///
/// Functions from `Request` to `ResponseTemplate` implement `Respond`, so for simple cases you
/// can use a closure to build a response dynamically, for instance to echo the request body back:
///
/// ```rust
/// use wiremock::{Match, MockServer, Mock, Request, ResponseTemplate};
/// use wiremock::matchers::path;
///
/// #[async_std::main]
/// async fn main() {
/// let mock_server = MockServer::start().await;
/// let body = "Mock Server!".to_string();
///
/// Mock::given(path("/echo"))
/// .respond_with(|req: &Request| {
/// let body_string = String::from_utf8(req.body.clone()).unwrap();
/// ResponseTemplate::new(200).set_body_string(body_string)
/// })
/// .mount(&mock_server)
/// .await;
///
/// let client = reqwest::Client::new();
/// let response = client.post(format!("{}/echo", &mock_server.uri()))
/// .body(body.clone())
/// .send()
/// .await
/// .unwrap();
/// assert_eq!(response.status(), 200);
/// assert_eq!(response.text().await.unwrap(), body);
/// }
/// ```
///
/// For more complex cases you may want to implement `Respond` yourself. As an example, this is a
/// `Respond` that propagates back a request header in the response:
///
/// ```rust
/// use http::HeaderName;
/// use wiremock::{Match, MockServer, Mock, Request, ResponseTemplate, Respond};
/// use wiremock::matchers::path;
/// use std::convert::TryInto;
/// use std::str::FromStr;
///
/// /// Responds using the specified `ResponseTemplate`, but it dynamically populates the
/// /// `X-Correlation-Id` header from the request data.
/// pub struct CorrelationIdResponder(pub ResponseTemplate);
///
/// impl Respond for CorrelationIdResponder {
/// fn respond(&self, request: &Request) -> ResponseTemplate {
/// const HEADER: HeaderName = HeaderName::from_static("x-correlation-id");
/// let mut response_template = self.0.clone();
/// if let Some(correlation_id) = request.headers.get(&HEADER) {
/// response_template = response_template.insert_header(
/// HEADER,
/// correlation_id.to_owned()
/// );
/// }
/// response_template
/// }
/// }
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
/// let correlation_id = "1241-1245-1548-4567";
///
/// Mock::given(path("/hello"))
/// .respond_with(CorrelationIdResponder(ResponseTemplate::new(200)))
/// .mount(&mock_server)
/// .await;
///
/// let client = reqwest::Client::new();
/// let response = client
/// .get(format!("{}/hello", &mock_server.uri()))
/// .header("X-Correlation-Id", correlation_id)
/// .send()
/// .await
/// .unwrap();
/// assert_eq!(response.status(), 200);
/// assert_eq!(response.headers().get("X-Correlation-Id").unwrap().to_str().unwrap(), correlation_id);
/// }
/// ```
///
/// [`Mock`]: crate::Mock
/// [`ResponseTemplate`]: crate::ResponseTemplate
pub trait Respond: Send + Sync {
/// Given a reference to a [`Request`] return a [`ResponseTemplate`] that will be used
/// by the [`MockServer`] as blueprint for the response returned to the client.
///
/// [`Request`]: crate::Request
/// [`MockServer`]: crate::MockServer
/// [`ResponseTemplate`]: crate::ResponseTemplate
fn respond(&self, request: &Request) -> ResponseTemplate;
}
/// A `ResponseTemplate` is the simplest `Respond` implementation: it returns a clone of itself
/// no matter what the incoming request contains!
impl Respond for ResponseTemplate {
fn respond(&self, _request: &Request) -> ResponseTemplate {
self.clone()
}
}
impl<F> Respond for F
where
F: Send + Sync + Fn(&Request) -> ResponseTemplate,
{
fn respond(&self, request: &Request) -> ResponseTemplate {
(self)(request)
}
}
/// Like [`Respond`], but it only allows returning an error through a function.
pub trait RespondErr: Send + Sync {
fn respond_err(&self, request: &Request) -> ErrorResponse;
}
impl<F, Err> RespondErr for F
where
F: Send + Sync + Fn(&Request) -> Err,
Err: std::error::Error + Send + Sync + 'static,
{
fn respond_err(&self, request: &Request) -> ErrorResponse {
Box::new((self)(request))
}
}

331
vendor/wiremock/src/response_template.rs vendored Normal file
View File

@@ -0,0 +1,331 @@
use http::{HeaderMap, HeaderName, HeaderValue, Response, StatusCode};
use http_body_util::Full;
use hyper::body::Bytes;
use serde::Serialize;
use std::convert::TryInto;
use std::time::Duration;
/// The blueprint for the response returned by a [`MockServer`] when a [`Mock`] matches on an incoming request.
///
/// [`Mock`]: crate::Mock
/// [`MockServer`]: crate::MockServer
#[derive(Clone, Debug)]
pub struct ResponseTemplate {
mime: String,
status_code: StatusCode,
headers: HeaderMap,
body: Option<Vec<u8>>,
delay: Option<Duration>,
}
// `wiremock` is a crate meant for testing - failures are most likely not handled/temporary mistakes.
// Hence we prefer to panic and provide an easier API than to use `Result`s thus pushing
// the burden of "correctness" (and conversions) on the user.
//
// All methods try to accept the widest possible set of inputs and then perform the fallible conversion
// internally, bailing if the fallible conversion fails.
//
// Same principle applies to allocation/cloning, freely used where convenient.
impl ResponseTemplate {
/// Start building a `ResponseTemplate` specifying the status code of the response.
pub fn new<S>(s: S) -> Self
where
S: TryInto<StatusCode>,
<S as TryInto<StatusCode>>::Error: std::fmt::Debug,
{
let status_code = s.try_into().expect("Failed to convert into status code.");
Self {
status_code,
headers: HeaderMap::new(),
mime: String::new(),
body: None,
delay: None,
}
}
/// Append a header `value` to list of headers with `key` as header name.
///
/// Unlike `insert_header`, this function will not override the contents of a header:
/// - if there are no header values with `key` as header name, it will insert one;
/// - if there are already some values with `key` as header name, it will append to the
/// existing list.
pub fn append_header<K, V>(mut self, key: K, value: V) -> Self
where
K: TryInto<HeaderName>,
<K as TryInto<HeaderName>>::Error: std::fmt::Debug,
V: TryInto<HeaderValue>,
<V as TryInto<HeaderValue>>::Error: std::fmt::Debug,
{
let key = key.try_into().expect("Failed to convert into header name.");
let value = value
.try_into()
.expect("Failed to convert into header value.");
self.headers.append(key, value);
self
}
/// Insert a header `value` with `key` as header name.
///
/// This function will override the contents of a header:
/// - if there are no header values with `key` as header name, it will insert one;
/// - if there are already some values with `key` as header name, it will drop them and
/// start a new list of header values, containing only `value`.
///
/// ### Example:
/// ```rust
/// use wiremock::{MockServer, Mock, ResponseTemplate};
/// use wiremock::matchers::method;
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
/// let correlation_id = "1311db4f-fe65-4cb2-b514-1bb47f781aa7";
/// let template = ResponseTemplate::new(200).insert_header(
/// "X-Correlation-ID",
/// correlation_id
/// );
/// Mock::given(method("GET"))
/// .respond_with(template)
/// .mount(&mock_server)
/// .await;
///
/// // Act
/// let res = reqwest::get(&mock_server.uri())
/// .await
/// .unwrap();
///
/// // Assert
/// assert_eq!(res.headers().get("X-Correlation-ID").unwrap().to_str().unwrap(), correlation_id);
/// }
/// ```
pub fn insert_header<K, V>(mut self, key: K, value: V) -> Self
where
K: TryInto<HeaderName>,
<K as TryInto<HeaderName>>::Error: std::fmt::Debug,
V: TryInto<HeaderValue>,
<V as TryInto<HeaderValue>>::Error: std::fmt::Debug,
{
let key = key.try_into().expect("Failed to convert into header name.");
let value = value
.try_into()
.expect("Failed to convert into header value.");
self.headers.insert(key, value);
self
}
/// Append multiple header key-value pairs.
///
/// Existing header values will not be overridden.
///
/// # Example
/// ```rust
/// use wiremock::{MockServer, Mock, ResponseTemplate};
/// use wiremock::matchers::method;
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
/// let headers = vec![
/// ("Set-Cookie", "name=value"),
/// ("Set-Cookie", "name2=value2; Domain=example.com"),
/// ];
/// let template = ResponseTemplate::new(200).append_headers(headers);
/// Mock::given(method("GET"))
/// .respond_with(template)
/// .mount(&mock_server)
/// .await;
///
/// // Act
/// let res = reqwest::get(&mock_server.uri())
/// .await
/// .unwrap();
///
/// // Assert
/// assert_eq!(res.headers().get_all("Set-Cookie").iter().count(), 2);
/// }
/// ```
pub fn append_headers<K, V, I>(mut self, headers: I) -> Self
where
K: TryInto<HeaderName>,
<K as TryInto<HeaderName>>::Error: std::fmt::Debug,
V: TryInto<HeaderValue>,
<V as TryInto<HeaderValue>>::Error: std::fmt::Debug,
I: IntoIterator<Item = (K, V)>,
{
let headers = headers.into_iter().map(|(key, value)| {
(
key.try_into().expect("Failed to convert into header name."),
value
.try_into()
.expect("Failed to convert into header value."),
)
});
// The `Extend<(HeaderName, T)>` impl uses `HeaderMap::append` internally: https://docs.rs/http/1.0.0/src/http/header/map.rs.html#1953
self.headers.extend(headers);
self
}
/// Set the response body with bytes.
///
/// It sets "Content-Type" to "application/octet-stream".
///
/// To set a body with bytes but a different "Content-Type"
/// [`set_body_raw`](#method.set_body_raw) can be used.
pub fn set_body_bytes<B>(mut self, body: B) -> Self
where
B: TryInto<Vec<u8>>,
<B as TryInto<Vec<u8>>>::Error: std::fmt::Debug,
{
let body = body.try_into().expect("Failed to convert into body.");
self.body = Some(body);
self
}
/// Set the response body from a JSON-serializable value.
///
/// It sets "Content-Type" to "application/json".
pub fn set_body_json<B: Serialize>(mut self, body: B) -> Self {
let body = serde_json::to_vec(&body).expect("Failed to convert into body.");
self.body = Some(body);
self.mime = "application/json".to_string();
self
}
/// Set the response body to a string.
///
/// It sets "Content-Type" to "text/plain".
pub fn set_body_string<T>(mut self, body: T) -> Self
where
T: TryInto<String>,
<T as TryInto<String>>::Error: std::fmt::Debug,
{
let body = body.try_into().expect("Failed to convert into body.");
self.body = Some(body.into_bytes());
self.mime = "text/plain".to_string();
self
}
/// Set a raw response body. The mime type needs to be set because the
/// raw body could be of any type.
///
/// ### Example:
/// ```rust
/// use wiremock::{MockServer, Mock, ResponseTemplate};
/// use wiremock::matchers::method;
///
/// mod external {
/// // This could be a method of a struct that is
/// // implemented in another crate and the struct
/// // does not implement Serialize.
/// pub fn body() -> Vec<u8>{
/// r#"{"hello": "world"}"#.as_bytes().to_owned()
/// }
/// }
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
/// let template = ResponseTemplate::new(200).set_body_raw(
/// external::body(),
/// "application/json"
/// );
/// Mock::given(method("GET"))
/// .respond_with(template)
/// .mount(&mock_server)
/// .await;
///
/// // Act
/// let mut res = reqwest::get(&mock_server.uri())
/// .await
/// .unwrap();
/// let content_type = res.headers().get(reqwest::header::CONTENT_TYPE).cloned();
/// let body = res.text()
/// .await
/// .unwrap();
///
/// // Assert
/// assert_eq!(body, r#"{"hello": "world"}"#);
/// assert_eq!(content_type, Some("application/json".parse().unwrap()));
/// }
/// ```
pub fn set_body_raw<B>(mut self, body: B, mime: &str) -> Self
where
B: TryInto<Vec<u8>>,
<B as TryInto<Vec<u8>>>::Error: std::fmt::Debug,
{
let body = body.try_into().expect("Failed to convert into body.");
self.body = Some(body);
self.mime = mime.to_string();
self
}
/// By default the [`MockServer`] tries to fulfill incoming requests as fast as possible.
///
/// You can use `set_delay` to introduce an artificial delay to simulate the behaviour of
/// a real server with a non-negligible latency.
///
/// In particular, you can use it to test the behaviour of your timeout policies.
///
/// ### Example:
/// ```rust
/// use wiremock::{MockServer, Mock, ResponseTemplate};
/// use wiremock::matchers::method;
/// use std::time::Duration;
/// use async_std::prelude::FutureExt;
///
/// #[async_std::main]
/// async fn main() {
/// // Arrange
/// let mock_server = MockServer::start().await;
/// let delay = Duration::from_secs(1);
/// let template = ResponseTemplate::new(200).set_delay(delay);
/// Mock::given(method("GET"))
/// .respond_with(template)
/// .mount(&mock_server)
/// .await;
///
/// // Act
/// let mut res = async_std::future::timeout(
/// // Shorter than the response delay!
/// delay / 3,
/// reqwest::get(&mock_server.uri())
/// )
/// .await;
///
/// // Assert - Timeout error!
/// assert!(res.is_err());
/// }
/// ```
///
/// [`MockServer`]: crate::mock_server::MockServer
pub fn set_delay(mut self, delay: Duration) -> Self {
self.delay = Some(delay);
self
}
/// Generate a response from the template.
pub(crate) fn generate_response(&self) -> Response<Full<Bytes>> {
let mut response = Response::builder().status(self.status_code);
let mut headers = self.headers.clone();
// Set content-type, if needed
if !self.mime.is_empty() {
headers.insert(http::header::CONTENT_TYPE, self.mime.parse().unwrap());
}
*response.headers_mut().unwrap() = headers;
let body = self.body.clone().unwrap_or_default();
response.body(body.into()).unwrap()
}
/// Retrieve the response delay.
pub(crate) fn delay(&self) -> &Option<Duration> {
&self.delay
}
}

47
vendor/wiremock/src/verification.rs vendored Normal file
View File

@@ -0,0 +1,47 @@
use crate::mock::Times;
/// A report returned by an `MountedMock` detailing what the user expectations were and
/// how many calls were actually received since the mock was mounted on the server.
#[derive(Clone)]
pub(crate) struct VerificationReport {
/// The mock name specified by the user.
pub(crate) mock_name: Option<String>,
/// What users specified
pub(crate) expectation_range: Times,
/// Actual number of received requests that matched the specification
pub(crate) n_matched_requests: u64,
/// The position occupied by the mock that generated the report within its parent
/// [`MountedMockSet`](crate::mock_set::MountedMockSet) collection of `MountedMock`s.
///
/// E.g. `0` if it is the first mock that we try to match against an incoming request, `1`
/// if it is the second, etc.
pub(crate) position_in_set: usize,
}
impl VerificationReport {
pub(crate) fn error_message(&self) -> String {
if let Some(ref mock_name) = self.mock_name {
format!(
"{}.\n\tExpected range of matching incoming requests: {}\n\tNumber of matched incoming requests: {}",
mock_name, self.expectation_range, self.n_matched_requests
)
} else {
format!(
"Mock #{}.\n\tExpected range of matching incoming requests: {}\n\tNumber of matched incoming requests: {}",
self.position_in_set, self.expectation_range, self.n_matched_requests
)
}
}
pub(crate) fn is_satisfied(&self) -> bool {
self.expectation_range.contains(self.n_matched_requests)
}
}
pub(crate) enum VerificationOutcome {
/// The expectations set on all active mocks were satisfied.
Success,
/// The expectations set for one or more of the active mocks were not satisfied.
/// All failed expectations are returned.
Failure(Vec<VerificationReport>),
}