@@ -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