@@ -155,10 +155,397 @@ impl From<i32> for PeersWanted {
155
155
}
156
156
157
157
#[ must_use]
158
- pub fn assign_ip_address_to_peer ( remote_client_ip : & IpAddr , tracker_external_ip : Option < IpAddr > ) -> IpAddr {
158
+ fn assign_ip_address_to_peer ( remote_client_ip : & IpAddr , tracker_external_ip : Option < IpAddr > ) -> IpAddr {
159
159
if let Some ( host_ip) = tracker_external_ip. filter ( |_| remote_client_ip. is_loopback ( ) ) {
160
160
host_ip
161
161
} else {
162
162
* remote_client_ip
163
163
}
164
164
}
165
+
166
+ #[ cfg( test) ]
167
+ mod tests {
168
+ // Integration tests for the core module.
169
+
170
+ mod the_announce_handler {
171
+
172
+ use std:: net:: { IpAddr , Ipv4Addr , SocketAddr } ;
173
+ use std:: str:: FromStr ;
174
+ use std:: sync:: Arc ;
175
+
176
+ use aquatic_udp_protocol:: { AnnounceEvent , NumberOfBytes , PeerId } ;
177
+ use bittorrent_primitives:: info_hash:: InfoHash ;
178
+ use torrust_tracker_primitives:: peer:: Peer ;
179
+ use torrust_tracker_primitives:: DurationSinceUnixEpoch ;
180
+ use torrust_tracker_test_helpers:: configuration;
181
+
182
+ use crate :: app_test:: initialize_tracker_dependencies;
183
+ use crate :: core:: announce_handler:: AnnounceHandler ;
184
+ use crate :: core:: scrape_handler:: ScrapeHandler ;
185
+ use crate :: core:: torrent:: manager:: TorrentsManager ;
186
+ use crate :: core:: torrent:: repository:: in_memory:: InMemoryTorrentRepository ;
187
+
188
+ fn public_tracker ( ) -> ( Arc < AnnounceHandler > , Arc < InMemoryTorrentRepository > , Arc < ScrapeHandler > ) {
189
+ let config = configuration:: ephemeral_public ( ) ;
190
+
191
+ let (
192
+ _database,
193
+ _in_memory_whitelist,
194
+ whitelist_authorization,
195
+ _authentication_service,
196
+ in_memory_torrent_repository,
197
+ db_torrent_repository,
198
+ _torrents_manager,
199
+ ) = initialize_tracker_dependencies ( & config) ;
200
+
201
+ let announce_handler = Arc :: new ( AnnounceHandler :: new (
202
+ & config. core ,
203
+ & in_memory_torrent_repository,
204
+ & db_torrent_repository,
205
+ ) ) ;
206
+
207
+ let scrape_handler = Arc :: new ( ScrapeHandler :: new ( & whitelist_authorization, & in_memory_torrent_repository) ) ;
208
+
209
+ ( announce_handler, in_memory_torrent_repository, scrape_handler)
210
+ }
211
+
212
+ pub fn tracker_persisting_torrents_in_database (
213
+ ) -> ( Arc < AnnounceHandler > , Arc < TorrentsManager > , Arc < InMemoryTorrentRepository > ) {
214
+ let mut config = configuration:: ephemeral_listed ( ) ;
215
+ config. core . tracker_policy . persistent_torrent_completed_stat = true ;
216
+
217
+ let (
218
+ _database,
219
+ _in_memory_whitelist,
220
+ _whitelist_authorization,
221
+ _authentication_service,
222
+ in_memory_torrent_repository,
223
+ db_torrent_repository,
224
+ torrents_manager,
225
+ ) = initialize_tracker_dependencies ( & config) ;
226
+
227
+ let announce_handler = Arc :: new ( AnnounceHandler :: new (
228
+ & config. core ,
229
+ & in_memory_torrent_repository,
230
+ & db_torrent_repository,
231
+ ) ) ;
232
+
233
+ ( announce_handler, torrents_manager, in_memory_torrent_repository)
234
+ }
235
+
236
+ fn sample_info_hash ( ) -> InfoHash {
237
+ "3b245504cf5f11bbdbe1201cea6a6bf45aee1bc0" . parse :: < InfoHash > ( ) . unwrap ( )
238
+ }
239
+
240
+ // The client peer IP
241
+ fn peer_ip ( ) -> IpAddr {
242
+ IpAddr :: V4 ( Ipv4Addr :: from_str ( "126.0.0.1" ) . unwrap ( ) )
243
+ }
244
+
245
+ /// Sample peer whose state is not relevant for the tests
246
+ fn sample_peer ( ) -> Peer {
247
+ complete_peer ( )
248
+ }
249
+
250
+ /// Sample peer when for tests that need more than one peer
251
+ fn sample_peer_1 ( ) -> Peer {
252
+ Peer {
253
+ peer_id : PeerId ( * b"-qB00000000000000001" ) ,
254
+ peer_addr : SocketAddr :: new ( IpAddr :: V4 ( Ipv4Addr :: new ( 126 , 0 , 0 , 1 ) ) , 8081 ) ,
255
+ updated : DurationSinceUnixEpoch :: new ( 1_669_397_478_934 , 0 ) ,
256
+ uploaded : NumberOfBytes :: new ( 0 ) ,
257
+ downloaded : NumberOfBytes :: new ( 0 ) ,
258
+ left : NumberOfBytes :: new ( 0 ) ,
259
+ event : AnnounceEvent :: Completed ,
260
+ }
261
+ }
262
+
263
+ /// Sample peer when for tests that need more than one peer
264
+ fn sample_peer_2 ( ) -> Peer {
265
+ Peer {
266
+ peer_id : PeerId ( * b"-qB00000000000000002" ) ,
267
+ peer_addr : SocketAddr :: new ( IpAddr :: V4 ( Ipv4Addr :: new ( 126 , 0 , 0 , 2 ) ) , 8082 ) ,
268
+ updated : DurationSinceUnixEpoch :: new ( 1_669_397_478_934 , 0 ) ,
269
+ uploaded : NumberOfBytes :: new ( 0 ) ,
270
+ downloaded : NumberOfBytes :: new ( 0 ) ,
271
+ left : NumberOfBytes :: new ( 0 ) ,
272
+ event : AnnounceEvent :: Completed ,
273
+ }
274
+ }
275
+
276
+ fn seeder ( ) -> Peer {
277
+ complete_peer ( )
278
+ }
279
+
280
+ fn leecher ( ) -> Peer {
281
+ incomplete_peer ( )
282
+ }
283
+
284
+ fn started_peer ( ) -> Peer {
285
+ incomplete_peer ( )
286
+ }
287
+
288
+ fn completed_peer ( ) -> Peer {
289
+ complete_peer ( )
290
+ }
291
+
292
+ /// A peer that counts as `complete` is swarm metadata
293
+ /// IMPORTANT!: it only counts if the it has been announce at least once before
294
+ /// announcing the `AnnounceEvent::Completed` event.
295
+ fn complete_peer ( ) -> Peer {
296
+ Peer {
297
+ peer_id : PeerId ( * b"-qB00000000000000000" ) ,
298
+ peer_addr : SocketAddr :: new ( IpAddr :: V4 ( Ipv4Addr :: new ( 126 , 0 , 0 , 1 ) ) , 8080 ) ,
299
+ updated : DurationSinceUnixEpoch :: new ( 1_669_397_478_934 , 0 ) ,
300
+ uploaded : NumberOfBytes :: new ( 0 ) ,
301
+ downloaded : NumberOfBytes :: new ( 0 ) ,
302
+ left : NumberOfBytes :: new ( 0 ) , // No bytes left to download
303
+ event : AnnounceEvent :: Completed ,
304
+ }
305
+ }
306
+
307
+ /// A peer that counts as `incomplete` is swarm metadata
308
+ fn incomplete_peer ( ) -> Peer {
309
+ Peer {
310
+ peer_id : PeerId ( * b"-qB00000000000000000" ) ,
311
+ peer_addr : SocketAddr :: new ( IpAddr :: V4 ( Ipv4Addr :: new ( 126 , 0 , 0 , 1 ) ) , 8080 ) ,
312
+ updated : DurationSinceUnixEpoch :: new ( 1_669_397_478_934 , 0 ) ,
313
+ uploaded : NumberOfBytes :: new ( 0 ) ,
314
+ downloaded : NumberOfBytes :: new ( 0 ) ,
315
+ left : NumberOfBytes :: new ( 1000 ) , // Still bytes to download
316
+ event : AnnounceEvent :: Started ,
317
+ }
318
+ }
319
+
320
+ mod for_all_tracker_config_modes {
321
+
322
+ mod handling_an_announce_request {
323
+
324
+ use std:: sync:: Arc ;
325
+
326
+ use crate :: core:: announce_handler:: tests:: the_announce_handler:: {
327
+ peer_ip, public_tracker, sample_info_hash, sample_peer, sample_peer_1, sample_peer_2,
328
+ } ;
329
+ use crate :: core:: announce_handler:: PeersWanted ;
330
+
331
+ mod should_assign_the_ip_to_the_peer {
332
+
333
+ use std:: net:: { IpAddr , Ipv4Addr } ;
334
+
335
+ use crate :: core:: announce_handler:: assign_ip_address_to_peer;
336
+
337
+ #[ test]
338
+ fn using_the_source_ip_instead_of_the_ip_in_the_announce_request ( ) {
339
+ let remote_ip = IpAddr :: V4 ( Ipv4Addr :: new ( 126 , 0 , 0 , 2 ) ) ;
340
+
341
+ let peer_ip = assign_ip_address_to_peer ( & remote_ip, None ) ;
342
+
343
+ assert_eq ! ( peer_ip, remote_ip) ;
344
+ }
345
+
346
+ mod and_when_the_client_ip_is_a_ipv4_loopback_ip {
347
+
348
+ use std:: net:: { IpAddr , Ipv4Addr , Ipv6Addr } ;
349
+ use std:: str:: FromStr ;
350
+
351
+ use crate :: core:: announce_handler:: assign_ip_address_to_peer;
352
+
353
+ #[ test]
354
+ fn it_should_use_the_loopback_ip_if_the_tracker_does_not_have_the_external_ip_configuration ( ) {
355
+ let remote_ip = IpAddr :: V4 ( Ipv4Addr :: LOCALHOST ) ;
356
+
357
+ let peer_ip = assign_ip_address_to_peer ( & remote_ip, None ) ;
358
+
359
+ assert_eq ! ( peer_ip, remote_ip) ;
360
+ }
361
+
362
+ #[ test]
363
+ fn it_should_use_the_external_tracker_ip_in_tracker_configuration_if_it_is_defined ( ) {
364
+ let remote_ip = IpAddr :: V4 ( Ipv4Addr :: LOCALHOST ) ;
365
+
366
+ let tracker_external_ip = IpAddr :: V4 ( Ipv4Addr :: from_str ( "126.0.0.1" ) . unwrap ( ) ) ;
367
+
368
+ let peer_ip = assign_ip_address_to_peer ( & remote_ip, Some ( tracker_external_ip) ) ;
369
+
370
+ assert_eq ! ( peer_ip, tracker_external_ip) ;
371
+ }
372
+
373
+ #[ test]
374
+ fn it_should_use_the_external_ip_in_the_tracker_configuration_if_it_is_defined_even_if_the_external_ip_is_an_ipv6_ip (
375
+ ) {
376
+ let remote_ip = IpAddr :: V4 ( Ipv4Addr :: LOCALHOST ) ;
377
+
378
+ let tracker_external_ip =
379
+ IpAddr :: V6 ( Ipv6Addr :: from_str ( "2345:0425:2CA1:0000:0000:0567:5673:23b5" ) . unwrap ( ) ) ;
380
+
381
+ let peer_ip = assign_ip_address_to_peer ( & remote_ip, Some ( tracker_external_ip) ) ;
382
+
383
+ assert_eq ! ( peer_ip, tracker_external_ip) ;
384
+ }
385
+ }
386
+
387
+ mod and_when_client_ip_is_a_ipv6_loopback_ip {
388
+
389
+ use std:: net:: { IpAddr , Ipv4Addr , Ipv6Addr } ;
390
+ use std:: str:: FromStr ;
391
+
392
+ use crate :: core:: announce_handler:: assign_ip_address_to_peer;
393
+
394
+ #[ test]
395
+ fn it_should_use_the_loopback_ip_if_the_tracker_does_not_have_the_external_ip_configuration ( ) {
396
+ let remote_ip = IpAddr :: V6 ( Ipv6Addr :: LOCALHOST ) ;
397
+
398
+ let peer_ip = assign_ip_address_to_peer ( & remote_ip, None ) ;
399
+
400
+ assert_eq ! ( peer_ip, remote_ip) ;
401
+ }
402
+
403
+ #[ test]
404
+ fn it_should_use_the_external_ip_in_tracker_configuration_if_it_is_defined ( ) {
405
+ let remote_ip = IpAddr :: V6 ( Ipv6Addr :: LOCALHOST ) ;
406
+
407
+ let tracker_external_ip =
408
+ IpAddr :: V6 ( Ipv6Addr :: from_str ( "2345:0425:2CA1:0000:0000:0567:5673:23b5" ) . unwrap ( ) ) ;
409
+
410
+ let peer_ip = assign_ip_address_to_peer ( & remote_ip, Some ( tracker_external_ip) ) ;
411
+
412
+ assert_eq ! ( peer_ip, tracker_external_ip) ;
413
+ }
414
+
415
+ #[ test]
416
+ fn it_should_use_the_external_ip_in_the_tracker_configuration_if_it_is_defined_even_if_the_external_ip_is_an_ipv4_ip (
417
+ ) {
418
+ let remote_ip = IpAddr :: V6 ( Ipv6Addr :: LOCALHOST ) ;
419
+
420
+ let tracker_external_ip = IpAddr :: V4 ( Ipv4Addr :: from_str ( "126.0.0.1" ) . unwrap ( ) ) ;
421
+
422
+ let peer_ip = assign_ip_address_to_peer ( & remote_ip, Some ( tracker_external_ip) ) ;
423
+
424
+ assert_eq ! ( peer_ip, tracker_external_ip) ;
425
+ }
426
+ }
427
+ }
428
+
429
+ #[ tokio:: test]
430
+ async fn it_should_return_the_announce_data_with_an_empty_peer_list_when_it_is_the_first_announced_peer ( ) {
431
+ let ( announce_handler, _in_memory_torrent_repository, _scrape_handler) = public_tracker ( ) ;
432
+
433
+ let mut peer = sample_peer ( ) ;
434
+
435
+ let announce_data = announce_handler. announce ( & sample_info_hash ( ) , & mut peer, & peer_ip ( ) , & PeersWanted :: All ) ;
436
+
437
+ assert_eq ! ( announce_data. peers, vec![ ] ) ;
438
+ }
439
+
440
+ #[ tokio:: test]
441
+ async fn it_should_return_the_announce_data_with_the_previously_announced_peers ( ) {
442
+ let ( announce_handler, _in_memory_torrent_repository, _scrape_handler) = public_tracker ( ) ;
443
+
444
+ let mut previously_announced_peer = sample_peer_1 ( ) ;
445
+ announce_handler. announce (
446
+ & sample_info_hash ( ) ,
447
+ & mut previously_announced_peer,
448
+ & peer_ip ( ) ,
449
+ & PeersWanted :: All ,
450
+ ) ;
451
+
452
+ let mut peer = sample_peer_2 ( ) ;
453
+ let announce_data = announce_handler. announce ( & sample_info_hash ( ) , & mut peer, & peer_ip ( ) , & PeersWanted :: All ) ;
454
+
455
+ assert_eq ! ( announce_data. peers, vec![ Arc :: new( previously_announced_peer) ] ) ;
456
+ }
457
+
458
+ mod it_should_update_the_swarm_stats_for_the_torrent {
459
+
460
+ use crate :: core:: announce_handler:: tests:: the_announce_handler:: {
461
+ completed_peer, leecher, peer_ip, public_tracker, sample_info_hash, seeder, started_peer,
462
+ } ;
463
+ use crate :: core:: announce_handler:: PeersWanted ;
464
+
465
+ #[ tokio:: test]
466
+ async fn when_the_peer_is_a_seeder ( ) {
467
+ let ( announce_handler, _in_memory_torrent_repository, _scrape_handler) = public_tracker ( ) ;
468
+
469
+ let mut peer = seeder ( ) ;
470
+
471
+ let announce_data =
472
+ announce_handler. announce ( & sample_info_hash ( ) , & mut peer, & peer_ip ( ) , & PeersWanted :: All ) ;
473
+
474
+ assert_eq ! ( announce_data. stats. complete, 1 ) ;
475
+ }
476
+
477
+ #[ tokio:: test]
478
+ async fn when_the_peer_is_a_leecher ( ) {
479
+ let ( announce_handler, _in_memory_torrent_repository, _scrape_handler) = public_tracker ( ) ;
480
+
481
+ let mut peer = leecher ( ) ;
482
+
483
+ let announce_data =
484
+ announce_handler. announce ( & sample_info_hash ( ) , & mut peer, & peer_ip ( ) , & PeersWanted :: All ) ;
485
+
486
+ assert_eq ! ( announce_data. stats. incomplete, 1 ) ;
487
+ }
488
+
489
+ #[ tokio:: test]
490
+ async fn when_a_previously_announced_started_peer_has_completed_downloading ( ) {
491
+ let ( announce_handler, _in_memory_torrent_repository, _scrape_handler) = public_tracker ( ) ;
492
+
493
+ // We have to announce with "started" event because peer does not count if peer was not previously known
494
+ let mut started_peer = started_peer ( ) ;
495
+ announce_handler. announce ( & sample_info_hash ( ) , & mut started_peer, & peer_ip ( ) , & PeersWanted :: All ) ;
496
+
497
+ let mut completed_peer = completed_peer ( ) ;
498
+ let announce_data =
499
+ announce_handler. announce ( & sample_info_hash ( ) , & mut completed_peer, & peer_ip ( ) , & PeersWanted :: All ) ;
500
+
501
+ assert_eq ! ( announce_data. stats. downloaded, 1 ) ;
502
+ }
503
+ }
504
+ }
505
+ }
506
+
507
+ mod handling_torrent_persistence {
508
+
509
+ use aquatic_udp_protocol:: AnnounceEvent ;
510
+ use torrust_tracker_torrent_repository:: entry:: EntrySync ;
511
+
512
+ use crate :: core:: announce_handler:: tests:: the_announce_handler:: {
513
+ peer_ip, sample_info_hash, sample_peer, tracker_persisting_torrents_in_database,
514
+ } ;
515
+ use crate :: core:: announce_handler:: PeersWanted ;
516
+
517
+ #[ tokio:: test]
518
+ async fn it_should_persist_the_number_of_completed_peers_for_all_torrents_into_the_database ( ) {
519
+ let ( announce_handler, torrents_manager, in_memory_torrent_repository) =
520
+ tracker_persisting_torrents_in_database ( ) ;
521
+
522
+ let info_hash = sample_info_hash ( ) ;
523
+
524
+ let mut peer = sample_peer ( ) ;
525
+
526
+ peer. event = AnnounceEvent :: Started ;
527
+ let announce_data = announce_handler. announce ( & info_hash, & mut peer, & peer_ip ( ) , & PeersWanted :: All ) ;
528
+ assert_eq ! ( announce_data. stats. downloaded, 0 ) ;
529
+
530
+ peer. event = AnnounceEvent :: Completed ;
531
+ let announce_data = announce_handler. announce ( & info_hash, & mut peer, & peer_ip ( ) , & PeersWanted :: All ) ;
532
+ assert_eq ! ( announce_data. stats. downloaded, 1 ) ;
533
+
534
+ // Remove the newly updated torrent from memory
535
+ let _unused = in_memory_torrent_repository. remove ( & info_hash) ;
536
+
537
+ torrents_manager. load_torrents_from_database ( ) . unwrap ( ) ;
538
+
539
+ let torrent_entry = in_memory_torrent_repository
540
+ . get ( & info_hash)
541
+ . expect ( "it should be able to get entry" ) ;
542
+
543
+ // It persists the number of completed peers.
544
+ assert_eq ! ( torrent_entry. get_swarm_metadata( ) . downloaded, 1 ) ;
545
+
546
+ // It does not persist the peers
547
+ assert ! ( torrent_entry. peers_is_empty( ) ) ;
548
+ }
549
+ }
550
+ }
551
+ }
0 commit comments