diff options
| author | Gus Power <gus@infinitesidequests.com> | 2025-05-12 10:52:28 +0100 |
|---|---|---|
| committer | Gus Power <gus@infinitesidequests.com> | 2025-05-12 10:52:28 +0100 |
| commit | 1d6fd8359fddbae8aeac17b90ed50d41143ffbb6 (patch) | |
| tree | 7872e1e112ea9ed440caecad9755da751de59ccd | |
| parent | e1f43bb48b23338ce103dc92a53a13e72021299d (diff) | |
skeleton dyndns service, split into ip and dyndns modules
| -rw-r--r-- | src/dyndns_service.rs | 67 | ||||
| -rw-r--r-- | src/ip_service.rs | 74 | ||||
| -rw-r--r-- | src/main.rs | 24 | ||||
| -rw-r--r-- | src/test.rs | 48 |
4 files changed, 143 insertions, 70 deletions
diff --git a/src/dyndns_service.rs b/src/dyndns_service.rs new file mode 100644 index 0000000..caa163d --- /dev/null +++ b/src/dyndns_service.rs @@ -0,0 +1,67 @@ +use reqwest::ClientBuilder; +use std::error::Error; + +struct DynDnsService {} + +impl DynDnsService { + pub async fn register(config: &impl DynDnsServiceConfiguration) -> Result<(), Box<dyn Error>> { + let client = ClientBuilder::new().build()?; + client.post(config.get_service_url()).send().await?; + Ok(()) + } +} + +trait DynDnsServiceConfiguration { + fn get_service_url(&self) -> String; +} + +#[cfg(test)] +mod tests { + use super::*; + use std::error::Error; + use wiremock::matchers::{method, path}; + use wiremock::{Mock, MockServer, ResponseTemplate}; + + async fn setup_dyndns_service(service_path: &str) -> MockServer { + let service = MockServer::start().await; + + Mock::given(method("POST")) + .and(path(service_path)) + .respond_with(ResponseTemplate::new(200)) + .mount(&service) + .await; + + service + } + + #[tokio::test] + async fn successful_dyndns_registration() -> Result<(), Box<dyn Error>> { + let service_path = "register-my-ip-address"; + + let dyndns_service = setup_dyndns_service(service_path).await; + let service_config = TestDynDnsServiceConfiguration::new(&dyndns_service, service_path); + + DynDnsService::register(&service_config).await?; + assert_eq!(1, dyndns_service.received_requests().await.unwrap().len()); + + Ok(()) + } + + struct TestDynDnsServiceConfiguration { + service_path: String, + } + + impl TestDynDnsServiceConfiguration { + fn new(server: &MockServer, path: &str) -> Self { + Self { + service_path: format!("{}/{}", server.uri(), path), + } + } + } + + impl DynDnsServiceConfiguration for TestDynDnsServiceConfiguration { + fn get_service_url(&self) -> String { + self.service_path.clone() + } + } +} diff --git a/src/ip_service.rs b/src/ip_service.rs new file mode 100644 index 0000000..8e2bee8 --- /dev/null +++ b/src/ip_service.rs @@ -0,0 +1,74 @@ +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<IpAddr, Box<dyn Error>> { + 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<dyn Error>> { + 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() + } + } +} diff --git a/src/main.rs b/src/main.rs index 428ebf9..1ae22b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,26 +1,6 @@ -use std::error::Error; -use std::net::IpAddr; -use std::str::FromStr; -use reqwest::Url; - -#[cfg(test)] -mod test; +mod dyndns_service; +mod ip_service; fn main() { println!("Hello, world!"); } - -pub struct IpService {} - -impl IpService { - - async fn resolve(config: &impl IpServiceConfiguration) -> Result<IpAddr, Box<dyn Error>> { - 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; -} diff --git a/src/test.rs b/src/test.rs deleted file mode 100644 index f2c4219..0000000 --- a/src/test.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::net::{IpAddr, Ipv4Addr}; -use reqwest::Url; -use wiremock::{Mock, MockServer, ResponseTemplate}; -use wiremock::matchers::{method, path}; -use crate::{IpService, IpServiceConfiguration}; - -async fn setup_ipv4_service(service_path: &str, response: &str) -> MockServer { - let ip_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(&ip_service) - .await; - - ip_service -} - -#[tokio::test] -async fn successful_ipv4_address_resolution() { - 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 = TestIpServiceConfiguration::new(&ip_service, service_path); - - let actual = IpService::resolve(&service_config).await.unwrap(); - assert_eq!(actual, IpAddr::V4(Ipv4Addr::new(17, 5, 7, 8))) -} - -struct TestIpServiceConfiguration { - service_url: Url, -} - -impl TestIpServiceConfiguration { - fn new(server: &MockServer, path: &str) -> Self { - Self { - service_url: Url::parse(format!("{}/{}", server.uri(), path).as_str()).unwrap() - } - } -} -impl IpServiceConfiguration for TestIpServiceConfiguration { - fn get_service_url(&self) -> Url { - self.service_url.clone() - } -} |
