From 05157f8d5ba321a8886505a086db9f9f26d44ffe Mon Sep 17 00:00:00 2001 From: Gus Power Date: Tue, 20 May 2025 17:39:48 +0100 Subject: added some test macros to reduce noise in ipservice tests --- src/ip_service.rs | 132 +++++++++++++++++++++++++++--------------------------- 1 file changed, 66 insertions(+), 66 deletions(-) (limited to 'src/ip_service.rs') diff --git a/src/ip_service.rs b/src/ip_service.rs index 1d7f253..fcf3a80 100644 --- a/src/ip_service.rs +++ b/src/ip_service.rs @@ -3,9 +3,8 @@ use crate::ip_service::IpServiceProvider::IDENTME; use http::StatusCode; use reqwest::{Client, Url}; use serde::{Deserialize, Serialize}; -use serde_with::serde_as; use serde_with::DisplayFromStr; -use std::error::Error; +use serde_with::serde_as; use std::net::IpAddr; use std::str::FromStr; @@ -54,7 +53,7 @@ pub struct IdentMe { impl IdentMe { fn convert_string_to_ip_address(input: String, url: &Url) -> AppResult { IpAddr::from_str(&input).map_err(|e| AppError::InvalidResponse { - url: url.to_string(), + url: url.clone(), reason: format!( "Failed to parse service response [{}] into valid ip address: {}", input, e @@ -64,21 +63,21 @@ impl IdentMe { fn request_failed(&self, e: reqwest::Error) -> AppError { AppError::RequestFailed { - url: self.url.to_string(), + url: self.url.clone(), source: e, } } fn invalid_status_code(&self, status: StatusCode) -> AppError { AppError::InvalidResponse { - url: self.url.to_string(), + url: self.url.clone(), reason: format!("Status code: {}", status), } } fn missing_text_response(&self, e: reqwest::Error) -> AppError { AppError::InvalidResponse { - url: self.url.to_string(), + url: self.url.clone(), reason: format!("Unable to get text from service response: {}", e), } } @@ -121,11 +120,23 @@ impl IpServiceConfiguration for IdentMe { #[cfg(test)] mod tests { use super::*; - use crate::error::AppError::{InvalidResponse, RequestFailed}; + use crate::{assert_error, assert_invalid_response, assert_request_failed}; use reqwest::ClientBuilder; + use std::error::Error; + use std::time::Duration; use wiremock::matchers::method; use wiremock::{Mock, MockServer, Respond, ResponseTemplate}; + trait HasUrl { + fn url(&self) -> Url; + } + + impl HasUrl for MockServer { + fn url(&self) -> Url { + Url::from_str(self.uri().as_str()).unwrap() + } + } + async fn setup_ipv4_service(response: impl Respond + 'static) -> MockServer { let service = MockServer::start().await; @@ -147,6 +158,10 @@ mod tests { ResponseTemplate::new(204) } + fn delayed_response(delay: u64) -> impl Respond + 'static { + ResponseTemplate::new(204).set_delay(Duration::from_millis(delay)) + } + fn server_error() -> impl Respond + 'static { ResponseTemplate::new(500) } @@ -155,101 +170,89 @@ mod tests { async fn successful_ipv4_address_resolution() -> Result<(), Box> { let expected = IpAddr::from_str("17.5.7.8")?; let input = plaintext_response(expected.to_string()); - let (ip_service, actual) = run(input).await?; + let (ip_service, actual) = run_identme(input).await?; assert_eq!(1, ip_service.received_requests().await.unwrap().len()); - assert_eq!(actual.unwrap(), expected); + assert_eq!(actual?, expected); Ok(()) } #[tokio::test] - async fn failed_address_resolution_no_server() -> Result<(), Box> { + async fn failed_no_reachable_server() -> Result<(), Box> { let client = ClientBuilder::new().build()?; - let server_url = "http://localhost:8765/path"; - let service = IdentMe { - url: server_url.parse()?, - }; + let url = Url::from_str("http://localhost:8765/path")?; + let service = IdentMe { url: url.clone() }; let actual = IDENTME(service).resolve(&client).await; - assert!(actual.is_err()); - - match actual.err().unwrap() { - RequestFailed { url, .. } => { - assert_eq!(url, server_url.to_string()); - } - _ => { - panic!("Unexpected error") - } - } + assert_request_failed!(actual, url); Ok(()) } #[tokio::test] - async fn failed_non_successful_status_code() -> Result<(), Box> { + async fn failed_status_code() -> Result<(), Box> { let input = server_error(); - let (ip_service, actual) = run(input).await?; + let (ip_service, actual) = run_identme(input).await?; assert_eq!(1, ip_service.received_requests().await.unwrap().len()); - assert!(actual.is_err()); - - match actual.err().unwrap() { - InvalidResponse { url, reason } => { - assert_eq!(Url::parse(&*url), Url::from_str(&*ip_service.uri())); - assert_eq!(reason, "Status code: 500 Internal Server Error"); - } - _ => { - panic!("Unexpected error") - } - } + assert_invalid_response!( + actual, + ip_service.url(), + "Status code: 500 Internal Server Error" + ); Ok(()) } #[tokio::test] - async fn failed_non_successful_empty_response() -> Result<(), Box> { + async fn failed_empty_response() -> Result<(), Box> { let input = empty_response(); - let (ip_service, actual) = run(input).await?; + let (ip_service, actual) = run_identme(input).await?; assert_eq!(1, ip_service.received_requests().await.unwrap().len()); - assert!(actual.is_err()); - - match actual.err().unwrap() { - InvalidResponse { url, .. } => { - assert_eq!(Url::parse(&*url), Url::from_str(&*ip_service.uri())); - } - _ => { - panic!("Unexpected error") - } - } + assert_invalid_response!( + actual, + ip_service.url(), + "Failed to parse service response [] into valid ip address" + ); Ok(()) } #[tokio::test] - async fn failed_non_successful_not_an_ip_address() -> Result<(), Box> { + async fn failed_not_an_ip_address() -> Result<(), Box> { let input = plaintext_response("not-an-ip-address".to_string()); - let (ip_service, actual) = run(input).await?; + let (ip_service, actual) = run_identme(input).await?; assert_eq!(1, ip_service.received_requests().await.unwrap().len()); - assert!(actual.is_err()); - - match actual.err().unwrap() { - InvalidResponse { url, .. } => { - assert_eq!(Url::parse(&*url), Url::from_str(&*ip_service.uri())); - } - _ => { - panic!("Unexpected error") - } - } + assert_invalid_response!( + actual, + ip_service.url(), + "Failed to parse service response [not-an-ip-address] into valid ip address" + ); Ok(()) } - async fn run(payload: impl Respond + 'static) -> Result<(MockServer, AppResult), Box> { + #[tokio::test] + async fn failed_timeout() -> Result<(), Box> { + let input = delayed_response(200); + let (ip_service, actual) = run_identme(input).await?; + + assert_eq!(1, ip_service.received_requests().await.unwrap().len()); + assert_request_failed!(actual, ip_service.url()); + + Ok(()) + } + + async fn run_identme( + payload: impl Respond + 'static, + ) -> Result<(MockServer, AppResult), Box> { let ip_service = setup_ipv4_service(payload).await; - let client = ClientBuilder::new().build()?; + let client = ClientBuilder::new() + .timeout(Duration::from_millis(100)) + .build()?; let service = IdentMe { url: ip_service.uri().parse()?, }; @@ -257,7 +260,4 @@ mod tests { let actual = IDENTME(service).resolve(&client).await; Ok((ip_service, actual)) } - - - } -- cgit v1.2.3