2024-11-04 10:22:31 +00:00
|
|
|
//
|
feat: Add Debug, Clone, Default, PartialEq, Eq, Send, Sync to as many or our types as possible.
- adds tokio::sync as a dependency to provide an async Mutex for Clone of Net and MockNet
## ƒs
- adds `Clone` to `DirItem`
- adds `Default`, `PartialEq` and `Eq` to `FileSystem`, `PathMarker`, `FileMarker` and `DirMarker`
- adds `Default` to `PathReal`
- adds `Clone`, `Debug`, `Default`, `PartialEq` and `Eq` to `Reader`
## net
- `MockNet::try_from` now returns a `Future`, so should be `await`ed
- adds `Debug` to `Plan`
- adds `Debug` and `Default` to `Net`
- adds `Debug`, `Clone` and `Default` to `MockNet`
- adds `Debug`, `Clone`, `PartialEq` and `Eq` to `MatchRequest`
- adds `Debug` and `Clone` to `WhenRequest`
WIP: mutation tests
2024-11-16 08:46:07 +00:00
|
|
|
|
2024-11-17 11:24:28 +00:00
|
|
|
use std::{
|
|
|
|
cell::RefCell, collections::HashMap, marker::PhantomData, ops::Deref, rc::Rc, sync::Arc,
|
|
|
|
};
|
2024-11-10 09:44:30 +00:00
|
|
|
|
2024-11-17 09:18:18 +00:00
|
|
|
use derive_more::derive::{Display, From};
|
2024-11-17 11:24:28 +00:00
|
|
|
use http::{Method, StatusCode};
|
|
|
|
use reqwest::Client;
|
feat: Add Debug, Clone, Default, PartialEq, Eq, Send, Sync to as many or our types as possible.
- adds tokio::sync as a dependency to provide an async Mutex for Clone of Net and MockNet
## ƒs
- adds `Clone` to `DirItem`
- adds `Default`, `PartialEq` and `Eq` to `FileSystem`, `PathMarker`, `FileMarker` and `DirMarker`
- adds `Default` to `PathReal`
- adds `Clone`, `Debug`, `Default`, `PartialEq` and `Eq` to `Reader`
## net
- `MockNet::try_from` now returns a `Future`, so should be `await`ed
- adds `Debug` to `Plan`
- adds `Debug` and `Default` to `Net`
- adds `Debug`, `Clone` and `Default` to `MockNet`
- adds `Debug`, `Clone`, `PartialEq` and `Eq` to `MatchRequest`
- adds `Debug` and `Clone` to `WhenRequest`
WIP: mutation tests
2024-11-16 08:46:07 +00:00
|
|
|
use tokio::sync::Mutex;
|
2024-11-17 09:18:18 +00:00
|
|
|
use url::Url;
|
2024-11-04 10:22:31 +00:00
|
|
|
|
2024-11-17 09:18:18 +00:00
|
|
|
use crate::net::{Request, RequestBuilder, Response};
|
2024-11-15 09:06:45 +00:00
|
|
|
|
2024-11-04 10:22:31 +00:00
|
|
|
use super::{Error, Result};
|
|
|
|
|
2024-11-09 21:07:43 +00:00
|
|
|
/// A list of planned requests and responses
|
2024-11-04 10:22:31 +00:00
|
|
|
type Plans = Vec<Plan>;
|
|
|
|
|
2024-11-09 21:07:43 +00:00
|
|
|
/// A planned request and the response to return
|
|
|
|
///
|
|
|
|
/// Contains a list of the criteria that a request must meet before being considered a match.
|
feat: Add Debug, Clone, Default, PartialEq, Eq, Send, Sync to as many or our types as possible.
- adds tokio::sync as a dependency to provide an async Mutex for Clone of Net and MockNet
## ƒs
- adds `Clone` to `DirItem`
- adds `Default`, `PartialEq` and `Eq` to `FileSystem`, `PathMarker`, `FileMarker` and `DirMarker`
- adds `Default` to `PathReal`
- adds `Clone`, `Debug`, `Default`, `PartialEq` and `Eq` to `Reader`
## net
- `MockNet::try_from` now returns a `Future`, so should be `await`ed
- adds `Debug` to `Plan`
- adds `Debug` and `Default` to `Net`
- adds `Debug`, `Clone` and `Default` to `MockNet`
- adds `Debug`, `Clone`, `PartialEq` and `Eq` to `MatchRequest`
- adds `Debug` and `Clone` to `WhenRequest`
WIP: mutation tests
2024-11-16 08:46:07 +00:00
|
|
|
#[derive(Debug)]
|
2024-11-09 12:12:11 +00:00
|
|
|
struct Plan {
|
2024-11-12 07:13:59 +00:00
|
|
|
match_request: Vec<MatchRequest>,
|
2024-11-17 11:24:28 +00:00
|
|
|
response: reqwest::Response,
|
2024-11-12 07:13:59 +00:00
|
|
|
}
|
|
|
|
impl Plan {
|
2024-11-15 09:06:45 +00:00
|
|
|
fn matches(&self, request: &Request) -> bool {
|
2024-11-12 07:13:59 +00:00
|
|
|
self.match_request.iter().all(|criteria| match criteria {
|
|
|
|
MatchRequest::Method(method) => request.method() == method,
|
|
|
|
MatchRequest::Url(uri) => request.url() == uri,
|
|
|
|
MatchRequest::Header { name, value } => {
|
|
|
|
request
|
|
|
|
.headers()
|
|
|
|
.iter()
|
|
|
|
.any(|(request_header_name, request_header_value)| {
|
|
|
|
let Ok(request_header_value) = request_header_value.to_str() else {
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
request_header_name.as_str() == name && request_header_value == value
|
|
|
|
})
|
|
|
|
}
|
|
|
|
MatchRequest::Body(body) => {
|
2024-11-17 11:24:28 +00:00
|
|
|
request.body().and_then(reqwest::Body::as_bytes) == Some(body)
|
2024-11-12 07:13:59 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2024-11-04 10:22:31 +00:00
|
|
|
}
|
|
|
|
|
2024-11-10 17:11:54 +00:00
|
|
|
/// An abstraction for the network
|
feat: Add Debug, Clone, Default, PartialEq, Eq, Send, Sync to as many or our types as possible.
- adds tokio::sync as a dependency to provide an async Mutex for Clone of Net and MockNet
## ƒs
- adds `Clone` to `DirItem`
- adds `Default`, `PartialEq` and `Eq` to `FileSystem`, `PathMarker`, `FileMarker` and `DirMarker`
- adds `Default` to `PathReal`
- adds `Clone`, `Debug`, `Default`, `PartialEq` and `Eq` to `Reader`
## net
- `MockNet::try_from` now returns a `Future`, so should be `await`ed
- adds `Debug` to `Plan`
- adds `Debug` and `Default` to `Net`
- adds `Debug`, `Clone` and `Default` to `MockNet`
- adds `Debug`, `Clone`, `PartialEq` and `Eq` to `MatchRequest`
- adds `Debug` and `Clone` to `WhenRequest`
WIP: mutation tests
2024-11-16 08:46:07 +00:00
|
|
|
#[derive(Debug, Clone, Default)]
|
2024-11-09 12:12:11 +00:00
|
|
|
pub struct Net {
|
feat: Add Debug, Clone, Default, PartialEq, Eq, Send, Sync to as many or our types as possible.
- adds tokio::sync as a dependency to provide an async Mutex for Clone of Net and MockNet
## ƒs
- adds `Clone` to `DirItem`
- adds `Default`, `PartialEq` and `Eq` to `FileSystem`, `PathMarker`, `FileMarker` and `DirMarker`
- adds `Default` to `PathReal`
- adds `Clone`, `Debug`, `Default`, `PartialEq` and `Eq` to `Reader`
## net
- `MockNet::try_from` now returns a `Future`, so should be `await`ed
- adds `Debug` to `Plan`
- adds `Debug` and `Default` to `Net`
- adds `Debug`, `Clone` and `Default` to `MockNet`
- adds `Debug`, `Clone`, `PartialEq` and `Eq` to `MatchRequest`
- adds `Debug` and `Clone` to `WhenRequest`
WIP: mutation tests
2024-11-16 08:46:07 +00:00
|
|
|
plans: Option<Arc<Mutex<RefCell<Plans>>>>,
|
2024-11-09 12:12:11 +00:00
|
|
|
}
|
|
|
|
impl Net {
|
2024-11-09 21:07:43 +00:00
|
|
|
/// Creates a new unmocked [Net] for creating real network requests.
|
2024-11-09 12:12:11 +00:00
|
|
|
pub(super) const fn new() -> Self {
|
2024-11-11 07:44:43 +00:00
|
|
|
Self { plans: None }
|
2024-11-09 12:12:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
impl Net {
|
2024-11-15 09:06:45 +00:00
|
|
|
/// Helper to create a default [Client].
|
2024-11-09 21:07:43 +00:00
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// # use kxio::net::Result;
|
|
|
|
/// let net = kxio::net::new();
|
|
|
|
/// let client = net.client();
|
|
|
|
/// let request = client.get("https://hyper.rs");
|
|
|
|
/// ```
|
2024-11-15 09:06:45 +00:00
|
|
|
pub fn client(&self) -> Client {
|
2024-11-09 17:19:42 +00:00
|
|
|
Default::default()
|
2024-11-09 12:12:11 +00:00
|
|
|
}
|
|
|
|
|
2024-11-09 21:07:43 +00:00
|
|
|
/// Constructs the Request and sends it to the target URL, returning a
|
|
|
|
/// future Response.
|
|
|
|
///
|
|
|
|
/// # Errors
|
|
|
|
///
|
|
|
|
/// This method fails if there was an error while sending request,
|
|
|
|
/// redirect loop was detected or redirect limit was exhausted.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```no_run
|
|
|
|
/// # use kxio::net::Result;
|
|
|
|
/// # async fn run() -> Result<()> {
|
|
|
|
/// let net = kxio::net::new();
|
|
|
|
/// let request = net.client().get("https://hyper.rs");
|
|
|
|
/// let response = net.send(request).await?;
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2024-11-15 09:06:45 +00:00
|
|
|
pub async fn send(&self, request: impl Into<RequestBuilder>) -> Result<Response> {
|
2024-11-11 07:44:43 +00:00
|
|
|
let Some(plans) = &self.plans else {
|
|
|
|
return request.into().send().await.map_err(Error::from);
|
|
|
|
};
|
|
|
|
let request = request.into().build()?;
|
2024-11-12 07:13:59 +00:00
|
|
|
let index = plans
|
feat: Add Debug, Clone, Default, PartialEq, Eq, Send, Sync to as many or our types as possible.
- adds tokio::sync as a dependency to provide an async Mutex for Clone of Net and MockNet
## ƒs
- adds `Clone` to `DirItem`
- adds `Default`, `PartialEq` and `Eq` to `FileSystem`, `PathMarker`, `FileMarker` and `DirMarker`
- adds `Default` to `PathReal`
- adds `Clone`, `Debug`, `Default`, `PartialEq` and `Eq` to `Reader`
## net
- `MockNet::try_from` now returns a `Future`, so should be `await`ed
- adds `Debug` to `Plan`
- adds `Debug` and `Default` to `Net`
- adds `Debug`, `Clone` and `Default` to `MockNet`
- adds `Debug`, `Clone`, `PartialEq` and `Eq` to `MatchRequest`
- adds `Debug` and `Clone` to `WhenRequest`
WIP: mutation tests
2024-11-16 08:46:07 +00:00
|
|
|
.lock()
|
|
|
|
.await
|
|
|
|
.deref()
|
2024-11-12 07:13:59 +00:00
|
|
|
.borrow()
|
|
|
|
.iter()
|
|
|
|
.position(|plan| plan.matches(&request));
|
2024-11-11 07:44:43 +00:00
|
|
|
match index {
|
|
|
|
Some(i) => {
|
feat: Add Debug, Clone, Default, PartialEq, Eq, Send, Sync to as many or our types as possible.
- adds tokio::sync as a dependency to provide an async Mutex for Clone of Net and MockNet
## ƒs
- adds `Clone` to `DirItem`
- adds `Default`, `PartialEq` and `Eq` to `FileSystem`, `PathMarker`, `FileMarker` and `DirMarker`
- adds `Default` to `PathReal`
- adds `Clone`, `Debug`, `Default`, `PartialEq` and `Eq` to `Reader`
## net
- `MockNet::try_from` now returns a `Future`, so should be `await`ed
- adds `Debug` to `Plan`
- adds `Debug` and `Default` to `Net`
- adds `Debug`, `Clone` and `Default` to `MockNet`
- adds `Debug`, `Clone`, `PartialEq` and `Eq` to `MatchRequest`
- adds `Debug` and `Clone` to `WhenRequest`
WIP: mutation tests
2024-11-16 08:46:07 +00:00
|
|
|
let response = plans.lock().await.borrow_mut().remove(i).response;
|
2024-11-11 07:44:43 +00:00
|
|
|
Ok(response)
|
|
|
|
}
|
|
|
|
None => Err(Error::UnexpectedMockRequest(request)),
|
2024-11-10 17:11:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
feat: Add Debug, Clone, Default, PartialEq, Eq, Send, Sync to as many or our types as possible.
- adds tokio::sync as a dependency to provide an async Mutex for Clone of Net and MockNet
## ƒs
- adds `Clone` to `DirItem`
- adds `Default`, `PartialEq` and `Eq` to `FileSystem`, `PathMarker`, `FileMarker` and `DirMarker`
- adds `Default` to `PathReal`
- adds `Clone`, `Debug`, `Default`, `PartialEq` and `Eq` to `Reader`
## net
- `MockNet::try_from` now returns a `Future`, so should be `await`ed
- adds `Debug` to `Plan`
- adds `Debug` and `Default` to `Net`
- adds `Debug`, `Clone` and `Default` to `MockNet`
- adds `Debug`, `Clone`, `PartialEq` and `Eq` to `MatchRequest`
- adds `Debug` and `Clone` to `WhenRequest`
WIP: mutation tests
2024-11-16 08:46:07 +00:00
|
|
|
impl MockNet {
|
|
|
|
pub async fn try_from(net: Net) -> std::result::Result<Self, super::Error> {
|
2024-11-11 07:44:43 +00:00
|
|
|
match &net.plans {
|
|
|
|
Some(plans) => Ok(MockNet {
|
feat: Add Debug, Clone, Default, PartialEq, Eq, Send, Sync to as many or our types as possible.
- adds tokio::sync as a dependency to provide an async Mutex for Clone of Net and MockNet
## ƒs
- adds `Clone` to `DirItem`
- adds `Default`, `PartialEq` and `Eq` to `FileSystem`, `PathMarker`, `FileMarker` and `DirMarker`
- adds `Default` to `PathReal`
- adds `Clone`, `Debug`, `Default`, `PartialEq` and `Eq` to `Reader`
## net
- `MockNet::try_from` now returns a `Future`, so should be `await`ed
- adds `Debug` to `Plan`
- adds `Debug` and `Default` to `Net`
- adds `Debug`, `Clone` and `Default` to `MockNet`
- adds `Debug`, `Clone`, `PartialEq` and `Eq` to `MatchRequest`
- adds `Debug` and `Clone` to `WhenRequest`
WIP: mutation tests
2024-11-16 08:46:07 +00:00
|
|
|
plans: Rc::new(RefCell::new(plans.lock().await.take())),
|
2024-11-11 07:44:43 +00:00
|
|
|
}),
|
feat: Add Debug, Clone, Default, PartialEq, Eq, Send, Sync to as many or our types as possible.
- adds tokio::sync as a dependency to provide an async Mutex for Clone of Net and MockNet
## ƒs
- adds `Clone` to `DirItem`
- adds `Default`, `PartialEq` and `Eq` to `FileSystem`, `PathMarker`, `FileMarker` and `DirMarker`
- adds `Default` to `PathReal`
- adds `Clone`, `Debug`, `Default`, `PartialEq` and `Eq` to `Reader`
## net
- `MockNet::try_from` now returns a `Future`, so should be `await`ed
- adds `Debug` to `Plan`
- adds `Debug` and `Default` to `Net`
- adds `Debug`, `Clone` and `Default` to `MockNet`
- adds `Debug`, `Clone`, `PartialEq` and `Eq` to `MatchRequest`
- adds `Debug` and `Clone` to `WhenRequest`
WIP: mutation tests
2024-11-16 08:46:07 +00:00
|
|
|
None => Err(super::Error::NetIsNotAMock),
|
2024-11-10 09:44:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-11-09 12:12:11 +00:00
|
|
|
|
2024-11-09 21:07:43 +00:00
|
|
|
/// A struct for defining the expected requests and their responses that should be made
|
|
|
|
/// during a test.
|
|
|
|
///
|
|
|
|
/// When the [MockNet] goes out of scope it will verify that all expected requests were consumed,
|
|
|
|
/// otherwise it will `panic`.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// # use kxio::net::Result;
|
2024-11-17 11:24:28 +00:00
|
|
|
/// use kxio::net::StatusCode;
|
feat: Add Debug, Clone, Default, PartialEq, Eq, Send, Sync to as many or our types as possible.
- adds tokio::sync as a dependency to provide an async Mutex for Clone of Net and MockNet
## ƒs
- adds `Clone` to `DirItem`
- adds `Default`, `PartialEq` and `Eq` to `FileSystem`, `PathMarker`, `FileMarker` and `DirMarker`
- adds `Default` to `PathReal`
- adds `Clone`, `Debug`, `Default`, `PartialEq` and `Eq` to `Reader`
## net
- `MockNet::try_from` now returns a `Future`, so should be `await`ed
- adds `Debug` to `Plan`
- adds `Debug` and `Default` to `Net`
- adds `Debug`, `Clone` and `Default` to `MockNet`
- adds `Debug`, `Clone`, `PartialEq` and `Eq` to `MatchRequest`
- adds `Debug` and `Clone` to `WhenRequest`
WIP: mutation tests
2024-11-16 08:46:07 +00:00
|
|
|
/// # #[tokio::main]
|
|
|
|
/// # async fn run() -> Result<()> {
|
2024-11-09 21:07:43 +00:00
|
|
|
/// let mock_net = kxio::net::mock();
|
|
|
|
/// let client = mock_net.client();
|
|
|
|
/// // define an expected requet, and the response that should be returned
|
2024-11-17 11:24:28 +00:00
|
|
|
/// mock_net.on().get("https://hyper.rs")
|
|
|
|
/// .respond(StatusCode::OK).body("Ok");
|
2024-11-09 21:07:43 +00:00
|
|
|
/// let net: kxio::net::Net = mock_net.into();
|
|
|
|
/// // use 'net' in your program, by passing it as a reference
|
|
|
|
///
|
|
|
|
/// // In some rare cases you don't want to assert that all expected requests were made.
|
|
|
|
/// // You should recover the `MockNet` from the `Net` and `MockNet::reset` it.
|
feat: Add Debug, Clone, Default, PartialEq, Eq, Send, Sync to as many or our types as possible.
- adds tokio::sync as a dependency to provide an async Mutex for Clone of Net and MockNet
## ƒs
- adds `Clone` to `DirItem`
- adds `Default`, `PartialEq` and `Eq` to `FileSystem`, `PathMarker`, `FileMarker` and `DirMarker`
- adds `Default` to `PathReal`
- adds `Clone`, `Debug`, `Default`, `PartialEq` and `Eq` to `Reader`
## net
- `MockNet::try_from` now returns a `Future`, so should be `await`ed
- adds `Debug` to `Plan`
- adds `Debug` and `Default` to `Net`
- adds `Debug`, `Clone` and `Default` to `MockNet`
- adds `Debug`, `Clone`, `PartialEq` and `Eq` to `MatchRequest`
- adds `Debug` and `Clone` to `WhenRequest`
WIP: mutation tests
2024-11-16 08:46:07 +00:00
|
|
|
/// let mock_net = kxio::net::MockNet::try_from(net).await?;
|
2024-11-09 21:07:43 +00:00
|
|
|
/// mock_net.reset(); // only if explicitly needed
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
feat: Add Debug, Clone, Default, PartialEq, Eq, Send, Sync to as many or our types as possible.
- adds tokio::sync as a dependency to provide an async Mutex for Clone of Net and MockNet
## ƒs
- adds `Clone` to `DirItem`
- adds `Default`, `PartialEq` and `Eq` to `FileSystem`, `PathMarker`, `FileMarker` and `DirMarker`
- adds `Default` to `PathReal`
- adds `Clone`, `Debug`, `Default`, `PartialEq` and `Eq` to `Reader`
## net
- `MockNet::try_from` now returns a `Future`, so should be `await`ed
- adds `Debug` to `Plan`
- adds `Debug` and `Default` to `Net`
- adds `Debug`, `Clone` and `Default` to `MockNet`
- adds `Debug`, `Clone`, `PartialEq` and `Eq` to `MatchRequest`
- adds `Debug` and `Clone` to `WhenRequest`
WIP: mutation tests
2024-11-16 08:46:07 +00:00
|
|
|
#[derive(Debug, Clone, Default)]
|
2024-11-09 12:12:11 +00:00
|
|
|
pub struct MockNet {
|
feat: Add Debug, Clone, Default, PartialEq, Eq, Send, Sync to as many or our types as possible.
- adds tokio::sync as a dependency to provide an async Mutex for Clone of Net and MockNet
## ƒs
- adds `Clone` to `DirItem`
- adds `Default`, `PartialEq` and `Eq` to `FileSystem`, `PathMarker`, `FileMarker` and `DirMarker`
- adds `Default` to `PathReal`
- adds `Clone`, `Debug`, `Default`, `PartialEq` and `Eq` to `Reader`
## net
- `MockNet::try_from` now returns a `Future`, so should be `await`ed
- adds `Debug` to `Plan`
- adds `Debug` and `Default` to `Net`
- adds `Debug`, `Clone` and `Default` to `MockNet`
- adds `Debug`, `Clone`, `PartialEq` and `Eq` to `MatchRequest`
- adds `Debug` and `Clone` to `WhenRequest`
WIP: mutation tests
2024-11-16 08:46:07 +00:00
|
|
|
plans: Rc<RefCell<Plans>>,
|
2024-11-09 12:12:11 +00:00
|
|
|
}
|
2024-11-10 09:44:30 +00:00
|
|
|
impl MockNet {
|
2024-11-15 09:06:45 +00:00
|
|
|
/// Helper to create a default [Client].
|
2024-11-09 21:07:43 +00:00
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// let mock_net = kxio::net::mock();
|
|
|
|
/// let client = mock_net.client();
|
|
|
|
/// let request = client.get("https://hyper.rs");
|
|
|
|
/// ```
|
2024-11-10 09:44:30 +00:00
|
|
|
pub fn client(&self) -> Client {
|
|
|
|
Default::default()
|
|
|
|
}
|
2024-11-09 21:07:43 +00:00
|
|
|
|
|
|
|
/// Specify an expected request.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
2024-11-17 11:24:28 +00:00
|
|
|
/// use kxio::net::StatusCode;
|
2024-11-09 21:07:43 +00:00
|
|
|
/// # use kxio::net::Result;
|
|
|
|
/// # fn run() -> Result<()> {
|
|
|
|
/// let mock_net = kxio::net::mock();
|
|
|
|
/// let client = mock_net.client();
|
2024-11-17 11:24:28 +00:00
|
|
|
/// mock_net.on().get("https://hyper.rs")
|
|
|
|
/// .respond(StatusCode::OK).body("Ok");
|
2024-11-09 21:07:43 +00:00
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2024-11-17 11:24:28 +00:00
|
|
|
#[must_use]
|
|
|
|
pub fn on(&self) -> WhenRequest<WhenBuildRequest> {
|
2024-11-17 09:18:18 +00:00
|
|
|
WhenRequest::new(self)
|
2024-11-11 07:44:43 +00:00
|
|
|
}
|
|
|
|
|
2024-11-12 07:13:59 +00:00
|
|
|
fn _when(&self, plan: Plan) {
|
|
|
|
self.plans.borrow_mut().push(plan);
|
2024-11-10 09:44:30 +00:00
|
|
|
}
|
2024-11-09 21:07:43 +00:00
|
|
|
|
|
|
|
/// Clears all the expected requests and responses from the [MockNet].
|
|
|
|
///
|
|
|
|
/// When the [MockNet] goes out of scope it will assert that all expected requests and
|
|
|
|
/// responses were consumed. If there are any left unconsumed, then it will `panic`.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// # use kxio::net::Result;
|
feat: Add Debug, Clone, Default, PartialEq, Eq, Send, Sync to as many or our types as possible.
- adds tokio::sync as a dependency to provide an async Mutex for Clone of Net and MockNet
## ƒs
- adds `Clone` to `DirItem`
- adds `Default`, `PartialEq` and `Eq` to `FileSystem`, `PathMarker`, `FileMarker` and `DirMarker`
- adds `Default` to `PathReal`
- adds `Clone`, `Debug`, `Default`, `PartialEq` and `Eq` to `Reader`
## net
- `MockNet::try_from` now returns a `Future`, so should be `await`ed
- adds `Debug` to `Plan`
- adds `Debug` and `Default` to `Net`
- adds `Debug`, `Clone` and `Default` to `MockNet`
- adds `Debug`, `Clone`, `PartialEq` and `Eq` to `MatchRequest`
- adds `Debug` and `Clone` to `WhenRequest`
WIP: mutation tests
2024-11-16 08:46:07 +00:00
|
|
|
/// # #[tokio::main]
|
|
|
|
/// # async fn run() -> Result<()> {
|
2024-11-09 21:07:43 +00:00
|
|
|
/// # let mock_net = kxio::net::mock();
|
|
|
|
/// # let net: kxio::net::Net = mock_net.into();
|
feat: Add Debug, Clone, Default, PartialEq, Eq, Send, Sync to as many or our types as possible.
- adds tokio::sync as a dependency to provide an async Mutex for Clone of Net and MockNet
## ƒs
- adds `Clone` to `DirItem`
- adds `Default`, `PartialEq` and `Eq` to `FileSystem`, `PathMarker`, `FileMarker` and `DirMarker`
- adds `Default` to `PathReal`
- adds `Clone`, `Debug`, `Default`, `PartialEq` and `Eq` to `Reader`
## net
- `MockNet::try_from` now returns a `Future`, so should be `await`ed
- adds `Debug` to `Plan`
- adds `Debug` and `Default` to `Net`
- adds `Debug`, `Clone` and `Default` to `MockNet`
- adds `Debug`, `Clone`, `PartialEq` and `Eq` to `MatchRequest`
- adds `Debug` and `Clone` to `WhenRequest`
WIP: mutation tests
2024-11-16 08:46:07 +00:00
|
|
|
/// let mock_net = kxio::net::MockNet::try_from(net).await?;
|
2024-11-09 21:07:43 +00:00
|
|
|
/// mock_net.reset(); // only if explicitly needed
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2024-11-11 07:44:43 +00:00
|
|
|
pub fn reset(&self) {
|
|
|
|
self.plans.take();
|
2024-11-09 12:12:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
impl From<MockNet> for Net {
|
|
|
|
fn from(mock_net: MockNet) -> Self {
|
|
|
|
Self {
|
2024-11-10 09:44:30 +00:00
|
|
|
// keep the original `inner` around to allow it's Drop impelmentation to run when we go
|
|
|
|
// out of scope at the end of the test
|
feat: Add Debug, Clone, Default, PartialEq, Eq, Send, Sync to as many or our types as possible.
- adds tokio::sync as a dependency to provide an async Mutex for Clone of Net and MockNet
## ƒs
- adds `Clone` to `DirItem`
- adds `Default`, `PartialEq` and `Eq` to `FileSystem`, `PathMarker`, `FileMarker` and `DirMarker`
- adds `Default` to `PathReal`
- adds `Clone`, `Debug`, `Default`, `PartialEq` and `Eq` to `Reader`
## net
- `MockNet::try_from` now returns a `Future`, so should be `await`ed
- adds `Debug` to `Plan`
- adds `Debug` and `Default` to `Net`
- adds `Debug`, `Clone` and `Default` to `MockNet`
- adds `Debug`, `Clone`, `PartialEq` and `Eq` to `MatchRequest`
- adds `Debug` and `Clone` to `WhenRequest`
WIP: mutation tests
2024-11-16 08:46:07 +00:00
|
|
|
plans: Some(Arc::new(Mutex::new(RefCell::new(mock_net.plans.take())))),
|
2024-11-09 12:12:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-11 07:44:43 +00:00
|
|
|
impl Drop for MockNet {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
assert!(self.plans.borrow().is_empty())
|
2024-11-04 10:22:31 +00:00
|
|
|
}
|
|
|
|
}
|
2024-11-11 07:44:43 +00:00
|
|
|
impl Drop for Net {
|
2024-11-04 10:22:31 +00:00
|
|
|
fn drop(&mut self) {
|
2024-11-11 07:44:43 +00:00
|
|
|
if let Some(plans) = &self.plans {
|
feat: Add Debug, Clone, Default, PartialEq, Eq, Send, Sync to as many or our types as possible.
- adds tokio::sync as a dependency to provide an async Mutex for Clone of Net and MockNet
## ƒs
- adds `Clone` to `DirItem`
- adds `Default`, `PartialEq` and `Eq` to `FileSystem`, `PathMarker`, `FileMarker` and `DirMarker`
- adds `Default` to `PathReal`
- adds `Clone`, `Debug`, `Default`, `PartialEq` and `Eq` to `Reader`
## net
- `MockNet::try_from` now returns a `Future`, so should be `await`ed
- adds `Debug` to `Plan`
- adds `Debug` and `Default` to `Net`
- adds `Debug`, `Clone` and `Default` to `MockNet`
- adds `Debug`, `Clone`, `PartialEq` and `Eq` to `MatchRequest`
- adds `Debug` and `Clone` to `WhenRequest`
WIP: mutation tests
2024-11-16 08:46:07 +00:00
|
|
|
assert!(plans.try_lock().expect("lock plans").take().is_empty())
|
2024-11-11 07:44:43 +00:00
|
|
|
}
|
2024-11-04 10:22:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
feat: Add Debug, Clone, Default, PartialEq, Eq, Send, Sync to as many or our types as possible.
- adds tokio::sync as a dependency to provide an async Mutex for Clone of Net and MockNet
## ƒs
- adds `Clone` to `DirItem`
- adds `Default`, `PartialEq` and `Eq` to `FileSystem`, `PathMarker`, `FileMarker` and `DirMarker`
- adds `Default` to `PathReal`
- adds `Clone`, `Debug`, `Default`, `PartialEq` and `Eq` to `Reader`
## net
- `MockNet::try_from` now returns a `Future`, so should be `await`ed
- adds `Debug` to `Plan`
- adds `Debug` and `Default` to `Net`
- adds `Debug`, `Clone` and `Default` to `MockNet`
- adds `Debug`, `Clone`, `PartialEq` and `Eq` to `MatchRequest`
- adds `Debug` and `Clone` to `WhenRequest`
WIP: mutation tests
2024-11-16 08:46:07 +00:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
2024-11-12 07:13:59 +00:00
|
|
|
pub enum MatchRequest {
|
2024-11-15 09:06:45 +00:00
|
|
|
Method(Method),
|
|
|
|
Url(Url),
|
2024-11-12 07:13:59 +00:00
|
|
|
Header { name: String, value: String },
|
2024-11-17 11:24:28 +00:00
|
|
|
Body(bytes::Bytes),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
pub enum RespondWith {
|
|
|
|
Status(StatusCode),
|
|
|
|
Header { name: String, value: String },
|
|
|
|
Body(bytes::Bytes),
|
2024-11-04 10:22:31 +00:00
|
|
|
}
|
2024-11-09 21:07:43 +00:00
|
|
|
|
2024-11-17 09:18:18 +00:00
|
|
|
#[derive(Clone, Debug, Display, From)]
|
2024-11-17 11:24:28 +00:00
|
|
|
pub enum MockError {
|
|
|
|
#[display("url parse: {}", 0)]
|
2024-11-17 09:18:18 +00:00
|
|
|
UrlParse(#[from] url::ParseError),
|
|
|
|
}
|
|
|
|
impl std::error::Error for MockError {}
|
|
|
|
|
2024-11-17 11:24:28 +00:00
|
|
|
pub trait WhenState {}
|
|
|
|
|
|
|
|
pub struct WhenBuildRequest;
|
|
|
|
impl WhenState for WhenBuildRequest {}
|
|
|
|
|
|
|
|
pub struct WhenBuildResponse;
|
|
|
|
impl WhenState for WhenBuildResponse {}
|
|
|
|
|
feat: Add Debug, Clone, Default, PartialEq, Eq, Send, Sync to as many or our types as possible.
- adds tokio::sync as a dependency to provide an async Mutex for Clone of Net and MockNet
## ƒs
- adds `Clone` to `DirItem`
- adds `Default`, `PartialEq` and `Eq` to `FileSystem`, `PathMarker`, `FileMarker` and `DirMarker`
- adds `Default` to `PathReal`
- adds `Clone`, `Debug`, `Default`, `PartialEq` and `Eq` to `Reader`
## net
- `MockNet::try_from` now returns a `Future`, so should be `await`ed
- adds `Debug` to `Plan`
- adds `Debug` and `Default` to `Net`
- adds `Debug`, `Clone` and `Default` to `MockNet`
- adds `Debug`, `Clone`, `PartialEq` and `Eq` to `MatchRequest`
- adds `Debug` and `Clone` to `WhenRequest`
WIP: mutation tests
2024-11-16 08:46:07 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2024-11-17 11:24:28 +00:00
|
|
|
pub struct WhenRequest<'net, State>
|
|
|
|
where
|
|
|
|
State: WhenState,
|
|
|
|
{
|
|
|
|
_state: PhantomData<State>,
|
2024-11-12 07:13:59 +00:00
|
|
|
net: &'net MockNet,
|
|
|
|
match_on: Vec<MatchRequest>,
|
2024-11-17 11:24:28 +00:00
|
|
|
respond_with: Vec<RespondWith>,
|
|
|
|
error: Option<MockError>,
|
2024-11-12 07:13:59 +00:00
|
|
|
}
|
2024-11-09 21:07:43 +00:00
|
|
|
|
2024-11-17 11:24:28 +00:00
|
|
|
impl<'net> WhenRequest<'net, WhenBuildRequest> {
|
|
|
|
fn new(net: &'net MockNet) -> Self {
|
|
|
|
Self {
|
|
|
|
_state: PhantomData,
|
|
|
|
net,
|
|
|
|
match_on: vec![],
|
|
|
|
respond_with: vec![],
|
|
|
|
error: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[must_use]
|
2024-11-17 09:18:18 +00:00
|
|
|
pub fn get(self, url: impl Into<String>) -> Self {
|
|
|
|
self._url(Method::GET, url)
|
|
|
|
}
|
2024-11-17 11:24:28 +00:00
|
|
|
|
|
|
|
#[must_use]
|
2024-11-17 09:18:18 +00:00
|
|
|
pub fn post(self, url: impl Into<String>) -> Self {
|
|
|
|
self._url(Method::POST, url)
|
|
|
|
}
|
2024-11-17 11:24:28 +00:00
|
|
|
|
|
|
|
#[must_use]
|
2024-11-17 09:18:18 +00:00
|
|
|
pub fn put(self, url: impl Into<String>) -> Self {
|
|
|
|
self._url(Method::PUT, url)
|
|
|
|
}
|
2024-11-17 11:24:28 +00:00
|
|
|
|
|
|
|
#[must_use]
|
2024-11-17 09:18:18 +00:00
|
|
|
pub fn delete(self, url: impl Into<String>) -> Self {
|
|
|
|
self._url(Method::DELETE, url)
|
|
|
|
}
|
2024-11-17 11:24:28 +00:00
|
|
|
|
|
|
|
#[must_use]
|
2024-11-17 09:18:18 +00:00
|
|
|
pub fn head(self, url: impl Into<String>) -> Self {
|
|
|
|
self._url(Method::HEAD, url)
|
|
|
|
}
|
2024-11-17 11:24:28 +00:00
|
|
|
|
|
|
|
#[must_use]
|
2024-11-17 09:18:18 +00:00
|
|
|
pub fn patch(self, url: impl Into<String>) -> Self {
|
|
|
|
self._url(Method::PATCH, url)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn _url(mut self, method: http::Method, url: impl Into<String>) -> Self {
|
|
|
|
self.match_on.push(MatchRequest::Method(method));
|
|
|
|
match Url::parse(&url.into()) {
|
|
|
|
Ok(url) => {
|
|
|
|
self.match_on.push(MatchRequest::Url(url));
|
|
|
|
}
|
2024-11-17 11:24:28 +00:00
|
|
|
Err(err) => {
|
|
|
|
self.error.replace(err.into());
|
|
|
|
}
|
2024-11-17 09:18:18 +00:00
|
|
|
}
|
2024-11-12 07:13:59 +00:00
|
|
|
self
|
|
|
|
}
|
2024-11-17 11:24:28 +00:00
|
|
|
|
|
|
|
#[must_use]
|
2024-11-12 07:13:59 +00:00
|
|
|
pub fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
|
|
|
|
self.match_on.push(MatchRequest::Header {
|
|
|
|
name: name.into(),
|
|
|
|
value: value.into(),
|
|
|
|
});
|
|
|
|
self
|
|
|
|
}
|
2024-11-17 11:24:28 +00:00
|
|
|
|
|
|
|
#[must_use]
|
|
|
|
pub fn body(mut self, body: impl Into<bytes::Bytes>) -> Self {
|
2024-11-12 07:13:59 +00:00
|
|
|
self.match_on.push(MatchRequest::Body(body.into()));
|
|
|
|
self
|
|
|
|
}
|
2024-11-17 11:24:28 +00:00
|
|
|
|
|
|
|
#[must_use]
|
|
|
|
pub fn respond(self, status: StatusCode) -> WhenRequest<'net, WhenBuildResponse> {
|
|
|
|
WhenRequest::<WhenBuildResponse> {
|
|
|
|
_state: PhantomData,
|
|
|
|
net: self.net,
|
|
|
|
match_on: self.match_on,
|
|
|
|
respond_with: vec![RespondWith::Status(status)],
|
|
|
|
error: self.error,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl<'net> WhenRequest<'net, WhenBuildResponse> {
|
|
|
|
#[must_use]
|
|
|
|
pub fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
|
|
|
|
let name = name.into();
|
|
|
|
let value = value.into();
|
|
|
|
self.respond_with.push(RespondWith::Header { name, value });
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
#[must_use]
|
|
|
|
pub fn headers(mut self, headers: impl Into<HashMap<String, String>>) -> Self {
|
|
|
|
let h: HashMap<String, String> = headers.into();
|
|
|
|
for (name, value) in h.into_iter() {
|
|
|
|
self.respond_with.push(RespondWith::Header { name, value });
|
|
|
|
}
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn body(mut self, body: impl Into<bytes::Bytes>) {
|
|
|
|
self.respond_with.push(RespondWith::Body(body.into()));
|
|
|
|
self.mock().expect("valid mock");
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn mock(self) -> Result<()> {
|
|
|
|
if let Some(error) = self.error {
|
|
|
|
return Err(crate::net::Error::InvalidMock(error));
|
|
|
|
}
|
|
|
|
let mut builder = http::response::Builder::default();
|
|
|
|
let mut response_body = None;
|
|
|
|
for part in self.respond_with {
|
|
|
|
builder = match part {
|
|
|
|
RespondWith::Status(status) => builder.status(status),
|
|
|
|
RespondWith::Header { name, value } => builder.header(name, value),
|
|
|
|
RespondWith::Body(body) => {
|
|
|
|
response_body.replace(body);
|
|
|
|
builder
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let Some(body) = response_body else {
|
|
|
|
return Err(crate::net::Error::MockResponseHasNoBody);
|
|
|
|
};
|
|
|
|
let response = builder.body(body)?;
|
2024-11-12 07:13:59 +00:00
|
|
|
self.net._when(Plan {
|
|
|
|
match_request: self.match_on,
|
|
|
|
response: response.into(),
|
|
|
|
});
|
2024-11-17 11:24:28 +00:00
|
|
|
Ok(())
|
2024-11-04 10:22:31 +00:00
|
|
|
}
|
|
|
|
}
|
feat: Add Debug, Clone, Default, PartialEq, Eq, Send, Sync to as many or our types as possible.
- adds tokio::sync as a dependency to provide an async Mutex for Clone of Net and MockNet
## ƒs
- adds `Clone` to `DirItem`
- adds `Default`, `PartialEq` and `Eq` to `FileSystem`, `PathMarker`, `FileMarker` and `DirMarker`
- adds `Default` to `PathReal`
- adds `Clone`, `Debug`, `Default`, `PartialEq` and `Eq` to `Reader`
## net
- `MockNet::try_from` now returns a `Future`, so should be `await`ed
- adds `Debug` to `Plan`
- adds `Debug` and `Default` to `Net`
- adds `Debug`, `Clone` and `Default` to `MockNet`
- adds `Debug`, `Clone`, `PartialEq` and `Eq` to `MatchRequest`
- adds `Debug` and `Clone` to `WhenRequest`
WIP: mutation tests
2024-11-16 08:46:07 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
fn is_normal<T: Sized + Send + Sync + Unpin>() {}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn normal_types() {
|
|
|
|
is_normal::<Net>();
|
|
|
|
// is_normal::<MockNet>(); // only used in test setup - no need to be Send or Sync
|
|
|
|
is_normal::<MatchRequest>();
|
|
|
|
is_normal::<Plan>();
|
|
|
|
// is_normal::<WhenRequest>(); // only used in test setup - no need to be Send or Sync
|
|
|
|
}
|
|
|
|
}
|