Skip to content

Commit 6efbb64

Browse files
committed
test: add test for tracker::torrent::TorrentPeer
1 parent 78809cb commit 6efbb64

File tree

1 file changed

+254
-0
lines changed

1 file changed

+254
-0
lines changed

src/tracker/peer.rs

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,257 @@ impl TorrentPeer {
8686
self.left.0 <= 0 && self.event != AnnounceEvent::Stopped
8787
}
8888
}
89+
90+
#[cfg(test)]
91+
mod test {
92+
mod torrent_peer {
93+
94+
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
95+
96+
use aquatic_udp_protocol::{AnnounceEvent, NumberOfBytes};
97+
98+
use crate::{
99+
peer::TorrentPeer,
100+
protocol::clock::{DefaultClock, Time},
101+
PeerId,
102+
};
103+
104+
#[test]
105+
fn it_should_be_serializable() {
106+
let torrent_peer = TorrentPeer {
107+
peer_id: PeerId(*b"-qB00000000000000000"),
108+
peer_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(126, 0, 0, 1)), 8080),
109+
updated: DefaultClock::now(),
110+
uploaded: NumberOfBytes(0),
111+
downloaded: NumberOfBytes(0),
112+
left: NumberOfBytes(0),
113+
event: AnnounceEvent::Started,
114+
};
115+
116+
let json_serialized_value = serde_json::to_string(&torrent_peer).unwrap();
117+
118+
assert_eq!(
119+
json_serialized_value,
120+
// todo: compare using pretty json format to improve readability
121+
r#"{"peer_id":{"id":"2d71423030303030303030303030303030303030","client":"qBittorrent"},"peer_addr":"126.0.0.1:8080","updated":0,"uploaded":0,"downloaded":0,"left":0,"event":"Started"}"#
122+
);
123+
}
124+
}
125+
126+
mod torrent_peer_constructor_from_udp_requests {
127+
128+
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
129+
130+
use aquatic_udp_protocol::{
131+
AnnounceEvent, AnnounceRequest, NumberOfBytes, NumberOfPeers, PeerId as AquaticPeerId, PeerKey, Port, TransactionId,
132+
};
133+
134+
use crate::protocol::utils::get_connection_id;
135+
136+
use crate::peer::TorrentPeer;
137+
138+
// todo: duplicate functions is PR 82. Remove duplication once both PR are merged.
139+
140+
fn sample_ipv4_remote_addr() -> SocketAddr {
141+
sample_ipv4_socket_address()
142+
}
143+
144+
fn sample_ipv4_socket_address() -> SocketAddr {
145+
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080)
146+
}
147+
148+
struct AnnounceRequestBuilder {
149+
request: AnnounceRequest,
150+
}
151+
152+
impl AnnounceRequestBuilder {
153+
pub fn default() -> AnnounceRequestBuilder {
154+
let client_ip = Ipv4Addr::new(126, 0, 0, 1);
155+
let client_port = 8080;
156+
let info_hash_aquatic = aquatic_udp_protocol::InfoHash([0u8; 20]);
157+
158+
let default_request = AnnounceRequest {
159+
connection_id: get_connection_id(&sample_ipv4_remote_addr()),
160+
transaction_id: TransactionId(0i32),
161+
info_hash: info_hash_aquatic,
162+
peer_id: AquaticPeerId(*b"-qB00000000000000000"),
163+
bytes_downloaded: NumberOfBytes(0i64),
164+
bytes_uploaded: NumberOfBytes(0i64),
165+
bytes_left: NumberOfBytes(0i64),
166+
event: AnnounceEvent::Started,
167+
ip_address: Some(client_ip),
168+
key: PeerKey(0u32),
169+
peers_wanted: NumberOfPeers(1i32),
170+
port: Port(client_port),
171+
};
172+
AnnounceRequestBuilder {
173+
request: default_request,
174+
}
175+
}
176+
177+
pub fn into(self) -> AnnounceRequest {
178+
self.request
179+
}
180+
}
181+
182+
#[test]
183+
fn it_should_use_the_udp_source_ip_as_the_peer_ip_address_instead_of_the_ip_in_the_announce_request() {
184+
let remote_ip = IpAddr::V4(Ipv4Addr::new(126, 0, 0, 2));
185+
let announce_request = AnnounceRequestBuilder::default().into();
186+
187+
let torrent_peer = TorrentPeer::from_udp_announce_request(&announce_request, remote_ip, None);
188+
189+
assert_eq!(torrent_peer.peer_addr, SocketAddr::new(remote_ip, announce_request.port.0));
190+
}
191+
192+
#[test]
193+
fn it_should_always_use_the_port_in_the_announce_request_for_the_peer_port() {
194+
let remote_ip = IpAddr::V4(Ipv4Addr::new(126, 0, 0, 2));
195+
let announce_request = AnnounceRequestBuilder::default().into();
196+
197+
let torrent_peer = TorrentPeer::from_udp_announce_request(&announce_request, remote_ip, None);
198+
199+
assert_eq!(torrent_peer.peer_addr, SocketAddr::new(remote_ip, announce_request.port.0));
200+
}
201+
202+
mod when_source_udp_ip_is_a_ipv_4_loopback_ip {
203+
204+
use std::{
205+
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
206+
str::FromStr,
207+
};
208+
209+
use crate::peer::{test::torrent_peer_constructor_from_udp_requests::AnnounceRequestBuilder, TorrentPeer};
210+
211+
#[test]
212+
fn it_should_use_the_loopback_ip_if_the_server_does_not_have_the_external_ip_configuration() {
213+
let remote_ip = IpAddr::V4(Ipv4Addr::LOCALHOST);
214+
let announce_request = AnnounceRequestBuilder::default().into();
215+
216+
let torrent_peer = TorrentPeer::from_udp_announce_request(&announce_request, remote_ip, None);
217+
218+
assert_eq!(torrent_peer.peer_addr, SocketAddr::new(remote_ip, announce_request.port.0));
219+
}
220+
221+
#[test]
222+
fn it_should_use_the_external_host_ip_in_tracker_configuration_if_defined() {
223+
let remote_ip = IpAddr::V4(Ipv4Addr::LOCALHOST);
224+
let announce_request = AnnounceRequestBuilder::default().into();
225+
226+
let host_opt_ip = IpAddr::V4(Ipv4Addr::from_str("126.0.0.1").unwrap());
227+
let torrent_peer = TorrentPeer::from_udp_announce_request(&announce_request, remote_ip, Some(host_opt_ip));
228+
229+
assert_eq!(torrent_peer.peer_addr, SocketAddr::new(host_opt_ip, announce_request.port.0));
230+
}
231+
232+
#[test]
233+
fn it_should_use_the_external_ip_in_tracker_configuration_if_defined_even_if_the_external_ip_is_an_ipv6_ip() {
234+
let remote_ip = IpAddr::V4(Ipv4Addr::LOCALHOST);
235+
let announce_request = AnnounceRequestBuilder::default().into();
236+
237+
let host_opt_ip = IpAddr::V6(Ipv6Addr::from_str("2345:0425:2CA1:0000:0000:0567:5673:23b5").unwrap());
238+
let torrent_peer = TorrentPeer::from_udp_announce_request(&announce_request, remote_ip, Some(host_opt_ip));
239+
240+
assert_eq!(torrent_peer.peer_addr, SocketAddr::new(host_opt_ip, announce_request.port.0));
241+
}
242+
}
243+
244+
mod when_source_udp_ip_is_a_ipv6_loopback_ip {
245+
246+
use std::{
247+
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
248+
str::FromStr,
249+
};
250+
251+
use crate::peer::{test::torrent_peer_constructor_from_udp_requests::AnnounceRequestBuilder, TorrentPeer};
252+
253+
#[test]
254+
fn it_should_use_the_loopback_ip_if_the_server_does_not_have_the_external_ip_configuration() {
255+
let remote_ip = IpAddr::V6(Ipv6Addr::LOCALHOST);
256+
let announce_request = AnnounceRequestBuilder::default().into();
257+
258+
let torrent_peer = TorrentPeer::from_udp_announce_request(&announce_request, remote_ip, None);
259+
260+
assert_eq!(torrent_peer.peer_addr, SocketAddr::new(remote_ip, announce_request.port.0));
261+
}
262+
263+
#[test]
264+
fn it_should_use_the_external_host_ip_in_tracker_configuration_if_defined() {
265+
let remote_ip = IpAddr::V6(Ipv6Addr::LOCALHOST);
266+
let announce_request = AnnounceRequestBuilder::default().into();
267+
268+
let host_opt_ip = IpAddr::V6(Ipv6Addr::from_str("2345:0425:2CA1:0000:0000:0567:5673:23b5").unwrap());
269+
let torrent_peer = TorrentPeer::from_udp_announce_request(&announce_request, remote_ip, Some(host_opt_ip));
270+
271+
assert_eq!(torrent_peer.peer_addr, SocketAddr::new(host_opt_ip, announce_request.port.0));
272+
}
273+
274+
#[test]
275+
fn it_should_use_the_external_ip_in_tracker_configuration_if_defined_even_if_the_external_ip_is_an_ipv4_ip() {
276+
let remote_ip = IpAddr::V6(Ipv6Addr::LOCALHOST);
277+
let announce_request = AnnounceRequestBuilder::default().into();
278+
279+
let host_opt_ip = IpAddr::V4(Ipv4Addr::from_str("126.0.0.1").unwrap());
280+
let torrent_peer = TorrentPeer::from_udp_announce_request(&announce_request, remote_ip, Some(host_opt_ip));
281+
282+
assert_eq!(torrent_peer.peer_addr, SocketAddr::new(host_opt_ip, announce_request.port.0));
283+
}
284+
}
285+
}
286+
287+
mod torrent_peer_constructor_from_for_http_requests {
288+
use crate::{http::AnnounceRequest, peer::TorrentPeer, InfoHash, PeerId};
289+
290+
use std::net::{IpAddr, Ipv4Addr};
291+
292+
fn sample_http_announce_request(peer_addr: IpAddr, port: u16) -> AnnounceRequest {
293+
AnnounceRequest {
294+
info_hash: InfoHash([0u8; 20]),
295+
peer_addr,
296+
downloaded: 0u64,
297+
uploaded: 0u64,
298+
peer_id: PeerId(*b"-qB00000000000000000"),
299+
port,
300+
left: 0u64,
301+
event: None,
302+
compact: None,
303+
}
304+
}
305+
306+
#[test]
307+
fn it_should_use_the_source_ip_in_the_udp_heder_as_the_peer_ip_address_ignoring_the_peer_ip_in_the_announce_request() {
308+
let remote_ip = IpAddr::V4(Ipv4Addr::new(126, 0, 0, 2));
309+
310+
let ip_in_announce_request = IpAddr::V4(Ipv4Addr::new(126, 0, 0, 1));
311+
let announce_request = sample_http_announce_request(ip_in_announce_request, 8080);
312+
313+
let torrent_peer = TorrentPeer::from_http_announce_request(&announce_request, remote_ip, None);
314+
315+
assert_eq!(torrent_peer.peer_addr.ip(), remote_ip);
316+
assert_ne!(torrent_peer.peer_addr.ip(), ip_in_announce_request);
317+
}
318+
319+
#[test]
320+
fn it_should_always_use_the_port_in_the_announce_request_for_the_peer_port_ignoring_the_port_in_the_udp_header() {
321+
let remote_ip = IpAddr::V4(Ipv4Addr::new(126, 0, 0, 2));
322+
let remote_port = 8080;
323+
324+
let port_in_announce_request = 8081;
325+
let announce_request =
326+
sample_http_announce_request(IpAddr::V4(Ipv4Addr::new(126, 0, 0, 1)), port_in_announce_request);
327+
328+
let torrent_peer = TorrentPeer::from_http_announce_request(&announce_request, remote_ip, None);
329+
330+
assert_eq!(torrent_peer.peer_addr.port(), announce_request.port);
331+
assert_ne!(torrent_peer.peer_addr.port(), remote_port);
332+
}
333+
334+
// todo: other cases are already covered by UDP cases.
335+
// Code review:
336+
// We should extract the method "peer_addr_from_ip_and_port_and_opt_host_ip" from TorrentPeer.
337+
// It could be another service responsible for assigning the IP to the peer.
338+
// So we can test that behavior independently from where you use it.
339+
// We could also build the peer with the IP in the announce request and let the tracker decide
340+
// wether it has to change it or not depending on tracker configuration.
341+
}
342+
}

0 commit comments

Comments
 (0)