Skip to content

Commit 554d32c

Browse files
committed
Merge #347: test: add comprehensive unit tests for endpoint_builder module
7621c1e test: add comprehensive unit tests for endpoint_builder module (Jose Celano) Pull request description: ## Description This PR adds comprehensive unit tests for the `endpoint_builder` module in the application layer's common command handlers. ## Changes - Added 16 unit tests covering all three public functions: - `build_api_endpoint()` - 5 tests - `build_http_tracker_endpoint()` - 5 tests - `build_all_tracker_endpoints()` - 6 tests ## Test Coverage The tests cover: - ✅ HTTP endpoint building (without TLS) - ✅ HTTPS endpoint building (with TLS) - ✅ Correct path handling (`/api/health_check` vs `/health_check`) - ✅ Port extraction from configuration - ✅ Instance IP usage and propagation - ✅ Multiple tracker endpoint building - ✅ Empty tracker list handling - ✅ Port configuration preservation ## Testing Approach Following the project's unit testing conventions: - Tests are in the same file using `#[cfg(test)] mod tests { ... }` - Using behavior-driven naming: `it_should_{behavior}_when_{condition}` - Pure unit tests with no external dependencies beyond domain types ## Pre-commit Checks All pre-commit checks pass: - ✅ Cargo machete (no unused dependencies) - ✅ Markdown linting - ✅ YAML linting - ✅ TOML linting - ✅ Spell checking - ✅ Clippy (all warnings fixed) - ✅ Rustfmt - ✅ ShellCheck - ✅ All tests pass (16/16 new tests + existing test suite) ACKs for top commit: josecelano: ACK 7621c1e Tree-SHA512: 72850de7a2e8bcfb7db45814b71d66dcd3e42802ee6dce22716a5d33ed6e784e4fcf795c5cdd355853e325de932576d31b6dabb5fd98e00a3a1c2fc5c4034ad1
2 parents b402562 + 7621c1e commit 554d32c

1 file changed

Lines changed: 320 additions & 0 deletions

File tree

src/application/command_handlers/common/endpoint_builder.rs

Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,323 @@ pub fn build_all_tracker_endpoints(
126126

127127
(api_endpoint, http_tracker_endpoints)
128128
}
129+
130+
#[cfg(test)]
131+
mod tests {
132+
use std::net::{IpAddr, Ipv4Addr};
133+
134+
use super::*;
135+
use crate::domain::tracker::config::{
136+
DatabaseConfig, HealthCheckApiConfig, HttpApiConfig, HttpTrackerConfig, SqliteConfig,
137+
TrackerConfig, TrackerCoreConfig, UdpTrackerConfig,
138+
};
139+
use crate::shared::{ApiToken, DomainName};
140+
141+
// Test fixtures
142+
143+
fn test_ip() -> IpAddr {
144+
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))
145+
}
146+
147+
fn http_api_config_without_tls() -> HttpApiConfig {
148+
HttpApiConfig::new(
149+
"0.0.0.0:1212".parse().unwrap(),
150+
ApiToken::from("test_token".to_string()),
151+
None,
152+
false,
153+
)
154+
.expect("valid config")
155+
}
156+
157+
fn http_api_config_with_tls() -> HttpApiConfig {
158+
HttpApiConfig::new(
159+
"0.0.0.0:1212".parse().unwrap(),
160+
ApiToken::from("test_token".to_string()),
161+
Some(DomainName::new("api.tracker.local").unwrap()),
162+
true,
163+
)
164+
.expect("valid config")
165+
}
166+
167+
fn http_tracker_config_without_tls() -> HttpTrackerConfig {
168+
HttpTrackerConfig::new("0.0.0.0:7070".parse().unwrap(), None, false).expect("valid config")
169+
}
170+
171+
fn http_tracker_config_with_tls() -> HttpTrackerConfig {
172+
HttpTrackerConfig::new(
173+
"0.0.0.0:7070".parse().unwrap(),
174+
Some(DomainName::new("tracker.example.com").unwrap()),
175+
true,
176+
)
177+
.expect("valid config")
178+
}
179+
180+
fn tracker_config_with_one_http_tracker() -> TrackerConfig {
181+
TrackerConfig::new(
182+
TrackerCoreConfig::new(
183+
DatabaseConfig::Sqlite(SqliteConfig::new("tracker.db").unwrap()),
184+
false,
185+
),
186+
vec![UdpTrackerConfig::new("0.0.0.0:6969".parse().unwrap(), None).unwrap()],
187+
vec![http_tracker_config_without_tls()],
188+
http_api_config_without_tls(),
189+
HealthCheckApiConfig::new("127.0.0.1:1313".parse().unwrap(), None, false).unwrap(),
190+
)
191+
.expect("valid config")
192+
}
193+
194+
fn tracker_config_with_multiple_http_trackers() -> TrackerConfig {
195+
TrackerConfig::new(
196+
TrackerCoreConfig::new(
197+
DatabaseConfig::Sqlite(SqliteConfig::new("tracker.db").unwrap()),
198+
false,
199+
),
200+
vec![],
201+
vec![
202+
HttpTrackerConfig::new("0.0.0.0:7070".parse().unwrap(), None, false)
203+
.expect("valid config"),
204+
HttpTrackerConfig::new("0.0.0.0:8080".parse().unwrap(), None, false)
205+
.expect("valid config"),
206+
HttpTrackerConfig::new(
207+
"0.0.0.0:9090".parse().unwrap(),
208+
Some(DomainName::new("tracker.example.com").unwrap()),
209+
true,
210+
)
211+
.expect("valid config"),
212+
],
213+
http_api_config_with_tls(),
214+
HealthCheckApiConfig::new("127.0.0.1:1313".parse().unwrap(), None, false).unwrap(),
215+
)
216+
.expect("valid config")
217+
}
218+
219+
// Tests for build_api_endpoint
220+
221+
#[test]
222+
fn it_should_build_http_api_endpoint_when_tls_is_disabled() {
223+
let config = http_api_config_without_tls();
224+
let endpoint = build_api_endpoint(test_ip(), &config);
225+
226+
assert!(!endpoint.uses_tls());
227+
assert_eq!(endpoint.server_ip(), test_ip());
228+
assert_eq!(endpoint.port(), 1212);
229+
assert_eq!(
230+
endpoint.url().as_str(),
231+
"http://10.0.0.1:1212/api/health_check" // DevSkim: ignore DS137138
232+
);
233+
}
234+
235+
#[test]
236+
fn it_should_build_https_api_endpoint_when_tls_is_enabled() {
237+
let config = http_api_config_with_tls();
238+
let endpoint = build_api_endpoint(test_ip(), &config);
239+
240+
assert!(endpoint.uses_tls());
241+
assert_eq!(endpoint.server_ip(), test_ip());
242+
assert_eq!(endpoint.port(), 443);
243+
assert_eq!(endpoint.domain(), Some("api.tracker.local"));
244+
assert_eq!(
245+
endpoint.url().as_str(),
246+
"https://api.tracker.local/api/health_check"
247+
);
248+
}
249+
250+
#[test]
251+
fn it_should_use_correct_path_when_building_api_endpoint() {
252+
let config = http_api_config_without_tls();
253+
let endpoint = build_api_endpoint(test_ip(), &config);
254+
255+
assert_eq!(endpoint.url().path(), "/api/health_check");
256+
}
257+
258+
#[test]
259+
fn it_should_extract_port_from_config_when_building_api_endpoint() {
260+
let config = HttpApiConfig::new(
261+
"0.0.0.0:9999".parse().unwrap(),
262+
ApiToken::from("test".to_string()),
263+
None,
264+
false,
265+
)
266+
.expect("valid config");
267+
268+
let endpoint = build_api_endpoint(test_ip(), &config);
269+
270+
assert_eq!(endpoint.port(), 9999);
271+
}
272+
273+
#[test]
274+
fn it_should_use_instance_ip_when_building_api_endpoint() {
275+
let different_ip = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100));
276+
let config = http_api_config_without_tls();
277+
278+
let endpoint = build_api_endpoint(different_ip, &config);
279+
280+
assert_eq!(endpoint.server_ip(), different_ip);
281+
}
282+
283+
// Tests for build_http_tracker_endpoint
284+
285+
#[test]
286+
fn it_should_build_http_tracker_endpoint_when_tls_is_disabled() {
287+
let config = http_tracker_config_without_tls();
288+
let endpoint = build_http_tracker_endpoint(test_ip(), &config);
289+
290+
assert!(!endpoint.uses_tls());
291+
assert_eq!(endpoint.server_ip(), test_ip());
292+
assert_eq!(endpoint.port(), 7070);
293+
assert_eq!(
294+
endpoint.url().as_str(),
295+
"http://10.0.0.1:7070/health_check" // DevSkim: ignore DS137138
296+
);
297+
}
298+
299+
#[test]
300+
fn it_should_build_https_tracker_endpoint_when_tls_is_enabled() {
301+
let config = http_tracker_config_with_tls();
302+
let endpoint = build_http_tracker_endpoint(test_ip(), &config);
303+
304+
assert!(endpoint.uses_tls());
305+
assert_eq!(endpoint.server_ip(), test_ip());
306+
assert_eq!(endpoint.port(), 443);
307+
assert_eq!(endpoint.domain(), Some("tracker.example.com"));
308+
assert_eq!(
309+
endpoint.url().as_str(),
310+
"https://tracker.example.com/health_check"
311+
);
312+
}
313+
314+
#[test]
315+
fn it_should_use_correct_path_when_building_tracker_endpoint() {
316+
let config = http_tracker_config_without_tls();
317+
let endpoint = build_http_tracker_endpoint(test_ip(), &config);
318+
319+
assert_eq!(endpoint.url().path(), "/health_check");
320+
}
321+
322+
#[test]
323+
fn it_should_extract_port_from_config_when_building_tracker_endpoint() {
324+
let config = HttpTrackerConfig::new("0.0.0.0:8888".parse().unwrap(), None, false)
325+
.expect("valid config");
326+
327+
let endpoint = build_http_tracker_endpoint(test_ip(), &config);
328+
329+
assert_eq!(endpoint.port(), 8888);
330+
}
331+
332+
#[test]
333+
fn it_should_use_instance_ip_when_building_tracker_endpoint() {
334+
let different_ip = IpAddr::V4(Ipv4Addr::new(172, 16, 0, 50));
335+
let config = http_tracker_config_without_tls();
336+
337+
let endpoint = build_http_tracker_endpoint(different_ip, &config);
338+
339+
assert_eq!(endpoint.server_ip(), different_ip);
340+
}
341+
342+
// Tests for build_all_tracker_endpoints
343+
344+
#[test]
345+
fn it_should_build_api_and_tracker_endpoints_when_given_tracker_config() {
346+
let config = tracker_config_with_one_http_tracker();
347+
348+
let (api_endpoint, tracker_endpoints) = build_all_tracker_endpoints(test_ip(), &config);
349+
350+
// Verify API endpoint
351+
assert!(!api_endpoint.uses_tls());
352+
assert_eq!(api_endpoint.port(), 1212);
353+
assert_eq!(api_endpoint.url().path(), "/api/health_check");
354+
355+
// Verify tracker endpoints
356+
assert_eq!(tracker_endpoints.len(), 1);
357+
assert!(!tracker_endpoints[0].uses_tls());
358+
assert_eq!(tracker_endpoints[0].port(), 7070);
359+
assert_eq!(tracker_endpoints[0].url().path(), "/health_check");
360+
}
361+
362+
#[test]
363+
fn it_should_build_multiple_tracker_endpoints_when_multiple_trackers_configured() {
364+
let config = tracker_config_with_multiple_http_trackers();
365+
366+
let (_api_endpoint, tracker_endpoints) = build_all_tracker_endpoints(test_ip(), &config);
367+
368+
assert_eq!(tracker_endpoints.len(), 3);
369+
370+
// First tracker (HTTP on 7070)
371+
assert!(!tracker_endpoints[0].uses_tls());
372+
assert_eq!(tracker_endpoints[0].port(), 7070);
373+
374+
// Second tracker (HTTP on 8080)
375+
assert!(!tracker_endpoints[1].uses_tls());
376+
assert_eq!(tracker_endpoints[1].port(), 8080);
377+
378+
// Third tracker (HTTPS on 9090 -> 443)
379+
assert!(tracker_endpoints[2].uses_tls());
380+
assert_eq!(tracker_endpoints[2].port(), 443);
381+
assert_eq!(tracker_endpoints[2].domain(), Some("tracker.example.com"));
382+
}
383+
384+
#[test]
385+
fn it_should_build_tls_api_endpoint_when_tracker_config_has_tls_enabled() {
386+
let config = tracker_config_with_multiple_http_trackers();
387+
388+
let (api_endpoint, _tracker_endpoints) = build_all_tracker_endpoints(test_ip(), &config);
389+
390+
assert!(api_endpoint.uses_tls());
391+
assert_eq!(api_endpoint.domain(), Some("api.tracker.local"));
392+
assert_eq!(
393+
api_endpoint.url().as_str(),
394+
"https://api.tracker.local/api/health_check"
395+
);
396+
}
397+
398+
#[test]
399+
fn it_should_return_empty_tracker_list_when_no_http_trackers_configured() {
400+
let config = TrackerConfig::new(
401+
TrackerCoreConfig::new(
402+
DatabaseConfig::Sqlite(SqliteConfig::new("tracker.db").unwrap()),
403+
false,
404+
),
405+
vec![UdpTrackerConfig::new("0.0.0.0:6969".parse().unwrap(), None).unwrap()],
406+
vec![], // No HTTP trackers
407+
http_api_config_without_tls(),
408+
HealthCheckApiConfig::new("127.0.0.1:1313".parse().unwrap(), None, false).unwrap(),
409+
)
410+
.expect("valid config");
411+
412+
let (_api_endpoint, tracker_endpoints) = build_all_tracker_endpoints(test_ip(), &config);
413+
414+
assert!(tracker_endpoints.is_empty());
415+
}
416+
417+
#[test]
418+
fn it_should_use_same_instance_ip_for_all_endpoints_when_building_all() {
419+
let config = tracker_config_with_multiple_http_trackers();
420+
let specific_ip = IpAddr::V4(Ipv4Addr::new(203, 0, 113, 42));
421+
422+
let (api_endpoint, tracker_endpoints) = build_all_tracker_endpoints(specific_ip, &config);
423+
424+
// All endpoints should use the same instance IP
425+
assert_eq!(api_endpoint.server_ip(), specific_ip);
426+
for endpoint in &tracker_endpoints {
427+
assert_eq!(endpoint.server_ip(), specific_ip);
428+
}
429+
}
430+
431+
#[test]
432+
fn it_should_preserve_individual_port_configurations_when_building_all() {
433+
let config = tracker_config_with_multiple_http_trackers();
434+
435+
let (_api_endpoint, tracker_endpoints) = build_all_tracker_endpoints(test_ip(), &config);
436+
437+
// Each tracker should preserve its configured port (or use 443 for HTTPS)
438+
let ports: Vec<u16> = tracker_endpoints
439+
.iter()
440+
.map(ServiceEndpoint::port)
441+
.collect();
442+
443+
// First two are HTTP (use configured ports), third is HTTPS (uses 443)
444+
assert_eq!(ports[0], 7070);
445+
assert_eq!(ports[1], 8080);
446+
assert_eq!(ports[2], 443); // HTTPS default port
447+
}
448+
}

0 commit comments

Comments
 (0)