Skip to content

Commit

Permalink
test(api): [#143] add test for invalid infohash URL path param
Browse files Browse the repository at this point in the history
  • Loading branch information
josecelano committed Jan 12, 2023
1 parent aa2a2ef commit 39c15c6
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 28 deletions.
4 changes: 2 additions & 2 deletions src/apis/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub fn router(tracker: &Arc<Tracker>) -> Router {
)
// Whitelist command
.route(
"/api/whitelist/:info_hash",
"/api/whitelist/reload",
get(reload_whitelist_handler).with_state(tracker.clone()),
)
// Keys
Expand Down Expand Up @@ -95,7 +95,7 @@ fn response_ok() -> Response {

fn response_invalid_info_hash_param(info_hash: &str) -> Response {
response_bad_request(&format!(
"Invalid URL: invalid infohash param: string \"{}\", expected expected a 40 character long string",
"Invalid URL: invalid infohash param: string \"{}\", expected a 40 character long string",
info_hash
))
}
Expand Down
4 changes: 2 additions & 2 deletions src/protocol/info_hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ impl<'v> serde::de::Visitor<'v> for InfoHashVisitor {
if v.len() != 40 {
return Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Str(v),
&"expected a 40 character long string",
&"a 40 character long string",
));
}

Expand All @@ -86,7 +86,7 @@ impl<'v> serde::de::Visitor<'v> for InfoHashVisitor {
if binascii::hex2bin(v.as_bytes(), &mut res.0).is_err() {
return Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Str(v),
&"expected a hexadecimal string",
&"a hexadecimal string",
));
};
Ok(res)
Expand Down
18 changes: 18 additions & 0 deletions tests/api/asserts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ pub async fn assert_bad_request(response: Response, body: &str) {
assert_eq!(response.text().await.unwrap(), body);
}

pub async fn assert_not_found(response: Response) {
assert_eq!(response.status(), 404);
// todo: missing header
//assert_eq!(response.headers().get("content-type").unwrap(), "text/plain; charset=utf-8");
assert_eq!(response.text().await.unwrap(), "");
}

pub async fn assert_method_not_allowed(response: Response) {
assert_eq!(response.status(), 405);
assert_eq!(response.headers().get("content-type").unwrap(), "text/plain; charset=utf-8");
Expand All @@ -68,6 +75,17 @@ pub async fn assert_torrent_not_known(response: Response) {
assert_eq!(response.text().await.unwrap(), "\"torrent not known\"");
}

pub async fn assert_invalid_infohash(response: Response, invalid_infohash: &str) {
assert_bad_request(
response,
&format!(
"Invalid URL: invalid infohash param: string \"{}\", expected a 40 character long string",
invalid_infohash
),
)
.await;
}

pub async fn assert_token_not_valid(response: Response) {
assert_unhandled_rejection(response, "token not valid").await;
}
Expand Down
126 changes: 102 additions & 24 deletions tests/tracker_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,26 @@ mod tracker_apis {
*/

// When these infohashes are used in URL path params
// the response is a custom response returned in the handler
fn invalid_infohashes_returning_bad_request() -> Vec<String> {
[
"0".to_string(),
"-1".to_string(),
"1.1".to_string(),
"INVALID INFOHASH".to_string(),
"9c38422213e30bff212b30c360d26f9a0213642".to_string(), // 39-char length instead of 40
"9c38422213e30bff212b30c360d26f9a0213642&".to_string(), // Invalid char
]
.to_vec()
}

// When these infohashes are used in URL path params
// the response is an Axum response returned in the handler
fn invalid_infohashes_returning_not_found() -> Vec<String> {
[String::new(), " ".to_string()].to_vec()
}

mod authentication {
use crate::api::asserts::{assert_token_not_valid, assert_unauthorized};
use crate::api::client::{Client, Query, QueryParam};
Expand Down Expand Up @@ -915,9 +935,10 @@ mod tracker_apis {
use torrust_tracker::api::resource::{self, torrent};
use torrust_tracker::protocol::info_hash::InfoHash;

use super::{invalid_infohashes_returning_bad_request, invalid_infohashes_returning_not_found};
use crate::api::asserts::{
assert_bad_request, assert_token_not_valid, assert_torrent_info, assert_torrent_list, assert_torrent_not_known,
assert_unauthorized,
assert_bad_request, assert_invalid_infohash, assert_not_found, assert_token_not_valid, assert_torrent_info,
assert_torrent_list, assert_torrent_not_known, assert_unauthorized,
};
use crate::api::client::{Client, Query, QueryParam};
use crate::api::connection_info::{connection_with_invalid_token, connection_with_no_token};
Expand Down Expand Up @@ -1007,24 +1028,33 @@ mod tracker_apis {
}

#[tokio::test]
async fn should_fail_getting_torrents_when_query_parameters_cannot_be_parsed() {
async fn should_fail_getting_torrents_when_the_offset_query_parameter_cannot_be_parsed() {
let api_server = start_default_api(&Version::Axum).await;

let invalid_offset = "INVALID OFFSET";
let invalid_offsets = [" ", "-1", "1.1", "INVALID OFFSET"];

let response = Client::new(api_server.get_connection_info())
.get_torrents(Query::params([QueryParam::new("offset", invalid_offset)].to_vec()))
.await;
for invalid_offset in &invalid_offsets {
let response = Client::new(api_server.get_connection_info())
.get_torrents(Query::params([QueryParam::new("offset", invalid_offset)].to_vec()))
.await;

assert_bad_request(response, "Failed to deserialize query string: invalid digit found in string").await;
assert_bad_request(response, "Failed to deserialize query string: invalid digit found in string").await;
}
}

let invalid_limit = "INVALID LIMIT";
#[tokio::test]
async fn should_fail_getting_torrents_when_the_limit_query_parameter_cannot_be_parsed() {
let api_server = start_default_api(&Version::Axum).await;

let response = Client::new(api_server.get_connection_info())
.get_torrents(Query::params([QueryParam::new("limit", invalid_limit)].to_vec()))
.await;
let invalid_limits = [" ", "-1", "1.1", "INVALID LIMIT"];

for invalid_limit in &invalid_limits {
let response = Client::new(api_server.get_connection_info())
.get_torrents(Query::params([QueryParam::new("limit", invalid_limit)].to_vec()))
.await;

assert_bad_request(response, "Failed to deserialize query string: invalid digit found in string").await;
assert_bad_request(response, "Failed to deserialize query string: invalid digit found in string").await;
}
}

#[tokio::test]
Expand Down Expand Up @@ -1085,20 +1115,24 @@ mod tracker_apis {
}

#[tokio::test]
async fn should_fail_getting_a_torrent_info_when_the_provided_infohash_cannot_be_parsed() {
async fn should_fail_getting_a_torrent_info_when_the_provided_infohash_is_invalid() {
let api_server = start_default_api(&Version::Axum).await;

let invalid_infohash = "INVALID INFOHASH";
for invalid_infohash in &invalid_infohashes_returning_bad_request() {
let response = Client::new(api_server.get_connection_info())
.get_torrent(invalid_infohash)
.await;

let response = Client::new(api_server.get_connection_info())
.get_torrent(invalid_infohash)
.await;
assert_invalid_infohash(response, invalid_infohash).await;
}

assert_bad_request(
response,
"Invalid URL: invalid infohash param: string \"INVALID INFOHASH\", expected expected a 40 character long string",
)
.await;
for invalid_infohash in &invalid_infohashes_returning_not_found() {
let response = Client::new(api_server.get_connection_info())
.get_torrent(invalid_infohash)
.await;

assert_not_found(response).await;
}
}

#[tokio::test]
Expand Down Expand Up @@ -1128,9 +1162,11 @@ mod tracker_apis {

use torrust_tracker::protocol::info_hash::InfoHash;

use super::{invalid_infohashes_returning_bad_request, invalid_infohashes_returning_not_found};
use crate::api::asserts::{
assert_failed_to_reload_whitelist, assert_failed_to_remove_torrent_from_whitelist,
assert_failed_to_whitelist_torrent, assert_ok, assert_token_not_valid, assert_unauthorized,
assert_failed_to_whitelist_torrent, assert_invalid_infohash, assert_not_found, assert_ok, assert_token_not_valid,
assert_unauthorized,
};
use crate::api::client::Client;
use crate::api::connection_info::{connection_with_invalid_token, connection_with_no_token};
Expand Down Expand Up @@ -1205,6 +1241,27 @@ mod tracker_apis {
assert_failed_to_whitelist_torrent(response).await;
}

#[tokio::test]
async fn should_fail_whitelisting_a_torrent_when_the_provided_infohash_is_invalid() {
let api_server = start_default_api(&Version::Axum).await;

for invalid_infohash in &invalid_infohashes_returning_bad_request() {
let response = Client::new(api_server.get_connection_info())
.whitelist_a_torrent(invalid_infohash)
.await;

assert_invalid_infohash(response, invalid_infohash).await;
}

for invalid_infohash in &invalid_infohashes_returning_not_found() {
let response = Client::new(api_server.get_connection_info())
.whitelist_a_torrent(invalid_infohash)
.await;

assert_not_found(response).await;
}
}

#[tokio::test]
async fn should_allow_removing_a_torrent_from_the_whitelist() {
let api_server = start_default_api(&Version::Axum).await;
Expand Down Expand Up @@ -1234,6 +1291,27 @@ mod tracker_apis {
assert_ok(response).await;
}

#[tokio::test]
async fn should_fail_removing_a_torrent_from_the_whitelist_when_the_provided_infohash_is_invalid() {
let api_server = start_default_api(&Version::Axum).await;

for invalid_infohash in &invalid_infohashes_returning_bad_request() {
let response = Client::new(api_server.get_connection_info())
.remove_torrent_from_whitelist(invalid_infohash)
.await;

assert_invalid_infohash(response, invalid_infohash).await;
}

for invalid_infohash in &invalid_infohashes_returning_not_found() {
let response = Client::new(api_server.get_connection_info())
.remove_torrent_from_whitelist(invalid_infohash)
.await;

assert_not_found(response).await;
}
}

#[tokio::test]
async fn should_fail_when_the_torrent_cannot_be_removed_from_the_whitelist() {
let api_server = start_default_api(&Version::Axum).await;
Expand Down

0 comments on commit 39c15c6

Please sign in to comment.