From 7da221bfde9cda22b523d223bf633a0ede709614 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Sun, 17 Nov 2024 09:18:18 +0000 Subject: [PATCH] feat(net)!: new api: .on().{get,post, etc}(url), replacing .on(method).get(url) The `http::Method` parameter is not needed as we now use named methods that the methods on the underlying `reqwest` client. The `url` parameter can be a `String` or `&str` rather than a parsed, and error handled `url::Url`. --- examples/get.rs | 7 +- src/net/mod.rs | 11 ++- src/net/system.rs | 60 ++++++++++++---- tests/net.rs | 172 +++++++++++++++++++++++++++++++++------------- 4 files changed, 175 insertions(+), 75 deletions(-) diff --git a/examples/get.rs b/examples/get.rs index 30b075c..ca8e878 100644 --- a/examples/get.rs +++ b/examples/get.rs @@ -104,8 +104,6 @@ fn delete_file(file_path: &Path, fs: &kxio::fs::FileSystem) -> kxio::Result<()> mod tests { use super::*; - use kxio::net::{Method, Url}; - // This test demonstrates how to use the `kxio` to test your program. #[tokio::test] async fn should_save_remote_body() { @@ -119,10 +117,7 @@ mod tests { // declare what response should be made for a given request let response = mock_net.response().body("contents").expect("response body"); - mock_net - .on(Method::GET) - .url(Url::parse(url).expect("parse url")) - .respond(response); + mock_net.on().get(url).respond(response); // Create a temporary directory that will be deleted with `fs` goes out of scope let fs = kxio::fs::temp().expect("temp fs"); diff --git a/src/net/mod.rs b/src/net/mod.rs index 014b747..6f5768e 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -79,16 +79,14 @@ //! //! ```rust //! use kxio::net; -//! use kxio::net::{Method, Url}; -//! //! # #[tokio::main] //! # async fn main() -> net::Result<()> { //! # let mock_net = net::mock(); -//! mock_net.on(Method::GET) -//! .url(Url::parse("https://example.com")?) +//! mock_net.on() +//! .get("https://example.com") //! .respond(mock_net.response().status(200).body("")?); -//! mock_net.on(Method::GET) -//! .url(Url::parse("https://example.com/foo")?) +//! mock_net.on() +//! .get("https://example.com/foo") //! .respond(mock_net.response().status(500).body("Mocked response")?); //! # mock_net.reset(); //! # Ok(()) @@ -157,7 +155,6 @@ pub use reqwest::Client; pub use reqwest::Request; pub use reqwest::RequestBuilder; pub use reqwest::Response; -pub use url::Url; /// Creates a new `Net`. pub const fn new() -> Net { diff --git a/src/net/system.rs b/src/net/system.rs index 5559307..d5c742d 100644 --- a/src/net/system.rs +++ b/src/net/system.rs @@ -2,10 +2,13 @@ use std::{cell::RefCell, ops::Deref, rc::Rc, sync::Arc}; +use derive_more::derive::{Display, From}; +use http::Method; use reqwest::{Body, Client}; use tokio::sync::Mutex; +use url::Url; -use crate::net::{Method, Request, RequestBuilder, Response, Url}; +use crate::net::{Request, RequestBuilder, Response}; use super::{Error, Result}; @@ -129,15 +132,14 @@ impl MockNet { /// # Example /// /// ```rust -/// use kxio::net::{Method, Url}; /// # use kxio::net::Result; /// # #[tokio::main] /// # async fn run() -> Result<()> { /// let mock_net = kxio::net::mock(); /// let client = mock_net.client(); /// // define an expected requet, and the response that should be returned -/// mock_net.on(Method::GET) -/// .url(Url::parse("https://hyper.rs")?) +/// mock_net.on() +/// .get("https://hyper.rs") /// .respond(mock_net.response().status(200).body("Ok")?); /// let net: kxio::net::Net = mock_net.into(); /// // use 'net' in your program, by passing it as a reference @@ -172,19 +174,18 @@ impl MockNet { /// # Example /// /// ```rust - /// use kxio::net::{Method, Url}; /// # use kxio::net::Result; /// # fn run() -> Result<()> { /// let mock_net = kxio::net::mock(); /// let client = mock_net.client(); - /// mock_net.on(Method::GET) - /// .url(Url::parse("https://hyper.rs")?) + /// mock_net.on() + /// .get("https://hyper.rs") /// .respond(mock_net.response().status(200).body("Ok")?); /// # Ok(()) /// # } /// ``` - pub fn on(&self, method: impl Into) -> WhenRequest { - WhenRequest::new(self, method) + pub fn on(&self) -> WhenRequest { + WhenRequest::new(self) } fn _when(&self, plan: Plan) { @@ -249,15 +250,47 @@ pub enum MatchRequest { Body(String), } +#[derive(Clone, Debug, Display, From)] +enum MockError { + UrlParse(#[from] url::ParseError), +} +impl std::error::Error for MockError {} + #[derive(Debug, Clone)] pub struct WhenRequest<'net> { net: &'net MockNet, match_on: Vec, + errors: Vec, } impl<'net> WhenRequest<'net> { - pub fn url(mut self, url: impl Into) -> Self { - self.match_on.push(MatchRequest::Url(url.into())); + pub fn get(self, url: impl Into) -> Self { + self._url(Method::GET, url) + } + pub fn post(self, url: impl Into) -> Self { + self._url(Method::POST, url) + } + pub fn put(self, url: impl Into) -> Self { + self._url(Method::PUT, url) + } + pub fn delete(self, url: impl Into) -> Self { + self._url(Method::DELETE, url) + } + pub fn head(self, url: impl Into) -> Self { + self._url(Method::HEAD, url) + } + pub fn patch(self, url: impl Into) -> Self { + self._url(Method::PATCH, url) + } + + fn _url(mut self, method: http::Method, url: impl Into) -> Self { + self.match_on.push(MatchRequest::Method(method)); + match Url::parse(&url.into()) { + Ok(url) => { + self.match_on.push(MatchRequest::Url(url)); + } + Err(err) => self.errors.push(err.into()), + } self } pub fn header(mut self, name: impl Into, value: impl Into) -> Self { @@ -281,10 +314,11 @@ impl<'net> WhenRequest<'net> { }); } - fn new(net: &'net MockNet, method: impl Into) -> Self { + fn new(net: &'net MockNet) -> Self { Self { net, - match_on: vec![MatchRequest::Method(method.into())], + match_on: vec![], + errors: vec![], } } } diff --git a/tests/net.rs b/tests/net.rs index ac41ad4..26ffe0d 100644 --- a/tests/net.rs +++ b/tests/net.rs @@ -1,5 +1,5 @@ // -use kxio::net::{Error, Method, MockNet, Net, Url}; +use kxio::net::{Error, MockNet, Net}; use assert2::let_assert; @@ -17,8 +17,8 @@ async fn test_get_url() { .expect("body"); mock_net - .on(Method::GET) - .url(Url::parse(url).expect("parse url")) + .on() + .get("https://www.example.com") .respond(my_response); //when @@ -32,6 +32,118 @@ async fn test_get_url() { assert_eq!(response.bytes().await.expect("response body"), "Get OK"); } +#[tokio::test] +async fn test_post_url() { + //given + let net = kxio::net::mock(); + let client = net.client(); + + let url = "https://www.example.com"; + + net.on() + .post(url) + .respond(net.response().status(200).body("post OK").expect("body")); + + //when + let response = Net::from(net) + .send(client.post(url)) + .await + .expect("reponse"); + + //then + assert_eq!(response.status(), http::StatusCode::OK); + assert_eq!(response.bytes().await.expect("response body"), "post OK"); +} + +#[tokio::test] +async fn test_put_url() { + //given + let net = kxio::net::mock(); + let client = net.client(); + + let url = "https://www.example.com"; + + net.on() + .put(url) + .respond(net.response().status(200).body("put OK").expect("body")); + + //when + let response = Net::from(net).send(client.put(url)).await.expect("reponse"); + + //then + assert_eq!(response.status(), http::StatusCode::OK); + assert_eq!(response.bytes().await.expect("response body"), "put OK"); +} + +#[tokio::test] +async fn test_delete_url() { + //given + let net = kxio::net::mock(); + let client = net.client(); + + let url = "https://www.example.com"; + + net.on() + .delete(url) + .respond(net.response().status(200).body("delete OK").expect("body")); + + //when + let response = Net::from(net) + .send(client.delete(url)) + .await + .expect("reponse"); + + //then + assert_eq!(response.status(), http::StatusCode::OK); + assert_eq!(response.bytes().await.expect("response body"), "delete OK"); +} + +#[tokio::test] +async fn test_head_url() { + //given + let net = kxio::net::mock(); + let client = net.client(); + + let url = "https://www.example.com"; + + net.on() + .head(url) + .respond(net.response().status(200).body("head OK").expect("body")); + + //when + let response = Net::from(net) + .send(client.head(url)) + .await + .expect("reponse"); + + //then + assert_eq!(response.status(), http::StatusCode::OK); + assert_eq!(response.bytes().await.expect("response body"), "head OK"); +} + +#[tokio::test] +async fn test_patch_url() { + //given + let net = kxio::net::mock(); + let client = net.client(); + + let url = "https://www.example.com"; + + net.on() + .patch(url) + .respond(net.response().status(200).body("patch OK").expect("body")); + + //when + let response = Net::from(net) + .send(client.patch(url)) + .await + .expect("reponse"); + + //then + assert_eq!(response.status(), http::StatusCode::OK); + assert_eq!(response.bytes().await.expect("response body"), "patch OK"); +} + #[tokio::test] async fn test_get_wrong_url() { //given @@ -45,10 +157,7 @@ async fn test_get_wrong_url() { .body("Get OK") .expect("body"); - mock_net - .on(Method::GET) - .url(Url::parse(url).expect("parse url")) - .respond(my_response); + mock_net.on().get(url).respond(my_response); let net = Net::from(mock_net); @@ -66,30 +175,6 @@ async fn test_get_wrong_url() { mock_net.reset(); } -#[tokio::test] -async fn test_post_url() { - //given - let net = kxio::net::mock(); - let client = net.client(); - - let url = "https://www.example.com"; - let my_response = net.response().status(200).body("Post OK").expect("body"); - - net.on(Method::POST) - .url(Url::parse(url).expect("parse url")) - .respond(my_response); - - //when - let response = Net::from(net) - .send(client.post(url)) - .await - .expect("reponse"); - - //then - assert_eq!(response.status(), http::StatusCode::OK); - assert_eq!(response.bytes().await.expect("response body"), "Post OK"); -} - #[tokio::test] async fn test_post_by_method() { //given @@ -98,8 +183,8 @@ async fn test_post_by_method() { let my_response = net.response().status(200).body("").expect("response body"); - net.on(Method::POST) - // NOTE: No URL specified - so shou∂ match any URL + net.on() + // NOTE: No URL specified - so should match any URL .respond(my_response); //when @@ -125,7 +210,7 @@ async fn test_post_by_body() { .body("response body") .expect("body"); - net.on(Method::POST) + net.on() // No URL - so any POST with a matching body .body("match on body") .respond(my_response); @@ -156,9 +241,7 @@ async fn test_post_by_header() { .body("response body") .expect("body"); - net.on(Method::POST) - .header("test", "match") - .respond(my_response); + net.on().header("test", "match").respond(my_response); //when let response = Net::from(net) @@ -191,10 +274,7 @@ async fn test_post_by_header_wrong_value() { .body("response body") .expect("body"); - mock_net - .on(Method::POST) - .header("test", "match") - .respond(my_response); + mock_net.on().header("test", "match").respond(my_response); let net = Net::from(mock_net); //when @@ -226,10 +306,7 @@ async fn test_unused_post_as_net() { .body("Post OK") .expect("body"); - mock_net - .on(Method::POST) - .url(Url::parse(url).expect("prase url")) - .respond(my_response); + mock_net.on().post(url).respond(my_response); let _net = Net::from(mock_net); @@ -254,10 +331,7 @@ async fn test_unused_post_as_mocknet() { .body("Post OK") .expect("body"); - mock_net - .on(Method::POST) - .url(Url::parse(url).expect("parse url")) - .respond(my_response); + mock_net.on().post(url).respond(my_response); //when // don't send the planned request