From 9983b7ecf310375282cb8842160fe55ffb833833 Mon Sep 17 00:00:00 2001 From: Mario Date: Tue, 13 Aug 2024 19:20:11 +0200 Subject: [PATCH 01/11] test: added tests for the about context --- .../e2e/web/api/v1/contexts/about/contract.rs | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/tests/e2e/web/api/v1/contexts/about/contract.rs b/tests/e2e/web/api/v1/contexts/about/contract.rs index 41906f26..7737f5b7 100644 --- a/tests/e2e/web/api/v1/contexts/about/contract.rs +++ b/tests/e2e/web/api/v1/contexts/about/contract.rs @@ -29,3 +29,109 @@ async fn it_should_load_the_license_page_at_the_api_entrypoint() { assert_text_ok(&response); assert_response_title(&response, "Licensing"); } + +mod for_guest_users { + use torrust_index::web::api; + + use crate::common::asserts::assert_text_ok; + use crate::common::client::Client; + use crate::e2e::environment::TestEnv; + + #[tokio::test] + async fn it_should_allow_guest_users_to_see_the_about_page() { + let mut env = TestEnv::new(); + + env.start(api::Version::V1).await; + + let client = Client::unauthenticated(&env.server_socket_addr().unwrap()); + + let response = client.about().await; + assert_text_ok(&response); + } + + #[tokio::test] + async fn it_should_allow_guest_users_to_see_the_license_page() { + let mut env = TestEnv::new(); + + env.start(api::Version::V1).await; + + let client = Client::unauthenticated(&env.server_socket_addr().unwrap()); + + let response = client.license().await; + assert_text_ok(&response); + } +} + +mod for_authenticated_users { + use torrust_index::web::api; + + use crate::common::asserts::assert_text_ok; + use crate::common::client::Client; + use crate::e2e::environment::TestEnv; + use crate::e2e::web::api::v1::contexts::user::steps::new_logged_in_user; + + #[tokio::test] + async fn it_should_allow_authenticated_users_to_see_the_about_page() { + let mut env = TestEnv::new(); + + env.start(api::Version::V1).await; + + let authenticated_user = new_logged_in_user(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &authenticated_user.token); + + let response = client.about().await; + assert_text_ok(&response); + } + + #[tokio::test] + async fn it_should_allow_authenticated_users_to_see_the_license_page() { + let mut env = TestEnv::new(); + + env.start(api::Version::V1).await; + + let authenticated_user = new_logged_in_user(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &authenticated_user.token); + + let response = client.license().await; + assert_text_ok(&response); + } +} + +mod for_admin_users { + use torrust_index::web::api; + + use crate::common::asserts::assert_text_ok; + use crate::common::client::Client; + use crate::e2e::environment::TestEnv; + use crate::e2e::web::api::v1::contexts::user::steps::new_logged_in_admin; + + #[tokio::test] + async fn it_should_allow_admin_users_to_see_the_about_page() { + let mut env = TestEnv::new(); + + env.start(api::Version::V1).await; + + let logged_in_admin = new_logged_in_admin(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + + let response = client.about().await; + assert_text_ok(&response); + } + + #[tokio::test] + async fn it_should_allow_admin_users_to_see_the_license_page() { + let mut env = TestEnv::new(); + + env.start(api::Version::V1).await; + + let logged_in_admin = new_logged_in_admin(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + + let response = client.license().await; + assert_text_ok(&response); + } +} From c1a41a787895b2832fcd7d58524ad94cc4e22545 Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 14 Aug 2024 16:04:23 +0200 Subject: [PATCH 02/11] test: added authorization tests for the category context --- .../web/api/v1/contexts/category/contract.rs | 273 +++++++++++------- 1 file changed, 173 insertions(+), 100 deletions(-) diff --git a/tests/e2e/web/api/v1/contexts/category/contract.rs b/tests/e2e/web/api/v1/contexts/category/contract.rs index b4775bd2..39b6bc84 100644 --- a/tests/e2e/web/api/v1/contexts/category/contract.rs +++ b/tests/e2e/web/api/v1/contexts/category/contract.rs @@ -4,13 +4,11 @@ use torrust_index::web::api; use crate::common::asserts::assert_json_ok_response; use crate::common::client::Client; -use crate::common::contexts::category::asserts::{assert_added_category_response, assert_deleted_category_response}; -use crate::common::contexts::category::fixtures::random_category_name; -use crate::common::contexts::category::forms::{AddCategoryForm, DeleteCategoryForm}; +use crate::common::contexts::category::forms::AddCategoryForm; use crate::common::contexts::category::responses::ListResponse; use crate::e2e::environment::TestEnv; use crate::e2e::web::api::v1::contexts::category::steps::{add_category, add_random_category}; -use crate::e2e::web::api::v1::contexts::user::steps::{new_logged_in_admin, new_logged_in_user}; +use crate::e2e::web::api::v1::contexts::user::steps::new_logged_in_admin; #[tokio::test] async fn it_should_return_an_empty_category_list_when_there_are_no_categories() { @@ -48,151 +46,226 @@ async fn it_should_return_a_category_list() { } #[tokio::test] -async fn it_should_not_allow_adding_a_new_category_to_unauthenticated_users() { +async fn it_should_not_allow_adding_empty_categories() { let mut env = TestEnv::new(); env.start(api::Version::V1).await; - let client = Client::unauthenticated(&env.server_socket_addr().unwrap()); + let logged_in_admin = new_logged_in_admin(&env).await; + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); - let response = client - .add_category(AddCategoryForm { - name: "CATEGORY NAME".to_string(), - icon: None, - }) - .await; + let invalid_category_names = vec![String::new(), " ".to_string()]; - assert_eq!(response.status, 401); + for invalid_name in invalid_category_names { + let response = client + .add_category(AddCategoryForm { + name: invalid_name, + icon: None, + }) + .await; + + assert_eq!(response.status, 400); + } } #[tokio::test] -async fn it_should_not_allow_adding_a_new_category_to_non_admins() { +async fn it_should_not_allow_adding_duplicated_categories() { let mut env = TestEnv::new(); env.start(api::Version::V1).await; - let logged_non_admin = new_logged_in_user(&env).await; - - let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_non_admin.token); + let added_category_name = add_random_category(&env).await; - let response = client - .add_category(AddCategoryForm { - name: "CATEGORY NAME".to_string(), - icon: None, - }) - .await; + // Try to add the same category again + let response = add_category(&added_category_name, &env).await; - assert_eq!(response.status, 403); + assert_eq!(response.status, 400); } -#[tokio::test] -async fn it_should_allow_admins_to_add_new_categories() { - let mut env = TestEnv::new(); - env.start(api::Version::V1).await; +mod for_guest_users { + use torrust_index::web::api; - let logged_in_admin = new_logged_in_admin(&env).await; - let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + use crate::common::client::Client; + use crate::common::contexts::category::forms::{AddCategoryForm, DeleteCategoryForm}; + use crate::e2e::environment::TestEnv; + use crate::e2e::web::api::v1::contexts::category::steps::add_random_category; - let category_name = random_category_name(); + #[tokio::test] + async fn it_should_allow_guest_users_to_get_the_categories() { + let mut env = TestEnv::new(); - let response = client - .add_category(AddCategoryForm { - name: category_name.to_string(), - icon: None, - }) - .await; + env.start(api::Version::V1).await; - assert_added_category_response(&response, &category_name); -} + let client = Client::unauthenticated(&env.server_socket_addr().unwrap()); -#[tokio::test] -async fn it_should_not_allow_adding_empty_categories() { - let mut env = TestEnv::new(); - env.start(api::Version::V1).await; + let response = client.get_categories().await; - let logged_in_admin = new_logged_in_admin(&env).await; - let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + assert_eq!(response.status, 200); + } - let invalid_category_names = vec![String::new(), " ".to_string()]; + #[tokio::test] + async fn it_should_not_allow_adding_a_new_category_to_guest_users() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + let client = Client::unauthenticated(&env.server_socket_addr().unwrap()); - for invalid_name in invalid_category_names { let response = client .add_category(AddCategoryForm { - name: invalid_name, + name: "CATEGORY NAME".to_string(), icon: None, }) .await; - assert_eq!(response.status, 400); + assert_eq!(response.status, 401); } -} -#[tokio::test] -async fn it_should_not_allow_adding_duplicated_categories() { - let mut env = TestEnv::new(); - env.start(api::Version::V1).await; + #[tokio::test] + async fn it_should_not_allow_guests_to_delete_categories() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; - let added_category_name = add_random_category(&env).await; + let client = Client::unauthenticated(&env.server_socket_addr().unwrap()); - // Try to add the same category again - let response = add_category(&added_category_name, &env).await; + let added_category_name = add_random_category(&env).await; - assert_eq!(response.status, 400); + let response = client + .delete_category(DeleteCategoryForm { + name: added_category_name.to_string(), + icon: None, + }) + .await; + + assert_eq!(response.status, 401); + } } -#[tokio::test] -async fn it_should_allow_admins_to_delete_categories() { - let mut env = TestEnv::new(); - env.start(api::Version::V1).await; +mod for_authenticated_users { + use torrust_index::web::api; - let logged_in_admin = new_logged_in_admin(&env).await; - let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + use crate::common::client::Client; + use crate::common::contexts::category::forms::{AddCategoryForm, DeleteCategoryForm}; + use crate::e2e::environment::TestEnv; + use crate::e2e::web::api::v1::contexts::category::steps::add_random_category; + use crate::e2e::web::api::v1::contexts::user::steps::new_logged_in_user; - let added_category_name = add_random_category(&env).await; + #[tokio::test] + async fn it_should_allow_authenticated_users_to_get_the_categories() { + let mut env = TestEnv::new(); - let response = client - .delete_category(DeleteCategoryForm { - name: added_category_name.to_string(), - icon: None, - }) - .await; + env.start(api::Version::V1).await; - assert_deleted_category_response(&response, &added_category_name); -} + let authenticated_user = new_logged_in_user(&env).await; -#[tokio::test] -async fn it_should_not_allow_non_admins_to_delete_categories() { - let mut env = TestEnv::new(); - env.start(api::Version::V1).await; + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &authenticated_user.token); - let added_category_name = add_random_category(&env).await; + let response = client.get_categories().await; - let logged_in_non_admin = new_logged_in_user(&env).await; - let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_non_admin.token); + assert_eq!(response.status, 200); + } - let response = client - .delete_category(DeleteCategoryForm { - name: added_category_name.to_string(), - icon: None, - }) - .await; + #[tokio::test] + async fn it_should_not_allow_adding_a_new_category_to_authenticated_users() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; - assert_eq!(response.status, 403); + let logged_non_admin = new_logged_in_user(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_non_admin.token); + + let response = client + .add_category(AddCategoryForm { + name: "CATEGORY NAME".to_string(), + icon: None, + }) + .await; + + assert_eq!(response.status, 403); + } + + #[tokio::test] + async fn it_should_not_allow_authenticated_users_to_delete_categories() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + let added_category_name = add_random_category(&env).await; + + let logged_in_non_admin = new_logged_in_user(&env).await; + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_non_admin.token); + + let response = client + .delete_category(DeleteCategoryForm { + name: added_category_name.to_string(), + icon: None, + }) + .await; + + assert_eq!(response.status, 403); + } } -#[tokio::test] -async fn it_should_not_allow_guests_to_delete_categories() { - let mut env = TestEnv::new(); - env.start(api::Version::V1).await; +mod for_admin_users { - let client = Client::unauthenticated(&env.server_socket_addr().unwrap()); + use torrust_index::web::api; - let added_category_name = add_random_category(&env).await; + use crate::common::client::Client; + use crate::common::contexts::category::asserts::{assert_added_category_response, assert_deleted_category_response}; + use crate::common::contexts::category::fixtures::random_category_name; + use crate::common::contexts::category::forms::{AddCategoryForm, DeleteCategoryForm}; + use crate::e2e::environment::TestEnv; + use crate::e2e::web::api::v1::contexts::category::steps::add_random_category; + use crate::e2e::web::api::v1::contexts::user::steps::new_logged_in_admin; + + #[tokio::test] + async fn it_should_allow_admins_to_get_categories() { + let mut env = TestEnv::new(); - let response = client - .delete_category(DeleteCategoryForm { - name: added_category_name.to_string(), - icon: None, - }) - .await; + env.start(api::Version::V1).await; - assert_eq!(response.status, 401); + let logged_in_admin = new_logged_in_admin(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + + let response = client.get_categories().await; + + assert_eq!(response.status, 200); + } + + #[tokio::test] + async fn it_should_allow_admins_to_add_new_categories() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + let logged_in_admin = new_logged_in_admin(&env).await; + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + + let category_name = random_category_name(); + + let response = client + .add_category(AddCategoryForm { + name: category_name.to_string(), + icon: None, + }) + .await; + + assert_added_category_response(&response, &category_name); + } + + #[tokio::test] + async fn it_should_allow_admins_to_delete_categories() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + let logged_in_admin = new_logged_in_admin(&env).await; + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + + let added_category_name = add_random_category(&env).await; + + let response = client + .delete_category(DeleteCategoryForm { + name: added_category_name.to_string(), + icon: None, + }) + .await; + + assert_deleted_category_response(&response, &added_category_name); + } } From 8ab1ce3ed5b114e9cfdee1a45b7597b360dc7697 Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 21 Aug 2024 00:36:57 +0200 Subject: [PATCH 03/11] test: added authorization tests for the settings context --- .../web/api/v1/contexts/settings/contract.rs | 299 ++++++++++++++---- 1 file changed, 235 insertions(+), 64 deletions(-) diff --git a/tests/e2e/web/api/v1/contexts/settings/contract.rs b/tests/e2e/web/api/v1/contexts/settings/contract.rs index 02d30a36..ce89d42e 100644 --- a/tests/e2e/web/api/v1/contexts/settings/contract.rs +++ b/tests/e2e/web/api/v1/contexts/settings/contract.rs @@ -1,83 +1,254 @@ //! API contract for `settings` context. -use torrust_index::services::settings::EmailOnSignup; -use torrust_index::web::api; - -use crate::common::asserts::assert_json_ok_response; -use crate::common::client::Client; -use crate::common::contexts::settings::responses::{AllSettingsResponse, Public, PublicSettingsResponse, SiteNameResponse}; -use crate::e2e::environment::TestEnv; -use crate::e2e::web::api::v1::contexts::user::steps::new_logged_in_admin; - -#[tokio::test] -async fn it_should_allow_guests_to_get_the_public_settings() { - let mut env = TestEnv::new(); - env.start(api::Version::V1).await; - - let client = Client::unauthenticated(&env.server_socket_addr().unwrap()); - - let response = client.get_public_settings().await; - - let res: PublicSettingsResponse = serde_json::from_str(&response.body) - .unwrap_or_else(|_| panic!("response {:#?} should be a PublicSettingsResponse", response.body)); - - let email_on_signup = match &env.server_settings().unwrap().registration { - Some(registration) => match ®istration.email { - Some(email) => { - if email.required { - EmailOnSignup::Required - } else { - EmailOnSignup::Optional +mod for_guest_users { + + use torrust_index::services::settings::EmailOnSignup; + use torrust_index::web::api; + + use crate::common::asserts::assert_json_ok_response; + use crate::common::client::Client; + use crate::common::contexts::settings::responses::{Public, PublicSettingsResponse, SiteNameResponse}; + use crate::e2e::environment::TestEnv; + + #[tokio::test] + async fn it_should_not_allow_guests_to_get_all_settings() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + let client = Client::unauthenticated(&env.server_socket_addr().unwrap()); + + let response = client.get_settings().await; + + assert_eq!(response.status, 401); + } + + #[tokio::test] + async fn it_should_allow_guests_to_get_the_public_settings() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + let client = Client::unauthenticated(&env.server_socket_addr().unwrap()); + + let response = client.get_public_settings().await; + + let res: PublicSettingsResponse = serde_json::from_str(&response.body) + .unwrap_or_else(|_| panic!("response {:#?} should be a PublicSettingsResponse", response.body)); + + let email_on_signup = match &env.server_settings().unwrap().registration { + Some(registration) => match ®istration.email { + Some(email) => { + if email.required { + EmailOnSignup::Required + } else { + EmailOnSignup::Optional + } } - } + None => EmailOnSignup::NotIncluded, + }, None => EmailOnSignup::NotIncluded, - }, - None => EmailOnSignup::NotIncluded, - }; - - assert_eq!( - res.data, - Public { - website_name: env.server_settings().unwrap().website.name, - tracker_url: env.server_settings().unwrap().tracker.url, - tracker_listed: env.server_settings().unwrap().tracker.listed, - tracker_private: env.server_settings().unwrap().tracker.private, - email_on_signup: email_on_signup.to_string(), - } - ); - - assert_json_ok_response(&response); + }; + + assert_eq!( + res.data, + Public { + website_name: env.server_settings().unwrap().website.name, + tracker_url: env.server_settings().unwrap().tracker.url, + tracker_listed: env.server_settings().unwrap().tracker.listed, + tracker_private: env.server_settings().unwrap().tracker.private, + email_on_signup: email_on_signup.to_string(), + } + ); + + assert_json_ok_response(&response); + } + + #[tokio::test] + async fn it_should_allow_guests_to_get_the_site_name() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + let client = Client::unauthenticated(&env.server_socket_addr().unwrap()); + + let response = client.get_site_name().await; + + let res: SiteNameResponse = serde_json::from_str(&response.body).unwrap(); + + assert_eq!(res.data, "Torrust"); + + assert_json_ok_response(&response); + } } -#[tokio::test] -async fn it_should_allow_guests_to_get_the_site_name() { - let mut env = TestEnv::new(); - env.start(api::Version::V1).await; +mod for_authenticated_users { + + use torrust_index::services::settings::EmailOnSignup; + use torrust_index::web::api; + + use crate::common::asserts::assert_json_ok_response; + use crate::common::client::Client; + use crate::common::contexts::settings::responses::{Public, PublicSettingsResponse, SiteNameResponse}; + use crate::e2e::environment::TestEnv; + use crate::e2e::web::api::v1::contexts::user::steps::new_logged_in_user; + + #[tokio::test] + async fn it_should_not_allow_authenticated_users_to_get_all_settings() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + let authenticated_user = new_logged_in_user(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &authenticated_user.token); + + let response = client.get_settings().await; + + assert_eq!(response.status, 403); + } + + #[tokio::test] + async fn it_should_allow_authenticated_users_to_get_the_public_settings() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + let authenticated_user = new_logged_in_user(&env).await; - let client = Client::unauthenticated(&env.server_socket_addr().unwrap()); + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &authenticated_user.token); - let response = client.get_site_name().await; + let response = client.get_public_settings().await; - let res: SiteNameResponse = serde_json::from_str(&response.body).unwrap(); + let res: PublicSettingsResponse = serde_json::from_str(&response.body) + .unwrap_or_else(|_| panic!("response {:#?} should be a PublicSettingsResponse", response.body)); - assert_eq!(res.data, "Torrust"); + let email_on_signup = match &env.server_settings().unwrap().registration { + Some(registration) => match ®istration.email { + Some(email) => { + if email.required { + EmailOnSignup::Required + } else { + EmailOnSignup::Optional + } + } + None => EmailOnSignup::NotIncluded, + }, + None => EmailOnSignup::NotIncluded, + }; + + assert_eq!( + res.data, + Public { + website_name: env.server_settings().unwrap().website.name, + tracker_url: env.server_settings().unwrap().tracker.url, + tracker_listed: env.server_settings().unwrap().tracker.listed, + tracker_private: env.server_settings().unwrap().tracker.private, + email_on_signup: email_on_signup.to_string(), + } + ); + + assert_json_ok_response(&response); + } + + #[tokio::test] + async fn it_should_allow_authenticated_users_to_get_the_site_name() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + let authenticated_user = new_logged_in_user(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &authenticated_user.token); + + let response = client.get_site_name().await; - assert_json_ok_response(&response); + let res: SiteNameResponse = serde_json::from_str(&response.body).unwrap(); + + assert_eq!(res.data, "Torrust"); + + assert_json_ok_response(&response); + } } -#[tokio::test] -async fn it_should_allow_admins_to_get_all_the_settings() { - let mut env = TestEnv::new(); - env.start(api::Version::V1).await; +mod for_admin_users { + use torrust_index::services::settings::EmailOnSignup; + use torrust_index::web::api; + + use crate::common::asserts::assert_json_ok_response; + use crate::common::client::Client; + use crate::common::contexts::settings::responses::{AllSettingsResponse, Public, PublicSettingsResponse, SiteNameResponse}; + use crate::e2e::environment::TestEnv; + use crate::e2e::web::api::v1::contexts::user::steps::new_logged_in_admin; + + #[tokio::test] + async fn it_should_allow_admins_to_get_all_the_settings() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + let logged_in_admin = new_logged_in_admin(&env).await; + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + + let response = client.get_settings().await; + + let res: AllSettingsResponse = serde_json::from_str(&response.body).unwrap(); + + assert_eq!(res.data, env.server_settings_masking_secrets().unwrap()); + + assert_json_ok_response(&response); + } + + #[tokio::test] + async fn it_should_allow_admins_to_get_the_public_settings() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + let logged_in_admin = new_logged_in_admin(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + + let response = client.get_public_settings().await; + + let res: PublicSettingsResponse = serde_json::from_str(&response.body) + .unwrap_or_else(|_| panic!("response {:#?} should be a PublicSettingsResponse", response.body)); + + let email_on_signup = match &env.server_settings().unwrap().registration { + Some(registration) => match ®istration.email { + Some(email) => { + if email.required { + EmailOnSignup::Required + } else { + EmailOnSignup::Optional + } + } + None => EmailOnSignup::NotIncluded, + }, + None => EmailOnSignup::NotIncluded, + }; + + assert_eq!( + res.data, + Public { + website_name: env.server_settings().unwrap().website.name, + tracker_url: env.server_settings().unwrap().tracker.url, + tracker_listed: env.server_settings().unwrap().tracker.listed, + tracker_private: env.server_settings().unwrap().tracker.private, + email_on_signup: email_on_signup.to_string(), + } + ); + + assert_json_ok_response(&response); + } + + #[tokio::test] + async fn it_should_allow_authenticated_users_to_get_the_site_name() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + let logged_in_admin = new_logged_in_admin(&env).await; - let logged_in_admin = new_logged_in_admin(&env).await; - let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); - let response = client.get_settings().await; + let response = client.get_site_name().await; - let res: AllSettingsResponse = serde_json::from_str(&response.body).unwrap(); + let res: SiteNameResponse = serde_json::from_str(&response.body).unwrap(); - assert_eq!(res.data, env.server_settings_masking_secrets().unwrap()); + assert_eq!(res.data, "Torrust"); - assert_json_ok_response(&response); + assert_json_ok_response(&response); + } } From 1481a3fb44516b1f221dbe29e2dc331b838cfe0c Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 21 Aug 2024 21:21:42 +0200 Subject: [PATCH 04/11] test: added authorization tests for the tag context --- tests/e2e/web/api/v1/contexts/tag/contract.rs | 275 +++++++++++------- 1 file changed, 174 insertions(+), 101 deletions(-) diff --git a/tests/e2e/web/api/v1/contexts/tag/contract.rs b/tests/e2e/web/api/v1/contexts/tag/contract.rs index 77771d49..ab31280c 100644 --- a/tests/e2e/web/api/v1/contexts/tag/contract.rs +++ b/tests/e2e/web/api/v1/contexts/tag/contract.rs @@ -1,16 +1,186 @@ //! API contract for `tag` context. +mod for_guest_users { + + use torrust_index::web::api; + + use crate::common::client::Client; + use crate::common::contexts::tag::forms::{AddTagForm, DeleteTagForm}; + use crate::e2e::environment::TestEnv; + use crate::e2e::web::api::v1::contexts::tag::steps::add_random_tag; + + #[tokio::test] + async fn it_should_allow_guest_users_to_get_the_tags() { + let mut env = TestEnv::new(); + + env.start(api::Version::V1).await; + + let client = Client::unauthenticated(&env.server_socket_addr().unwrap()); + + let response = client.get_tags().await; + + assert_eq!(response.status, 200); + } + + #[tokio::test] + async fn it_should_not_allow_adding_a_new_tag_to_unauthenticated_users() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + let client = Client::unauthenticated(&env.server_socket_addr().unwrap()); + + let response = client + .add_tag(AddTagForm { + name: "TAG NAME".to_string(), + }) + .await; + + assert_eq!(response.status, 401); + } + + #[tokio::test] + async fn it_should_not_allow_guests_to_delete_tags() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + let client = Client::unauthenticated(&env.server_socket_addr().unwrap()); + + let (tag_id, _tag_name) = add_random_tag(&env).await; + + let response = client.delete_tag(DeleteTagForm { tag_id }).await; + + assert_eq!(response.status, 401); + } +} +mod for_authenticated_users { + + use torrust_index::web::api; + + use crate::common::client::Client; + use crate::common::contexts::tag::forms::{AddTagForm, DeleteTagForm}; + use crate::e2e::environment::TestEnv; + use crate::e2e::web::api::v1::contexts::tag::steps::add_random_tag; + use crate::e2e::web::api::v1::contexts::user::steps::new_logged_in_user; + + #[tokio::test] + async fn it_should_allow_authenticated_users_to_get_the_tags() { + let mut env = TestEnv::new(); + + env.start(api::Version::V1).await; + + let logged_non_admin = new_logged_in_user(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_non_admin.token); + + let response = client.get_tags().await; + + assert_eq!(response.status, 200); + } + + #[tokio::test] + async fn it_should_not_allow_adding_a_new_tag_to_non_admins() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + let logged_non_admin = new_logged_in_user(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_non_admin.token); + + let response = client + .add_tag(AddTagForm { + name: "TAG NAME".to_string(), + }) + .await; + + assert_eq!(response.status, 403); + } + + #[tokio::test] + async fn it_should_not_allow_non_admins_to_delete_tags() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + let logged_in_non_admin = new_logged_in_user(&env).await; + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_non_admin.token); + + let (tag_id, _tag_name) = add_random_tag(&env).await; + + let response = client.delete_tag(DeleteTagForm { tag_id }).await; + + assert_eq!(response.status, 403); + } +} + +mod for_admin_users { + + use torrust_index::web::api; + + use crate::common::client::Client; + use crate::common::contexts::tag::asserts::{assert_added_tag_response, assert_deleted_tag_response}; + use crate::common::contexts::tag::fixtures::random_tag_name; + use crate::common::contexts::tag::forms::{AddTagForm, DeleteTagForm}; + use crate::e2e::environment::TestEnv; + use crate::e2e::web::api::v1::contexts::tag::steps::add_random_tag; + use crate::e2e::web::api::v1::contexts::user::steps::new_logged_in_admin; + + #[tokio::test] + async fn it_should_allow_admins_to_get_the_tags() { + let mut env = TestEnv::new(); + + env.start(api::Version::V1).await; + + let logged_in_admin = new_logged_in_admin(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + + let response = client.get_tags().await; + + assert_eq!(response.status, 200); + } + + #[tokio::test] + async fn it_should_allow_admins_to_add_new_tags() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + let logged_in_admin = new_logged_in_admin(&env).await; + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + + let tag_name = random_tag_name(); + + let response = client + .add_tag(AddTagForm { + name: tag_name.to_string(), + }) + .await; + + assert_added_tag_response(&response, &tag_name); + } + + #[tokio::test] + async fn it_should_allow_admins_to_delete_tags() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + let logged_in_admin = new_logged_in_admin(&env).await; + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + + let (tag_id, _tag_name) = add_random_tag(&env).await; + + let response = client.delete_tag(DeleteTagForm { tag_id }).await; + + assert_deleted_tag_response(&response, tag_id); + } +} + use torrust_index::web::api; use crate::common::asserts::assert_json_ok_response; use crate::common::client::Client; -use crate::common::contexts::tag::asserts::{assert_added_tag_response, assert_deleted_tag_response}; use crate::common::contexts::tag::fixtures::random_tag_name; -use crate::common::contexts::tag::forms::{AddTagForm, DeleteTagForm}; use crate::common::contexts::tag::responses::ListResponse; use crate::e2e::environment::TestEnv; -use crate::e2e::web::api::v1::contexts::tag::steps::{add_random_tag, add_tag}; -use crate::e2e::web::api::v1::contexts::user::steps::{new_logged_in_admin, new_logged_in_user}; +use crate::e2e::web::api::v1::contexts::tag::steps::add_tag; #[tokio::test] async fn it_should_return_an_empty_tag_list_when_there_are_no_tags() { @@ -50,59 +220,6 @@ async fn it_should_return_a_tag_list() { assert_eq!(response.status, 200); } -#[tokio::test] -async fn it_should_not_allow_adding_a_new_tag_to_unauthenticated_users() { - let mut env = TestEnv::new(); - env.start(api::Version::V1).await; - - let client = Client::unauthenticated(&env.server_socket_addr().unwrap()); - - let response = client - .add_tag(AddTagForm { - name: "TAG NAME".to_string(), - }) - .await; - - assert_eq!(response.status, 401); -} - -#[tokio::test] -async fn it_should_not_allow_adding_a_new_tag_to_non_admins() { - let mut env = TestEnv::new(); - env.start(api::Version::V1).await; - - let logged_non_admin = new_logged_in_user(&env).await; - - let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_non_admin.token); - - let response = client - .add_tag(AddTagForm { - name: "TAG NAME".to_string(), - }) - .await; - - assert_eq!(response.status, 403); -} - -#[tokio::test] -async fn it_should_allow_admins_to_add_new_tags() { - let mut env = TestEnv::new(); - env.start(api::Version::V1).await; - - let logged_in_admin = new_logged_in_admin(&env).await; - let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); - - let tag_name = random_tag_name(); - - let response = client - .add_tag(AddTagForm { - name: tag_name.to_string(), - }) - .await; - - assert_added_tag_response(&response, &tag_name); -} - #[tokio::test] async fn it_should_not_allow_adding_duplicated_tags() { let mut env = TestEnv::new(); @@ -132,47 +249,3 @@ async fn it_should_not_allow_adding_a_tag_with_an_empty_name() { assert_eq!(response.status, 400); } } - -#[tokio::test] -async fn it_should_allow_admins_to_delete_tags() { - let mut env = TestEnv::new(); - env.start(api::Version::V1).await; - - let logged_in_admin = new_logged_in_admin(&env).await; - let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); - - let (tag_id, _tag_name) = add_random_tag(&env).await; - - let response = client.delete_tag(DeleteTagForm { tag_id }).await; - - assert_deleted_tag_response(&response, tag_id); -} - -#[tokio::test] -async fn it_should_not_allow_non_admins_to_delete_tags() { - let mut env = TestEnv::new(); - env.start(api::Version::V1).await; - - let logged_in_non_admin = new_logged_in_user(&env).await; - let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_non_admin.token); - - let (tag_id, _tag_name) = add_random_tag(&env).await; - - let response = client.delete_tag(DeleteTagForm { tag_id }).await; - - assert_eq!(response.status, 403); -} - -#[tokio::test] -async fn it_should_not_allow_guests_to_delete_tags() { - let mut env = TestEnv::new(); - env.start(api::Version::V1).await; - - let client = Client::unauthenticated(&env.server_socket_addr().unwrap()); - - let (tag_id, _tag_name) = add_random_tag(&env).await; - - let response = client.delete_tag(DeleteTagForm { tag_id }).await; - - assert_eq!(response.status, 401); -} From 5bc9e42dbd9864f313590d44d34159000d1a5c98 Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 11 Sep 2024 20:18:43 +0200 Subject: [PATCH 05/11] test: authorization tests for the torrent context --- .../web/api/v1/contexts/torrent/contract.rs | 641 +++++++++++++++++- 1 file changed, 635 insertions(+), 6 deletions(-) diff --git a/tests/e2e/web/api/v1/contexts/torrent/contract.rs b/tests/e2e/web/api/v1/contexts/torrent/contract.rs index ed3b4f33..8005e2da 100644 --- a/tests/e2e/web/api/v1/contexts/torrent/contract.rs +++ b/tests/e2e/web/api/v1/contexts/torrent/contract.rs @@ -250,8 +250,10 @@ mod for_guests { let _result = upload_test_torrent(&client, &second_torrent).await; + let unauthenticated_client = Client::unauthenticated(&env.server_socket_addr().unwrap()); + // Get torrent details using the non-canonical info-hash (second torrent info-hash) - let response = client.get_torrent(&second_torrent.file_info_hash()).await; + let response = unauthenticated_client.get_torrent(&second_torrent.file_info_hash()).await; let torrent_details_response: TorrentDetailsResponse = serde_json::from_str(&response.body).unwrap(); // The returned torrent info should be the same as the first torrent @@ -808,12 +810,21 @@ mod for_authenticated_users { mod and_non_admins { + use torrust_index::utils::parse_torrent::decode_torrent; use torrust_index::web::api; + use uuid::Uuid; use crate::common::client::Client; - use crate::common::contexts::torrent::forms::UpdateTorrentFrom; + use crate::common::contexts::category::fixtures::software_predefined_category_id; + use crate::common::contexts::torrent::asserts::assert_expected_torrent_details; + use crate::common::contexts::torrent::fixtures::{random_torrent, TestTorrent}; + use crate::common::contexts::torrent::forms::{UpdateTorrentFrom, UploadTorrentMultipartForm}; + use crate::common::contexts::torrent::responses::{ + Category, File, TorrentDetails, TorrentDetailsResponse, TorrentListResponse, + }; + use crate::common::http::Query; use crate::e2e::environment::TestEnv; - use crate::e2e::web::api::v1::contexts::torrent::steps::upload_random_torrent_to_index; + use crate::e2e::web::api::v1::contexts::torrent::steps::{upload_random_torrent_to_index, upload_test_torrent}; use crate::e2e::web::api::v1::contexts::user::steps::new_logged_in_user; #[tokio::test] @@ -871,6 +882,294 @@ mod for_authenticated_users { assert_eq!(response.status, 403); } + + #[tokio::test] + async fn it_should_allow_non_admin_users_to_get_torrent_details_searching_by_info_hash() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + if !env.provides_a_tracker() { + println!("test skipped. It requires a tracker to be running."); + return; + } + + let uploader = new_logged_in_user(&env).await; + let (test_torrent, uploaded_torrent) = upload_random_torrent_to_index(&uploader, &env).await; + + let authenticated_user = new_logged_in_user(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &authenticated_user.token); + + let response = client.get_torrent(&test_torrent.file_info_hash()).await; + + let torrent_details_response: TorrentDetailsResponse = serde_json::from_str(&response.body).unwrap(); + + let tracker_url = env.server_settings().unwrap().tracker.url.to_string(); + let encoded_tracker_url = urlencoding::encode(&tracker_url); + + let expected_torrent = TorrentDetails { + torrent_id: uploaded_torrent.torrent_id, + uploader: uploader.username, + info_hash: test_torrent.file_info.info_hash.to_lowercase(), + title: test_torrent.index_info.title.clone(), + description: test_torrent.index_info.description, + category: Category { + id: software_predefined_category_id(), + name: test_torrent.index_info.category, + num_torrents: 19, // Ignored in assertion + }, + upload_date: "2023-04-27 07:56:08".to_string(), // Ignored in assertion + file_size: test_torrent.file_info.content_size, + seeders: 0, + leechers: 0, + files: vec![File { + path: vec![test_torrent.file_info.files[0].clone()], + // Using one file torrent for testing: content_size = first file size + length: test_torrent.file_info.content_size, + md5sum: None, // DevSkim: ignore DS126858 + }], + trackers: vec![tracker_url.clone().to_string()], + magnet_link: format!( + // cspell:disable-next-line + "magnet:?xt=urn:btih:{}&dn={}&tr={}", + test_torrent.file_info.info_hash.to_lowercase(), + urlencoding::encode(&test_torrent.index_info.title), + encoded_tracker_url + ), + tags: vec![], + name: test_torrent.index_info.name.clone(), + comment: test_torrent.file_info.comment.clone(), + creation_date: test_torrent.file_info.creation_date, + created_by: test_torrent.file_info.created_by.clone(), + encoding: test_torrent.file_info.encoding.clone(), + canonical_info_hash_group: vec![test_torrent.file_info.info_hash.to_lowercase()], + }; + + assert_expected_torrent_details(&torrent_details_response.data, &expected_torrent); + assert!(response.is_json_and_ok()); + } + + #[tokio::test] + async fn it_should_allow_non_admin_users_to_find_torrent_details_using_a_non_canonical_info_hash() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + if !env.provides_a_tracker() { + println!("test skipped. It requires a tracker to be running."); + return; + } + + let uploader = new_logged_in_user(&env).await; + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &uploader.token); + + // Sample data needed to build two torrents with the same canonical info-hash. + // Those torrents belong to the same Canonical Infohash Group. + let id = Uuid::new_v4(); + let title = format!("title-{id}"); + let file_contents = "data".to_string(); + + // Upload the first torrent + let mut first_torrent = TestTorrent::with_custom_info_dict_field(id, &file_contents, "custom 01"); + first_torrent.index_info.title.clone_from(&title); + + let first_torrent_canonical_info_hash = upload_test_torrent(&client, &first_torrent) + .await + .expect("first torrent should be uploaded"); + + // Upload the second torrent with the same canonical info-hash + let mut second_torrent = TestTorrent::with_custom_info_dict_field(id, &file_contents, "custom 02"); + second_torrent.index_info.title = format!("{title}-clone"); + + let _result = upload_test_torrent(&client, &second_torrent).await; + + // Get torrent details using the non-canonical info-hash (second torrent info-hash) + let response = client.get_torrent(&second_torrent.file_info_hash()).await; + let torrent_details_response: TorrentDetailsResponse = serde_json::from_str(&response.body).unwrap(); + + // The returned torrent info should be the same as the first torrent + assert_eq!(response.status, 200); + assert_eq!( + torrent_details_response.data.info_hash, + first_torrent_canonical_info_hash.to_hex_string() + ); + } + + #[tokio::test] + async fn it_should_allow_non_admin_users_to_get_torrents() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + if !env.provides_a_tracker() { + println!("test skipped. It requires a tracker to be running."); + return; + } + + let uploader = new_logged_in_user(&env).await; + let (_test_torrent, _indexed_torrent) = upload_random_torrent_to_index(&uploader, &env).await; + + let authenticated_user = new_logged_in_user(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &authenticated_user.token); + + let response = client.get_torrents(Query::empty()).await; + + let torrent_list_response: TorrentListResponse = serde_json::from_str(&response.body).unwrap(); + + assert!(torrent_list_response.data.total > 0); + assert!(response.is_json_and_ok()); + } + + mod it_should_allow_non_admin_users_to_download_a_torrent_file_searching_by_info_hash { + + use torrust_index::utils::parse_torrent::{calculate_info_hash, decode_torrent}; + use torrust_index::web::api; + + use crate::common::client::Client; + use crate::e2e::environment::TestEnv; + use crate::e2e::web::api::v1::contexts::torrent::asserts::canonical_torrent_for; + use crate::e2e::web::api::v1::contexts::torrent::steps::upload_random_torrent_to_index; + use crate::e2e::web::api::v1::contexts::user::steps::new_logged_in_user; + + #[tokio::test] + async fn returning_a_bittorrent_binary_ok_response() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + if !env.provides_a_tracker() { + println!("test skipped. It requires a tracker to be running."); + return; + } + + let authenticated_user = new_logged_in_user(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &authenticated_user.token); + + let uploader = new_logged_in_user(&env).await; + + // Upload + let (test_torrent, _torrent_listed_in_index) = upload_random_torrent_to_index(&uploader, &env).await; + + // Download + let response = client.download_torrent(&test_torrent.file_info_hash()).await; + + assert!(response.is_a_bit_torrent_file()); + } + + #[tokio::test] + async fn the_downloaded_torrent_should_keep_the_same_info_hash_if_the_torrent_does_not_have_non_standard_fields_in_the_info_dict( + ) { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + if !env.provides_a_tracker() { + println!("test skipped. It requires a tracker to be running."); + return; + } + + let authenticated_user = new_logged_in_user(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &authenticated_user.token); + + let uploader = new_logged_in_user(&env).await; + + // Upload + let (test_torrent, _torrent_listed_in_index) = upload_random_torrent_to_index(&uploader, &env).await; + + // Download + let response = client.download_torrent(&test_torrent.file_info_hash()).await; + + let downloaded_torrent_info_hash = + calculate_info_hash(&response.bytes).expect("failed to calculate info-hash of the downloaded torrent"); + + assert_eq!( + downloaded_torrent_info_hash.to_hex_string(), + test_torrent.file_info_hash(), + "downloaded torrent info-hash does not match uploaded torrent info-hash" + ); + } + + #[tokio::test] + async fn the_downloaded_torrent_should_be_the_canonical_version_of_the_uploaded_one() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + if !env.provides_a_tracker() { + println!("test skipped. It requires a tracker to be running."); + return; + } + + let authenticated_user = new_logged_in_user(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &authenticated_user.token); + + let uploader = new_logged_in_user(&env).await; + + // Upload + let (test_torrent, _torrent_listed_in_index) = upload_random_torrent_to_index(&uploader, &env).await; + + let uploaded_torrent = + decode_torrent(&test_torrent.index_info.torrent_file.contents).expect("could not decode uploaded torrent"); + + // Download + let response = client.download_torrent(&test_torrent.file_info_hash()).await; + + let downloaded_torrent = decode_torrent(&response.bytes).expect("could not decode downloaded torrent"); + + let expected_downloaded_torrent = canonical_torrent_for(uploaded_torrent, &env, &None).await; + + assert_eq!(downloaded_torrent, expected_downloaded_torrent); + } + } + + #[tokio::test] + async fn it_should_allow_non_admin_users_to_download_a_torrent_using_a_non_canonical_info_hash() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + /* if !env.provides_a_tracker() { + println!("test skipped. It requires a tracker to be running."); + return; + } */ + + let uploader = new_logged_in_user(&env).await; + let upload_client = Client::authenticated(&env.server_socket_addr().unwrap(), &uploader.token); + + // Sample data needed to build two torrents with the same canonical info-hash. + // Those torrents belong to the same Canonical Infohash Group. + let id = Uuid::new_v4(); + let title = format!("title-{id}"); + let file_contents = "data".to_string(); + + // Upload the first torrent + let mut first_torrent = TestTorrent::with_custom_info_dict_field(id, &file_contents, "custom 01"); + first_torrent.index_info.title.clone_from(&title); + + let first_torrent_canonical_info_hash = upload_test_torrent(&upload_client, &first_torrent) + .await + .expect("first torrent should be uploaded"); + + // Upload the second torrent with the same canonical info-hash + let mut second_torrent = TestTorrent::with_custom_info_dict_field(id, &file_contents, "custom 02"); + second_torrent.index_info.title = format!("{title}-clone"); + + let _result = upload_test_torrent(&upload_client, &second_torrent).await; + + let authenticated_user = new_logged_in_user(&env).await; + + let download_client = Client::authenticated(&env.server_socket_addr().unwrap(), &authenticated_user.token); + + // Download the torrent using the non-canonical info-hash (second torrent info-hash) + let response = download_client.download_torrent(&second_torrent.file_info_hash()).await; + + let torrent = decode_torrent(&response.bytes).expect("could not decode downloaded torrent"); + + // The returned torrent info-hash should be the same as the first torrent + assert_eq!(response.status, 200); + assert_eq!( + torrent.canonical_info_hash_hex(), + first_torrent_canonical_info_hash.to_hex_string() + ); + } } mod and_torrent_owners { @@ -926,13 +1225,22 @@ mod for_authenticated_users { mod and_admins { + use torrust_index::utils::parse_torrent::decode_torrent; use torrust_index::web::api; + use uuid::Uuid; use crate::common::client::Client; - use crate::common::contexts::torrent::forms::UpdateTorrentFrom; - use crate::common::contexts::torrent::responses::{DeletedTorrentResponse, UpdatedTorrentResponse}; + use crate::common::contexts::category::fixtures::software_predefined_category_id; + use crate::common::contexts::torrent::asserts::assert_expected_torrent_details; + use crate::common::contexts::torrent::fixtures::{random_torrent, TestTorrent}; + use crate::common::contexts::torrent::forms::{UpdateTorrentFrom, UploadTorrentMultipartForm}; + use crate::common::contexts::torrent::responses::{ + Category, DeletedTorrentResponse, File, TorrentDetails, TorrentDetailsResponse, TorrentListResponse, + UpdatedTorrentResponse, UploadedTorrentResponse, + }; + use crate::common::http::Query; use crate::e2e::environment::TestEnv; - use crate::e2e::web::api::v1::contexts::torrent::steps::upload_random_torrent_to_index; + use crate::e2e::web::api::v1::contexts::torrent::steps::{upload_random_torrent_to_index, upload_test_torrent}; use crate::e2e::web::api::v1::contexts::user::steps::{new_logged_in_admin, new_logged_in_user}; #[tokio::test] @@ -998,5 +1306,326 @@ mod for_authenticated_users { assert_eq!(torrent.description, new_description); assert!(response.is_json_and_ok()); } + + #[tokio::test] + async fn it_should_allow_admin_users_to_upload_new_torrents() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + if !env.provides_a_tracker() { + println!("test skipped. It requires a tracker to be running."); + return; + } + + let logged_in_admin = new_logged_in_admin(&env).await; + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + + let test_torrent = random_torrent(); + let info_hash = test_torrent.file_info_hash().clone(); + + let form: UploadTorrentMultipartForm = test_torrent.index_info.into(); + + let response = client.upload_torrent(form.into()).await; + + let uploaded_torrent_response: UploadedTorrentResponse = serde_json::from_str(&response.body).unwrap(); + + assert_eq!( + uploaded_torrent_response.data.canonical_info_hash.to_lowercase(), + info_hash.to_lowercase() + ); + assert!(response.is_json_and_ok()); + } + + #[tokio::test] + async fn it_should_allow_admin_users_to_get_torrent_details_searching_by_info_hash() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + if !env.provides_a_tracker() { + println!("test skipped. It requires a tracker to be running."); + return; + } + + let uploader = new_logged_in_user(&env).await; + let (test_torrent, uploaded_torrent) = upload_random_torrent_to_index(&uploader, &env).await; + + let logged_in_admin = new_logged_in_admin(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + + let response = client.get_torrent(&test_torrent.file_info_hash()).await; + + let torrent_details_response: TorrentDetailsResponse = serde_json::from_str(&response.body).unwrap(); + + let tracker_url = env.server_settings().unwrap().tracker.url.to_string(); + let encoded_tracker_url = urlencoding::encode(&tracker_url); + + let expected_torrent = TorrentDetails { + torrent_id: uploaded_torrent.torrent_id, + uploader: uploader.username, + info_hash: test_torrent.file_info.info_hash.to_lowercase(), + title: test_torrent.index_info.title.clone(), + description: test_torrent.index_info.description, + category: Category { + id: software_predefined_category_id(), + name: test_torrent.index_info.category, + num_torrents: 19, // Ignored in assertion + }, + upload_date: "2023-04-27 07:56:08".to_string(), // Ignored in assertion + file_size: test_torrent.file_info.content_size, + seeders: 0, + leechers: 0, + files: vec![File { + path: vec![test_torrent.file_info.files[0].clone()], + // Using one file torrent for testing: content_size = first file size + length: test_torrent.file_info.content_size, + md5sum: None, // DevSkim: ignore DS126858 + }], + trackers: vec![tracker_url.clone().to_string()], + magnet_link: format!( + // cspell:disable-next-line + "magnet:?xt=urn:btih:{}&dn={}&tr={}", + test_torrent.file_info.info_hash.to_lowercase(), + urlencoding::encode(&test_torrent.index_info.title), + encoded_tracker_url + ), + tags: vec![], + name: test_torrent.index_info.name.clone(), + comment: test_torrent.file_info.comment.clone(), + creation_date: test_torrent.file_info.creation_date, + created_by: test_torrent.file_info.created_by.clone(), + encoding: test_torrent.file_info.encoding.clone(), + canonical_info_hash_group: vec![test_torrent.file_info.info_hash.to_lowercase()], + }; + + assert_expected_torrent_details(&torrent_details_response.data, &expected_torrent); + assert!(response.is_json_and_ok()); + } + + #[tokio::test] + async fn it_should_allow_admin_users_to_find_torrent_details_using_a_non_canonical_info_hash() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + if !env.provides_a_tracker() { + println!("test skipped. It requires a tracker to be running."); + return; + } + + let uploader = new_logged_in_user(&env).await; + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &uploader.token); + + // Sample data needed to build two torrents with the same canonical info-hash. + // Those torrents belong to the same Canonical Infohash Group. + let id = Uuid::new_v4(); + let title = format!("title-{id}"); + let file_contents = "data".to_string(); + + // Upload the first torrent + let mut first_torrent = TestTorrent::with_custom_info_dict_field(id, &file_contents, "custom 01"); + first_torrent.index_info.title.clone_from(&title); + + let first_torrent_canonical_info_hash = upload_test_torrent(&client, &first_torrent) + .await + .expect("first torrent should be uploaded"); + + // Upload the second torrent with the same canonical info-hash + let mut second_torrent = TestTorrent::with_custom_info_dict_field(id, &file_contents, "custom 02"); + second_torrent.index_info.title = format!("{title}-clone"); + + let _result = upload_test_torrent(&client, &second_torrent).await; + + let logged_in_admin = new_logged_in_admin(&env).await; + + let logged_in_admin_client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + + // Get torrent details using the non-canonical info-hash (second torrent info-hash) + let response = logged_in_admin_client.get_torrent(&second_torrent.file_info_hash()).await; + let torrent_details_response: TorrentDetailsResponse = serde_json::from_str(&response.body).unwrap(); + + // The returned torrent info should be the same as the first torrent + assert_eq!(response.status, 200); + assert_eq!( + torrent_details_response.data.info_hash, + first_torrent_canonical_info_hash.to_hex_string() + ); + } + + #[tokio::test] + async fn it_should_allow_admin_users_to_get_torrents() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + if !env.provides_a_tracker() { + println!("test skipped. It requires a tracker to be running."); + return; + } + + let uploader = new_logged_in_user(&env).await; + + let (_test_torrent, _indexed_torrent) = upload_random_torrent_to_index(&uploader, &env).await; + + let logged_in_admin = new_logged_in_admin(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + + let response = client.get_torrents(Query::empty()).await; + + let torrent_list_response: TorrentListResponse = serde_json::from_str(&response.body).unwrap(); + + assert!(torrent_list_response.data.total > 0); + assert!(response.is_json_and_ok()); + } + mod it_should_allow_admin_users_to_download_a_torrent_file_searching_by_info_hash { + + use torrust_index::utils::parse_torrent::{calculate_info_hash, decode_torrent}; + use torrust_index::web::api; + + use crate::common::client::Client; + use crate::e2e::environment::TestEnv; + use crate::e2e::web::api::v1::contexts::torrent::asserts::canonical_torrent_for; + use crate::e2e::web::api::v1::contexts::torrent::steps::upload_random_torrent_to_index; + use crate::e2e::web::api::v1::contexts::user::steps::{new_logged_in_admin, new_logged_in_user}; + + #[tokio::test] + async fn returning_a_bittorrent_binary_ok_response() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + if !env.provides_a_tracker() { + println!("test skipped. It requires a tracker to be running."); + return; + } + + let logged_in_admin = new_logged_in_admin(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + + let uploader = new_logged_in_user(&env).await; + + // Upload + let (test_torrent, _torrent_listed_in_index) = upload_random_torrent_to_index(&uploader, &env).await; + + // Download + let response = client.download_torrent(&test_torrent.file_info_hash()).await; + + assert!(response.is_a_bit_torrent_file()); + } + + #[tokio::test] + async fn the_downloaded_torrent_should_keep_the_same_info_hash_if_the_torrent_does_not_have_non_standard_fields_in_the_info_dict( + ) { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + if !env.provides_a_tracker() { + println!("test skipped. It requires a tracker to be running."); + return; + } + + let logged_in_admin = new_logged_in_admin(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + + let uploader = new_logged_in_user(&env).await; + + // Upload + let (test_torrent, _torrent_listed_in_index) = upload_random_torrent_to_index(&uploader, &env).await; + + // Download + let response = client.download_torrent(&test_torrent.file_info_hash()).await; + + let downloaded_torrent_info_hash = + calculate_info_hash(&response.bytes).expect("failed to calculate info-hash of the downloaded torrent"); + + assert_eq!( + downloaded_torrent_info_hash.to_hex_string(), + test_torrent.file_info_hash(), + "downloaded torrent info-hash does not match uploaded torrent info-hash" + ); + } + + #[tokio::test] + async fn the_downloaded_torrent_should_be_the_canonical_version_of_the_uploaded_one() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + if !env.provides_a_tracker() { + println!("test skipped. It requires a tracker to be running."); + return; + } + + let logged_in_admin = new_logged_in_admin(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + + let uploader = new_logged_in_user(&env).await; + + // Upload + let (test_torrent, _torrent_listed_in_index) = upload_random_torrent_to_index(&uploader, &env).await; + + let uploaded_torrent = + decode_torrent(&test_torrent.index_info.torrent_file.contents).expect("could not decode uploaded torrent"); + + // Download + let response = client.download_torrent(&test_torrent.file_info_hash()).await; + + let downloaded_torrent = decode_torrent(&response.bytes).expect("could not decode downloaded torrent"); + + let expected_downloaded_torrent = canonical_torrent_for(uploaded_torrent, &env, &None).await; + + assert_eq!(downloaded_torrent, expected_downloaded_torrent); + } + } + + #[tokio::test] + async fn it_should_allow_admin_users_to_download_a_torrent_using_a_non_canonical_info_hash() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + if !env.provides_a_tracker() { + println!("test skipped. It requires a tracker to be running."); + return; + } + + let uploader = new_logged_in_user(&env).await; + let upload_client = Client::authenticated(&env.server_socket_addr().unwrap(), &uploader.token); + + // Sample data needed to build two torrents with the same canonical info-hash. + // Those torrents belong to the same Canonical Infohash Group. + let id = Uuid::new_v4(); + let title = format!("title-{id}"); + let file_contents = "data".to_string(); + + // Upload the first torrent + let mut first_torrent = TestTorrent::with_custom_info_dict_field(id, &file_contents, "custom 01"); + first_torrent.index_info.title.clone_from(&title); + + let first_torrent_canonical_info_hash = upload_test_torrent(&upload_client, &first_torrent) + .await + .expect("first torrent should be uploaded"); + + // Upload the second torrent with the same canonical info-hash + let mut second_torrent = TestTorrent::with_custom_info_dict_field(id, &file_contents, "custom 02"); + second_torrent.index_info.title = format!("{title}-clone"); + + let _result = upload_test_torrent(&upload_client, &second_torrent).await; + + let logged_in_admin = new_logged_in_admin(&env).await; + + let download_client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + + // Download the torrent using the non-canonical info-hash (second torrent info-hash) + let response = download_client.download_torrent(&second_torrent.file_info_hash()).await; + + let torrent = decode_torrent(&response.bytes).expect("could not decode downloaded torrent"); + + // The returned torrent info-hash should be the same as the first torrent + assert_eq!(response.status, 200); + assert_eq!( + torrent.canonical_info_hash_hex(), + first_torrent_canonical_info_hash.to_hex_string() + ); + } } } From d8ba81ec407ac521bda9dbaeb19e1cf679224e75 Mon Sep 17 00:00:00 2001 From: Mario Date: Thu, 12 Sep 2024 14:21:00 +0200 Subject: [PATCH 06/11] refactor: uncommented lines and fixed clippy errors --- tests/e2e/web/api/v1/contexts/torrent/contract.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/e2e/web/api/v1/contexts/torrent/contract.rs b/tests/e2e/web/api/v1/contexts/torrent/contract.rs index 8005e2da..e4232fe1 100644 --- a/tests/e2e/web/api/v1/contexts/torrent/contract.rs +++ b/tests/e2e/web/api/v1/contexts/torrent/contract.rs @@ -817,8 +817,8 @@ mod for_authenticated_users { use crate::common::client::Client; use crate::common::contexts::category::fixtures::software_predefined_category_id; use crate::common::contexts::torrent::asserts::assert_expected_torrent_details; - use crate::common::contexts::torrent::fixtures::{random_torrent, TestTorrent}; - use crate::common::contexts::torrent::forms::{UpdateTorrentFrom, UploadTorrentMultipartForm}; + use crate::common::contexts::torrent::fixtures::TestTorrent; + use crate::common::contexts::torrent::forms::UpdateTorrentFrom; use crate::common::contexts::torrent::responses::{ Category, File, TorrentDetails, TorrentDetailsResponse, TorrentListResponse, }; @@ -1126,10 +1126,10 @@ mod for_authenticated_users { let mut env = TestEnv::new(); env.start(api::Version::V1).await; - /* if !env.provides_a_tracker() { + if !env.provides_a_tracker() { println!("test skipped. It requires a tracker to be running."); return; - } */ + } let uploader = new_logged_in_user(&env).await; let upload_client = Client::authenticated(&env.server_socket_addr().unwrap(), &uploader.token); From 6706e356566d072aca753f94a60856c14f7ff460 Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 18 Sep 2024 21:37:16 +0200 Subject: [PATCH 07/11] refactor: remove unnecessary slash in tracker url --- tests/e2e/web/api/v1/contexts/torrent/asserts.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/web/api/v1/contexts/torrent/asserts.rs b/tests/e2e/web/api/v1/contexts/torrent/asserts.rs index e268c9d6..b26b9d6f 100644 --- a/tests/e2e/web/api/v1/contexts/torrent/asserts.rs +++ b/tests/e2e/web/api/v1/contexts/torrent/asserts.rs @@ -58,7 +58,7 @@ pub async fn get_user_tracker_key(logged_in_user: &LoggedInUserData, env: &TestE pub fn build_announce_url(tracker_url: &str, tracker_key: &Option) -> String { if let Some(key) = &tracker_key { - format!("{tracker_url}/{}", key.key) + format!("{tracker_url}{}", key.key) } else { tracker_url.to_string() } @@ -66,7 +66,7 @@ pub fn build_announce_url(tracker_url: &str, tracker_key: &Option) - fn build_announce_list(tracker_url: &str, tracker_key: &Option) -> Vec> { if let Some(key) = &tracker_key { - vec![vec![format!("{tracker_url}/{}", key.key)], vec![format!("{tracker_url}")]] + vec![vec![format!("{tracker_url}{}", key.key)], vec![format!("{tracker_url}")]] } else { vec![vec![format!("{tracker_url}")]] } From 5147d1920869da1c54ea94386f11718a6deefc18 Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 18 Sep 2024 21:49:46 +0200 Subject: [PATCH 08/11] test: added test for downloading the canonical version of the torrent file as an admin --- .../web/api/v1/contexts/torrent/contract.rs | 40 +++++++++++++++++-- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/tests/e2e/web/api/v1/contexts/torrent/contract.rs b/tests/e2e/web/api/v1/contexts/torrent/contract.rs index e4232fe1..9f968ca1 100644 --- a/tests/e2e/web/api/v1/contexts/torrent/contract.rs +++ b/tests/e2e/web/api/v1/contexts/torrent/contract.rs @@ -1546,12 +1546,12 @@ mod for_authenticated_users { } #[tokio::test] - async fn the_downloaded_torrent_should_be_the_canonical_version_of_the_uploaded_one() { + async fn the_downloaded_torrent_should_be_the_canonical_version_of_the_uploaded_one_using_a_public_tracker() { let mut env = TestEnv::new(); env.start(api::Version::V1).await; - if !env.provides_a_tracker() { - println!("test skipped. It requires a tracker to be running."); + if !env.provides_a_public_tracker() { + println!("test skipped. It requires a public tracker to be running."); return; } @@ -1572,7 +1572,39 @@ mod for_authenticated_users { let downloaded_torrent = decode_torrent(&response.bytes).expect("could not decode downloaded torrent"); - let expected_downloaded_torrent = canonical_torrent_for(uploaded_torrent, &env, &None).await; + let expected_downloaded_torrent = canonical_torrent_for(uploaded_torrent, &env, &Some(logged_in_admin)).await; + + assert_eq!(downloaded_torrent, expected_downloaded_torrent); + } + + #[tokio::test] + async fn the_downloaded_torrent_should_be_the_canonical_version_of_the_uploaded_one_using_a_private_tracker() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + if !env.provides_a_private_tracker() { + println!("test skipped. It requires a public tracker to be running."); + return; + } + + let logged_in_admin = new_logged_in_admin(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + + let uploader = new_logged_in_user(&env).await; + + // Upload + let (test_torrent, _torrent_listed_in_index) = upload_random_torrent_to_index(&uploader, &env).await; + + let uploaded_torrent = + decode_torrent(&test_torrent.index_info.torrent_file.contents).expect("could not decode uploaded torrent"); + + // Download + let response = client.download_torrent(&test_torrent.file_info_hash()).await; + + let downloaded_torrent = decode_torrent(&response.bytes).expect("could not decode downloaded torrent"); + + let expected_downloaded_torrent = canonical_torrent_for(uploaded_torrent, &env, &Some(logged_in_admin)).await; assert_eq!(downloaded_torrent, expected_downloaded_torrent); } From 5f499d7494f5f7a9194b48530029f348ff09c5ab Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 18 Sep 2024 22:21:51 +0200 Subject: [PATCH 09/11] feat: new provides a public tracker method --- tests/e2e/environment.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/e2e/environment.rs b/tests/e2e/environment.rs index fd72458e..cf1ada9c 100644 --- a/tests/e2e/environment.rs +++ b/tests/e2e/environment.rs @@ -92,6 +92,19 @@ impl TestEnv { } } + #[allow(dead_code)] + /// Some test requires a real tracker running in `public` mode. + pub fn provides_a_public_tracker(&self) -> bool { + if !self.is_shared() { + return false; + }; + + match self.server_settings() { + Some(settings) => !settings.tracker.private, + None => false, + } + } + /// Returns the server starting settings if the servers was already started. /// We do not know the settings until we start the server. pub fn server_settings(&self) -> Option { From a6d52e126cec6eb52607e5859aaa35b555abd209 Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 18 Sep 2024 22:30:29 +0200 Subject: [PATCH 10/11] test: fixed test not passing with public tracker --- tests/e2e/web/api/v1/contexts/torrent/contract.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/web/api/v1/contexts/torrent/contract.rs b/tests/e2e/web/api/v1/contexts/torrent/contract.rs index 9f968ca1..2efa1479 100644 --- a/tests/e2e/web/api/v1/contexts/torrent/contract.rs +++ b/tests/e2e/web/api/v1/contexts/torrent/contract.rs @@ -1572,7 +1572,7 @@ mod for_authenticated_users { let downloaded_torrent = decode_torrent(&response.bytes).expect("could not decode downloaded torrent"); - let expected_downloaded_torrent = canonical_torrent_for(uploaded_torrent, &env, &Some(logged_in_admin)).await; + let expected_downloaded_torrent = canonical_torrent_for(uploaded_torrent, &env, &None).await; assert_eq!(downloaded_torrent, expected_downloaded_torrent); } From cc26263ccead553ac5beae03e1cdc36248eee333 Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 25 Sep 2024 13:34:48 +0200 Subject: [PATCH 11/11] wip --- tests/common/client.rs | 6 + .../e2e/web/api/v1/contexts/proxy/contract.rs | 40 ++++- .../web/api/v1/contexts/torrent/contract.rs | 146 +++++++++++++++++- .../e2e/web/api/v1/contexts/torrent/steps.rs | 31 ++++ 4 files changed, 218 insertions(+), 5 deletions(-) diff --git a/tests/common/client.rs b/tests/common/client.rs index c9c22d2e..e91ea608 100644 --- a/tests/common/client.rs +++ b/tests/common/client.rs @@ -177,6 +177,12 @@ impl Client { pub async fn ban_user(&self, username: Username) -> TextResponse { self.http_client.delete(&format!("/user/ban/{}", &username.value)).await } + + // Context: proxy + + /* pub async fn get_image_by_url(&self, url: &str) -> TextResponse { + self.http_client.get(&format!("/proxy/image/{url}"), Query::empty()).await + } */ } /// Generic HTTP Client diff --git a/tests/e2e/web/api/v1/contexts/proxy/contract.rs b/tests/e2e/web/api/v1/contexts/proxy/contract.rs index 0b63dfc4..560e522d 100644 --- a/tests/e2e/web/api/v1/contexts/proxy/contract.rs +++ b/tests/e2e/web/api/v1/contexts/proxy/contract.rs @@ -1,3 +1,41 @@ //! API contract for `proxy` context. -// todo +/* mod for_guest_users { + use torrust_index::web::api; + + use crate::common::client::Client; + use crate::common::contexts::torrent::fixtures::random_torrent; + use crate::e2e::environment::TestEnv; + use crate::e2e::web::api::v1::contexts::torrent::steps::upload_torrent; + use crate::e2e::web::api::v1::contexts::user::steps::new_logged_in_user; + + #[tokio::test] + async fn it_should_not_allow_guest_user_to_get_a_proxied_image_by_url() { + let mut env = TestEnv::new(); + + env.start(api::Version::V1).await; + + /* if !env.provides_a_tracker() { + println!("test skipped. It requires a tracker to be running."); + return; + } */ + + let mut test_torrent = random_torrent(); + + test_torrent.index_info.description = String::from( + "![image info](https://upload.wikimedia.org/wikipedia/commons/thumb/7/71/Zaadpluizen_van_een_Clematis_texensis_%27Princess_Diana%27._18-07-2023_%28actm.%29_02.jpg/1024px-Zaadpluizen_van_een_Clematis_texensis_%27Princess_Diana%27._18-07-2023_%28actm.%29_02.jpg)", + ); + + let torrent_uploader = new_logged_in_user(&env).await; + + upload_torrent(&torrent_uploader, &test_torrent.index_info, &env).await; + + let client = Client::unauthenticated(&env.server_socket_addr().unwrap()); + + let url = "https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fthumb%2F7%2F71%2FZaadpluizen_van_een_Clematis_texensis_%2527Princess_Diana%2527._18-07-2023_%2528actm.%2529_02.jpg%2F1024px-Zaadpluizen_van_een_Clematis_texensis_%2527Princess_Diana%2527._18-07-2023_%2528actm.%2529_02.jpg"; + + let response = client.get_image_by_url(url).await; + + assert_eq!(response.status, 401); + } +} */ diff --git a/tests/e2e/web/api/v1/contexts/torrent/contract.rs b/tests/e2e/web/api/v1/contexts/torrent/contract.rs index 2efa1479..b1a59651 100644 --- a/tests/e2e/web/api/v1/contexts/torrent/contract.rs +++ b/tests/e2e/web/api/v1/contexts/torrent/contract.rs @@ -1337,12 +1337,150 @@ mod for_authenticated_users { } #[tokio::test] - async fn it_should_allow_admin_users_to_get_torrent_details_searching_by_info_hash() { + async fn it_should_allow_admin_users_to_get_torrent_details_searching_by_info_hash_using_a_public_tracker() { let mut env = TestEnv::new(); env.start(api::Version::V1).await; - if !env.provides_a_tracker() { - println!("test skipped. It requires a tracker to be running."); + if !env.provides_a_public_tracker() { + println!("test skipped. It requires a public tracker to be running."); + return; + } + + let uploader = new_logged_in_user(&env).await; + let (test_torrent, uploaded_torrent) = upload_random_torrent_to_index(&uploader, &env).await; + + let logged_in_admin = new_logged_in_admin(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + + let response = client.get_torrent(&test_torrent.file_info_hash()).await; + + let torrent_details_response: TorrentDetailsResponse = serde_json::from_str(&response.body).unwrap(); + + let tracker_url = env.server_settings().unwrap().tracker.url.to_string(); + let encoded_tracker_url = urlencoding::encode(&tracker_url); + + let expected_torrent = TorrentDetails { + torrent_id: uploaded_torrent.torrent_id, + uploader: uploader.username, + info_hash: test_torrent.file_info.info_hash.to_lowercase(), + title: test_torrent.index_info.title.clone(), + description: test_torrent.index_info.description, + category: Category { + id: software_predefined_category_id(), + name: test_torrent.index_info.category, + num_torrents: 19, // Ignored in assertion + }, + upload_date: "2023-04-27 07:56:08".to_string(), // Ignored in assertion + file_size: test_torrent.file_info.content_size, + seeders: 0, + leechers: 0, + files: vec![File { + path: vec![test_torrent.file_info.files[0].clone()], + // Using one file torrent for testing: content_size = first file size + length: test_torrent.file_info.content_size, + md5sum: None, // DevSkim: ignore DS126858 + }], + trackers: vec![tracker_url.clone().to_string()], + magnet_link: format!( + // cspell:disable-next-line + "magnet:?xt=urn:btih:{}&dn={}&tr={}", + test_torrent.file_info.info_hash.to_lowercase(), + urlencoding::encode(&test_torrent.index_info.title), + encoded_tracker_url + ), + tags: vec![], + name: test_torrent.index_info.name.clone(), + comment: test_torrent.file_info.comment.clone(), + creation_date: test_torrent.file_info.creation_date, + created_by: test_torrent.file_info.created_by.clone(), + encoding: test_torrent.file_info.encoding.clone(), + canonical_info_hash_group: vec![test_torrent.file_info.info_hash.to_lowercase()], + }; + + assert_expected_torrent_details(&torrent_details_response.data, &expected_torrent); + assert!(response.is_json_and_ok()); + } + + /* #[tokio::test] + async fn it_should_allow_admin_users_to_get_torrent_details_searching_by_info_hash_using_a_private_tracker() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + if !env.provides_a_private_tracker() { + println!("test skipped. It requires a private tracker to be running."); + return; + } + + let uploader = new_logged_in_user(&env).await; + let (test_torrent, uploaded_torrent) = upload_random_torrent_to_index(&uploader, &env).await; + + let logged_in_admin = new_logged_in_admin(&env).await; + + let client = Client::authenticated(&env.server_socket_addr().unwrap(), &logged_in_admin.token); + + let response = client.get_torrent(&test_torrent.file_info_hash()).await; + + let torrent_details_response: TorrentDetailsResponse = serde_json::from_str(&response.body).unwrap(); + + let tracker_url = env.server_settings().unwrap().tracker.url.to_string(); + let encoded_tracker_url = urlencoding::encode(&tracker_url); + + let expected_torrent_info = client.get_torrent(&test_torrent.file_info.info_hash.to_lowercase()).await; + + //expected_torrent_info.body. + + let parsed_response = serde_json::from_str(&expected_torrent_info.body); + + let expected_torrent = TorrentDetails { + torrent_id: uploaded_torrent.torrent_id, + uploader: uploader.username, + info_hash: test_torrent.file_info.info_hash.to_lowercase(), + title: test_torrent.index_info.title.clone(), + description: test_torrent.index_info.description, + category: Category { + id: software_predefined_category_id(), + name: test_torrent.index_info.category, + num_torrents: 19, // Ignored in assertion + }, + upload_date: "2023-04-27 07:56:08".to_string(), // Ignored in assertion + file_size: test_torrent.file_info.content_size, + seeders: 0, + leechers: 0, + files: vec![File { + path: vec![test_torrent.file_info.files[0].clone()], + // Using one file torrent for testing: content_size = first file size + length: test_torrent.file_info.content_size, + md5sum: None, // DevSkim: ignore DS126858 + }], + trackers: vec![tracker_url.clone().to_string()], + magnet_link: format!( + // cspell:disable-next-line + "magnet:?xt=urn:btih:{}&dn={}&tr={}", + test_torrent.file_info.info_hash.to_lowercase(), + urlencoding::encode(&test_torrent.index_info.title), + encoded_tracker_url + ), + tags: vec![], + name: test_torrent.index_info.name.clone(), + comment: test_torrent.file_info.comment.clone(), + creation_date: test_torrent.file_info.creation_date, + created_by: test_torrent.file_info.created_by.clone(), + encoding: test_torrent.file_info.encoding.clone(), + canonical_info_hash_group: vec![test_torrent.file_info.info_hash.to_lowercase()], + }; + + assert_expected_torrent_details(&torrent_details_response.data, &expected_torrent); + assert!(response.is_json_and_ok()); + } */ + + #[tokio::test] + async fn it_should_allow_admin_users_to_get_torrent_details_searching_by_info_hash_using_a_private_tracker() { + let mut env = TestEnv::new(); + env.start(api::Version::V1).await; + + if !env.provides_a_private_tracker() { + println!("test skipped. It requires a private tracker to be running."); return; } @@ -1583,7 +1721,7 @@ mod for_authenticated_users { env.start(api::Version::V1).await; if !env.provides_a_private_tracker() { - println!("test skipped. It requires a public tracker to be running."); + println!("test skipped. It requires a private tracker to be running."); return; } diff --git a/tests/e2e/web/api/v1/contexts/torrent/steps.rs b/tests/e2e/web/api/v1/contexts/torrent/steps.rs index 27b21c7d..42e3e923 100644 --- a/tests/e2e/web/api/v1/contexts/torrent/steps.rs +++ b/tests/e2e/web/api/v1/contexts/torrent/steps.rs @@ -1,6 +1,9 @@ use std::str::FromStr; +use std::sync::Arc; +use torrust_index::databases::database; use torrust_index::models::info_hash::InfoHash; +use torrust_index::services::torrent::Index; use torrust_index::web::api::server::v1::responses::ErrorResponseData; use crate::common::client::Client; @@ -57,3 +60,31 @@ pub async fn upload_test_torrent(client: &Client, test_torrent: &TestTorrent) -> Ok(canonical_info_hash) } + +/// Gets tracker announce urls. +/// +/// # Errors +/// +/// Returns an `ErrorResponseData` if the response is not a 200. +pub async fn get_trackers(env: &TestEnv, user_id: i64, torrent_id: i64) -> () { + let database = Arc::new( + database::connect(&env.database_connect_url().unwrap()) + .await + .expect("Database error."), + ); + + let announce_urls = database.get_torrent_announce_urls_from_id(torrent_id); + + let user_tracker_key = database.get_user_tracker_key(user_id); + + announce_urls.retain(|tracker| *tracker != tracker_url.to_string()); +} + +/* /// It adds the tracker URL in the first position of the tracker list. + pub fn include_url_as_main_tracker(&mut self, tracker_url: &Url) { + // Remove any existing instances of tracker_url + self.trackers.retain(|tracker| *tracker != tracker_url.to_string()); + + // Insert tracker_url at the first position + self.trackers.insert(0, tracker_url.to_owned().to_string()); +} */