Skip to content

Commit 40182b4

Browse files
committed
test: add tests for PeerList type
1 parent 42f1b30 commit 40182b4

File tree

3 files changed

+276
-23
lines changed

3 files changed

+276
-23
lines changed

packages/primitives/src/peer.rs

+39
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,38 @@ pub mod fixture {
362362
}
363363

364364
impl PeerBuilder {
365+
#[allow(dead_code)]
366+
#[must_use]
367+
pub fn seeder() -> Self {
368+
let peer = Peer {
369+
peer_id: Id(*b"-qB00000000000000001"),
370+
peer_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080),
371+
updated: DurationSinceUnixEpoch::new(1_669_397_478_934, 0),
372+
uploaded: NumberOfBytes(0),
373+
downloaded: NumberOfBytes(0),
374+
left: NumberOfBytes(0),
375+
event: AnnounceEvent::Completed,
376+
};
377+
378+
Self { peer }
379+
}
380+
381+
#[allow(dead_code)]
382+
#[must_use]
383+
pub fn leecher() -> Self {
384+
let peer = Peer {
385+
peer_id: Id(*b"-qB00000000000000002"),
386+
peer_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 8080),
387+
updated: DurationSinceUnixEpoch::new(1_669_397_478_934, 0),
388+
uploaded: NumberOfBytes(0),
389+
downloaded: NumberOfBytes(0),
390+
left: NumberOfBytes(10),
391+
event: AnnounceEvent::Started,
392+
};
393+
394+
Self { peer }
395+
}
396+
365397
#[allow(dead_code)]
366398
#[must_use]
367399
pub fn with_peer_id(mut self, peer_id: &Id) -> Self {
@@ -390,6 +422,13 @@ pub mod fixture {
390422
self
391423
}
392424

425+
#[allow(dead_code)]
426+
#[must_use]
427+
pub fn last_updated_on(mut self, updated: DurationSinceUnixEpoch) -> Self {
428+
self.peer.updated = updated;
429+
self
430+
}
431+
393432
#[allow(dead_code)]
394433
#[must_use]
395434
pub fn build(self) -> Peer {
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1+
//! A peer list.
12
use std::net::SocketAddr;
23
use std::sync::Arc;
34

4-
use torrust_tracker_primitives::peer;
5+
use torrust_tracker_primitives::{peer, DurationSinceUnixEpoch};
6+
7+
// code-review: the current implementation uses the peer Id as the ``BTreeMap``
8+
// key. That would allow adding two identical peers except for the Id.
9+
// For example, two peers with the same socket address but a different peer Id
10+
// would be allowed. That would lead to duplicated peers in the tracker responses.
511

612
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
713
pub struct PeerList {
@@ -19,45 +25,48 @@ impl PeerList {
1925
self.peers.is_empty()
2026
}
2127

22-
pub fn insert(&mut self, key: peer::Id, value: Arc<peer::Peer>) -> Option<Arc<peer::Peer>> {
23-
self.peers.insert(key, value)
28+
pub fn upsert(&mut self, value: Arc<peer::Peer>) -> Option<Arc<peer::Peer>> {
29+
self.peers.insert(value.peer_id, value)
2430
}
2531

2632
pub fn remove(&mut self, key: &peer::Id) -> Option<Arc<peer::Peer>> {
2733
self.peers.remove(key)
2834
}
2935

30-
pub fn retain<F>(&mut self, f: F)
31-
where
32-
F: FnMut(&peer::Id, &mut Arc<peer::Peer>) -> bool,
33-
{
34-
self.peers.retain(f);
36+
pub fn remove_inactive_peers(&mut self, current_cutoff: DurationSinceUnixEpoch) {
37+
self.peers
38+
.retain(|_, peer| peer::ReadInfo::get_updated(peer) > current_cutoff);
3539
}
3640

3741
#[must_use]
38-
pub fn seeders_and_leechers(&self) -> (usize, usize) {
39-
let seeders = self.peers.values().filter(|peer| peer.is_seeder()).count();
40-
let leechers = self.len() - seeders;
41-
42-
(seeders, leechers)
42+
pub fn get(&self, peer_id: &peer::Id) -> Option<&Arc<peer::Peer>> {
43+
self.peers.get(peer_id)
4344
}
4445

4546
#[must_use]
46-
pub fn get_peers(&self, limit: Option<usize>) -> Vec<Arc<peer::Peer>> {
47+
pub fn get_all(&self, limit: Option<usize>) -> Vec<Arc<peer::Peer>> {
4748
match limit {
4849
Some(limit) => self.peers.values().take(limit).cloned().collect(),
4950
None => self.peers.values().cloned().collect(),
5051
}
5152
}
5253

5354
#[must_use]
54-
pub fn get_peers_for_client(&self, client: &SocketAddr, limit: Option<usize>) -> Vec<Arc<peer::Peer>> {
55+
pub fn seeders_and_leechers(&self) -> (usize, usize) {
56+
let seeders = self.peers.values().filter(|peer| peer.is_seeder()).count();
57+
let leechers = self.len() - seeders;
58+
59+
(seeders, leechers)
60+
}
61+
62+
#[must_use]
63+
pub fn get_peers_excluding_addr(&self, peer_addr: &SocketAddr, limit: Option<usize>) -> Vec<Arc<peer::Peer>> {
5564
match limit {
5665
Some(limit) => self
5766
.peers
5867
.values()
5968
// Take peers which are not the client peer
60-
.filter(|peer| peer::ReadInfo::get_address(peer.as_ref()) != *client)
69+
.filter(|peer| peer::ReadInfo::get_address(peer.as_ref()) != *peer_addr)
6170
// Limit the number of peers on the result
6271
.take(limit)
6372
.cloned()
@@ -66,9 +75,215 @@ impl PeerList {
6675
.peers
6776
.values()
6877
// Take peers which are not the client peer
69-
.filter(|peer| peer::ReadInfo::get_address(peer.as_ref()) != *client)
78+
.filter(|peer| peer::ReadInfo::get_address(peer.as_ref()) != *peer_addr)
7079
.cloned()
7180
.collect(),
7281
}
7382
}
7483
}
84+
85+
#[cfg(test)]
86+
mod tests {
87+
88+
mod it_should {
89+
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
90+
use std::sync::Arc;
91+
92+
use torrust_tracker_primitives::peer::fixture::PeerBuilder;
93+
use torrust_tracker_primitives::peer::{self};
94+
use torrust_tracker_primitives::DurationSinceUnixEpoch;
95+
96+
use crate::entry::peer_list::PeerList;
97+
98+
#[test]
99+
fn be_empty_when_no_peers_have_been_inserted() {
100+
let peer_list = PeerList::default();
101+
102+
assert!(peer_list.is_empty());
103+
}
104+
105+
#[test]
106+
fn have_zero_length_when_no_peers_have_been_inserted() {
107+
let peer_list = PeerList::default();
108+
109+
assert_eq!(peer_list.len(), 0);
110+
}
111+
112+
#[test]
113+
fn allow_inserting_a_new_peer() {
114+
let mut peer_list = PeerList::default();
115+
116+
let peer = PeerBuilder::default().build();
117+
118+
assert_eq!(peer_list.upsert(peer.into()), None);
119+
}
120+
121+
#[test]
122+
fn allow_updating_a_preexisting_peer() {
123+
let mut peer_list = PeerList::default();
124+
125+
let peer = PeerBuilder::default().build();
126+
127+
peer_list.upsert(peer.into());
128+
129+
assert_eq!(peer_list.upsert(peer.into()), Some(Arc::new(peer)));
130+
}
131+
132+
#[test]
133+
fn allow_getting_all_peers() {
134+
let mut peer_list = PeerList::default();
135+
136+
let peer = PeerBuilder::default().build();
137+
138+
peer_list.upsert(peer.into());
139+
140+
assert_eq!(peer_list.get_all(None), [Arc::new(peer)]);
141+
}
142+
143+
#[test]
144+
fn allow_getting_one_peer_by_id() {
145+
let mut peer_list = PeerList::default();
146+
147+
let peer = PeerBuilder::default().build();
148+
149+
peer_list.upsert(peer.into());
150+
151+
assert_eq!(peer_list.get(&peer.peer_id), Some(Arc::new(peer)).as_ref());
152+
}
153+
154+
#[test]
155+
fn increase_the_number_of_peers_after_inserting_a_new_one() {
156+
let mut peer_list = PeerList::default();
157+
158+
let peer = PeerBuilder::default().build();
159+
160+
peer_list.upsert(peer.into());
161+
162+
assert_eq!(peer_list.len(), 1);
163+
}
164+
165+
#[test]
166+
fn decrease_the_number_of_peers_after_removing_one() {
167+
let mut peer_list = PeerList::default();
168+
169+
let peer = PeerBuilder::default().build();
170+
171+
peer_list.upsert(peer.into());
172+
173+
peer_list.remove(&peer.peer_id);
174+
175+
assert!(peer_list.is_empty());
176+
}
177+
178+
#[test]
179+
fn allow_removing_an_existing_peer() {
180+
let mut peer_list = PeerList::default();
181+
182+
let peer = PeerBuilder::default().build();
183+
184+
peer_list.upsert(peer.into());
185+
186+
peer_list.remove(&peer.peer_id);
187+
188+
assert_eq!(peer_list.get(&peer.peer_id), None);
189+
}
190+
191+
#[test]
192+
fn allow_getting_all_peers_excluding_peers_with_a_given_address() {
193+
let mut peer_list = PeerList::default();
194+
195+
let peer1 = PeerBuilder::default()
196+
.with_peer_id(&peer::Id(*b"-qB00000000000000001"))
197+
.with_peer_addr(&SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 6969))
198+
.build();
199+
peer_list.upsert(peer1.into());
200+
201+
let peer2 = PeerBuilder::default()
202+
.with_peer_id(&peer::Id(*b"-qB00000000000000002"))
203+
.with_peer_addr(&SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 6969))
204+
.build();
205+
peer_list.upsert(peer2.into());
206+
207+
assert_eq!(peer_list.get_peers_excluding_addr(&peer2.peer_addr, None), [Arc::new(peer1)]);
208+
}
209+
210+
#[test]
211+
fn return_the_number_of_seeders_in_the_list() {
212+
let mut peer_list = PeerList::default();
213+
214+
let seeder = PeerBuilder::seeder().build();
215+
let leecher = PeerBuilder::leecher().build();
216+
217+
peer_list.upsert(seeder.into());
218+
peer_list.upsert(leecher.into());
219+
220+
let (seeders, _leechers) = peer_list.seeders_and_leechers();
221+
222+
assert_eq!(seeders, 1);
223+
}
224+
225+
#[test]
226+
fn return_the_number_of_leechers_in_the_list() {
227+
let mut peer_list = PeerList::default();
228+
229+
let seeder = PeerBuilder::seeder().build();
230+
let leecher = PeerBuilder::leecher().build();
231+
232+
peer_list.upsert(seeder.into());
233+
peer_list.upsert(leecher.into());
234+
235+
let (_seeders, leechers) = peer_list.seeders_and_leechers();
236+
237+
assert_eq!(leechers, 1);
238+
}
239+
240+
#[test]
241+
fn remove_inactive_peers() {
242+
let mut peer_list = PeerList::default();
243+
let one_second = DurationSinceUnixEpoch::new(1, 0);
244+
245+
// Insert the peer
246+
let last_update_time = DurationSinceUnixEpoch::new(1_669_397_478_934, 0);
247+
let peer = PeerBuilder::default().last_updated_on(last_update_time).build();
248+
peer_list.upsert(peer.into());
249+
250+
// Remove peers not updated since one second after inserting the peer
251+
peer_list.remove_inactive_peers(last_update_time + one_second);
252+
253+
assert_eq!(peer_list.len(), 0);
254+
}
255+
256+
#[test]
257+
fn not_remove_active_peers() {
258+
let mut peer_list = PeerList::default();
259+
let one_second = DurationSinceUnixEpoch::new(1, 0);
260+
261+
// Insert the peer
262+
let last_update_time = DurationSinceUnixEpoch::new(1_669_397_478_934, 0);
263+
let peer = PeerBuilder::default().last_updated_on(last_update_time).build();
264+
peer_list.upsert(peer.into());
265+
266+
// Remove peers not updated since one second before inserting the peer.
267+
peer_list.remove_inactive_peers(last_update_time - one_second);
268+
269+
assert_eq!(peer_list.len(), 1);
270+
}
271+
272+
#[test]
273+
fn allow_inserting_two_identical_peers_except_for_the_id() {
274+
let mut peer_list = PeerList::default();
275+
276+
let peer1 = PeerBuilder::default()
277+
.with_peer_id(&peer::Id(*b"-qB00000000000000001"))
278+
.build();
279+
peer_list.upsert(peer1.into());
280+
281+
let peer2 = PeerBuilder::default()
282+
.with_peer_id(&peer::Id(*b"-qB00000000000000002"))
283+
.build();
284+
peer_list.upsert(peer2.into());
285+
286+
assert_eq!(peer_list.len(), 2);
287+
}
288+
}
289+
}

packages/torrent-repository/src/entry/single.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ impl Entry for EntrySingle {
4343
}
4444

4545
fn get_peers(&self, limit: Option<usize>) -> Vec<Arc<peer::Peer>> {
46-
self.swarm.get_peers(limit)
46+
self.swarm.get_all(limit)
4747
}
4848

4949
fn get_peers_for_client(&self, client: &SocketAddr, limit: Option<usize>) -> Vec<Arc<peer::Peer>> {
50-
self.swarm.get_peers_for_client(client, limit)
50+
self.swarm.get_peers_excluding_addr(client, limit)
5151
}
5252

5353
fn upsert_peer(&mut self, peer: &peer::Peer) -> bool {
@@ -58,23 +58,22 @@ impl Entry for EntrySingle {
5858
drop(self.swarm.remove(&peer::ReadInfo::get_id(peer)));
5959
}
6060
AnnounceEvent::Completed => {
61-
let previous = self.swarm.insert(peer::ReadInfo::get_id(peer), Arc::new(*peer));
61+
let previous = self.swarm.upsert(Arc::new(*peer));
6262
// Don't count if peer was not previously known and not already completed.
6363
if previous.is_some_and(|p| p.event != AnnounceEvent::Completed) {
6464
self.downloaded += 1;
6565
downloaded_stats_updated = true;
6666
}
6767
}
6868
_ => {
69-
drop(self.swarm.insert(peer::ReadInfo::get_id(peer), Arc::new(*peer)));
69+
drop(self.swarm.upsert(Arc::new(*peer)));
7070
}
7171
}
7272

7373
downloaded_stats_updated
7474
}
7575

7676
fn remove_inactive_peers(&mut self, current_cutoff: DurationSinceUnixEpoch) {
77-
self.swarm
78-
.retain(|_, peer| peer::ReadInfo::get_updated(peer) > current_cutoff);
77+
self.swarm.remove_inactive_peers(current_cutoff);
7978
}
8079
}

0 commit comments

Comments
 (0)