torrust_tracker_deployer_lib/application/command_handlers/test/
result.rs1use std::fmt;
8use std::net::IpAddr;
9
10use crate::shared::domain_name::DomainName;
11
12#[derive(Debug)]
20pub struct TestResult {
21 pub instance_ip: IpAddr,
23 pub dns_warnings: Vec<DnsWarning>,
25}
26
27impl TestResult {
28 #[must_use]
30 pub fn success(instance_ip: IpAddr) -> Self {
31 Self {
32 instance_ip,
33 dns_warnings: Vec::new(),
34 }
35 }
36
37 #[must_use]
39 pub fn with_dns_warnings(instance_ip: IpAddr, dns_warnings: Vec<DnsWarning>) -> Self {
40 Self {
41 instance_ip,
42 dns_warnings,
43 }
44 }
45
46 #[must_use]
48 pub fn has_dns_warnings(&self) -> bool {
49 !self.dns_warnings.is_empty()
50 }
51}
52
53#[derive(Debug)]
55pub struct DnsWarning {
56 pub domain: DomainName,
58
59 pub expected_ip: IpAddr,
61
62 pub issue: DnsIssue,
64}
65
66impl fmt::Display for DnsWarning {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 match &self.issue {
69 DnsIssue::ResolutionFailed(reason) => {
70 write!(
71 f,
72 "{domain} does not resolve (expected: {ip}): {reason}",
73 domain = self.domain,
74 ip = self.expected_ip,
75 )
76 }
77 DnsIssue::IpMismatch { resolved_ips } => {
78 let ips: Vec<String> = resolved_ips.iter().map(ToString::to_string).collect();
79 write!(
80 f,
81 "{domain} resolves to [{ips}] but expected {expected}",
82 domain = self.domain,
83 ips = ips.join(", "),
84 expected = self.expected_ip,
85 )
86 }
87 }
88 }
89}
90
91#[derive(Debug)]
93pub enum DnsIssue {
94 ResolutionFailed(String),
96
97 IpMismatch {
99 resolved_ips: Vec<IpAddr>,
101 },
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 fn test_ip() -> IpAddr {
109 "10.0.0.1".parse().unwrap()
110 }
111
112 #[test]
113 fn it_should_create_success_result_with_no_warnings() {
114 let result = TestResult::success(test_ip());
115 assert!(!result.has_dns_warnings());
116 assert!(result.dns_warnings.is_empty());
117 assert_eq!(result.instance_ip, test_ip());
118 }
119
120 #[test]
121 fn it_should_create_result_with_dns_warnings() {
122 let warnings = vec![DnsWarning {
123 domain: DomainName::new("tracker.local").unwrap(),
124 expected_ip: "10.0.0.1".parse().unwrap(),
125 issue: DnsIssue::ResolutionFailed("name resolution failed".to_string()),
126 }];
127
128 let result = TestResult::with_dns_warnings(test_ip(), warnings);
129 assert!(result.has_dns_warnings());
130 assert_eq!(result.dns_warnings.len(), 1);
131 }
132
133 #[test]
134 fn it_should_display_resolution_failed_warning() {
135 let warning = DnsWarning {
136 domain: DomainName::new("tracker.local").unwrap(),
137 expected_ip: "10.0.0.1".parse().unwrap(),
138 issue: DnsIssue::ResolutionFailed("name resolution failed".to_string()),
139 };
140
141 let display = format!("{warning}");
142 assert!(display.contains("tracker.local"));
143 assert!(display.contains("does not resolve"));
144 assert!(display.contains("10.0.0.1"));
145 }
146
147 #[test]
148 fn it_should_display_ip_mismatch_warning() {
149 let warning = DnsWarning {
150 domain: DomainName::new("tracker.local").unwrap(),
151 expected_ip: "10.0.0.1".parse().unwrap(),
152 issue: DnsIssue::IpMismatch {
153 resolved_ips: vec!["192.168.1.1".parse().unwrap()],
154 },
155 };
156
157 let display = format!("{warning}");
158 assert!(display.contains("tracker.local"));
159 assert!(display.contains("192.168.1.1"));
160 assert!(display.contains("10.0.0.1"));
161 }
162}