diff --git a/src/net/mod.rs b/src/net/mod.rs index b98df06..89691a5 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -7,15 +7,14 @@ mod system; pub use result::{Error, Result}; -pub use system::{MatchOn, Net}; -use system::{Mocked, Unmocked}; +pub use system::{MatchOn, MockNet, Net}; /// Creates a new `Net`. -pub const fn new() -> Net { - Net::::new() +pub const fn new() -> Net { + Net::new() } /// Creates a new `MockNet` for use in tests. -pub fn mock() -> Net { - Net::::new() +pub fn mock() -> MockNet { + Net::mock() } diff --git a/src/net/result.rs b/src/net/result.rs index 17a4de0..0397dba 100644 --- a/src/net/result.rs +++ b/src/net/result.rs @@ -8,14 +8,14 @@ pub enum Error { Request(String), #[display("Unexpected request: {0}", 0.to_string())] UnexpectedMockRequest(reqwest::Request), - RwLockLocked + RwLockLocked, } impl std::error::Error for Error {} impl Clone for Error { fn clone(&self) -> Self { match self { Self::Reqwest(req) => Self::Request(req.to_string()), - err => err.clone() + err => err.clone(), } } } diff --git a/src/net/system.rs b/src/net/system.rs index fa6369d..e6efeee 100644 --- a/src/net/system.rs +++ b/src/net/system.rs @@ -1,12 +1,15 @@ // -use std::{marker::PhantomData, sync::RwLock}; +use std::{marker::PhantomData, ops::Deref, sync::RwLock}; use super::{Error, Result}; pub trait NetType {} +#[derive(Debug)] pub struct Mocked; impl NetType for Mocked {} + +#[derive(Debug)] pub struct Unmocked; impl NetType for Unmocked {} @@ -21,19 +24,73 @@ pub enum MatchOn { } #[derive(Debug)] -pub struct Plan { +struct Plan { request: reqwest::Request, response: reqwest::Response, match_on: Vec, } #[derive(Debug)] -pub struct Net { +pub struct Net { + inner: InnerNet, + mock: Option>, +} +impl Net { + // constructors + pub(super) const fn new() -> Self { + Self { + inner: InnerNet::::new(), + mock: None, + } + } + + pub(super) const fn mock() -> MockNet { + MockNet { + inner: InnerNet::::new(), + } + } +} +impl Net { + // public interface + pub fn client(&self) -> reqwest::Client { + self.inner.client() + } + + pub async fn send(&self, request: reqwest::RequestBuilder) -> Result { + match &self.mock { + Some(mock) => mock.send(request).await, + None => self.inner.send(request).await, + } + } +} + +#[derive(Debug)] +pub struct MockNet { + inner: InnerNet, +} +impl Deref for MockNet { + type Target = InnerNet; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} +impl From for Net { + fn from(mock_net: MockNet) -> Self { + Self { + inner: InnerNet::::new(), + mock: Some(mock_net.inner), + } + } +} + +#[derive(Debug)] +pub struct InnerNet { _type: PhantomData, plans: RwLock, } -impl Net { - pub(crate) const fn new() -> Self { +impl InnerNet { + const fn new() -> Self { Self { _type: PhantomData, plans: RwLock::new(vec![]), @@ -45,13 +102,13 @@ impl Net { } } -impl Net { +impl InnerNet { pub fn client(&self) -> reqwest::Client { Default::default() } } -impl Net { - pub(crate) const fn new() -> Self { +impl InnerNet { + const fn new() -> Self { Self { _type: PhantomData, plans: RwLock::new(vec![]), @@ -135,15 +192,17 @@ impl Net { Ok(()) } } -impl Drop for Net { +impl Drop for InnerNet { fn drop(&mut self) { - let Ok(read_plans) = self.plans.read() else { return }; + let Ok(read_plans) = self.plans.read() else { + return; + }; assert!(read_plans.is_empty()) } } pub struct OnRequest<'net> { - net: &'net Net, + net: &'net InnerNet, request: reqwest::Request, match_on: Vec, } diff --git a/tests/net.rs b/tests/net.rs index a7eff70..248dc56 100644 --- a/tests/net.rs +++ b/tests/net.rs @@ -1,6 +1,6 @@ use assert2::let_assert; // -use kxio::net::{Error, MatchOn}; +use kxio::net::{Error, MatchOn, Net}; #[tokio::test] async fn test_get_url() { @@ -16,10 +16,15 @@ async fn test_get_url() { .body("Get OK") .expect("request body"); - net.on(request).respond(my_response.into()).expect("on request, respond"); + net.on(request) + .respond(my_response.into()) + .expect("on request, respond"); //when - let response = net.send(client.get(url)).await.expect("response"); + let response = Net::from(net) + .send(client.get(url)) + .await + .expect("response"); //then assert_eq!(response.status(), http::StatusCode::OK); @@ -40,7 +45,9 @@ async fn test_get_wrong_url() { .body("Get OK") .expect("request body"); - net.on(request).respond(my_response.into()).expect("on request, respond"); + net.on(request) + .respond(my_response.into()) + .expect("on request, respond"); //when let_assert!( @@ -69,10 +76,15 @@ async fn test_post_url() { .body("Post OK") .expect("request body"); - net.on(request).respond(my_response.into()).expect("on request, respond"); + net.on(request) + .respond(my_response.into()) + .expect("on request, respond"); //when - let response = net.send(client.post(url)).await.expect("reponse"); + let response = Net::from(net) + .send(client.post(url)) + .await + .expect("reponse"); //then assert_eq!(response.status(), http::StatusCode::OK); @@ -98,11 +110,12 @@ async fn test_post_by_method() { MatchOn::Method, // MatchOn::Url ]) - .respond(my_response.into()).expect("on request, respond"); + .respond(my_response.into()) + .expect("on request, respond"); //when // This request is a different url - but should still match - let response = net + let response = Net::from(net) .send(client.post("https://some.other.url")) .await .expect("response"); @@ -131,11 +144,15 @@ async fn test_post_by_url() { // MatchOn::Method, MatchOn::Url, ]) - .respond(my_response.into()).expect("on request, respond"); + .respond(my_response.into()) + .expect("on request, respond"); //when // This request is a GET, not POST - but should still match - let response = net.send(client.get(url)).await.expect("response"); + let response = Net::from(net) + .send(client.get(url)) + .await + .expect("response"); //then assert_eq!(response.status(), http::StatusCode::OK); @@ -166,11 +183,12 @@ async fn test_post_by_body() { // MatchOn::Url MatchOn::Body, ]) - .respond(my_response.into()).expect("on request, respond"); + .respond(my_response.into()) + .expect("on request, respond"); //when // This request is a GET, not POST - but should still match - let response = net + let response = Net::from(net) .send(client.get("https://some.other.url").body("match on body")) .await .expect("response"); @@ -208,11 +226,12 @@ async fn test_post_by_headers() { // MatchOn::Url MatchOn::Headers, ]) - .respond(my_response.into()).expect("on request, respond"); + .respond(my_response.into()) + .expect("on request, respond"); //when // This request is a GET, not POST - but should still match - let response = net + let response = Net::from(net) .send( client .get("https://some.other.url") @@ -245,11 +264,13 @@ async fn test_unused_post() { .body("Post OK") .expect("request body"); - net.on(request).respond(my_response.into()).expect("on request, respond"); + net.on(request) + .respond(my_response.into()) + .expect("on request, respond"); //when // don't send the planned request - // let _response = net.send(client.post(url)).await.expect("send"); + // let _response = Net::from(net).send(client.post(url)).await.expect("send"); //then // Drop implementation for net should panic