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`.
This commit is contained in:
Paul Campbell 2024-11-17 09:18:18 +00:00
parent 782307a856
commit f759884517
4 changed files with 175 additions and 76 deletions

View file

@ -104,8 +104,6 @@ fn delete_file(file_path: &Path, fs: &kxio::fs::FileSystem) -> kxio::Result<()>
mod tests { mod tests {
use super::*; use super::*;
use kxio::net::{Method, Url};
// This test demonstrates how to use the `kxio` to test your program. // This test demonstrates how to use the `kxio` to test your program.
#[tokio::test] #[tokio::test]
async fn should_save_remote_body() { async fn should_save_remote_body() {
@ -119,10 +117,7 @@ mod tests {
// declare what response should be made for a given request // declare what response should be made for a given request
let response = mock_net.response().body("contents").expect("response body"); let response = mock_net.response().body("contents").expect("response body");
mock_net mock_net.on().get(url).respond(response);
.on(Method::GET)
.url(Url::parse(url).expect("parse url"))
.respond(response);
// Create a temporary directory that will be deleted with `fs` goes out of scope // Create a temporary directory that will be deleted with `fs` goes out of scope
let fs = kxio::fs::temp().expect("temp fs"); let fs = kxio::fs::temp().expect("temp fs");

View file

@ -79,16 +79,14 @@
//! //!
//! ```rust //! ```rust
//! use kxio::net; //! use kxio::net;
//! use kxio::net::{Method, Url};
//!
//! # #[tokio::main] //! # #[tokio::main]
//! # async fn main() -> net::Result<()> { //! # async fn main() -> net::Result<()> {
//! # let mock_net = net::mock(); //! # let mock_net = net::mock();
//! mock_net.on(Method::GET) //! mock_net.on()
//! .url(Url::parse("https://example.com")?) //! .get("https://example.com")
//! .respond(mock_net.response().status(200).body("")?); //! .respond(mock_net.response().status(200).body("")?);
//! mock_net.on(Method::GET) //! mock_net.on()
//! .url(Url::parse("https://example.com/foo")?) //! .get("https://example.com/foo")
//! .respond(mock_net.response().status(500).body("Mocked response")?); //! .respond(mock_net.response().status(500).body("Mocked response")?);
//! # mock_net.reset(); //! # mock_net.reset();
//! # Ok(()) //! # Ok(())
@ -151,13 +149,11 @@ pub use result::{Error, Result};
pub use system::{MockNet, Net}; pub use system::{MockNet, Net};
pub use http::Method;
pub use http::HeaderMap; pub use http::HeaderMap;
pub use reqwest::Client; pub use reqwest::Client;
pub use reqwest::Request; pub use reqwest::Request;
pub use reqwest::RequestBuilder; pub use reqwest::RequestBuilder;
pub use reqwest::Response; pub use reqwest::Response;
pub use url::Url;
/// Creates a new `Net`. /// Creates a new `Net`.
pub const fn new() -> Net { pub const fn new() -> Net {

View file

@ -2,10 +2,13 @@
use std::{cell::RefCell, ops::Deref, rc::Rc, sync::Arc}; use std::{cell::RefCell, ops::Deref, rc::Rc, sync::Arc};
use derive_more::derive::{Display, From};
use http::Method;
use reqwest::{Body, Client}; use reqwest::{Body, Client};
use tokio::sync::Mutex; 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}; use super::{Error, Result};
@ -129,15 +132,14 @@ impl MockNet {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use kxio::net::{Method, Url};
/// # use kxio::net::Result; /// # use kxio::net::Result;
/// # #[tokio::main] /// # #[tokio::main]
/// # async fn run() -> Result<()> { /// # async fn run() -> Result<()> {
/// let mock_net = kxio::net::mock(); /// let mock_net = kxio::net::mock();
/// let client = mock_net.client(); /// let client = mock_net.client();
/// // define an expected requet, and the response that should be returned /// // define an expected requet, and the response that should be returned
/// mock_net.on(Method::GET) /// mock_net.on()
/// .url(Url::parse("https://hyper.rs")?) /// .get("https://hyper.rs")
/// .respond(mock_net.response().status(200).body("Ok")?); /// .respond(mock_net.response().status(200).body("Ok")?);
/// let net: kxio::net::Net = mock_net.into(); /// let net: kxio::net::Net = mock_net.into();
/// // use 'net' in your program, by passing it as a reference /// // use 'net' in your program, by passing it as a reference
@ -172,19 +174,18 @@ impl MockNet {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use kxio::net::{Method, Url};
/// # use kxio::net::Result; /// # use kxio::net::Result;
/// # fn run() -> Result<()> { /// # fn run() -> Result<()> {
/// let mock_net = kxio::net::mock(); /// let mock_net = kxio::net::mock();
/// let client = mock_net.client(); /// let client = mock_net.client();
/// mock_net.on(Method::GET) /// mock_net.on()
/// .url(Url::parse("https://hyper.rs")?) /// .get("https://hyper.rs")
/// .respond(mock_net.response().status(200).body("Ok")?); /// .respond(mock_net.response().status(200).body("Ok")?);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn on(&self, method: impl Into<Method>) -> WhenRequest { pub fn on(&self) -> WhenRequest {
WhenRequest::new(self, method) WhenRequest::new(self)
} }
fn _when(&self, plan: Plan) { fn _when(&self, plan: Plan) {
@ -249,15 +250,47 @@ pub enum MatchRequest {
Body(String), Body(String),
} }
#[derive(Clone, Debug, Display, From)]
enum MockError {
UrlParse(#[from] url::ParseError),
}
impl std::error::Error for MockError {}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct WhenRequest<'net> { pub struct WhenRequest<'net> {
net: &'net MockNet, net: &'net MockNet,
match_on: Vec<MatchRequest>, match_on: Vec<MatchRequest>,
errors: Vec<MockError>,
} }
impl<'net> WhenRequest<'net> { impl<'net> WhenRequest<'net> {
pub fn url(mut self, url: impl Into<Url>) -> Self { pub fn get(self, url: impl Into<String>) -> Self {
self.match_on.push(MatchRequest::Url(url.into())); self._url(Method::GET, url)
}
pub fn post(self, url: impl Into<String>) -> Self {
self._url(Method::POST, url)
}
pub fn put(self, url: impl Into<String>) -> Self {
self._url(Method::PUT, url)
}
pub fn delete(self, url: impl Into<String>) -> Self {
self._url(Method::DELETE, url)
}
pub fn head(self, url: impl Into<String>) -> Self {
self._url(Method::HEAD, url)
}
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));
}
Err(err) => self.errors.push(err.into()),
}
self self
} }
pub fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self { pub fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
@ -281,10 +314,11 @@ impl<'net> WhenRequest<'net> {
}); });
} }
fn new(net: &'net MockNet, method: impl Into<Method>) -> Self { fn new(net: &'net MockNet) -> Self {
Self { Self {
net, net,
match_on: vec![MatchRequest::Method(method.into())], match_on: vec![],
errors: vec![],
} }
} }
} }

View file

@ -1,5 +1,5 @@
// //
use kxio::net::{Error, Method, MockNet, Net, Url}; use kxio::net::{Error, MockNet, Net};
use assert2::let_assert; use assert2::let_assert;
@ -17,8 +17,8 @@ async fn test_get_url() {
.expect("body"); .expect("body");
mock_net mock_net
.on(Method::GET) .on()
.url(Url::parse(url).expect("parse url")) .get("https://www.example.com")
.respond(my_response); .respond(my_response);
//when //when
@ -32,6 +32,118 @@ async fn test_get_url() {
assert_eq!(response.bytes().await.expect("response body"), "Get OK"); 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] #[tokio::test]
async fn test_get_wrong_url() { async fn test_get_wrong_url() {
//given //given
@ -45,10 +157,7 @@ async fn test_get_wrong_url() {
.body("Get OK") .body("Get OK")
.expect("body"); .expect("body");
mock_net mock_net.on().get(url).respond(my_response);
.on(Method::GET)
.url(Url::parse(url).expect("parse url"))
.respond(my_response);
let net = Net::from(mock_net); let net = Net::from(mock_net);
@ -66,30 +175,6 @@ async fn test_get_wrong_url() {
mock_net.reset(); 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] #[tokio::test]
async fn test_post_by_method() { async fn test_post_by_method() {
//given //given
@ -98,8 +183,8 @@ async fn test_post_by_method() {
let my_response = net.response().status(200).body("").expect("response body"); let my_response = net.response().status(200).body("").expect("response body");
net.on(Method::POST) net.on()
// NOTE: No URL specified - so shou match any URL // NOTE: No URL specified - so should match any URL
.respond(my_response); .respond(my_response);
//when //when
@ -125,7 +210,7 @@ async fn test_post_by_body() {
.body("response body") .body("response body")
.expect("body"); .expect("body");
net.on(Method::POST) net.on()
// No URL - so any POST with a matching body // No URL - so any POST with a matching body
.body("match on body") .body("match on body")
.respond(my_response); .respond(my_response);
@ -156,9 +241,7 @@ async fn test_post_by_header() {
.body("response body") .body("response body")
.expect("body"); .expect("body");
net.on(Method::POST) net.on().header("test", "match").respond(my_response);
.header("test", "match")
.respond(my_response);
//when //when
let response = Net::from(net) let response = Net::from(net)
@ -191,10 +274,7 @@ async fn test_post_by_header_wrong_value() {
.body("response body") .body("response body")
.expect("body"); .expect("body");
mock_net mock_net.on().header("test", "match").respond(my_response);
.on(Method::POST)
.header("test", "match")
.respond(my_response);
let net = Net::from(mock_net); let net = Net::from(mock_net);
//when //when
@ -226,10 +306,7 @@ async fn test_unused_post_as_net() {
.body("Post OK") .body("Post OK")
.expect("body"); .expect("body");
mock_net mock_net.on().post(url).respond(my_response);
.on(Method::POST)
.url(Url::parse(url).expect("prase url"))
.respond(my_response);
let _net = Net::from(mock_net); let _net = Net::from(mock_net);
@ -254,10 +331,7 @@ async fn test_unused_post_as_mocknet() {
.body("Post OK") .body("Post OK")
.expect("body"); .expect("body");
mock_net mock_net.on().post(url).respond(my_response);
.on(Method::POST)
.url(Url::parse(url).expect("parse url"))
.respond(my_response);
//when //when
// don't send the planned request // don't send the planned request