feat(net): add tracing
All checks were successful
Test / build (map[name:nightly]) (push) Successful in 3m38s
Test / build (map[name:stable]) (push) Successful in 5m4s
Release Please / Release-plz (push) Successful in 38s

This commit is contained in:
Paul Campbell 2024-12-04 22:38:44 +00:00
parent 6e5ea556a9
commit 436ad890d8
4 changed files with 64 additions and 44 deletions

View file

@ -161,6 +161,7 @@ pub const fn new() -> Net {
}
/// Creates a new `MockNet` for use in tests.
#[tracing::instrument]
pub fn mock() -> MockNet {
Default::default()
}

View file

@ -23,8 +23,10 @@ pub enum Error {
Url(url::ParseError),
/// There was network request that doesn't match any that were expected
#[display("Unexpected request: {0}", 0.to_string())]
UnexpectedMockRequest(Request),
#[display("Unexpected request: {} {}", request.method(), request.url())]
UnexpectedMockRequest {
request: Request,
},
/// There was an error accessing the list of expected requests.
RwLockLocked,

View file

@ -1,5 +1,4 @@
//
use std::{
cell::RefCell,
collections::HashMap,
@ -33,8 +32,10 @@ struct Plan {
response: reqwest::Response,
}
impl Plan {
#[tracing::instrument]
fn matches(&self, request: &Request) -> bool {
let url = request.url();
let is_match =
self.match_request.iter().all(|criteria| match criteria {
MatchRequest::Body(body) => {
request.body().and_then(reqwest::Body::as_bytes) == Some(body)
@ -55,14 +56,14 @@ impl Plan {
MatchRequest::Host(host) => url.host_str() == Some(host),
MatchRequest::Path(path) => url.path() == path,
MatchRequest::Fragment(fragment) => url.fragment() == Some(fragment),
MatchRequest::Query { name, value } => {
url.query_pairs()
.into_iter()
.any(|(request_query_name, request_query_value)| {
MatchRequest::Query { name, value } => url.query_pairs().into_iter().any(
|(request_query_name, request_query_value)| {
request_query_name.as_ref() == name && request_query_value.as_ref() == value
})
}
})
},
),
});
tracing::debug!(?is_match);
is_match
}
}
impl Display for Plan {
@ -129,17 +130,16 @@ impl Net {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument(skip_all)]
pub async fn send(&self, request: impl Into<RequestBuilder>) -> Result<Response> {
let request: RequestBuilder = request.into();
tracing::debug!(?request);
let Some(plans) = &self.plans else {
return request.into().send().await.map_err(Error::from);
tracing::debug!("no plans - sending real request");
return request.send().await.map_err(Error::from);
};
let request = request.into().build()?;
eprintln!(
"? {} {} {:?}",
request.method(),
request.url(),
request.headers()
);
tracing::debug!("build request");
let request = request.build()?;
let index = plans
.lock()
.await
@ -150,15 +150,19 @@ impl Net {
match index {
Some(i) => {
let plan = plans.lock().await.borrow_mut().remove(i);
eprintln!("- matched: {plan}");
let response = plan.response;
if response.status().is_success() {
tracing::debug!(?request, "matched success response");
Ok(response)
} else {
tracing::debug!(?request, "matched error response");
Err(crate::net::Error::ResponseError { response })
}
}
None => Err(Error::UnexpectedMockRequest(request)),
None => {
tracing::warn!(?request, "unexpected mock request");
Err(Error::UnexpectedMockRequest { request })
}
}
}
@ -376,14 +380,17 @@ impl<'net> ReqBuilder<'net> {
/// # Ok(())
/// # }
/// ```
#[tracing::instrument(skip_all)]
pub async fn send(self) -> Result<Response> {
let client = self.net.client();
// URL
let mut url = self.url;
tracing::trace!(?url);
// Query Parameters
if !self.query.is_empty() {
url.push('?');
for (i, (name, value)) in self.query.into_iter().enumerate() {
tracing::trace!(?name, ?value, "query parameters");
if i > 0 {
url.push('&');
}
@ -391,6 +398,7 @@ impl<'net> ReqBuilder<'net> {
url.push('=');
url.push_str(&value);
}
tracing::trace!(?url, "with query parameters");
}
// Method
let mut req = match self.method {
@ -409,7 +417,7 @@ impl<'net> ReqBuilder<'net> {
if let Some(bytes) = self.body {
req = req.body(bytes);
}
tracing::debug!(?req);
self.net.send(req).await
}
@ -588,6 +596,7 @@ impl MockNet {
/// # }
/// ```
pub fn reset(&self) {
tracing::debug!("reset plans");
self.plans.take();
}
}
@ -602,19 +611,23 @@ impl From<MockNet> for Net {
}
impl Drop for MockNet {
#[tracing::instrument]
fn drop(&mut self) {
let unused = self.plans.take();
if unused.is_empty() {
tracing::trace!("no unused expected requests");
return; // all good
}
panic_with_unused_plans(unused);
}
}
impl Drop for Net {
#[tracing::instrument]
fn drop(&mut self) {
if let Some(plans) = &self.plans {
let unused = plans.try_lock().expect("lock plans").take();
if unused.is_empty() {
tracing::trace!("no unused expected requests");
return; // all good
}
panic_with_unused_plans(unused);
@ -672,9 +685,11 @@ impl std::error::Error for MockError {}
pub trait WhenState {}
#[derive(Debug)]
pub struct WhenBuildRequest;
impl WhenState for WhenBuildRequest {}
#[derive(Debug)]
pub struct WhenBuildResponse;
impl WhenState for WhenBuildResponse {}
@ -737,6 +752,7 @@ impl<'net> WhenRequest<'net, WhenBuildRequest> {
self._url(NetMethod::Patch, url)
}
#[tracing::instrument(skip_all)]
fn _url(mut self, method: NetMethod, url: impl Into<String>) -> Self {
self.match_on.push(MatchRequest::Method(method));
match Url::parse(&url.into()) {
@ -774,6 +790,7 @@ impl<'net> WhenRequest<'net, WhenBuildRequest> {
self.error.replace(err.into());
}
}
tracing::debug!(match_on = ?self.match_on, error = ?self.error);
self
}

View file

@ -214,12 +214,12 @@ async fn test_get_wrong_url() {
//when
let_assert!(
Err(Error::UnexpectedMockRequest(invalid_request)) =
Err(Error::UnexpectedMockRequest { request }) =
net.send(client.get("https://some.other.url/")).await
);
//then
assert_eq!(invalid_request.url().to_string(), "https://some.other.url/");
assert_eq!(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).await.expect("recover net");
@ -329,7 +329,7 @@ async fn test_post_by_header_wrong_value() {
.await;
//then
let_assert!(Err(kxio::net::Error::UnexpectedMockRequest(_)) = response);
let_assert!(Err(kxio::net::Error::UnexpectedMockRequest { request: _ }) = response);
MockNet::try_from(net).await.expect("recover mock").reset();
}