Compare commits
3 commits
e8a4b04533
...
aad02be6cb
Author | SHA1 | Date | |
---|---|---|---|
aad02be6cb | |||
7285cff6e7 | |||
ff8b6c64b6 |
5 changed files with 86 additions and 47 deletions
|
@ -110,35 +110,44 @@ mod tests {
|
||||||
// When `net` goes out of scope it will check that all the expected network requests (see
|
// When `net` goes out of scope it will check that all the expected network requests (see
|
||||||
// `net.on(...)` below) were all made. If there are any that were not made, the test will
|
// `net.on(...)` below) were all made. If there are any that were not made, the test will
|
||||||
// be failed. If you want to avoid this, then call `net.reset()` before your test ends.
|
// be failed. If you want to avoid this, then call `net.reset()` before your test ends.
|
||||||
let net: kxio::net::MockNet = kxio::net::mock();
|
let mock_net: kxio::net::MockNet = kxio::net::mock();
|
||||||
let url = "http://localhost:8080";
|
let url = "http://localhost:8080";
|
||||||
|
|
||||||
// declare what response should be made for a given request
|
// declare what response should be made for a given request
|
||||||
let response: http::Response<&str> =
|
let response: http::Response<&str> =
|
||||||
net.response().body("contents").expect("response body");
|
mock_net.response().body("contents").expect("response body");
|
||||||
let request = net.client().get(url).build().expect("request");
|
let request = mock_net.client().get(url).build().expect("request");
|
||||||
net.on(request)
|
mock_net
|
||||||
|
.on(request)
|
||||||
// By default, the METHOD and URL must match, equivalent to:
|
// By default, the METHOD and URL must match, equivalent to:
|
||||||
//.match_on(vec![MatchOn::Method, MatchOn::Url])
|
//.match_on(vec![MatchOn::Method, MatchOn::Url])
|
||||||
.respond(response.into())
|
.respond(response)
|
||||||
.expect("mock");
|
.expect("mock");
|
||||||
|
|
||||||
// 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");
|
||||||
let file_path = fs.base().join("foo");
|
let file_path = fs.base().join("foo");
|
||||||
|
|
||||||
|
// Create a [Net] from the [MockNet] to pass to the system under tets
|
||||||
|
let net = kxio::net::Net::from(mock_net);
|
||||||
|
|
||||||
//when
|
//when
|
||||||
// Pass the file sytsem and network abstractions to the code to be tested
|
// Pass the file sytsem and network abstractions to the code to be tested
|
||||||
download_and_save_to_file(url, &file_path, &fs, &net.into())
|
download_and_save_to_file(url, &file_path, &fs, &net)
|
||||||
.await
|
.await
|
||||||
.expect("system under test");
|
.expect("system under test");
|
||||||
|
|
||||||
//then
|
//then
|
||||||
// Open a file and read it
|
// Read the file
|
||||||
let file = fs.file(&file_path);
|
let file = fs.file(&file_path);
|
||||||
let reader = file.reader().expect("reader");
|
let reader = file.reader().expect("reader");
|
||||||
let contents = reader.as_str();
|
let contents = reader.as_str();
|
||||||
|
|
||||||
assert_eq!(contents, "contents");
|
assert_eq!(contents, "contents");
|
||||||
|
|
||||||
|
// not needed for this test, but should it be needed, we can avoid checking for any
|
||||||
|
// unconsumed request matches.
|
||||||
|
let mock_net = kxio::net::MockNet::try_from(net).expect("recover mock");
|
||||||
|
mock_net.reset().expect("reset mock");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
//! Provides an injectable reference to part of the filesystem.
|
//! Provides an injectable reference to part of the filesystem.
|
||||||
//!
|
//!
|
||||||
//! Create a new `FileSystem` to access a directory using `kxio::fs::new(path)`.
|
//! Create a new [FileSystem] to access a directory using [crate::fs::new].
|
||||||
//! Create a new `TempFileSystem` to access a temporary directory using `kxio::fs::temp()?`;
|
//! Create a new [TempFileSystem] to access a temporary directory using [crate::fs::temp()].
|
||||||
//!
|
//!
|
||||||
//! `TempFileSystem` derefs automatically to `FileSystem` so can be used anywhere
|
//! [TempFileSystem] derefs automatically to [FileSystem] so can be used anywhere
|
||||||
//! you would use `FileSystem`.
|
//! you would use [FileSystem].
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # use std::path::PathBuf;
|
//! # use std::path::PathBuf;
|
||||||
|
@ -29,12 +29,12 @@
|
||||||
//!
|
//!
|
||||||
//! # Standard library equivalents
|
//! # Standard library equivalents
|
||||||
//!
|
//!
|
||||||
//! Given a `FileSystem` `fs`:
|
//! Given a [FileSystem] `fs`:
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! let fs = kxio::fs::temp().expect("temp fs"); // for testing
|
//! let fs = kxio::fs::temp().expect("temp fs"); // for testing
|
||||||
//! // or
|
//! // or
|
||||||
//! # let pathbuf = fs.base().join("foo").to_path_buf();
|
//! # let pathbuf = fs.base().join("foo");
|
||||||
//! let fs = kxio::fs::new(pathbuf);
|
//! let fs = kxio::fs::new(pathbuf);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
@ -68,10 +68,11 @@ mod system;
|
||||||
mod temp;
|
mod temp;
|
||||||
|
|
||||||
pub use dir_item::{DirItem, DirItemIterator};
|
pub use dir_item::{DirItem, DirItemIterator};
|
||||||
pub use path::*;
|
pub use path::{DirMarker, FileMarker, PathMarker, PathReal};
|
||||||
pub use reader::Reader;
|
pub use reader::Reader;
|
||||||
pub use result::{Error, Result};
|
pub use result::{Error, Result};
|
||||||
pub use system::{DirHandle, FileHandle, FileSystem, PathHandle};
|
pub use system::{DirHandle, FileHandle, FileSystem, PathHandle};
|
||||||
|
pub use temp::TempFileSystem;
|
||||||
|
|
||||||
/// Creates a new `FileSystem` for the path.
|
/// Creates a new `FileSystem` for the path.
|
||||||
///
|
///
|
||||||
|
|
|
@ -9,6 +9,8 @@ pub enum Error {
|
||||||
#[display("Unexpected request: {0}", 0.to_string())]
|
#[display("Unexpected request: {0}", 0.to_string())]
|
||||||
UnexpectedMockRequest(reqwest::Request),
|
UnexpectedMockRequest(reqwest::Request),
|
||||||
RwLockLocked,
|
RwLockLocked,
|
||||||
|
Http(http::Error),
|
||||||
|
NetIsNotAMock,
|
||||||
}
|
}
|
||||||
impl std::error::Error for Error {}
|
impl std::error::Error for Error {}
|
||||||
impl Clone for Error {
|
impl Clone for Error {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
//
|
//
|
||||||
use std::{marker::PhantomData, ops::Deref, sync::RwLock};
|
use std::{marker::PhantomData, sync::RwLock};
|
||||||
|
|
||||||
|
use reqwest::Client;
|
||||||
|
|
||||||
use super::{Error, Result};
|
use super::{Error, Result};
|
||||||
|
|
||||||
|
@ -56,29 +58,58 @@ impl Net {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send(&self, request: reqwest::RequestBuilder) -> Result<reqwest::Response> {
|
pub async fn send(
|
||||||
|
&self,
|
||||||
|
request: impl Into<reqwest::RequestBuilder>,
|
||||||
|
) -> Result<reqwest::Response> {
|
||||||
match &self.mock {
|
match &self.mock {
|
||||||
Some(mock) => mock.send(request).await,
|
Some(mock) => mock.send(request).await,
|
||||||
None => self.inner.send(request).await,
|
None => self.inner.send(request).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl TryFrom<Net> for MockNet {
|
||||||
|
type Error = super::Error;
|
||||||
|
|
||||||
|
fn try_from(net: Net) -> std::result::Result<Self, Self::Error> {
|
||||||
|
match net.mock {
|
||||||
|
Some(inner_mock) => Ok(MockNet { inner: inner_mock }),
|
||||||
|
None => Err(Self::Error::NetIsNotAMock),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MockNet {
|
pub struct MockNet {
|
||||||
inner: InnerNet<Mocked>,
|
inner: InnerNet<Mocked>,
|
||||||
}
|
}
|
||||||
impl Deref for MockNet {
|
impl MockNet {
|
||||||
type Target = InnerNet<Mocked>;
|
pub fn client(&self) -> Client {
|
||||||
|
Default::default()
|
||||||
fn deref(&self) -> &Self::Target {
|
}
|
||||||
&self.inner
|
pub fn on(&self, request: impl Into<reqwest::Request>) -> OnRequest {
|
||||||
|
self.inner.on(request)
|
||||||
|
}
|
||||||
|
/// Creates a [http::response::Builder] to be extended and returned by a mocked network request.
|
||||||
|
pub fn response(&self) -> http::response::Builder {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
pub async fn send(
|
||||||
|
&self,
|
||||||
|
request: impl Into<reqwest::RequestBuilder>,
|
||||||
|
) -> Result<reqwest::Response> {
|
||||||
|
self.inner.send(request).await
|
||||||
|
}
|
||||||
|
pub fn reset(&self) -> Result<()> {
|
||||||
|
self.inner.reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<MockNet> for Net {
|
impl From<MockNet> for Net {
|
||||||
fn from(mock_net: MockNet) -> Self {
|
fn from(mock_net: MockNet) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: InnerNet::<Unmocked>::new(),
|
inner: InnerNet::<Unmocked>::new(),
|
||||||
|
// 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
|
||||||
mock: Some(mock_net.inner),
|
mock: Some(mock_net.inner),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,16 +128,14 @@ impl InnerNet<Unmocked> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send(&self, request: reqwest::RequestBuilder) -> Result<reqwest::Response> {
|
pub async fn send(
|
||||||
request.send().await.map_err(Error::from)
|
&self,
|
||||||
|
request: impl Into<reqwest::RequestBuilder>,
|
||||||
|
) -> Result<reqwest::Response> {
|
||||||
|
request.into().send().await.map_err(Error::from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: NetType> InnerNet<T> {
|
|
||||||
pub fn client(&self) -> reqwest::Client {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl InnerNet<Mocked> {
|
impl InnerNet<Mocked> {
|
||||||
const fn new() -> Self {
|
const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -115,8 +144,11 @@ impl InnerNet<Mocked> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send(&self, request: reqwest::RequestBuilder) -> Result<reqwest::Response> {
|
pub async fn send(
|
||||||
let request = request.build()?;
|
&self,
|
||||||
|
request: impl Into<reqwest::RequestBuilder>,
|
||||||
|
) -> Result<reqwest::Response> {
|
||||||
|
let request = request.into().build()?;
|
||||||
let read_plans = self.plans.read().map_err(|_| Error::RwLockLocked)?;
|
let read_plans = self.plans.read().map_err(|_| Error::RwLockLocked)?;
|
||||||
let index = read_plans.iter().position(|plan| {
|
let index = read_plans.iter().position(|plan| {
|
||||||
// METHOD
|
// METHOD
|
||||||
|
@ -158,15 +190,10 @@ impl InnerNet<Mocked> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a [http::response::Builder] to be extended and returned by a mocked network request.
|
pub fn on(&self, request: impl Into<reqwest::Request>) -> OnRequest {
|
||||||
pub fn response(&self) -> http::response::Builder {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on(&self, request: reqwest::Request) -> OnRequest {
|
|
||||||
OnRequest {
|
OnRequest {
|
||||||
net: self,
|
net: self,
|
||||||
request,
|
request: request.into(),
|
||||||
match_on: vec![MatchOn::Method, MatchOn::Url],
|
match_on: vec![MatchOn::Method, MatchOn::Url],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,7 +241,7 @@ impl<'net> OnRequest<'net> {
|
||||||
match_on,
|
match_on,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn respond(self, response: reqwest::Response) -> Result<()> {
|
pub fn respond(self, response: impl Into<reqwest::Response>) -> Result<()> {
|
||||||
self.net._on(self.request, response, self.match_on)
|
self.net._on(self.request, response.into(), self.match_on)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
16
tests/net.rs
16
tests/net.rs
|
@ -17,7 +17,7 @@ async fn test_get_url() {
|
||||||
.expect("request body");
|
.expect("request body");
|
||||||
|
|
||||||
net.on(request)
|
net.on(request)
|
||||||
.respond(my_response.into())
|
.respond(my_response)
|
||||||
.expect("on request, respond");
|
.expect("on request, respond");
|
||||||
|
|
||||||
//when
|
//when
|
||||||
|
@ -46,7 +46,7 @@ async fn test_get_wrong_url() {
|
||||||
.expect("request body");
|
.expect("request body");
|
||||||
|
|
||||||
net.on(request)
|
net.on(request)
|
||||||
.respond(my_response.into())
|
.respond(my_response)
|
||||||
.expect("on request, respond");
|
.expect("on request, respond");
|
||||||
|
|
||||||
//when
|
//when
|
||||||
|
@ -77,7 +77,7 @@ async fn test_post_url() {
|
||||||
.expect("request body");
|
.expect("request body");
|
||||||
|
|
||||||
net.on(request)
|
net.on(request)
|
||||||
.respond(my_response.into())
|
.respond(my_response)
|
||||||
.expect("on request, respond");
|
.expect("on request, respond");
|
||||||
|
|
||||||
//when
|
//when
|
||||||
|
@ -110,7 +110,7 @@ async fn test_post_by_method() {
|
||||||
MatchOn::Method,
|
MatchOn::Method,
|
||||||
// MatchOn::Url
|
// MatchOn::Url
|
||||||
])
|
])
|
||||||
.respond(my_response.into())
|
.respond(my_response)
|
||||||
.expect("on request, respond");
|
.expect("on request, respond");
|
||||||
|
|
||||||
//when
|
//when
|
||||||
|
@ -144,7 +144,7 @@ async fn test_post_by_url() {
|
||||||
// MatchOn::Method,
|
// MatchOn::Method,
|
||||||
MatchOn::Url,
|
MatchOn::Url,
|
||||||
])
|
])
|
||||||
.respond(my_response.into())
|
.respond(my_response)
|
||||||
.expect("on request, respond");
|
.expect("on request, respond");
|
||||||
|
|
||||||
//when
|
//when
|
||||||
|
@ -183,7 +183,7 @@ async fn test_post_by_body() {
|
||||||
// MatchOn::Url
|
// MatchOn::Url
|
||||||
MatchOn::Body,
|
MatchOn::Body,
|
||||||
])
|
])
|
||||||
.respond(my_response.into())
|
.respond(my_response)
|
||||||
.expect("on request, respond");
|
.expect("on request, respond");
|
||||||
|
|
||||||
//when
|
//when
|
||||||
|
@ -226,7 +226,7 @@ async fn test_post_by_headers() {
|
||||||
// MatchOn::Url
|
// MatchOn::Url
|
||||||
MatchOn::Headers,
|
MatchOn::Headers,
|
||||||
])
|
])
|
||||||
.respond(my_response.into())
|
.respond(my_response)
|
||||||
.expect("on request, respond");
|
.expect("on request, respond");
|
||||||
|
|
||||||
//when
|
//when
|
||||||
|
@ -265,7 +265,7 @@ async fn test_unused_post() {
|
||||||
.expect("request body");
|
.expect("request body");
|
||||||
|
|
||||||
net.on(request)
|
net.on(request)
|
||||||
.respond(my_response.into())
|
.respond(my_response)
|
||||||
.expect("on request, respond");
|
.expect("on request, respond");
|
||||||
|
|
||||||
//when
|
//when
|
||||||
|
|
Loading…
Reference in a new issue