diff --git a/Cargo.toml b/Cargo.toml index 35deaae..e017a77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ path-clean = "1.0" reqwest = { version = "0.12", features = [ "json" ] } url = "2.5" tempfile = "3.10" +tokio = { version = "1.41", features = [ "sync" ] } [dev-dependencies] assert2 = "0.3" diff --git a/src/fs/dir_item.rs b/src/fs/dir_item.rs index c3bad62..daad67d 100644 --- a/src/fs/dir_item.rs +++ b/src/fs/dir_item.rs @@ -7,7 +7,7 @@ use std::{ use super::Error; /// Represents an item in a directory -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum DirItem { File(PathBuf), Dir(PathBuf), diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 3906eae..df3a4f5 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -98,3 +98,25 @@ pub fn new(base: impl Into) -> FileSystem { pub fn temp() -> Result { temp::TempFileSystem::new() } + +#[cfg(test)] +mod tests { + use super::*; + + fn is_normal() {} + + #[test] + fn normal_types() { + is_normal::(); + is_normal::(); + is_normal::(); + is_normal::(); + is_normal::>(); + is_normal::(); + is_normal::(); + is_normal::(); + is_normal::(); + is_normal::(); + is_normal::>(); + } +} diff --git a/src/fs/path.rs b/src/fs/path.rs index eb44442..aa2ea8c 100644 --- a/src/fs/path.rs +++ b/src/fs/path.rs @@ -12,24 +12,24 @@ use super::{DirHandle, FileHandle, PathHandle}; pub trait PathType {} /// Path marker for the type of [PathReal]. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct PathMarker; impl PathType for PathMarker {} /// File marker for the type of [PathReal]. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct FileMarker; impl PathType for FileMarker {} /// Dir marker for the type of [PathReal]. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct DirMarker; impl PathType for DirMarker {} /// Represents a path in the filesystem. /// /// It can be a simple path, or it can be a file or a directory. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct PathReal { base: PathBuf, path: PathBuf, diff --git a/src/fs/reader.rs b/src/fs/reader.rs index e17c960..93e2ad2 100644 --- a/src/fs/reader.rs +++ b/src/fs/reader.rs @@ -6,6 +6,7 @@ use crate::fs::Result; use super::Error; /// A reader for a file. +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct Reader { contents: String, } diff --git a/src/fs/system.rs b/src/fs/system.rs index 8598668..e98cf7c 100644 --- a/src/fs/system.rs +++ b/src/fs/system.rs @@ -9,7 +9,7 @@ use super::{ }; /// Represents to base of a section of a file system. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct FileSystem { base: PathBuf, } diff --git a/src/net/mod.rs b/src/net/mod.rs index db17f20..62bf414 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -134,10 +134,14 @@ //! //! ```rust //! # use kxio::net; +//! # #[tokio::main] +//! # async fn main() -> net::Result<()> { //! # let mock_net = net::mock(); //! # let net = net::Net::from(mock_net); -//! let mock_net = kxio::net::MockNet::try_from(net).expect("recover mock"); +//! let mock_net = kxio::net::MockNet::try_from(net).await.expect("recover mock"); //! mock_net.reset(); +//! # Ok(()) +//! # } //! ```` //! //! ## Error Handling diff --git a/src/net/system.rs b/src/net/system.rs index 7c59a05..cdba38e 100644 --- a/src/net/system.rs +++ b/src/net/system.rs @@ -1,7 +1,9 @@ // -use std::{cell::RefCell, rc::Rc}; + +use std::{cell::RefCell, ops::Deref, rc::Rc, sync::Arc}; use reqwest::{Body, Client}; +use tokio::sync::Mutex; use crate::net::{Method, Request, RequestBuilder, Response, Url}; @@ -29,6 +31,7 @@ pub enum MatchOn { /// A planned request and the response to return /// /// Contains a list of the criteria that a request must meet before being considered a match. +#[derive(Debug)] struct Plan { match_request: Vec, response: Response, @@ -57,9 +60,9 @@ impl Plan { } /// An abstraction for the network -#[derive(Clone)] +#[derive(Debug, Clone, Default)] pub struct Net { - plans: Option>>, + plans: Option>>>, } impl Net { /// Creates a new unmocked [Net] for creating real network requests. @@ -68,9 +71,9 @@ impl Net { } /// Creats a new [MockNet] for use in tests. - pub(super) const fn mock() -> MockNet { + pub(super) fn mock() -> MockNet { MockNet { - plans: RefCell::new(vec![]), + plans: Rc::new(RefCell::new(vec![])), } } } @@ -114,27 +117,28 @@ impl Net { }; let request = request.into().build()?; let index = plans + .lock() + .await + .deref() .borrow() .iter() .position(|plan| plan.matches(&request)); match index { Some(i) => { - let response = plans.borrow_mut().remove(i).response; + let response = plans.lock().await.borrow_mut().remove(i).response; Ok(response) } None => Err(Error::UnexpectedMockRequest(request)), } } } -impl TryFrom for MockNet { - type Error = super::Error; - - fn try_from(net: Net) -> std::result::Result { +impl MockNet { + pub async fn try_from(net: Net) -> std::result::Result { match &net.plans { Some(plans) => Ok(MockNet { - plans: RefCell::new(plans.take()), + plans: Rc::new(RefCell::new(plans.lock().await.take())), }), - None => Err(Self::Error::NetIsNotAMock), + None => Err(super::Error::NetIsNotAMock), } } } @@ -150,7 +154,8 @@ impl TryFrom for MockNet { /// ```rust /// use kxio::net::{Method, Url}; /// # use kxio::net::Result; -/// # fn run() -> 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 @@ -162,13 +167,14 @@ impl TryFrom for MockNet { /// /// // In some rare cases you don't want to assert that all expected requests were made. /// // You should recover the `MockNet` from the `Net` and `MockNet::reset` it. -/// let mock_net = kxio::net::MockNet::try_from(net)?; +/// let mock_net = kxio::net::MockNet::try_from(net).await?; /// mock_net.reset(); // only if explicitly needed /// # Ok(()) /// # } /// ``` +#[derive(Debug, Clone, Default)] pub struct MockNet { - plans: RefCell, + plans: Rc>, } impl MockNet { /// Helper to create a default [Client]. @@ -222,10 +228,11 @@ impl MockNet { /// /// ```rust /// # use kxio::net::Result; - /// # fn run() -> Result<()> { + /// # #[tokio::main] + /// # async fn run() -> Result<()> { /// # let mock_net = kxio::net::mock(); /// # let net: kxio::net::Net = mock_net.into(); - /// let mock_net = kxio::net::MockNet::try_from(net)?; + /// let mock_net = kxio::net::MockNet::try_from(net).await?; /// mock_net.reset(); // only if explicitly needed /// # Ok(()) /// # } @@ -239,7 +246,7 @@ impl From for Net { Self { // 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 - plans: Some(Rc::new(RefCell::new(mock_net.plans.take()))), + plans: Some(Arc::new(Mutex::new(RefCell::new(mock_net.plans.take())))), } } } @@ -252,11 +259,12 @@ impl Drop for MockNet { impl Drop for Net { fn drop(&mut self) { if let Some(plans) = &self.plans { - assert!(plans.borrow().is_empty()) + assert!(plans.try_lock().expect("lock plans").take().is_empty()) } } } +#[derive(Debug, Clone, PartialEq, Eq)] pub enum MatchRequest { Method(Method), Url(Url), @@ -264,6 +272,7 @@ pub enum MatchRequest { Body(String), } +#[derive(Debug, Clone)] pub struct WhenRequest<'net> { net: &'net MockNet, match_on: Vec, @@ -302,3 +311,19 @@ impl<'net> WhenRequest<'net> { } } } + +#[cfg(test)] +mod tests { + use super::*; + + fn is_normal() {} + + #[test] + fn normal_types() { + is_normal::(); + // is_normal::(); // only used in test setup - no need to be Send or Sync + is_normal::(); + is_normal::(); + // is_normal::(); // only used in test setup - no need to be Send or Sync + } +} diff --git a/tests/net.rs b/tests/net.rs index c7f28e9..ac41ad4 100644 --- a/tests/net.rs +++ b/tests/net.rs @@ -62,7 +62,7 @@ async fn test_get_wrong_url() { assert_eq!(invalid_request.url().to_string(), "https://some.other.url/"); // remove pending unmatched request - we never meant to match against it - let mock_net = MockNet::try_from(net).expect("recover net"); + let mock_net = MockNet::try_from(net).await.expect("recover net"); mock_net.reset(); } @@ -210,7 +210,7 @@ async fn test_post_by_header_wrong_value() { //then let_assert!(Err(kxio::net::Error::UnexpectedMockRequest(_)) = response); - MockNet::try_from(net).expect("recover mock").reset(); + MockNet::try_from(net).await.expect("recover mock").reset(); } #[tokio::test]