use reqwest::Url; use std::error::Error; use std::net::IpAddr; use std::str::FromStr; pub struct IpService {} impl IpService { async fn resolve(config: &impl IpServiceConfiguration) -> Result> { let response = reqwest::get(config.get_service_url()).await.unwrap(); Ok(IpAddr::from_str(&response.text().await.unwrap())?) } } pub trait IpServiceConfiguration { fn get_service_url(&self) -> Url; } #[cfg(test)] mod tests { use super::*; use std::net::Ipv4Addr; use wiremock::matchers::{method, path}; use wiremock::{Mock, MockServer, ResponseTemplate}; async fn setup_ipv4_service(service_path: &str, response: &str) -> MockServer { let service = MockServer::start().await; Mock::given(method("GET")) .and(path(service_path)) .respond_with( ResponseTemplate::new(200) .insert_header("Content-Type", "text/plain; charset=utf-8") .set_body_string(response), ) .mount(&service) .await; service } #[tokio::test] async fn successful_ipv4_address_resolution() -> Result<(), Box> { let service_path = "get-my-ip-address"; let service_response = "17.5.7.8"; let ip_service = setup_ipv4_service(service_path, service_response).await; let service_config = MockConfig::new(&ip_service, service_path); let actual = IpService::resolve(&service_config).await?; assert_eq!(actual, IpAddr::V4(Ipv4Addr::new(17, 5, 7, 8))); assert_eq!(1, ip_service.received_requests().await.unwrap().len()); Ok(()) } struct MockConfig { service_url: Url, } impl MockConfig { fn new(server: &MockServer, path: &str) -> Self { Self { service_url: Url::parse(format!("{}/{}", server.uri(), path).as_str()).unwrap(), } } } impl IpServiceConfiguration for MockConfig { fn get_service_url(&self) -> Url { self.service_url.clone() } } }