aboutsummaryrefslogtreecommitdiff
path: root/src/ip_service.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/ip_service.rs')
-rw-r--r--src/ip_service.rs132
1 files changed, 66 insertions, 66 deletions
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> {
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<dyn Error>> {
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<dyn Error>> {
+ async fn failed_no_reachable_server() -> Result<(), Box<dyn Error>> {
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<dyn Error>> {
+ async fn failed_status_code() -> Result<(), Box<dyn Error>> {
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<dyn Error>> {
+ async fn failed_empty_response() -> Result<(), Box<dyn Error>> {
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<dyn Error>> {
+ async fn failed_not_an_ip_address() -> Result<(), Box<dyn Error>> {
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<IpAddr>), Box<dyn Error>> {
+ #[tokio::test]
+ async fn failed_timeout() -> Result<(), Box<dyn Error>> {
+ 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<IpAddr>), Box<dyn Error>> {
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))
}
-
-
-
}