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 {
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");

View file

@ -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(())
@ -151,13 +149,11 @@ pub use result::{Error, Result};
pub use system::{MockNet, Net};
pub use http::Method;
pub use http::HeaderMap;
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 {

View file

@ -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<Method>) -> 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<MatchRequest>,
errors: Vec<MockError>,
}
impl<'net> WhenRequest<'net> {
pub fn url(mut self, url: impl Into<Url>) -> Self {
self.match_on.push(MatchRequest::Url(url.into()));
pub fn get(self, url: impl Into<String>) -> Self {
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
}
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 {
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;
@ -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