From 60c535e861a77387474b238940b083b13e422947 Mon Sep 17 00:00:00 2001 From: Wyatt Verchere Date: Wed, 20 Dec 2023 11:46:53 -0800 Subject: [PATCH] Misc testing improvements (#805) * made dummy data more consistent; not an option * fixed variable dropping issue crashing actix (?) * removed scopes specific tests, removed schedule tests * team routes use api * removed printlns, fmt clippy prepare --- ...21cb0557623abe3242e79a21868482e6d9898.json | 16 - ...ee840f6e2a6394e89a851b83fc78220594cf2.json | 16 - src/routes/v2/versions.rs | 37 +- src/routes/v3/projects.rs | 97 +- src/routes/v3/versions.rs | 104 -- tests/analytics.rs | 32 +- tests/common/api_common/generic.rs | 31 +- tests/common/api_common/mod.rs | 73 +- tests/common/api_v2/mod.rs | 1 + tests/common/api_v2/project.rs | 144 ++- tests/common/api_v2/team.rs | 72 ++ tests/common/api_v2/user.rs | 47 + tests/common/api_v2/version.rs | 16 + tests/common/api_v3/collections.rs | 155 +++ tests/common/api_v3/mod.rs | 2 + tests/common/api_v3/organization.rs | 16 + tests/common/api_v3/project.rs | 145 ++- tests/common/api_v3/team.rs | 73 ++ tests/common/api_v3/user.rs | 48 + tests/common/api_v3/version.rs | 16 + tests/common/database.rs | 23 +- tests/common/environment.rs | 27 +- tests/common/scopes.rs | 20 +- tests/loader_fields.rs | 59 +- tests/notifications.rs | 8 +- tests/oauth.rs | 26 +- tests/oauth_clients.rs | 12 +- tests/organizations.rs | 79 +- tests/pats.rs | 3 + tests/project.rs | 198 ++-- tests/scopes.rs | 889 ++++++++---------- tests/teams.rs | 563 ++++++----- tests/user.rs | 8 +- tests/v2/notifications.rs | 8 +- tests/v2/project.rs | 14 +- tests/v2/scopes.rs | 23 +- tests/v2/teams.rs | 2 +- tests/v2/version.rs | 29 +- tests/version.rs | 79 +- 39 files changed, 1775 insertions(+), 1436 deletions(-) delete mode 100644 .sqlx/query-03006da8781d9c07d564c6b406221cb0557623abe3242e79a21868482e6d9898.json delete mode 100644 .sqlx/query-a440cb2567825c3cc540c9b0831ee840f6e2a6394e89a851b83fc78220594cf2.json create mode 100644 tests/common/api_v2/user.rs create mode 100644 tests/common/api_v3/collections.rs create mode 100644 tests/common/api_v3/user.rs diff --git a/.sqlx/query-03006da8781d9c07d564c6b406221cb0557623abe3242e79a21868482e6d9898.json b/.sqlx/query-03006da8781d9c07d564c6b406221cb0557623abe3242e79a21868482e6d9898.json deleted file mode 100644 index e210ba94..00000000 --- a/.sqlx/query-03006da8781d9c07d564c6b406221cb0557623abe3242e79a21868482e6d9898.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE versions\n SET status = $1, date_published = $2\n WHERE (id = $3)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Timestamptz", - "Int8" - ] - }, - "nullable": [] - }, - "hash": "03006da8781d9c07d564c6b406221cb0557623abe3242e79a21868482e6d9898" -} diff --git a/.sqlx/query-a440cb2567825c3cc540c9b0831ee840f6e2a6394e89a851b83fc78220594cf2.json b/.sqlx/query-a440cb2567825c3cc540c9b0831ee840f6e2a6394e89a851b83fc78220594cf2.json deleted file mode 100644 index 9d9fd606..00000000 --- a/.sqlx/query-a440cb2567825c3cc540c9b0831ee840f6e2a6394e89a851b83fc78220594cf2.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE mods\n SET status = $1, approved = $2\n WHERE (id = $3)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Timestamptz", - "Int8" - ] - }, - "nullable": [] - }, - "hash": "a440cb2567825c3cc540c9b0831ee840f6e2a6394e89a851b83fc78220594cf2" -} diff --git a/src/routes/v2/versions.rs b/src/routes/v2/versions.rs index 9054201f..ceeaf7d7 100644 --- a/src/routes/v2/versions.rs +++ b/src/routes/v2/versions.rs @@ -9,8 +9,7 @@ use crate::models::v2::projects::LegacyVersion; use crate::queue::session::AuthQueue; use crate::routes::{v2_reroute, v3}; use crate::search::SearchConfig; -use actix_web::{delete, get, patch, post, web, HttpRequest, HttpResponse}; -use chrono::{DateTime, Utc}; +use actix_web::{delete, get, patch, web, HttpRequest, HttpResponse}; use serde::{Deserialize, Serialize}; use sqlx::PgPool; use validator::Validate; @@ -24,7 +23,6 @@ pub fn config(cfg: &mut web::ServiceConfig) { .service(version_get) .service(version_delete) .service(version_edit) - .service(version_schedule) .service(super::version_creation::upload_file_to_version), ); } @@ -275,36 +273,3 @@ pub async fn version_delete( .await .or_else(v2_reroute::flatten_404_error) } - -#[derive(Deserialize)] -pub struct SchedulingData { - pub time: DateTime, - pub requested_status: VersionStatus, -} - -#[post("{id}/schedule")] -pub async fn version_schedule( - req: HttpRequest, - info: web::Path<(VersionId,)>, - pool: web::Data, - redis: web::Data, - scheduling_data: web::Json, - session_queue: web::Data, -) -> Result { - // Returns NoContent, so we don't need to convert the response - let scheduling_data = scheduling_data.into_inner(); - let scheduling_data = v3::versions::SchedulingData { - time: scheduling_data.time, - requested_status: scheduling_data.requested_status, - }; - v3::versions::version_schedule( - req, - info, - pool, - redis, - web::Json(scheduling_data), - session_queue, - ) - .await - .or_else(v2_reroute::flatten_404_error) -} diff --git a/src/routes/v3/projects.rs b/src/routes/v3/projects.rs index 1e9a72ff..bcf26500 100644 --- a/src/routes/v3/projects.rs +++ b/src/routes/v3/projects.rs @@ -27,7 +27,7 @@ use crate::util::img; use crate::util::routes::read_from_payload; use crate::util::validate::validation_errors_to_string; use actix_web::{web, HttpRequest, HttpResponse}; -use chrono::{DateTime, Utc}; +use chrono::Utc; use futures::TryStreamExt; use itertools::Itertools; use serde::{Deserialize, Serialize}; @@ -54,7 +54,6 @@ pub fn config(cfg: &mut web::ServiceConfig) { .route("{id}/gallery", web::delete().to(delete_gallery_item)) .route("{id}/follow", web::post().to(project_follow)) .route("{id}/follow", web::delete().to(project_unfollow)) - .route("{id}/schedule", web::post().to(project_schedule)) .service( web::scope("{project_id}") .route( @@ -1305,100 +1304,6 @@ pub async fn bulk_edit_project_categories( Ok(()) } -#[derive(Deserialize)] -pub struct SchedulingData { - pub time: DateTime, - pub requested_status: ProjectStatus, -} - -pub async fn project_schedule( - req: HttpRequest, - info: web::Path<(String,)>, - pool: web::Data, - redis: web::Data, - session_queue: web::Data, - scheduling_data: web::Json, -) -> Result { - let user = get_user_from_headers( - &req, - &**pool, - &redis, - &session_queue, - Some(&[Scopes::PROJECT_WRITE]), - ) - .await? - .1; - - if scheduling_data.time < Utc::now() { - return Err(ApiError::InvalidInput( - "You cannot schedule a project to be released in the past!".to_string(), - )); - } - - if !scheduling_data.requested_status.can_be_requested() { - return Err(ApiError::InvalidInput( - "Specified requested status cannot be requested!".to_string(), - )); - } - - let string = info.into_inner().0; - let result = db_models::Project::get(&string, &**pool, &redis).await?; - - if let Some(project_item) = result { - let (team_member, organization_team_member) = - db_models::TeamMember::get_for_project_permissions( - &project_item.inner, - user.id.into(), - &**pool, - ) - .await?; - - let permissions = ProjectPermissions::get_permissions_by_role( - &user.role, - &team_member.clone(), - &organization_team_member.clone(), - ) - .unwrap_or_default(); - - if !user.role.is_mod() && !permissions.contains(ProjectPermissions::EDIT_DETAILS) { - return Err(ApiError::CustomAuthentication( - "You do not have permission to edit this project's scheduling data!".to_string(), - )); - } - - if !project_item.inner.status.is_approved() { - return Err(ApiError::InvalidInput( - "This project has not been approved yet. Submit to the queue with the private status to schedule it in the future!".to_string(), - )); - } - - sqlx::query!( - " - UPDATE mods - SET status = $1, approved = $2 - WHERE (id = $3) - ", - ProjectStatus::Scheduled.as_str(), - scheduling_data.time, - project_item.inner.id as db_ids::ProjectId, - ) - .execute(&**pool) - .await?; - - db_models::Project::clear_cache( - project_item.inner.id, - project_item.inner.slug, - None, - &redis, - ) - .await?; - - Ok(HttpResponse::NoContent().body("")) - } else { - Err(ApiError::NotFound) - } -} - #[derive(Serialize, Deserialize)] pub struct Extension { pub ext: String, diff --git a/src/routes/v3/versions.rs b/src/routes/v3/versions.rs index 9ad6495d..3d53d24f 100644 --- a/src/routes/v3/versions.rs +++ b/src/routes/v3/versions.rs @@ -25,7 +25,6 @@ use crate::search::SearchConfig; use crate::util::img; use crate::util::validate::validation_errors_to_string; use actix_web::{web, HttpRequest, HttpResponse}; -use chrono::{DateTime, Utc}; use itertools::Itertools; use serde::{Deserialize, Serialize}; use sqlx::PgPool; @@ -43,7 +42,6 @@ pub fn config(cfg: &mut web::ServiceConfig) { .route("{id}", web::get().to(version_get)) .route("{id}", web::patch().to(version_edit)) .route("{id}", web::delete().to(version_delete)) - .route("{id}/schedule", web::post().to(version_schedule)) .route( "{version_id}/file", web::post().to(super::version_creation::upload_file_to_version), @@ -828,108 +826,6 @@ pub async fn version_list( } } -#[derive(Deserialize)] -pub struct SchedulingData { - pub time: DateTime, - pub requested_status: VersionStatus, -} - -pub async fn version_schedule( - req: HttpRequest, - info: web::Path<(models::ids::VersionId,)>, - pool: web::Data, - redis: web::Data, - scheduling_data: web::Json, - session_queue: web::Data, -) -> Result { - let user = get_user_from_headers( - &req, - &**pool, - &redis, - &session_queue, - Some(&[Scopes::VERSION_WRITE]), - ) - .await? - .1; - - if scheduling_data.time < Utc::now() { - return Err(ApiError::InvalidInput( - "You cannot schedule a version to be released in the past!".to_string(), - )); - } - - if !scheduling_data.requested_status.can_be_requested() { - return Err(ApiError::InvalidInput( - "Specified requested status cannot be requested!".to_string(), - )); - } - - let string = info.into_inner().0; - let result = database::models::Version::get(string.into(), &**pool, &redis).await?; - - if let Some(version_item) = result { - let team_member = database::models::TeamMember::get_from_user_id_project( - version_item.inner.project_id, - user.id.into(), - &**pool, - ) - .await?; - - let organization_item = - database::models::Organization::get_associated_organization_project_id( - version_item.inner.project_id, - &**pool, - ) - .await - .map_err(ApiError::Database)?; - - let organization_team_member = if let Some(organization) = &organization_item { - database::models::TeamMember::get_from_user_id( - organization.team_id, - user.id.into(), - &**pool, - ) - .await? - } else { - None - }; - - let permissions = ProjectPermissions::get_permissions_by_role( - &user.role, - &team_member, - &organization_team_member, - ) - .unwrap_or_default(); - - if !user.role.is_mod() && !permissions.contains(ProjectPermissions::EDIT_DETAILS) { - return Err(ApiError::CustomAuthentication( - "You do not have permission to edit this version's scheduling data!".to_string(), - )); - } - - let mut transaction = pool.begin().await?; - sqlx::query!( - " - UPDATE versions - SET status = $1, date_published = $2 - WHERE (id = $3) - ", - VersionStatus::Scheduled.as_str(), - scheduling_data.time, - version_item.inner.id as database::models::ids::VersionId, - ) - .execute(&mut *transaction) - .await?; - - transaction.commit().await?; - database::models::Version::clear_cache(&version_item, &redis).await?; - - Ok(HttpResponse::NoContent().body("")) - } else { - Err(ApiError::NotFound) - } -} - pub async fn version_delete( req: HttpRequest, info: web::Path<(models::ids::VersionId,)>, diff --git a/tests/analytics.rs b/tests/analytics.rs index 7318daa5..a524a767 100644 --- a/tests/analytics.rs +++ b/tests/analytics.rs @@ -19,13 +19,7 @@ pub async fn analytics_revenue() { with_test_environment(None, |test_env: TestEnvironment| async move { let api = &test_env.api; - let alpha_project_id = test_env - .dummy - .as_ref() - .unwrap() - .project_alpha - .project_id - .clone(); + let alpha_project_id = test_env.dummy.project_alpha.project_id.clone(); let pool = test_env.db.pool.clone(); @@ -143,27 +137,9 @@ fn to_f64_vec_rounded_up(d: Vec) -> Vec { #[actix_rt::test] pub async fn permissions_analytics_revenue() { with_test_environment(None, |test_env: TestEnvironment| async move { - let alpha_project_id = test_env - .dummy - .as_ref() - .unwrap() - .project_alpha - .project_id - .clone(); - let alpha_version_id = test_env - .dummy - .as_ref() - .unwrap() - .project_alpha - .version_id - .clone(); - let alpha_team_id = test_env - .dummy - .as_ref() - .unwrap() - .project_alpha - .team_id - .clone(); + let alpha_project_id = test_env.dummy.project_alpha.project_id.clone(); + let alpha_version_id = test_env.dummy.project_alpha.version_id.clone(); + let alpha_team_id = test_env.dummy.project_alpha.team_id.clone(); let api = &test_env.api; diff --git a/tests/common/api_common/generic.rs b/tests/common/api_common/generic.rs index 84f0d960..f07a153e 100644 --- a/tests/common/api_common/generic.rs +++ b/tests/common/api_common/generic.rs @@ -12,7 +12,7 @@ use crate::common::{api_v2::ApiV2, api_v3::ApiV3, dummy_data::TestFile}; use super::{ models::{CommonProject, CommonVersion}, request_data::{ImageData, ProjectCreationRequestData}, - Api, ApiProject, ApiTags, ApiTeams, ApiVersion, + Api, ApiProject, ApiTags, ApiTeams, ApiUser, ApiVersion, }; #[derive(Clone)] @@ -71,17 +71,28 @@ delegate_api_variant!( [remove_project, ServiceResponse, project_slug_or_id: &str, pat: Option<&str>], [get_project, ServiceResponse, id_or_slug: &str, pat: Option<&str>], [get_project_deserialized_common, CommonProject, id_or_slug: &str, pat: Option<&str>], + [get_projects, ServiceResponse, ids_or_slugs: &[&str], pat: Option<&str>], + [get_project_dependencies, ServiceResponse, id_or_slug: &str, pat: Option<&str>], [get_user_projects, ServiceResponse, user_id_or_username: &str, pat: Option<&str>], [get_user_projects_deserialized_common, Vec, user_id_or_username: &str, pat: Option<&str>], [edit_project, ServiceResponse, id_or_slug: &str, patch: serde_json::Value, pat: Option<&str>], [edit_project_bulk, ServiceResponse, ids_or_slugs: &[&str], patch: serde_json::Value, pat: Option<&str>], [edit_project_icon, ServiceResponse, id_or_slug: &str, icon: Option, pat: Option<&str>], - [schedule_project, ServiceResponse, id_or_slug: &str, requested_status: &str, date : chrono::DateTime, pat: Option<&str>], [add_gallery_item, ServiceResponse, id_or_slug: &str, image: ImageData, featured: bool, title: Option, description: Option, ordering: Option, pat: Option<&str>], [remove_gallery_item, ServiceResponse, id_or_slug: &str, image_url: &str, pat: Option<&str>], [edit_gallery_item, ServiceResponse, id_or_slug: &str, image_url: &str, patch: HashMap, pat: Option<&str>], [create_report, ServiceResponse, report_type: &str, id: &str, item_type: crate::common::api_common::models::CommonItemType, body: &str, pat: Option<&str>], [get_report, ServiceResponse, id: &str, pat: Option<&str>], + [get_reports, ServiceResponse, ids: &[&str], pat: Option<&str>], + [get_user_reports, ServiceResponse, pat: Option<&str>], + [edit_report, ServiceResponse, id: &str, patch: serde_json::Value, pat: Option<&str>], + [delete_report, ServiceResponse, id: &str, pat: Option<&str>], + [get_thread, ServiceResponse, id: &str, pat: Option<&str>], + [get_threads, ServiceResponse, ids: &[&str], pat: Option<&str>], + [write_to_thread, ServiceResponse, id: &str, r#type : &str, message: &str, pat: Option<&str>], + [get_moderation_inbox, ServiceResponse, pat: Option<&str>], + [read_thread, ServiceResponse, id: &str, pat: Option<&str>], + [delete_thread_message, ServiceResponse, id: &str, pat: Option<&str>], } ); @@ -100,6 +111,7 @@ delegate_api_variant!( impl ApiTeams for GenericApi { [get_team_members, ServiceResponse, team_id: &str, pat: Option<&str>], [get_team_members_deserialized_common, Vec, team_id: &str, pat: Option<&str>], + [get_teams_members, ServiceResponse, ids: &[&str], pat: Option<&str>], [get_project_members, ServiceResponse, id_or_slug: &str, pat: Option<&str>], [get_project_members_deserialized_common, Vec, id_or_slug: &str, pat: Option<&str>], [get_organization_members, ServiceResponse, id_or_title: &str, pat: Option<&str>], @@ -110,9 +122,23 @@ delegate_api_variant!( [transfer_team_ownership, ServiceResponse, team_id: &str, user_id: &str, pat: Option<&str>], [get_user_notifications, ServiceResponse, user_id: &str, pat: Option<&str>], [get_user_notifications_deserialized_common, Vec, user_id: &str, pat: Option<&str>], + [get_notification, ServiceResponse, notification_id: &str, pat: Option<&str>], + [get_notifications, ServiceResponse, ids: &[&str], pat: Option<&str>], [mark_notification_read, ServiceResponse, notification_id: &str, pat: Option<&str>], + [mark_notifications_read, ServiceResponse, ids: &[&str], pat: Option<&str>], [add_user_to_team, ServiceResponse, team_id: &str, user_id: &str, project_permissions: Option, organization_permissions: Option, pat: Option<&str>], [delete_notification, ServiceResponse, notification_id: &str, pat: Option<&str>], + [delete_notifications, ServiceResponse, ids: &[&str], pat: Option<&str>], + } +); + +delegate_api_variant!( + #[async_trait(?Send)] + impl ApiUser for GenericApi { + [get_user, ServiceResponse, id_or_username: &str, pat: Option<&str>], + [get_current_user, ServiceResponse, pat: Option<&str>], + [edit_user, ServiceResponse, id_or_username: &str, patch: serde_json::Value, pat: Option<&str>], + [delete_user, ServiceResponse, id_or_username: &str, pat: Option<&str>], } ); @@ -125,6 +151,7 @@ delegate_api_variant!( [get_version_deserialized_common, CommonVersion, id_or_slug: &str, pat: Option<&str>], [get_versions, ServiceResponse, ids_or_slugs: Vec, pat: Option<&str>], [get_versions_deserialized_common, Vec, ids_or_slugs: Vec, pat: Option<&str>], + [download_version_redirect, ServiceResponse, hash: &str, algorithm: &str, pat: Option<&str>], [edit_version, ServiceResponse, id_or_slug: &str, patch: serde_json::Value, pat: Option<&str>], [get_version_from_hash, ServiceResponse, id_or_slug: &str, hash: &str, pat: Option<&str>], [get_version_from_hash_deserialized_common, CommonVersion, id_or_slug: &str, hash: &str, pat: Option<&str>], diff --git a/tests/common/api_common/mod.rs b/tests/common/api_common/mod.rs index 0fa7faf9..c5c58534 100644 --- a/tests/common/api_common/mod.rs +++ b/tests/common/api_common/mod.rs @@ -7,7 +7,6 @@ use self::models::{ use self::request_data::{ImageData, ProjectCreationRequestData}; use actix_web::dev::ServiceResponse; use async_trait::async_trait; -use chrono::{DateTime, Utc}; use labrinth::{ models::{ projects::{ProjectId, VersionType}, @@ -27,7 +26,7 @@ pub trait ApiBuildable: Api { } #[async_trait(?Send)] -pub trait Api: ApiProject + ApiTags + ApiTeams + ApiVersion { +pub trait Api: ApiProject + ApiTags + ApiTeams + ApiUser + ApiVersion { async fn call(&self, req: actix_http::Request) -> ServiceResponse; async fn reset_search_index(&self) -> ServiceResponse; } @@ -59,6 +58,12 @@ pub trait ApiProject { id_or_slug: &str, pat: Option<&str>, ) -> CommonProject; + async fn get_projects(&self, ids_or_slugs: &[&str], pat: Option<&str>) -> ServiceResponse; + async fn get_project_dependencies( + &self, + id_or_slug: &str, + pat: Option<&str>, + ) -> ServiceResponse; async fn get_user_projects( &self, user_id_or_username: &str, @@ -87,13 +92,6 @@ pub trait ApiProject { icon: Option, pat: Option<&str>, ) -> ServiceResponse; - async fn schedule_project( - &self, - id_or_slug: &str, - requested_status: &str, - date: DateTime, - pat: Option<&str>, - ) -> ServiceResponse; #[allow(clippy::too_many_arguments)] async fn add_gallery_item( &self, @@ -127,6 +125,27 @@ pub trait ApiProject { pat: Option<&str>, ) -> ServiceResponse; async fn get_report(&self, id: &str, pat: Option<&str>) -> ServiceResponse; + async fn get_reports(&self, ids: &[&str], pat: Option<&str>) -> ServiceResponse; + async fn get_user_reports(&self, pat: Option<&str>) -> ServiceResponse; + async fn edit_report( + &self, + id: &str, + patch: serde_json::Value, + pat: Option<&str>, + ) -> ServiceResponse; + async fn delete_report(&self, id: &str, pat: Option<&str>) -> ServiceResponse; + async fn get_thread(&self, id: &str, pat: Option<&str>) -> ServiceResponse; + async fn get_threads(&self, ids: &[&str], pat: Option<&str>) -> ServiceResponse; + async fn write_to_thread( + &self, + id: &str, + r#type: &str, + message: &str, + pat: Option<&str>, + ) -> ServiceResponse; + async fn get_moderation_inbox(&self, pat: Option<&str>) -> ServiceResponse; + async fn read_thread(&self, id: &str, pat: Option<&str>) -> ServiceResponse; + async fn delete_thread_message(&self, id: &str, pat: Option<&str>) -> ServiceResponse; } #[async_trait(?Send)] @@ -145,6 +164,7 @@ pub trait ApiTeams { team_id: &str, pat: Option<&str>, ) -> Vec; + async fn get_teams_members(&self, team_ids: &[&str], pat: Option<&str>) -> ServiceResponse; async fn get_project_members(&self, id_or_slug: &str, pat: Option<&str>) -> ServiceResponse; async fn get_project_members_deserialized_common( &self, @@ -187,11 +207,14 @@ pub trait ApiTeams { user_id: &str, pat: Option<&str>, ) -> Vec; + async fn get_notification(&self, notification_id: &str, pat: Option<&str>) -> ServiceResponse; + async fn get_notifications(&self, ids: &[&str], pat: Option<&str>) -> ServiceResponse; async fn mark_notification_read( &self, notification_id: &str, pat: Option<&str>, ) -> ServiceResponse; + async fn mark_notifications_read(&self, ids: &[&str], pat: Option<&str>) -> ServiceResponse; async fn add_user_to_team( &self, team_id: &str, @@ -205,6 +228,20 @@ pub trait ApiTeams { notification_id: &str, pat: Option<&str>, ) -> ServiceResponse; + async fn delete_notifications(&self, ids: &[&str], pat: Option<&str>) -> ServiceResponse; +} + +#[async_trait(?Send)] +pub trait ApiUser { + async fn get_user(&self, id_or_username: &str, pat: Option<&str>) -> ServiceResponse; + async fn get_current_user(&self, pat: Option<&str>) -> ServiceResponse; + async fn edit_user( + &self, + id_or_username: &str, + patch: serde_json::Value, + pat: Option<&str>, + ) -> ServiceResponse; + async fn delete_user(&self, id_or_username: &str, pat: Option<&str>) -> ServiceResponse; } #[async_trait(?Send)] @@ -227,34 +264,40 @@ pub trait ApiVersion { modify_json: Option, pat: Option<&str>, ) -> CommonVersion; - async fn get_version(&self, id_or_slug: &str, pat: Option<&str>) -> ServiceResponse; + async fn get_version(&self, id: &str, pat: Option<&str>) -> ServiceResponse; async fn get_version_deserialized_common( &self, id_or_slug: &str, pat: Option<&str>, ) -> CommonVersion; - async fn get_versions(&self, ids_or_slugs: Vec, pat: Option<&str>) -> ServiceResponse; + async fn get_versions(&self, ids: Vec, pat: Option<&str>) -> ServiceResponse; async fn get_versions_deserialized_common( &self, - ids_or_slugs: Vec, + ids: Vec, pat: Option<&str>, ) -> Vec; + async fn download_version_redirect( + &self, + hash: &str, + algorithm: &str, + pat: Option<&str>, + ) -> ServiceResponse; async fn edit_version( &self, - id_or_slug: &str, + id: &str, patch: serde_json::Value, pat: Option<&str>, ) -> ServiceResponse; async fn get_version_from_hash( &self, - id_or_slug: &str, hash: &str, + algorithm: &str, pat: Option<&str>, ) -> ServiceResponse; async fn get_version_from_hash_deserialized_common( &self, - id_or_slug: &str, hash: &str, + algorithm: &str, pat: Option<&str>, ) -> CommonVersion; async fn get_versions_from_hashes( diff --git a/tests/common/api_v2/mod.rs b/tests/common/api_v2/mod.rs index 2ca30635..84365e6f 100644 --- a/tests/common/api_v2/mod.rs +++ b/tests/common/api_v2/mod.rs @@ -13,6 +13,7 @@ pub mod project; pub mod request_data; pub mod tags; pub mod team; +pub mod user; pub mod version; #[derive(Clone)] diff --git a/tests/common/api_v2/project.rs b/tests/common/api_v2/project.rs index 56fe12b3..7a42d506 100644 --- a/tests/common/api_v2/project.rs +++ b/tests/common/api_v2/project.rs @@ -15,7 +15,6 @@ use actix_web::{ }; use async_trait::async_trait; use bytes::Bytes; -use chrono::{DateTime, Utc}; use labrinth::{ models::v2::{projects::LegacyProject, search::LegacySearchResults}, util::actix::AppendsMultipart, @@ -173,6 +172,30 @@ impl ApiProject for ApiV2 { serde_json::from_value(value).unwrap() } + async fn get_projects(&self, ids_or_slugs: &[&str], pat: Option<&str>) -> ServiceResponse { + let ids_or_slugs = serde_json::to_string(ids_or_slugs).unwrap(); + let req = test::TestRequest::get() + .uri(&format!( + "/v2/projects?ids={encoded}", + encoded = urlencoding::encode(&ids_or_slugs) + )) + .append_pat(pat) + .to_request(); + self.call(req).await + } + + async fn get_project_dependencies( + &self, + id_or_slug: &str, + pat: Option<&str>, + ) -> ServiceResponse { + let req = TestRequest::get() + .uri(&format!("/v2/project/{id_or_slug}/dependencies")) + .append_pat(pat) + .to_request(); + self.call(req).await + } + async fn get_user_projects( &self, user_id_or_username: &str, @@ -275,7 +298,7 @@ impl ApiProject for ApiV2 { pat: Option<&str>, ) -> ServiceResponse { let req = test::TestRequest::post() - .uri("/v3/report") + .uri("/v2/report") .append_pat(pat) .set_json(json!( { @@ -292,28 +315,123 @@ impl ApiProject for ApiV2 { async fn get_report(&self, id: &str, pat: Option<&str>) -> ServiceResponse { let req = test::TestRequest::get() - .uri(&format!("/v3/report/{id}", id = id)) + .uri(&format!("/v2/report/{id}")) .append_pat(pat) .to_request(); self.call(req).await } - async fn schedule_project( + async fn get_reports(&self, ids: &[&str], pat: Option<&str>) -> ServiceResponse { + let ids_str = serde_json::to_string(ids).unwrap(); + let req = test::TestRequest::get() + .uri(&format!( + "/v2/reports?ids={encoded}", + encoded = urlencoding::encode(&ids_str) + )) + .append_pat(pat) + .to_request(); + + self.call(req).await + } + + async fn get_user_reports(&self, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::get() + .uri("/v2/report") + .append_pat(pat) + .to_request(); + + self.call(req).await + } + + async fn delete_report(&self, id: &str, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::delete() + .uri(&format!("/v2/report/{id}")) + .append_pat(pat) + .to_request(); + + self.call(req).await + } + + async fn edit_report( &self, - id_or_slug: &str, - requested_status: &str, - date: DateTime, + id: &str, + patch: serde_json::Value, + pat: Option<&str>, + ) -> ServiceResponse { + let req = test::TestRequest::patch() + .uri(&format!("/v2/report/{id}")) + .append_pat(pat) + .set_json(patch) + .to_request(); + + self.call(req).await + } + + async fn get_thread(&self, id: &str, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::get() + .uri(&format!("/v3/thread/{id}")) + .append_pat(pat) + .to_request(); + + self.call(req).await + } + + async fn get_threads(&self, ids: &[&str], pat: Option<&str>) -> ServiceResponse { + let ids_str = serde_json::to_string(ids).unwrap(); + let req = test::TestRequest::get() + .uri(&format!( + "/v3/threads?ids={encoded}", + encoded = urlencoding::encode(&ids_str) + )) + .append_pat(pat) + .to_request(); + + self.call(req).await + } + + async fn write_to_thread( + &self, + id: &str, + r#type: &str, + content: &str, pat: Option<&str>, ) -> ServiceResponse { let req = test::TestRequest::post() - .uri(&format!("/v2/version/{id_or_slug}/schedule")) - .set_json(json!( - { - "requested_status": requested_status, - "time": date, + .uri(&format!("/v2/thread/{id}")) + .append_pat(pat) + .set_json(json!({ + "body" : { + "type": r#type, + "body": content, } - )) + })) + .to_request(); + + self.call(req).await + } + + async fn get_moderation_inbox(&self, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::get() + .uri("/v2/thread/inbox") + .append_pat(pat) + .to_request(); + + self.call(req).await + } + + async fn read_thread(&self, id: &str, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::post() + .uri(&format!("/v2/thread/{id}/read")) + .append_pat(pat) + .to_request(); + + self.call(req).await + } + + async fn delete_thread_message(&self, id: &str, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::delete() + .uri(&format!("/v2/message/{id}")) .append_pat(pat) .to_request(); diff --git a/tests/common/api_v2/team.rs b/tests/common/api_v2/team.rs index 49a959fe..a7de717d 100644 --- a/tests/common/api_v2/team.rs +++ b/tests/common/api_v2/team.rs @@ -72,6 +72,22 @@ impl ApiTeams for ApiV2 { test::read_body_json(resp).await } + async fn get_teams_members( + &self, + ids_or_titles: &[&str], + pat: Option<&str>, + ) -> ServiceResponse { + let ids_or_titles = serde_json::to_string(ids_or_titles).unwrap(); + let req = test::TestRequest::get() + .uri(&format!( + "/v2/teams?ids={}", + urlencoding::encode(&ids_or_titles) + )) + .append_pat(pat) + .to_request(); + self.call(req).await + } + async fn get_project_members(&self, id_or_title: &str, pat: Option<&str>) -> ServiceResponse { let req = test::TestRequest::get() .uri(&format!("/v2/project/{id_or_title}/members")) @@ -192,6 +208,30 @@ impl ApiTeams for ApiV2 { serde_json::from_value(value).unwrap() } + async fn get_notification(&self, notification_id: &str, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::get() + .uri(&format!("/v2/notification/{notification_id}")) + .append_pat(pat) + .to_request(); + self.call(req).await + } + + async fn get_notifications( + &self, + notification_ids: &[&str], + pat: Option<&str>, + ) -> ServiceResponse { + let notification_ids = serde_json::to_string(notification_ids).unwrap(); + let req = test::TestRequest::get() + .uri(&format!( + "/v2/notifications?ids={}", + urlencoding::encode(¬ification_ids) + )) + .append_pat(pat) + .to_request(); + self.call(req).await + } + async fn mark_notification_read( &self, notification_id: &str, @@ -204,6 +244,22 @@ impl ApiTeams for ApiV2 { self.call(req).await } + async fn mark_notifications_read( + &self, + notification_ids: &[&str], + pat: Option<&str>, + ) -> ServiceResponse { + let notification_ids = serde_json::to_string(notification_ids).unwrap(); + let req = test::TestRequest::patch() + .uri(&format!( + "/v2/notifications?ids={}", + urlencoding::encode(¬ification_ids) + )) + .append_pat(pat) + .to_request(); + self.call(req).await + } + async fn add_user_to_team( &self, team_id: &str, @@ -235,4 +291,20 @@ impl ApiTeams for ApiV2 { .to_request(); self.call(req).await } + + async fn delete_notifications( + &self, + notification_ids: &[&str], + pat: Option<&str>, + ) -> ServiceResponse { + let notification_ids = serde_json::to_string(notification_ids).unwrap(); + let req = test::TestRequest::delete() + .uri(&format!( + "/v2/notifications?ids={}", + urlencoding::encode(¬ification_ids) + )) + .append_pat(pat) + .to_request(); + self.call(req).await + } } diff --git a/tests/common/api_v2/user.rs b/tests/common/api_v2/user.rs new file mode 100644 index 00000000..1fde5b12 --- /dev/null +++ b/tests/common/api_v2/user.rs @@ -0,0 +1,47 @@ +use super::ApiV2; +use crate::common::api_common::{Api, ApiUser, AppendsOptionalPat}; +use actix_web::{dev::ServiceResponse, test}; +use async_trait::async_trait; + +#[async_trait(?Send)] +impl ApiUser for ApiV2 { + async fn get_user(&self, user_id_or_username: &str, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::get() + .uri(&format!("/v2/user/{}", user_id_or_username)) + .append_pat(pat) + .to_request(); + self.call(req).await + } + + async fn get_current_user(&self, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::get() + .uri("/v2/user") + .append_pat(pat) + .to_request(); + self.call(req).await + } + + async fn edit_user( + &self, + user_id_or_username: &str, + patch: serde_json::Value, + pat: Option<&str>, + ) -> ServiceResponse { + let req = test::TestRequest::patch() + .uri(&format!("/v2/user/{}", user_id_or_username)) + .append_pat(pat) + .set_json(patch) + .to_request(); + + self.call(req).await + } + + async fn delete_user(&self, user_id_or_username: &str, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::delete() + .uri(&format!("/v2/user/{}", user_id_or_username)) + .append_pat(pat) + .to_request(); + + self.call(req).await + } +} diff --git a/tests/common/api_v2/version.rs b/tests/common/api_v2/version.rs index 6234f814..1a8b2d19 100644 --- a/tests/common/api_v2/version.rs +++ b/tests/common/api_v2/version.rs @@ -161,6 +161,22 @@ impl ApiVersion for ApiV2 { serde_json::from_value(value).unwrap() } + async fn download_version_redirect( + &self, + hash: &str, + algorithm: &str, + pat: Option<&str>, + ) -> ServiceResponse { + let req = test::TestRequest::get() + .uri(&format!("/v2/version_file/{hash}/download",)) + .set_json(json!({ + "algorithm": algorithm, + })) + .append_pat(pat) + .to_request(); + self.call(req).await + } + async fn edit_version( &self, version_id: &str, diff --git a/tests/common/api_v3/collections.rs b/tests/common/api_v3/collections.rs new file mode 100644 index 00000000..eea4b14c --- /dev/null +++ b/tests/common/api_v3/collections.rs @@ -0,0 +1,155 @@ +use actix_web::{ + dev::ServiceResponse, + test::{self, TestRequest}, +}; +use bytes::Bytes; +use labrinth::models::{collections::Collection, v3::projects::Project}; +use serde_json::json; + +use crate::common::api_common::{request_data::ImageData, Api, AppendsOptionalPat}; + +use super::ApiV3; + +impl ApiV3 { + pub async fn create_collection( + &self, + collection_title: &str, + description: &str, + projects: &[&str], + pat: Option<&str>, + ) -> ServiceResponse { + let req = test::TestRequest::post() + .uri("/v3/collection") + .append_pat(pat) + .set_json(json!({ + "name": collection_title, + "description": description, + "projects": projects, + })) + .to_request(); + self.call(req).await + } + + pub async fn get_collection(&self, id: &str, pat: Option<&str>) -> ServiceResponse { + let req = TestRequest::get() + .uri(&format!("/v3/collection/{id}")) + .append_pat(pat) + .to_request(); + self.call(req).await + } + + pub async fn get_collection_deserialized(&self, id: &str, pat: Option<&str>) -> Collection { + let resp = self.get_collection(id, pat).await; + assert_eq!(resp.status(), 200); + test::read_body_json(resp).await + } + + pub async fn get_collections(&self, ids: &[&str], pat: Option<&str>) -> ServiceResponse { + let ids = serde_json::to_string(ids).unwrap(); + let req = test::TestRequest::get() + .uri(&format!( + "/v3/collections?ids={}", + urlencoding::encode(&ids) + )) + .append_pat(pat) + .to_request(); + self.call(req).await + } + + pub async fn get_collection_projects(&self, id: &str, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::get() + .uri(&format!("/v3/collection/{id}/projects")) + .append_pat(pat) + .to_request(); + self.call(req).await + } + + pub async fn get_collection_projects_deserialized( + &self, + id: &str, + pat: Option<&str>, + ) -> Vec { + let resp = self.get_collection_projects(id, pat).await; + assert_eq!(resp.status(), 200); + test::read_body_json(resp).await + } + + pub async fn edit_collection( + &self, + id: &str, + patch: serde_json::Value, + pat: Option<&str>, + ) -> ServiceResponse { + let req = test::TestRequest::patch() + .uri(&format!("/v3/collection/{id}")) + .append_pat(pat) + .set_json(patch) + .to_request(); + + self.call(req).await + } + + pub async fn edit_collection_icon( + &self, + id: &str, + icon: Option, + pat: Option<&str>, + ) -> ServiceResponse { + if let Some(icon) = icon { + // If an icon is provided, upload it + let req = test::TestRequest::patch() + .uri(&format!( + "/v3/collection/{id}/icon?ext={ext}", + ext = icon.extension + )) + .append_pat(pat) + .set_payload(Bytes::from(icon.icon)) + .to_request(); + + self.call(req).await + } else { + // If no icon is provided, delete the icon + let req = test::TestRequest::delete() + .uri(&format!("/v3/collection/{id}/icon")) + .append_pat(pat) + .to_request(); + + self.call(req).await + } + } + + pub async fn delete_collection(&self, id: &str, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::delete() + .uri(&format!("/v3/collection/{id}")) + .append_pat(pat) + .to_request(); + + self.call(req).await + } + + pub async fn get_user_collections( + &self, + user_id_or_username: &str, + pat: Option<&str>, + ) -> ServiceResponse { + let req = test::TestRequest::get() + .uri(&format!("/v3/user/{}/collections", user_id_or_username)) + .append_pat(pat) + .to_request(); + self.call(req).await + } + + pub async fn get_user_collections_deserialized_common( + &self, + user_id_or_username: &str, + pat: Option<&str>, + ) -> Vec { + let resp = self.get_user_collections(user_id_or_username, pat).await; + assert_eq!(resp.status(), 200); + // First, deserialize to the non-common format (to test the response is valid for this api version) + let projects: Vec = test::read_body_json(resp).await; + // Then, deserialize to the common format + let value = serde_json::to_value(projects).unwrap(); + serde_json::from_value(value).unwrap() + } +} diff --git a/tests/common/api_v3/mod.rs b/tests/common/api_v3/mod.rs index ee65744f..caab4ab6 100644 --- a/tests/common/api_v3/mod.rs +++ b/tests/common/api_v3/mod.rs @@ -9,6 +9,7 @@ use async_trait::async_trait; use labrinth::LabrinthConfig; use std::rc::Rc; +pub mod collections; pub mod oauth; pub mod oauth_clients; pub mod organization; @@ -16,6 +17,7 @@ pub mod project; pub mod request_data; pub mod tags; pub mod team; +pub mod user; pub mod version; #[derive(Clone)] diff --git a/tests/common/api_v3/organization.rs b/tests/common/api_v3/organization.rs index 57b90659..45e66ef2 100644 --- a/tests/common/api_v3/organization.rs +++ b/tests/common/api_v3/organization.rs @@ -46,6 +46,22 @@ impl ApiV3 { test::read_body_json(resp).await } + pub async fn get_organizations( + &self, + ids_or_titles: &[&str], + pat: Option<&str>, + ) -> ServiceResponse { + let ids_or_titles = serde_json::to_string(ids_or_titles).unwrap(); + let req = test::TestRequest::get() + .uri(&format!( + "/v3/organizations?ids={}", + urlencoding::encode(&ids_or_titles) + )) + .append_pat(pat) + .to_request(); + self.call(req).await + } + pub async fn get_organization_projects( &self, id_or_title: &str, diff --git a/tests/common/api_v3/project.rs b/tests/common/api_v3/project.rs index 2c678f04..46c087ad 100644 --- a/tests/common/api_v3/project.rs +++ b/tests/common/api_v3/project.rs @@ -126,6 +126,30 @@ impl ApiProject for ApiV3 { serde_json::from_value(value).unwrap() } + async fn get_projects(&self, ids_or_slugs: &[&str], pat: Option<&str>) -> ServiceResponse { + let ids_or_slugs = serde_json::to_string(ids_or_slugs).unwrap(); + let req = test::TestRequest::get() + .uri(&format!( + "/v2/projects?ids={encoded}", + encoded = urlencoding::encode(&ids_or_slugs) + )) + .append_pat(pat) + .to_request(); + self.call(req).await + } + + async fn get_project_dependencies( + &self, + id_or_slug: &str, + pat: Option<&str>, + ) -> ServiceResponse { + let req = TestRequest::get() + .uri(&format!("/v2/project/{id_or_slug}/dependencies")) + .append_pat(pat) + .to_request(); + self.call(req).await + } + async fn get_user_projects( &self, user_id_or_username: &str, @@ -245,28 +269,53 @@ impl ApiProject for ApiV3 { async fn get_report(&self, id: &str, pat: Option<&str>) -> ServiceResponse { let req = test::TestRequest::get() - .uri(&format!("/v3/report/{id}", id = id)) + .uri(&format!("/v3/report/{id}")) .append_pat(pat) .to_request(); self.call(req).await } - async fn schedule_project( + async fn get_reports(&self, ids: &[&str], pat: Option<&str>) -> ServiceResponse { + let ids_str = serde_json::to_string(ids).unwrap(); + let req = test::TestRequest::get() + .uri(&format!( + "/v3/reports?ids={encoded}", + encoded = urlencoding::encode(&ids_str) + )) + .append_pat(pat) + .to_request(); + + self.call(req).await + } + + async fn get_user_reports(&self, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::get() + .uri("/v3/report") + .append_pat(pat) + .to_request(); + + self.call(req).await + } + + async fn edit_report( &self, - id_or_slug: &str, - requested_status: &str, - date: DateTime, + id: &str, + patch: serde_json::Value, pat: Option<&str>, ) -> ServiceResponse { - let req = test::TestRequest::post() - .uri(&format!("/v3/version/{id_or_slug}/schedule")) - .set_json(json!( - { - "requested_status": requested_status, - "time": date, - } - )) + let req = test::TestRequest::patch() + .uri(&format!("/v3/report/{id}")) + .append_pat(pat) + .set_json(patch) + .to_request(); + + self.call(req).await + } + + async fn delete_report(&self, id: &str, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::delete() + .uri(&format!("/v3/report/{id}")) .append_pat(pat) .to_request(); @@ -355,6 +404,76 @@ impl ApiProject for ApiV3 { self.call(req).await } + + async fn get_thread(&self, id: &str, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::get() + .uri(&format!("/v3/thread/{id}")) + .append_pat(pat) + .to_request(); + + self.call(req).await + } + + async fn get_threads(&self, ids: &[&str], pat: Option<&str>) -> ServiceResponse { + let ids_str = serde_json::to_string(ids).unwrap(); + let req = test::TestRequest::get() + .uri(&format!( + "/v3/threads?ids={encoded}", + encoded = urlencoding::encode(&ids_str) + )) + .append_pat(pat) + .to_request(); + + self.call(req).await + } + + async fn write_to_thread( + &self, + id: &str, + r#type: &str, + content: &str, + pat: Option<&str>, + ) -> ServiceResponse { + let req = test::TestRequest::post() + .uri(&format!("/v3/thread/{id}")) + .append_pat(pat) + .set_json(json!({ + "body": { + "type": r#type, + "body": content + } + })) + .to_request(); + + self.call(req).await + } + + async fn get_moderation_inbox(&self, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::get() + .uri("/v3/thread/inbox") + .append_pat(pat) + .to_request(); + + self.call(req).await + } + + async fn read_thread(&self, id: &str, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::post() + .uri(&format!("/v3/thread/{id}/read")) + .append_pat(pat) + .to_request(); + + self.call(req).await + } + + async fn delete_thread_message(&self, id: &str, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::delete() + .uri(&format!("/v3/message/{id}")) + .append_pat(pat) + .to_request(); + + self.call(req).await + } } impl ApiV3 { diff --git a/tests/common/api_v3/team.rs b/tests/common/api_v3/team.rs index 9e9f1971..bbc6002b 100644 --- a/tests/common/api_v3/team.rs +++ b/tests/common/api_v3/team.rs @@ -63,6 +63,22 @@ impl ApiTeams for ApiV3 { serde_json::from_value(value).unwrap() } + async fn get_teams_members( + &self, + ids_or_titles: &[&str], + pat: Option<&str>, + ) -> ServiceResponse { + let ids_or_titles = serde_json::to_string(ids_or_titles).unwrap(); + let req = test::TestRequest::get() + .uri(&format!( + "/v3/teams?ids={}", + urlencoding::encode(&ids_or_titles) + )) + .append_pat(pat) + .to_request(); + self.call(req).await + } + async fn get_project_members(&self, id_or_title: &str, pat: Option<&str>) -> ServiceResponse { let req = test::TestRequest::get() .uri(&format!("/v3/project/{id_or_title}/members")) @@ -185,6 +201,30 @@ impl ApiTeams for ApiV3 { serde_json::from_value(value).unwrap() } + async fn get_notification(&self, notification_id: &str, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::get() + .uri(&format!("/v3/notification/{notification_id}")) + .append_pat(pat) + .to_request(); + self.call(req).await + } + + async fn get_notifications( + &self, + notification_ids: &[&str], + pat: Option<&str>, + ) -> ServiceResponse { + let notification_ids = serde_json::to_string(notification_ids).unwrap(); + let req = test::TestRequest::get() + .uri(&format!( + "/v3/notifications?ids={}", + urlencoding::encode(¬ification_ids) + )) + .append_pat(pat) + .to_request(); + self.call(req).await + } + async fn mark_notification_read( &self, notification_id: &str, @@ -196,6 +236,23 @@ impl ApiTeams for ApiV3 { .to_request(); self.call(req).await } + + async fn mark_notifications_read( + &self, + notification_ids: &[&str], + pat: Option<&str>, + ) -> ServiceResponse { + let notification_ids = serde_json::to_string(notification_ids).unwrap(); + let req = test::TestRequest::patch() + .uri(&format!( + "/v3/notifications?ids={}", + urlencoding::encode(¬ification_ids) + )) + .append_pat(pat) + .to_request(); + self.call(req).await + } + async fn add_user_to_team( &self, team_id: &str, @@ -227,4 +284,20 @@ impl ApiTeams for ApiV3 { .to_request(); self.call(req).await } + + async fn delete_notifications( + &self, + notification_ids: &[&str], + pat: Option<&str>, + ) -> ServiceResponse { + let notification_ids = serde_json::to_string(notification_ids).unwrap(); + let req = test::TestRequest::delete() + .uri(&format!( + "/v3/notifications?ids={}", + urlencoding::encode(¬ification_ids) + )) + .append_pat(pat) + .to_request(); + self.call(req).await + } } diff --git a/tests/common/api_v3/user.rs b/tests/common/api_v3/user.rs new file mode 100644 index 00000000..5bfaf780 --- /dev/null +++ b/tests/common/api_v3/user.rs @@ -0,0 +1,48 @@ +use actix_web::{dev::ServiceResponse, test}; +use async_trait::async_trait; + +use crate::common::api_common::{Api, ApiUser, AppendsOptionalPat}; + +use super::ApiV3; + +#[async_trait(?Send)] +impl ApiUser for ApiV3 { + async fn get_user(&self, user_id_or_username: &str, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::get() + .uri(&format!("/v3/user/{}", user_id_or_username)) + .append_pat(pat) + .to_request(); + self.call(req).await + } + + async fn get_current_user(&self, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::get() + .uri("/v3/user") + .append_pat(pat) + .to_request(); + self.call(req).await + } + + async fn edit_user( + &self, + user_id_or_username: &str, + patch: serde_json::Value, + pat: Option<&str>, + ) -> ServiceResponse { + let req = test::TestRequest::patch() + .uri(&format!("/v3/user/{}", user_id_or_username)) + .append_pat(pat) + .set_json(patch) + .to_request(); + + self.call(req).await + } + + async fn delete_user(&self, user_id_or_username: &str, pat: Option<&str>) -> ServiceResponse { + let req = test::TestRequest::delete() + .uri(&format!("/v3/user/{}", user_id_or_username)) + .append_pat(pat) + .to_request(); + self.call(req).await + } +} diff --git a/tests/common/api_v3/version.rs b/tests/common/api_v3/version.rs index 0a270c13..1c2a230c 100644 --- a/tests/common/api_v3/version.rs +++ b/tests/common/api_v3/version.rs @@ -181,6 +181,22 @@ impl ApiVersion for ApiV3 { self.call(req).await } + async fn download_version_redirect( + &self, + hash: &str, + algorithm: &str, + pat: Option<&str>, + ) -> ServiceResponse { + let req = test::TestRequest::get() + .uri(&format!("/v3/version_file/{hash}/download",)) + .set_json(json!({ + "algorithm": algorithm, + })) + .append_pat(pat) + .to_request(); + self.call(req).await + } + async fn get_version_from_hash( &self, hash: &str, diff --git a/tests/common/database.rs b/tests/common/database.rs index 7e0b8218..ba6e9cae 100644 --- a/tests/common/database.rs +++ b/tests/common/database.rs @@ -168,21 +168,14 @@ impl TemporaryDatabase { if !dummy_data_exists { // Add dummy data - let temporary_test_env = - TestEnvironment::::build_with_db(TemporaryDatabase { - pool: pool.clone(), - database_name: TEMPLATE_DATABASE_NAME.to_string(), - redis_pool: RedisPool::new(Some(generate_random_name( - "test_template_", - ))), - }) - .await; - dummy_data::add_dummy_data( - &temporary_test_env.setup_api, - temporary_test_env.db.clone(), - ) - .await; - temporary_test_env.db.pool.close().await; + let db = TemporaryDatabase { + pool: pool.clone(), + database_name: TEMPLATE_DATABASE_NAME.to_string(), + redis_pool: RedisPool::new(Some(generate_random_name("test_template_"))), + }; + let setup_api = TestEnvironment::::build_setup_api(&db).await; + dummy_data::add_dummy_data(&setup_api, db.clone()).await; + db.pool.close().await; } pool.close().await; drop(pool); diff --git a/tests/common/environment.rs b/tests/common/environment.rs index a3b09e63..36ba4582 100644 --- a/tests/common/environment.rs +++ b/tests/common/environment.rs @@ -65,33 +65,30 @@ where // Use .call(req) on it directly to make a test call as if test::call_service(req) were being used. #[derive(Clone)] pub struct TestEnvironment { - // test_app: Rc, // Rc as it's not Send pub db: TemporaryDatabase, pub api: A, pub setup_api: ApiV3, // Used for setting up tests only (ie: in ScopesTest) - pub dummy: Option, + pub dummy: dummy_data::DummyData, } impl TestEnvironment { async fn build(max_connections: Option) -> Self { let db = TemporaryDatabase::create(max_connections).await; - let mut test_env = Self::build_with_db(db).await; - - let dummy = dummy_data::get_dummy_data(&test_env.setup_api).await; - test_env.dummy = Some(dummy); - test_env - } - - pub async fn build_with_db(db: TemporaryDatabase) -> Self { let labrinth_config = setup(&db).await; + let api = A::build(labrinth_config.clone()).await; + let setup_api = ApiV3::build(labrinth_config).await; + let dummy = dummy_data::get_dummy_data(&setup_api).await; Self { db, - api: A::build(labrinth_config.clone()).await, - setup_api: ApiV3::build(labrinth_config.clone()).await, - dummy: None, - // test_app + api, + setup_api, + dummy, } } + pub async fn build_setup_api(db: &TemporaryDatabase) -> ApiV3 { + let labrinth_config = setup(db).await; + ApiV3::build(labrinth_config).await + } } impl TestEnvironment { @@ -108,7 +105,7 @@ impl TestEnvironment { let resp = self .api .add_user_to_team( - &self.dummy.as_ref().unwrap().project_alpha.team_id, + &self.dummy.project_alpha.team_id, FRIEND_USER_ID, None, None, diff --git a/tests/common/scopes.rs b/tests/common/scopes.rs index c43ee98e..7839c211 100644 --- a/tests/common/scopes.rs +++ b/tests/common/scopes.rs @@ -1,5 +1,6 @@ #![allow(dead_code)] -use actix_web::test::{self, TestRequest}; +use actix_web::{dev::ServiceResponse, test}; +use futures::Future; use labrinth::models::pats::Scopes; use super::{ @@ -59,13 +60,14 @@ impl<'a, A: Api> ScopeTest<'a, A> { // success_scopes : the scopes that we are testing that should succeed // returns a tuple of (failure_body, success_body) // Should return a String error if on unexpected status code, allowing unwrapping in tests. - pub async fn test( + pub async fn test( &self, req_gen: T, success_scopes: Scopes, ) -> Result<(serde_json::Value, serde_json::Value), String> where - T: Fn() -> TestRequest, + T: Fn(Option) -> Fut, + Fut: Future, // Ensure Fut is Send and 'static { // First, create a PAT with failure scopes let failure_scopes = self @@ -79,11 +81,7 @@ impl<'a, A: Api> ScopeTest<'a, A> { // Perform test twice, once with each PAT // the first time, we expect a 401 (or known failure code) - let req = req_gen() - .append_header(("Authorization", access_token_all_others.as_str())) - .to_request(); - let resp = self.test_env.call(req).await; - + let resp = req_gen(Some(access_token_all_others.clone())).await; if resp.status().as_u16() != self.expected_failure_code { return Err(format!( "Expected failure code {}, got {} ({:#?})", @@ -103,11 +101,7 @@ impl<'a, A: Api> ScopeTest<'a, A> { }; // The second time, we expect a success code - let req = req_gen() - .append_header(("Authorization", access_token.as_str())) - .to_request(); - let resp = self.test_env.call(req).await; - + let resp = req_gen(Some(access_token.clone())).await; if !(resp.status().is_success() || resp.status().is_redirection()) { return Err(format!( "Expected success code, got {} ({:#?})", diff --git a/tests/loader_fields.rs b/tests/loader_fields.rs index f42e2102..e67a04d5 100644 --- a/tests/loader_fields.rs +++ b/tests/loader_fields.rs @@ -7,7 +7,7 @@ use serde_json::json; use crate::common::api_common::ApiVersion; use crate::common::database::*; -use crate::common::dummy_data::TestFile; +use crate::common::dummy_data::{DummyProjectAlpha, DummyProjectBeta, TestFile}; // importing common module. mod common; @@ -17,33 +17,16 @@ mod common; async fn creating_loader_fields() { with_test_environment(None, |test_env: TestEnvironment| async move { let api = &test_env.api; - - let alpha_project_id = &test_env - .dummy - .as_ref() - .unwrap() - .project_alpha - .project_id - .clone(); - let alpha_project_id_parsed = test_env - .dummy - .as_ref() - .unwrap() - .project_alpha - .project_id_parsed; - let beta_project_id_parsed = test_env - .dummy - .as_ref() - .unwrap() - .project_beta - .project_id_parsed; - let alpha_version_id = &test_env - .dummy - .as_ref() - .unwrap() - .project_alpha - .version_id - .clone(); + let DummyProjectAlpha { + project_id: alpha_project_id, + project_id_parsed: alpha_project_id_parsed, + version_id: alpha_version_id, + .. + } = &test_env.dummy.project_alpha; + let DummyProjectBeta { + project_id_parsed: beta_project_id_parsed, + .. + } = &test_env.dummy.project_beta; // ALL THE FOLLOWING FOR CREATE AND PATCH // Cannot create a version with an extra argument that cannot be tied to a loader field ("invalid loader field") @@ -51,7 +34,7 @@ async fn creating_loader_fields() { // - Create version let resp = api .add_public_version( - alpha_project_id_parsed, + *alpha_project_id_parsed, "1.0.0", TestFile::build_random_jar(), None, @@ -84,7 +67,7 @@ async fn creating_loader_fields() { // - Create version let resp = api .add_public_version( - alpha_project_id_parsed, + *alpha_project_id_parsed, "1.0.0", TestFile::build_random_jar(), None, @@ -117,7 +100,7 @@ async fn creating_loader_fields() { // - Create version let resp = api .add_public_version( - alpha_project_id_parsed, + *alpha_project_id_parsed, "1.0.0", TestFile::build_random_jar(), None, @@ -139,7 +122,7 @@ async fn creating_loader_fields() { // - Create version let resp = api .add_public_version( - alpha_project_id_parsed, + *alpha_project_id_parsed, "1.0.0", TestFile::build_random_jar(), None, @@ -161,7 +144,7 @@ async fn creating_loader_fields() { // - Create version let resp: actix_web::dev::ServiceResponse = api .add_public_version( - alpha_project_id_parsed, + *alpha_project_id_parsed, "1.0.0", TestFile::build_random_jar(), None, @@ -201,7 +184,7 @@ async fn creating_loader_fields() { // - Create version let resp = api .add_public_version( - alpha_project_id_parsed, + *alpha_project_id_parsed, "1.0.0", TestFile::build_random_jar(), None, @@ -236,7 +219,7 @@ async fn creating_loader_fields() { // - Create version let v = api .add_public_version_deserialized( - alpha_project_id_parsed, + *alpha_project_id_parsed, "1.0.0", TestFile::build_random_jar(), None, @@ -272,7 +255,7 @@ async fn creating_loader_fields() { // - Create let v = api .add_public_version_deserialized( - alpha_project_id_parsed, + *alpha_project_id_parsed, "1.0.0", TestFile::build_random_jar(), None, @@ -325,7 +308,7 @@ async fn creating_loader_fields() { // Now that we've created a version, we need to make sure that the Project's loader fields are updated (aggregate) // First, add a new version api.add_public_version_deserialized( - alpha_project_id_parsed, + *alpha_project_id_parsed, "1.0.1", TestFile::build_random_jar(), None, @@ -347,7 +330,7 @@ async fn creating_loader_fields() { // Also, add one to the beta project api.add_public_version_deserialized( - beta_project_id_parsed, + *beta_project_id_parsed, "1.0.1", TestFile::build_random_jar(), None, diff --git a/tests/notifications.rs b/tests/notifications.rs index 0d246338..60956e6c 100644 --- a/tests/notifications.rs +++ b/tests/notifications.rs @@ -10,13 +10,7 @@ mod common; #[actix_rt::test] pub async fn get_user_notifications_after_team_invitation_returns_notification() { with_test_environment_all(None, |test_env| async move { - let alpha_team_id = test_env - .dummy - .as_ref() - .unwrap() - .project_alpha - .team_id - .clone(); + let alpha_team_id = test_env.dummy.project_alpha.team_id.clone(); let api = test_env.api; api.get_user_notifications_deserialized_common(FRIEND_USER_ID, FRIEND_USER_PAT) .await; diff --git a/tests/oauth.rs b/tests/oauth.rs index 101cefbf..e9737912 100644 --- a/tests/oauth.rs +++ b/tests/oauth.rs @@ -24,7 +24,7 @@ async fn oauth_flow_happy_path() { valid_redirect_uri: base_redirect_uri, client_id, client_secret, - } = env.dummy.as_ref().unwrap().oauth_client_alpha.clone(); + } = &env.dummy.oauth_client_alpha; // Initiate authorization let redirect_uri = format!("{}?foo=bar", base_redirect_uri); @@ -32,7 +32,7 @@ async fn oauth_flow_happy_path() { let resp = env .api .oauth_authorize( - &client_id, + client_id, Some("USER_READ NOTIFICATION_READ"), Some(&redirect_uri), Some(original_state), @@ -60,7 +60,7 @@ async fn oauth_flow_happy_path() { auth_code.to_string(), Some(redirect_uri.clone()), client_id.to_string(), - &client_secret, + client_secret, ) .await; assert_status(&resp, StatusCode::OK); @@ -82,7 +82,7 @@ async fn oauth_flow_happy_path() { #[actix_rt::test] async fn oauth_authorize_for_already_authorized_scopes_returns_auth_code() { with_test_environment(None, |env: TestEnvironment| async move { - let DummyOAuthClientAlpha { client_id, .. } = env.dummy.unwrap().oauth_client_alpha; + let DummyOAuthClientAlpha { client_id, .. } = env.dummy.oauth_client_alpha; let resp = env .api @@ -119,7 +119,7 @@ async fn get_oauth_token_with_already_used_auth_code_fails() { client_id, client_secret, .. - } = env.dummy.unwrap().oauth_client_alpha; + } = env.dummy.oauth_client_alpha; let resp = env .api @@ -152,7 +152,7 @@ async fn authorize_with_broader_scopes_can_complete_flow() { client_id, client_secret, .. - } = env.dummy.as_ref().unwrap().oauth_client_alpha.clone(); + } = env.dummy.oauth_client_alpha.clone(); let first_access_token = env .api @@ -209,7 +209,7 @@ async fn authorize_with_broader_scopes_can_complete_flow() { #[actix_rt::test] async fn oauth_authorize_with_broader_scopes_requires_user_accept() { with_test_environment(None, |env: TestEnvironment| async move { - let client_id = env.dummy.unwrap().oauth_client_alpha.client_id.clone(); + let client_id = env.dummy.oauth_client_alpha.client_id; let resp = env .api .oauth_authorize(&client_id, Some("USER_READ"), None, None, USER_USER_PAT) @@ -237,7 +237,7 @@ async fn oauth_authorize_with_broader_scopes_requires_user_accept() { #[actix_rt::test] async fn reject_authorize_ends_authorize_flow() { with_test_environment(None, |env: TestEnvironment| async move { - let client_id = env.dummy.unwrap().oauth_client_alpha.client_id.clone(); + let client_id = env.dummy.oauth_client_alpha.client_id; let resp = env .api .oauth_authorize(&client_id, None, None, None, USER_USER_PAT) @@ -256,7 +256,7 @@ async fn reject_authorize_ends_authorize_flow() { #[actix_rt::test] async fn accept_authorize_after_already_accepting_fails() { with_test_environment(None, |env: TestEnvironment| async move { - let client_id = env.dummy.unwrap().oauth_client_alpha.client_id.clone(); + let client_id = env.dummy.oauth_client_alpha.client_id; let resp = env .api .oauth_authorize(&client_id, None, None, None, USER_USER_PAT) @@ -278,12 +278,12 @@ async fn revoke_authorization_after_issuing_token_revokes_token() { client_id, client_secret, .. - } = env.dummy.as_ref().unwrap().oauth_client_alpha.clone(); + } = &env.dummy.oauth_client_alpha; let access_token = env .api .complete_full_authorize_flow( - &client_id, - &client_secret, + client_id, + client_secret, Some("NOTIFICATION_READ"), None, None, @@ -295,7 +295,7 @@ async fn revoke_authorization_after_issuing_token_revokes_token() { let resp = env .api - .revoke_oauth_authorization(&client_id, USER_USER_PAT) + .revoke_oauth_authorization(client_id, USER_USER_PAT) .await; assert_status(&resp, StatusCode::OK); diff --git a/tests/oauth_clients.rs b/tests/oauth_clients.rs index ab41c0d6..7ca018df 100644 --- a/tests/oauth_clients.rs +++ b/tests/oauth_clients.rs @@ -98,8 +98,7 @@ async fn create_oauth_client_with_restricted_scopes_fails() { #[actix_rt::test] async fn get_oauth_client_for_client_creator_succeeds() { with_test_environment(None, |env: TestEnvironment| async move { - let DummyOAuthClientAlpha { client_id, .. } = - env.dummy.as_ref().unwrap().oauth_client_alpha.clone(); + let DummyOAuthClientAlpha { client_id, .. } = env.dummy.oauth_client_alpha.clone(); let resp = env .api @@ -116,8 +115,7 @@ async fn get_oauth_client_for_client_creator_succeeds() { #[actix_rt::test] async fn get_oauth_client_for_unrelated_user_fails() { with_test_environment(None, |env: TestEnvironment| async move { - let DummyOAuthClientAlpha { client_id, .. } = - env.dummy.as_ref().unwrap().oauth_client_alpha.clone(); + let DummyOAuthClientAlpha { client_id, .. } = env.dummy.oauth_client_alpha.clone(); let resp = env .api @@ -132,7 +130,7 @@ async fn get_oauth_client_for_unrelated_user_fails() { #[actix_rt::test] async fn can_delete_oauth_client() { with_test_environment(None, |env: TestEnvironment| async move { - let client_id = env.dummy.unwrap().oauth_client_alpha.client_id.clone(); + let client_id = env.dummy.oauth_client_alpha.client_id.clone(); let resp = env.api.delete_oauth_client(&client_id, USER_USER_PAT).await; assert_status(&resp, StatusCode::NO_CONTENT); @@ -152,7 +150,7 @@ async fn delete_oauth_client_after_issuing_access_tokens_revokes_tokens() { client_id, client_secret, .. - } = env.dummy.as_ref().unwrap().oauth_client_alpha.clone(); + } = env.dummy.oauth_client_alpha.clone(); let access_token = env .api .complete_full_authorize_flow( @@ -184,7 +182,7 @@ async fn can_list_user_oauth_authorizations() { client_id, client_secret, .. - } = env.dummy.as_ref().unwrap().oauth_client_alpha.clone(); + } = env.dummy.oauth_client_alpha.clone(); env.api .complete_full_authorize_flow( &client_id, diff --git a/tests/organizations.rs b/tests/organizations.rs index 02a58239..191ae99f 100644 --- a/tests/organizations.rs +++ b/tests/organizations.rs @@ -18,12 +18,7 @@ mod common; async fn create_organization() { with_test_environment(None, |test_env: TestEnvironment| async move { let api = &test_env.api; - let zeta_organization_slug = &test_env - .dummy - .as_ref() - .unwrap() - .organization_zeta - .organization_id; + let zeta_organization_slug = &test_env.dummy.organization_zeta.organization_id; // Failed creations title: // - slug collision with zeta @@ -88,12 +83,7 @@ async fn patch_organization() { with_test_environment(None, |test_env: TestEnvironment| async move { let api = &test_env.api; - let zeta_organization_id = &test_env - .dummy - .as_ref() - .unwrap() - .organization_zeta - .organization_id; + let zeta_organization_id = &test_env.dummy.organization_zeta.organization_id; // Create 'theta' organization let resp = api @@ -169,12 +159,7 @@ async fn patch_organization() { async fn add_remove_icon() { with_test_environment(None, |test_env: TestEnvironment| async move { let api = &test_env.api; - let zeta_organization_id = &test_env - .dummy - .as_ref() - .unwrap() - .organization_zeta - .organization_id; + let zeta_organization_id = &test_env.dummy.organization_zeta.organization_id; // Get project let resp = test_env @@ -221,12 +206,7 @@ async fn add_remove_icon() { async fn delete_org() { with_test_environment(None, |test_env: TestEnvironment| async move { let api = &test_env.api; - let zeta_organization_id = &test_env - .dummy - .as_ref() - .unwrap() - .organization_zeta - .organization_id; + let zeta_organization_id = &test_env.dummy.organization_zeta.organization_id; let resp = api .delete_organization(zeta_organization_id, USER_USER_PAT) @@ -246,14 +226,9 @@ async fn delete_org() { #[actix_rt::test] async fn add_remove_organization_projects() { with_test_environment(None, |test_env: TestEnvironment| async move { - let alpha_project_id: &str = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; - let alpha_project_slug: &str = &test_env.dummy.as_ref().unwrap().project_alpha.project_slug; - let zeta_organization_id: &str = &test_env - .dummy - .as_ref() - .unwrap() - .organization_zeta - .organization_id; + let alpha_project_id: &str = &test_env.dummy.project_alpha.project_id; + let alpha_project_slug: &str = &test_env.dummy.project_alpha.project_slug; + let zeta_organization_id: &str = &test_env.dummy.organization_zeta.organization_id; // Add/remove project to organization, first by ID, then by slug for alpha in [alpha_project_id, alpha_project_slug] { @@ -331,13 +306,8 @@ async fn permissions_patch_organization() { #[actix_rt::test] async fn permissions_edit_details() { with_test_environment(None, |test_env: TestEnvironment| async move { - let zeta_organization_id = &test_env - .dummy - .as_ref() - .unwrap() - .organization_zeta - .organization_id; - let zeta_team_id = &test_env.dummy.as_ref().unwrap().organization_zeta.team_id; + let zeta_organization_id = &test_env.dummy.organization_zeta.organization_id; + let zeta_team_id = &test_env.dummy.organization_zeta.team_id; let api = &test_env.api; let edit_details = OrganizationPermissions::EDIT_DETAILS; @@ -381,13 +351,8 @@ async fn permissions_manage_invites() { with_test_environment_all(None, |test_env| async move { let api = &test_env.api; - let zeta_organization_id = &test_env - .dummy - .as_ref() - .unwrap() - .organization_zeta - .organization_id; - let zeta_team_id = &test_env.dummy.as_ref().unwrap().organization_zeta.team_id; + let zeta_organization_id = &test_env.dummy.organization_zeta.organization_id; + let zeta_team_id = &test_env.dummy.organization_zeta.team_id; let manage_invites = OrganizationPermissions::MANAGE_INVITES; @@ -472,15 +437,10 @@ async fn permissions_add_remove_project() { with_test_environment(None, |test_env: TestEnvironment| async move { let api = &test_env.api; - let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; - let alpha_team_id = &test_env.dummy.as_ref().unwrap().project_alpha.team_id; - let zeta_organization_id = &test_env - .dummy - .as_ref() - .unwrap() - .organization_zeta - .organization_id; - let zeta_team_id = &test_env.dummy.as_ref().unwrap().organization_zeta.team_id; + let alpha_project_id = &test_env.dummy.project_alpha.project_id; + let alpha_team_id = &test_env.dummy.project_alpha.team_id; + let zeta_organization_id = &test_env.dummy.organization_zeta.organization_id; + let zeta_team_id = &test_env.dummy.organization_zeta.team_id; let add_project = OrganizationPermissions::ADD_PROJECT; @@ -557,13 +517,8 @@ async fn permissions_delete_organization() { #[actix_rt::test] async fn permissions_add_default_project_permissions() { with_test_environment_all(None, |test_env| async move { - let zeta_organization_id = &test_env - .dummy - .as_ref() - .unwrap() - .organization_zeta - .organization_id; - let zeta_team_id = &test_env.dummy.as_ref().unwrap().organization_zeta.team_id; + let zeta_organization_id = &test_env.dummy.organization_zeta.organization_id; + let zeta_team_id = &test_env.dummy.organization_zeta.team_id; let api = &test_env.api; diff --git a/tests/pats.rs b/tests/pats.rs index 37e95c26..832f4d3d 100644 --- a/tests/pats.rs +++ b/tests/pats.rs @@ -60,6 +60,9 @@ pub async fn pat_full_test() { let mock_pat_test = |token: &str| { let token = token.to_string(); async { + // This uses a route directly instead of an api call because it doesn't relaly matter and we + // want it to succeed no matter what. + // This is an arbitrary request. let req = test::TestRequest::post() .uri("/v3/collection") .append_header(("Authorization", token)) diff --git a/tests/project.rs b/tests/project.rs index c57af867..2eb9755f 100644 --- a/tests/project.rs +++ b/tests/project.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use actix_http::StatusCode; use actix_web::test; -use chrono::{Duration, Utc}; use common::api_v3::ApiV3; use common::database::*; use common::dummy_data::DUMMY_CATEGORIES; @@ -20,25 +19,29 @@ use serde_json::json; use crate::common::api_common::models::CommonItemType; use crate::common::api_common::request_data::ProjectCreationRequestData; -use crate::common::api_common::{ApiProject, ApiTeams, ApiVersion, AppendsOptionalPat}; -use crate::common::dummy_data::{DummyImage, TestFile}; +use crate::common::api_common::{ApiProject, ApiTeams, ApiVersion}; +use crate::common::dummy_data::{DummyImage, DummyProjectAlpha, DummyProjectBeta, TestFile}; mod common; #[actix_rt::test] async fn test_get_project() { // Test setup and dummy data with_test_environment_all(None, |test_env| async move { - let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; - let beta_project_id = &test_env.dummy.as_ref().unwrap().project_beta.project_id; - let alpha_project_slug = &test_env.dummy.as_ref().unwrap().project_alpha.project_slug; - let alpha_version_id = &test_env.dummy.as_ref().unwrap().project_alpha.version_id; + let DummyProjectAlpha { + project_id: alpha_project_id, + project_slug: alpha_project_slug, + version_id: alpha_version_id, + .. + } = &test_env.dummy.project_alpha; + let DummyProjectBeta { + project_id: beta_project_id, + .. + } = &test_env.dummy.project_beta; + + let api = &test_env.api; // Perform request on dummy data - let req = test::TestRequest::get() - .uri(&format!("/v3/project/{alpha_project_id}")) - .append_pat(USER_USER_PAT) - .to_request(); - let resp = test_env.call(req).await; + let resp = api.get_project(alpha_project_id, USER_USER_PAT).await; let status = resp.status(); let body: serde_json::Value = test::read_body_json(resp).await; @@ -71,11 +74,7 @@ async fn test_get_project() { assert_eq!(cached_project["inner"]["slug"], json!(alpha_project_slug)); // Make the request again, this time it should be cached - let req = test::TestRequest::get() - .uri(&format!("/v3/project/{alpha_project_id}")) - .append_pat(USER_USER_PAT) - .to_request(); - let resp = test_env.call(req).await; + let resp = api.get_project(alpha_project_id, USER_USER_PAT).await; let status = resp.status(); assert_eq!(status, 200); @@ -84,21 +83,11 @@ async fn test_get_project() { assert_eq!(body["slug"], json!(alpha_project_slug)); // Request should fail on non-existent project - let req = test::TestRequest::get() - .uri("/v3/project/nonexistent") - .append_pat(USER_USER_PAT) - .to_request(); - - let resp = test_env.call(req).await; + let resp = api.get_project("nonexistent", USER_USER_PAT).await; assert_eq!(resp.status(), 404); // Similarly, request should fail on non-authorized user, on a yet-to-be-approved or hidden project, with a 404 (hiding the existence of the project) - let req = test::TestRequest::get() - .uri(&format!("/v3/project/{beta_project_id}")) - .append_pat(ENEMY_USER_PAT) - .to_request(); - - let resp = test_env.call(req).await; + let resp = api.get_project(beta_project_id, ENEMY_USER_PAT).await; assert_eq!(resp.status(), 404); }) .await; @@ -290,8 +279,8 @@ pub async fn test_patch_project() { with_test_environment(None, |test_env: TestEnvironment| async move { let api = &test_env.api; - let alpha_project_slug = &test_env.dummy.as_ref().unwrap().project_alpha.project_slug; - let beta_project_slug = &test_env.dummy.as_ref().unwrap().project_beta.project_slug; + let alpha_project_slug = &test_env.dummy.project_alpha.project_slug; + let beta_project_slug = &test_env.dummy.project_beta.project_slug; // First, we do some patch requests that should fail. // Failure because the user is not authorized. @@ -472,7 +461,7 @@ pub async fn test_patch_v3() { with_test_environment(None, |test_env: TestEnvironment| async move { let api = &test_env.api; - let alpha_project_slug = &test_env.dummy.as_ref().unwrap().project_alpha.project_slug; + let alpha_project_slug = &test_env.dummy.project_alpha.project_slug; // Sucessful request to patch many fields. let resp = api @@ -503,8 +492,8 @@ pub async fn test_patch_v3() { pub async fn test_bulk_edit_categories() { with_test_environment_all(None, |test_env| async move { let api = &test_env.api; - let alpha_project_id: &str = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; - let beta_project_id: &str = &test_env.dummy.as_ref().unwrap().project_beta.project_id; + let alpha_project_id: &str = &test_env.dummy.project_alpha.project_id; + let beta_project_id: &str = &test_env.dummy.project_beta.project_id; let resp = api .edit_project_bulk( @@ -544,8 +533,8 @@ pub async fn test_bulk_edit_categories() { pub async fn test_bulk_edit_links() { with_test_environment(None, |test_env: TestEnvironment| async move { let api = &test_env.api; - let alpha_project_id: &str = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; - let beta_project_id: &str = &test_env.dummy.as_ref().unwrap().project_beta.project_id; + let alpha_project_id: &str = &test_env.dummy.project_alpha.project_id; + let beta_project_id: &str = &test_env.dummy.project_beta.project_id; // Sets links for issue, source, wiki, and patreon for all projects // The first loop, sets issue, the second, clears it for all projects. @@ -595,8 +584,8 @@ pub async fn test_bulk_edit_links() { async fn delete_project_with_report() { with_test_environment(None, |test_env: TestEnvironment| async move { let api = &test_env.api; - let alpha_project_id: &str = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; - let beta_project_id: &str = &test_env.dummy.as_ref().unwrap().project_beta.project_id; + let alpha_project_id: &str = &test_env.dummy.project_alpha.project_id; + let beta_project_id: &str = &test_env.dummy.project_beta.project_id; // Create a report for the project let resp = api @@ -682,8 +671,8 @@ async fn delete_project_with_report() { #[actix_rt::test] async fn permissions_patch_project_v3() { with_test_environment(Some(8), |test_env: TestEnvironment| async move { - let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; - let alpha_team_id = &test_env.dummy.as_ref().unwrap().project_alpha.team_id; + let alpha_project_id = &test_env.dummy.project_alpha.project_id; + let alpha_team_id = &test_env.dummy.project_alpha.team_id; let api = &test_env.api; // TODO: This should be a separate test from v3 @@ -797,47 +786,69 @@ async fn permissions_patch_project_v3() { .await; } +// TODO: Project scheduling has been temporarily disabled, so this test is disabled as well +// #[actix_rt::test] +// async fn permissions_schedule() { +// with_test_environment(None, |test_env : TestEnvironment| async move { +// let DummyProjectAlpha { +// project_id: alpha_project_id, +// team_id: alpha_team_id, +// .. +// } = &test_env.dummy.project_alpha; +// let DummyProjectBeta { +// project_id: beta_project_id, +// version_id: beta_version_id, +// team_id: beta_team_id, +// .. +// } = &test_env.dummy.project_beta; + +// let edit_details = ProjectPermissions::EDIT_DETAILS; +// let api = &test_env.api; + +// // Approve beta version as private so we can schedule it +// let resp = api +// .edit_version( +// beta_version_id, +// json!({ +// "status": "unlisted" +// }), +// MOD_USER_PAT, +// ) +// .await; +// assert_eq!(resp.status(), 204); + +// // Schedule version +// let req_gen = |ctx: PermissionsTestContext| async move { +// api.schedule_version( +// beta_version_id, +// "archived", +// Utc::now() + Duration::days(1), +// ctx.test_pat.as_deref(), +// ) +// .await +// }; +// PermissionsTest::new(&test_env) +// .with_existing_project(beta_project_id, beta_team_id) +// .with_user(FRIEND_USER_ID, FRIEND_USER_PAT, true) +// .simple_project_permissions_test(edit_details, req_gen) +// .await +// .unwrap(); +// }).await +// } + // Not covered by PATCH /project #[actix_rt::test] async fn permissions_edit_details() { with_test_environment_all(None, |test_env| async move { - let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; - let alpha_team_id = &test_env.dummy.as_ref().unwrap().project_alpha.team_id; - let beta_project_id = &test_env.dummy.as_ref().unwrap().project_beta.project_id; - let beta_team_id = &test_env.dummy.as_ref().unwrap().project_beta.team_id; - let beta_version_id = &test_env.dummy.as_ref().unwrap().project_beta.version_id; + let DummyProjectAlpha { + project_id: alpha_project_id, + team_id: alpha_team_id, + .. + } = &test_env.dummy.project_alpha; let edit_details = ProjectPermissions::EDIT_DETAILS; let api = &test_env.api; - // Approve beta version as private so we can schedule it - let req = test::TestRequest::patch() - .uri(&format!("/v3/version/{beta_version_id}")) - .append_pat(MOD_USER_PAT) - .set_json(json!({ - "status": "unlisted" - })) - .to_request(); - let resp = test_env.call(req).await; - assert_eq!(resp.status(), 204); - - // Schedule version - let req_gen = |ctx: PermissionsTestContext| async move { - api.schedule_project( - beta_version_id, - "archived", - Utc::now() + Duration::days(1), - ctx.test_pat.as_deref(), - ) - .await - }; - PermissionsTest::new(&test_env) - .with_existing_project(beta_project_id, beta_team_id) - .with_user(FRIEND_USER_ID, FRIEND_USER_PAT, true) - .simple_project_permissions_test(edit_details, req_gen) - .await - .unwrap(); - // Icon edit // Uses alpha project to delete this icon let req_gen = |ctx: PermissionsTestContext| async move { @@ -889,11 +900,7 @@ async fn permissions_edit_details() { .await .unwrap(); // Get project, as we need the gallery image url - let req = test::TestRequest::get() - .uri(&format!("/v3/project/{alpha_project_id}")) - .append_pat(USER_USER_PAT) - .to_request(); - let resp = test_env.call(req).await; + let resp = api.get_project(alpha_project_id, USER_USER_PAT).await; let project: serde_json::Value = test::read_body_json(resp).await; let gallery_url = project["gallery"][0]["url"].as_str().unwrap(); @@ -940,10 +947,10 @@ async fn permissions_edit_details() { #[actix_rt::test] async fn permissions_upload_version() { with_test_environment(None, |test_env: TestEnvironment| async move { - let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; - let alpha_version_id = &test_env.dummy.as_ref().unwrap().project_alpha.version_id; - let alpha_team_id = &test_env.dummy.as_ref().unwrap().project_alpha.team_id; - let alpha_file_hash = &test_env.dummy.as_ref().unwrap().project_alpha.file_hash; + let alpha_project_id = &test_env.dummy.project_alpha.project_id; + let alpha_version_id = &test_env.dummy.project_alpha.version_id; + let alpha_team_id = &test_env.dummy.project_alpha.team_id; + let alpha_file_hash = &test_env.dummy.project_alpha.file_hash; let api = &test_env.api; @@ -1038,8 +1045,8 @@ async fn permissions_upload_version() { async fn permissions_manage_invites() { // Add member, remove member, edit member with_test_environment_all(None, |test_env| async move { - let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; - let alpha_team_id = &test_env.dummy.as_ref().unwrap().project_alpha.team_id; + let alpha_project_id = &test_env.dummy.project_alpha.project_id; + let alpha_team_id = &test_env.dummy.project_alpha.team_id; let api = &test_env.api; let manage_invites = ProjectPermissions::MANAGE_INVITES; @@ -1096,22 +1103,19 @@ async fn permissions_manage_invites() { .unwrap(); // re-add member for testing - let req = test::TestRequest::post() - .uri(&format!("/v3/team/{}/members", alpha_team_id)) - .append_pat(ADMIN_USER_PAT) - .set_json(json!({ - "user_id": MOD_USER_ID, - })) - .to_request(); - let resp = test_env.call(req).await; + let resp = api + .add_user_to_team( + alpha_team_id, + MOD_USER_ID, + Some(ProjectPermissions::empty()), + None, + ADMIN_USER_PAT, + ) + .await; assert_eq!(resp.status(), 204); // Accept invite - let req = test::TestRequest::post() - .uri(&format!("/v3/team/{}/join", alpha_team_id)) - .append_pat(MOD_USER_PAT) - .to_request(); - let resp = test_env.call(req).await; + let resp = api.join_team(alpha_team_id, MOD_USER_PAT).await; assert_eq!(resp.status(), 204); // remove existing member (requires remove_member) diff --git a/tests/scopes.rs b/tests/scopes.rs index 0aa53ce5..b10b9ff7 100644 --- a/tests/scopes.rs +++ b/tests/scopes.rs @@ -1,9 +1,12 @@ -use actix_web::test::{self, TestRequest}; -use bytes::Bytes; +use std::collections::HashMap; + +use crate::common::api_common::{ApiProject, ApiTeams, ApiUser, ApiVersion, AppendsOptionalPat}; +use crate::common::dummy_data::{DummyImage, DummyProjectAlpha, DummyProjectBeta}; +use actix_web::test; use chrono::{Duration, Utc}; -use common::api_v3::request_data::{ - get_public_project_creation_data, get_public_version_creation_data, -}; +use common::api_common::models::CommonItemType; +use common::api_common::Api; +use common::api_v3::request_data::get_public_project_creation_data; use common::api_v3::ApiV3; use common::dummy_data::TestFile; use common::environment::{with_test_environment, with_test_environment_all, TestEnvironment}; @@ -11,12 +14,8 @@ use common::{database::*, scopes::ScopeTest}; use labrinth::models::ids::base62_impl::parse_base62; use labrinth::models::pats::Scopes; use labrinth::models::projects::ProjectId; -use labrinth::util::actix::{AppendsMultipart, MultipartSegment, MultipartSegmentData}; use serde_json::json; -use crate::common::api_common::{ApiTeams, AppendsOptionalPat}; -use crate::common::dummy_data::DummyImage; - // For each scope, we (using test_scope): // - create a PAT with a given set of scopes for a function // - create a PAT with all other scopes for a function @@ -30,9 +29,11 @@ mod common; async fn user_scopes() { // Test setup and dummy data with_test_environment_all(None, |test_env| async move { + let api = &test_env.api; // User reading let read_user = Scopes::USER_READ; - let req_gen = || TestRequest::get().uri("/v3/user"); + let req_gen = + |pat: Option| async move { api.get_current_user(pat.as_deref()).await }; let (_, success) = ScopeTest::new(&test_env) .test(req_gen, read_user) .await @@ -42,7 +43,8 @@ async fn user_scopes() { // Email reading let read_email = Scopes::USER_READ | Scopes::USER_READ_EMAIL; - let req_gen = || TestRequest::get().uri("/v3/user"); + let req_gen = + |pat: Option| async move { api.get_current_user(pat.as_deref()).await }; let (_, success) = ScopeTest::new(&test_env) .test(req_gen, read_email) .await @@ -51,7 +53,8 @@ async fn user_scopes() { // Payout reading let read_payout = Scopes::USER_READ | Scopes::PAYOUTS_READ; - let req_gen = || TestRequest::get().uri("/v3/user"); + let req_gen = + |pat: Option| async move { api.get_current_user(pat.as_deref()).await }; let (_, success) = ScopeTest::new(&test_env) .test(req_gen, read_payout) .await @@ -61,16 +64,21 @@ async fn user_scopes() { // User writing // We use the Admin PAT for this test, on the 'user' user let write_user = Scopes::USER_WRITE; - let req_gen = || { - TestRequest::patch().uri("/v3/user/user").set_json(json!( { - // Do not include 'username', as to not change the rest of the tests - "name": "NewName", - "bio": "New bio", - "location": "New location", - "role": "admin", - "badges": 5, - // Do not include payout info, different scope - })) + let req_gen = |pat: Option| async move { + api.edit_user( + "user", + json!( { + // Do not include 'username', as to not change the rest of the tests + "name": "NewName", + "bio": "New bio", + "location": "New location", + "role": "admin", + "badges": 5, + // Do not include payout info, different scope + }), + pat.as_deref(), + ) + .await }; ScopeTest::new(&test_env) .with_user_id(ADMIN_USER_ID_PARSED) @@ -81,7 +89,8 @@ async fn user_scopes() { // User deletion // (The failure is first, and this is the last test for this test function, we can delete it and use the same PAT for both tests) let delete_user = Scopes::USER_DELETE; - let req_gen = || TestRequest::delete().uri("/v3/user/enemy"); + let req_gen = + |pat: Option| async move { api.delete_user("enemy", pat.as_deref()).await }; ScopeTest::new(&test_env) .with_user_id(ENEMY_USER_ID_PARSED) .test(req_gen, delete_user) @@ -95,13 +104,8 @@ async fn user_scopes() { #[actix_rt::test] pub async fn notifications_scopes() { with_test_environment_all(None, |test_env| async move { - let alpha_team_id = &test_env - .dummy - .as_ref() - .unwrap() - .project_alpha - .team_id - .clone(); + let api = &test_env.api; + let alpha_team_id = &test_env.dummy.project_alpha.team_id; // We will invite user 'friend' to project team, and use that as a notification // Get notifications @@ -113,8 +117,10 @@ pub async fn notifications_scopes() { // Notification get let read_notifications = Scopes::NOTIFICATION_READ; - let req_gen = - || test::TestRequest::get().uri(&format!("/v3/user/{FRIEND_USER_ID}/notifications")); + let req_gen = |pat: Option| async move { + api.get_user_notifications(FRIEND_USER_ID, pat.as_deref()) + .await + }; let (_, success) = ScopeTest::new(&test_env) .with_user_id(FRIEND_USER_ID_PARSED) .test(req_gen, read_notifications) @@ -122,11 +128,9 @@ pub async fn notifications_scopes() { .unwrap(); let notification_id = success[0]["id"].as_str().unwrap(); - let req_gen = || { - test::TestRequest::get().uri(&format!( - "/v3/notifications?ids=[{uri}]", - uri = urlencoding::encode(&format!("\"{notification_id}\"")) - )) + let req_gen = |pat: Option| async move { + api.get_notifications(&[notification_id], pat.as_deref()) + .await }; ScopeTest::new(&test_env) .with_user_id(FRIEND_USER_ID_PARSED) @@ -134,8 +138,9 @@ pub async fn notifications_scopes() { .await .unwrap(); - let req_gen = - || test::TestRequest::get().uri(&format!("/v3/notification/{notification_id}")); + let req_gen = |pat: Option| async move { + api.get_notification(notification_id, pat.as_deref()).await + }; ScopeTest::new(&test_env) .with_user_id(FRIEND_USER_ID_PARSED) .test(req_gen, read_notifications) @@ -144,11 +149,9 @@ pub async fn notifications_scopes() { // Notification mark as read let write_notifications = Scopes::NOTIFICATION_WRITE; - let req_gen = || { - test::TestRequest::patch().uri(&format!( - "/v3/notifications?ids=[{uri}]", - uri = urlencoding::encode(&format!("\"{notification_id}\"")) - )) + let req_gen = |pat: Option| async move { + api.mark_notifications_read(&[notification_id], pat.as_deref()) + .await }; ScopeTest::new(&test_env) .with_user_id(FRIEND_USER_ID_PARSED) @@ -156,8 +159,10 @@ pub async fn notifications_scopes() { .await .unwrap(); - let req_gen = - || test::TestRequest::patch().uri(&format!("/v3/notification/{notification_id}")); + let req_gen = |pat: Option| async move { + api.mark_notification_read(notification_id, pat.as_deref()) + .await + }; ScopeTest::new(&test_env) .with_user_id(FRIEND_USER_ID_PARSED) .test(req_gen, write_notifications) @@ -165,8 +170,10 @@ pub async fn notifications_scopes() { .unwrap(); // Notification delete - let req_gen = - || test::TestRequest::delete().uri(&format!("/v3/notification/{notification_id}")); + let req_gen = |pat: Option| async move { + api.delete_notification(notification_id, pat.as_deref()) + .await + }; ScopeTest::new(&test_env) .with_user_id(FRIEND_USER_ID_PARSED) .test(req_gen, write_notifications) @@ -181,8 +188,10 @@ pub async fn notifications_scopes() { .await; assert_eq!(resp.status(), 204); let read_notifications = Scopes::NOTIFICATION_READ; - let req_gen = - || test::TestRequest::get().uri(&format!("/v3/user/{MOD_USER_ID}/notifications")); + let req_gen = |pat: Option| async move { + api.get_user_notifications(MOD_USER_ID, pat.as_deref()) + .await + }; let (_, success) = ScopeTest::new(&test_env) .with_user_id(MOD_USER_ID_PARSED) .test(req_gen, read_notifications) @@ -190,11 +199,9 @@ pub async fn notifications_scopes() { .unwrap(); let notification_id = success[0]["id"].as_str().unwrap(); - let req_gen = || { - test::TestRequest::delete().uri(&format!( - "/v3/notifications?ids=[{uri}]", - uri = urlencoding::encode(&format!("\"{notification_id}\"")) - )) + let req_gen = |pat: Option| async move { + api.delete_notifications(&[notification_id], pat.as_deref()) + .await }; ScopeTest::new(&test_env) .with_user_id(MOD_USER_ID_PARSED) @@ -210,16 +217,14 @@ pub async fn notifications_scopes() { pub async fn project_version_create_scopes_v3() { with_test_environment(None, |test_env: TestEnvironment| async move { // TODO: If possible, find a way to use generic api functions with the Permissions/Scopes test, then this can be recombined with the V2 version of this test - // let api = &test_env.api; + let api = &test_env.api; // Create project let create_project = Scopes::PROJECT_CREATE; - let req_gen = || { + let req_gen = |pat: Option| async move { let creation_data = get_public_project_creation_data("demo", Some(TestFile::BasicMod), None); - test::TestRequest::post() - .uri("/v3/project") - .set_multipart(creation_data.segment_data) + api.create_project(creation_data, pat.as_deref()).await }; let (_, success) = ScopeTest::new(&test_env) .test(req_gen, create_project) @@ -230,18 +235,16 @@ pub async fn project_version_create_scopes_v3() { // Add version to project let create_version = Scopes::VERSION_CREATE; - let req_gen = || { - let creation_data = get_public_version_creation_data( + let req_gen = |pat: Option| async move { + api.add_public_version( project_id, "1.2.3.4", TestFile::BasicModDifferent, None, None, - ); - - test::TestRequest::post() - .uri("/v3/version") - .set_multipart(creation_data.segment_data) + pat.as_deref(), + ) + .await }; ScopeTest::new(&test_env) .test(req_gen, create_version) @@ -255,58 +258,43 @@ pub async fn project_version_create_scopes_v3() { #[actix_rt::test] pub async fn project_version_reads_scopes() { with_test_environment_all(None, |test_env| async move { - let beta_project_id = &test_env - .dummy - .as_ref() - .unwrap() - .project_beta - .project_id - .clone(); - let beta_version_id = &test_env - .dummy - .as_ref() - .unwrap() - .project_beta - .version_id - .clone(); - let alpha_team_id = &test_env - .dummy - .as_ref() - .unwrap() - .project_alpha - .team_id - .clone(); - let beta_file_hash = &test_env - .dummy - .as_ref() - .unwrap() - .project_beta - .file_hash - .clone(); + let api = &test_env.api; + let DummyProjectAlpha { + team_id: alpha_team_id, + .. + } = &test_env.dummy.project_alpha; + let DummyProjectBeta { + project_id: beta_project_id, + version_id: beta_version_id, + file_hash: beta_file_hash, + .. + } = &test_env.dummy.project_beta; // Project reading // Uses 404 as the expected failure code (or 200 and an empty list for mass reads) let read_project = Scopes::PROJECT_READ; - let req_gen = || test::TestRequest::get().uri(&format!("/v3/project/{beta_project_id}")); + let req_gen = |pat: Option| async move { + api.get_project(beta_project_id, pat.as_deref()).await + }; ScopeTest::new(&test_env) .with_failure_code(404) .test(req_gen, read_project) .await .unwrap(); - let req_gen = - || test::TestRequest::get().uri(&format!("/v3/project/{beta_project_id}/dependencies")); + let req_gen = |pat: Option| async move { + api.get_project_dependencies(beta_project_id, pat.as_deref()) + .await + }; ScopeTest::new(&test_env) .with_failure_code(404) .test(req_gen, read_project) .await .unwrap(); - let req_gen = || { - test::TestRequest::get().uri(&format!( - "/v3/projects?ids=[{uri}]", - uri = urlencoding::encode(&format!("\"{beta_project_id}\"")) - )) + let req_gen = |pat: Option| async move { + api.get_projects(&[beta_project_id.as_str()], pat.as_deref()) + .await }; let (failure, success) = ScopeTest::new(&test_env) .with_failure_code(200) @@ -317,8 +305,10 @@ pub async fn project_version_reads_scopes() { assert!(!success.as_array().unwrap().is_empty()); // Team project reading - let req_gen = - || test::TestRequest::get().uri(&format!("/v3/project/{beta_project_id}/members")); + let req_gen = |pat: Option| async move { + api.get_project_members(beta_project_id, pat.as_deref()) + .await + }; ScopeTest::new(&test_env) .with_failure_code(404) .test(req_gen, read_project) @@ -328,7 +318,9 @@ pub async fn project_version_reads_scopes() { // Get team members // In this case, as these are public endpoints, logging in only is relevant to showing permissions // So for our test project (with 1 user, 'user') we will check the permissions before and after having the scope. - let req_gen = || test::TestRequest::get().uri(&format!("/v3/team/{alpha_team_id}/members")); + let req_gen = |pat: Option| async move { + api.get_team_members(alpha_team_id, pat.as_deref()).await + }; let (failure, success) = ScopeTest::new(&test_env) .with_failure_code(200) .test(req_gen, read_project) @@ -337,11 +329,9 @@ pub async fn project_version_reads_scopes() { assert!(!failure[0]["permissions"].is_number()); assert!(success[0]["permissions"].is_number()); - let req_gen = || { - test::TestRequest::get().uri(&format!( - "/v3/teams?ids=[{uri}]", - uri = urlencoding::encode(&format!("\"{alpha_team_id}\"")) - )) + let req_gen = |pat: Option| async move { + api.get_teams_members(&[alpha_team_id.as_str()], pat.as_deref()) + .await }; let (failure, success) = ScopeTest::new(&test_env) .with_failure_code(200) @@ -353,7 +343,9 @@ pub async fn project_version_reads_scopes() { // User project reading // Test user has two projects, one public and one private - let req_gen = || test::TestRequest::get().uri(&format!("/v3/user/{USER_USER_ID}/projects")); + let req_gen = |pat: Option| async move { + api.get_user_projects(USER_USER_ID, pat.as_deref()).await + }; let (failure, success) = ScopeTest::new(&test_env) .with_failure_code(200) .test(req_gen, read_project) @@ -371,10 +363,14 @@ pub async fn project_version_reads_scopes() { .any(|x| x["status"] == "processing")); // Project metadata reading - let req_gen = || { - test::TestRequest::get().uri(&format!( - "/maven/maven/modrinth/{beta_project_id}/maven-metadata.xml" - )) + let req_gen = |pat: Option| async move { + let req = test::TestRequest::get() + .uri(&format!( + "/maven/maven/modrinth/{beta_project_id}/maven-metadata.xml" + )) + .append_pat(pat.as_deref()) + .to_request(); + api.call(req).await }; ScopeTest::new(&test_env) .with_failure_code(404) @@ -385,26 +381,26 @@ pub async fn project_version_reads_scopes() { // Version reading // First, set version to hidden (which is when the scope is required to read it) let read_version = Scopes::VERSION_READ; - let req = test::TestRequest::patch() - .uri(&format!("/v3/version/{beta_version_id}")) - .append_pat(USER_USER_PAT) - .set_json(json!({ - "status": "draft" - })) - .to_request(); - let resp = test_env.call(req).await; + let resp = test_env + .api + .edit_version(beta_version_id, json!({ "status": "draft" }), USER_USER_PAT) + .await; assert_eq!(resp.status(), 204); - let req_gen = - || test::TestRequest::get().uri(&format!("/v3/version_file/{beta_file_hash}")); + let req_gen = |pat: Option| async move { + api.get_version_from_hash(beta_file_hash, "sha1", pat.as_deref()) + .await + }; ScopeTest::new(&test_env) .with_failure_code(404) .test(req_gen, read_version) .await .unwrap(); - let req_gen = - || test::TestRequest::get().uri(&format!("/v3/version_file/{beta_file_hash}/download")); + let req_gen = |pat: Option| async move { + api.download_version_redirect(beta_file_hash, "sha1", pat.as_deref()) + .await + }; ScopeTest::new(&test_env) .with_failure_code(404) .test(req_gen, read_version) @@ -421,12 +417,9 @@ pub async fn project_version_reads_scopes() { // ScopeTest::new(&test_env).with_failure_code(404).test(req_gen, read_version).await.unwrap(); // TODO: Should this be /POST? Looks like /GET - let req_gen = || { - test::TestRequest::post() - .uri("/v3/version_files") - .set_json(json!({ - "hashes": [beta_file_hash] - })) + let req_gen = |pat: Option| async move { + api.get_versions_from_hashes(&[beta_file_hash], "sha1", pat.as_deref()) + .await }; let (failure, success) = ScopeTest::new(&test_env) .with_failure_code(200) @@ -468,8 +461,19 @@ pub async fn project_version_reads_scopes() { // Both project and version reading let read_project_and_version = Scopes::PROJECT_READ | Scopes::VERSION_READ; - let req_gen = - || test::TestRequest::get().uri(&format!("/v3/project/{beta_project_id}/version")); + let req_gen = |pat: Option| async move { + api.get_project_versions( + beta_project_id, + None, + None, + None, + None, + None, + None, + pat.as_deref(), + ) + .await + }; ScopeTest::new(&test_env) .with_failure_code(404) .test(req_gen, read_project_and_version) @@ -491,76 +495,39 @@ pub async fn project_version_reads_scopes() { pub async fn project_write_scopes() { // Test setup and dummy data with_test_environment_all(None, |test_env| async move { - let beta_project_id = &test_env - .dummy - .as_ref() - .unwrap() - .project_beta - .project_id - .clone(); - let alpha_team_id = &test_env - .dummy - .as_ref() - .unwrap() - .project_alpha - .team_id - .clone(); - - let test_icon = DummyImage::SmallIcon; + let api = &test_env.api; + let beta_project_id = &test_env.dummy.project_beta.project_id; + let alpha_team_id = &test_env.dummy.project_alpha.team_id; // Projects writing let write_project = Scopes::PROJECT_WRITE; - let req_gen = || { - test::TestRequest::patch() - .uri(&format!("/v3/project/{beta_project_id}")) - .set_json(json!( + let req_gen = |pat: Option| async move { + api.edit_project( + beta_project_id, + json!( { "name": "test_project_version_write_scopes Title", } - )) + ), + pat.as_deref(), + ) + .await }; ScopeTest::new(&test_env) .test(req_gen, write_project) .await .unwrap(); - let req_gen = || { - test::TestRequest::patch() - .uri(&format!( - "/v3/projects?ids=[{uri}]", - uri = urlencoding::encode(&format!("\"{beta_project_id}\"")) - )) - .set_json(json!( - { - "description": "test_project_version_write_scopes Description", - } - )) - }; - ScopeTest::new(&test_env) - .test(req_gen, write_project) + let req_gen = |pat: Option| async move { + api.edit_project_bulk( + &[beta_project_id.as_str()], + json!( + { + "description": "test_project_version_write_scopes Description" + }), + pat.as_deref(), + ) .await - .unwrap(); - - // Approve beta as private so we can schedule it - let req = test::TestRequest::patch() - .uri(&format!("/v3/project/{beta_project_id}")) - .append_pat(MOD_USER_PAT) - .set_json(json!({ - "status": "private" - })) - .to_request(); - let resp = test_env.call(req).await; - assert_eq!(resp.status(), 204); - - let req_gen = || { - test::TestRequest::post() - .uri(&format!("/v3/project/{beta_project_id}/schedule")) // beta_project_id is an unpublished can schedule it - .set_json(json!( - { - "requested_status": "private", - "time": Utc::now() + Duration::days(1), - } - )) }; ScopeTest::new(&test_env) .test(req_gen, write_project) @@ -568,33 +535,39 @@ pub async fn project_write_scopes() { .unwrap(); // Icons and gallery images - let req_gen = || { - test::TestRequest::patch() - .uri(&format!( - "/v3/project/{beta_project_id}/icon?ext={ext}", - ext = test_icon.extension() - )) - .set_payload(test_icon.bytes()) + let req_gen = |pat: Option| async move { + api.edit_project_icon( + beta_project_id, + Some(DummyImage::SmallIcon.get_icon_data()), + pat.as_deref(), + ) + .await }; ScopeTest::new(&test_env) .test(req_gen, write_project) .await .unwrap(); - let req_gen = - || test::TestRequest::delete().uri(&format!("/v3/project/{beta_project_id}/icon")); + let req_gen = |pat: Option| async move { + api.edit_project_icon(beta_project_id, None, pat.as_deref()) + .await + }; ScopeTest::new(&test_env) .test(req_gen, write_project) .await .unwrap(); - let req_gen = || { - test::TestRequest::post() - .uri(&format!( - "/v3/project/{beta_project_id}/gallery?ext={ext}&featured=true", - ext = test_icon.extension() - )) - .set_payload(test_icon.bytes()) + let req_gen = |pat: Option| async move { + api.add_gallery_item( + beta_project_id, + DummyImage::SmallIcon.get_icon_data(), + true, + None, + None, + None, + pat.as_deref(), + ) + .await }; ScopeTest::new(&test_env) .test(req_gen, write_project) @@ -602,28 +575,22 @@ pub async fn project_write_scopes() { .unwrap(); // Get project, as we need the gallery image url - let req_gen = test::TestRequest::get() - .uri(&format!("/v3/project/{beta_project_id}")) - .append_pat(USER_USER_PAT) - .to_request(); - let resp = test_env.call(req_gen).await; + let resp = api.get_project(beta_project_id, USER_USER_PAT).await; let project: serde_json::Value = test::read_body_json(resp).await; let gallery_url = project["gallery"][0]["url"].as_str().unwrap(); - let req_gen = || { - test::TestRequest::patch().uri(&format!( - "/v3/project/{beta_project_id}/gallery?url={gallery_url}" - )) + let req_gen = |pat: Option| async move { + api.edit_gallery_item(beta_project_id, gallery_url, HashMap::new(), pat.as_deref()) + .await }; ScopeTest::new(&test_env) .test(req_gen, write_project) .await .unwrap(); - let req_gen = || { - test::TestRequest::delete().uri(&format!( - "/v3/project/{beta_project_id}/gallery?url={gallery_url}" - )) + let req_gen = |pat: Option| async move { + api.remove_gallery_item(beta_project_id, gallery_url, pat.as_deref()) + .await }; ScopeTest::new(&test_env) .test(req_gen, write_project) @@ -631,12 +598,9 @@ pub async fn project_write_scopes() { .unwrap(); // Team scopes - add user 'friend' - let req_gen = || { - test::TestRequest::post() - .uri(&format!("/v3/team/{alpha_team_id}/members")) - .set_json(json!({ - "user_id": FRIEND_USER_ID - })) + let req_gen = |pat: Option| async move { + api.add_user_to_team(alpha_team_id, FRIEND_USER_ID, None, None, pat.as_deref()) + .await }; ScopeTest::new(&test_env) .test(req_gen, write_project) @@ -644,7 +608,8 @@ pub async fn project_write_scopes() { .unwrap(); // Accept team invite as 'friend' - let req_gen = || test::TestRequest::post().uri(&format!("/v3/team/{alpha_team_id}/join")); + let req_gen = + |pat: Option| async move { api.join_team(alpha_team_id, pat.as_deref()).await }; ScopeTest::new(&test_env) .with_user_id(FRIEND_USER_ID_PARSED) .test(req_gen, write_project) @@ -652,14 +617,16 @@ pub async fn project_write_scopes() { .unwrap(); // Patch 'friend' user - let req_gen = || { - test::TestRequest::patch() - .uri(&format!( - "/v3/team/{alpha_team_id}/members/{FRIEND_USER_ID}" - )) - .set_json(json!({ + let req_gen = |pat: Option| async move { + api.edit_team_member( + alpha_team_id, + FRIEND_USER_ID, + json!({ "permissions": 1 - })) + }), + pat.as_deref(), + ) + .await }; ScopeTest::new(&test_env) .test(req_gen, write_project) @@ -667,12 +634,9 @@ pub async fn project_write_scopes() { .unwrap(); // Transfer ownership to 'friend' - let req_gen = || { - test::TestRequest::patch() - .uri(&format!("/v3/team/{alpha_team_id}/owner")) - .set_json(json!({ - "user_id": FRIEND_USER_ID - })) + let req_gen = |pat: Option| async move { + api.transfer_team_ownership(alpha_team_id, FRIEND_USER_ID, pat.as_deref()) + .await }; ScopeTest::new(&test_env) .test(req_gen, write_project) @@ -680,9 +644,9 @@ pub async fn project_write_scopes() { .unwrap(); // Now as 'friend', delete 'user' - let req_gen = || { - test::TestRequest::delete() - .uri(&format!("/v3/team/{alpha_team_id}/members/{USER_USER_ID}")) + let req_gen = |pat: Option| async move { + api.remove_from_team(alpha_team_id, USER_USER_ID, pat.as_deref()) + .await }; ScopeTest::new(&test_env) .with_user_id(FRIEND_USER_ID_PARSED) @@ -709,105 +673,37 @@ pub async fn project_write_scopes() { pub async fn version_write_scopes() { // Test setup and dummy data with_test_environment_all(None, |test_env| async move { - let alpha_version_id = &test_env - .dummy - .as_ref() - .unwrap() - .project_alpha - .version_id - .clone(); - let beta_version_id = &test_env - .dummy - .as_ref() - .unwrap() - .project_beta - .version_id - .clone(); - let alpha_file_hash = &test_env - .dummy - .as_ref() - .unwrap() - .project_alpha - .file_hash - .clone(); - - let basic_zip = TestFile::BasicZip; + let api = &test_env.api; + let DummyProjectAlpha { + version_id: alpha_version_id, + file_hash: alpha_file_hash, + .. + } = &test_env.dummy.project_alpha; let write_version = Scopes::VERSION_WRITE; - // Approve beta version as private so we can schedule it - let req = test::TestRequest::patch() - .uri(&format!("/v3/version/{beta_version_id}")) - .append_pat(MOD_USER_PAT) - .set_json(json!({ - "status": "unlisted" - })) - .to_request(); - let resp = test_env.call(req).await; - assert_eq!(resp.status(), 204); - - // Schedule version - let req_gen = || { - test::TestRequest::post() - .uri(&format!("/v3/version/{beta_version_id}/schedule")) // beta_version_id is an *approved* version, so we can schedule it - .set_json(json!( - { - "requested_status": "archived", - "time": Utc::now() + Duration::days(1), - } - )) - }; - ScopeTest::new(&test_env) - .test(req_gen, write_version) - .await - .unwrap(); - // Patch version - let req_gen = || { - test::TestRequest::patch() - .uri(&format!("/v3/version/{alpha_version_id}")) - .set_json(json!( + let req_gen = |pat: Option| async move { + api.edit_version( + alpha_version_id, + json!( { "name": "test_version_write_scopes Title", } - )) + ), + pat.as_deref(), + ) + .await }; ScopeTest::new(&test_env) .test(req_gen, write_version) .await .unwrap(); - // Generate test project data. - // Basic json - let json_segment = MultipartSegment { - name: "data".to_string(), - filename: None, - content_type: Some("application/json".to_string()), - data: MultipartSegmentData::Text( - serde_json::to_string(&json!( - { - "file_types": { - basic_zip.filename(): "required-resource-pack" - }, - } - )) - .unwrap(), - ), - }; - - // Differently named file, with different content - let content_segment = MultipartSegment { - name: basic_zip.filename(), - filename: Some(basic_zip.filename()), - content_type: basic_zip.content_type(), - data: MultipartSegmentData::Binary(basic_zip.bytes()), - }; - // Upload version file - let req_gen = || { - test::TestRequest::post() - .uri(&format!("/v3/version/{alpha_version_id}/file")) - .set_multipart(vec![json_segment.clone(), content_segment.clone()]) + let req_gen = |pat: Option| async move { + api.upload_file_to_version(alpha_version_id, &TestFile::BasicZip, pat.as_deref()) + .await }; ScopeTest::new(&test_env) .test(req_gen, write_version) @@ -816,8 +712,9 @@ pub async fn version_write_scopes() { // Delete version file // TODO: Should this scope be VERSION_DELETE? - let req_gen = || { - test::TestRequest::delete().uri(&format!("/v3/version_file/{alpha_file_hash}")) + let req_gen = |pat: Option| async move { + api.remove_version_file(alpha_file_hash, pat.as_deref()) + .await // Delete from alpha_version_id, as we uploaded to alpha_version_id and it needs another file }; ScopeTest::new(&test_env) @@ -827,8 +724,9 @@ pub async fn version_write_scopes() { // Delete version let delete_version = Scopes::VERSION_DELETE; - let req_gen = - || test::TestRequest::delete().uri(&format!("/v3/version/{alpha_version_id}")); + let req_gen = |pat: Option| async move { + api.remove_version(alpha_version_id, pat.as_deref()).await + }; ScopeTest::new(&test_env) .test(req_gen, delete_version) .await @@ -842,23 +740,20 @@ pub async fn version_write_scopes() { pub async fn report_scopes() { // Test setup and dummy data with_test_environment_all(None, |test_env| async move { - let beta_project_id = &test_env - .dummy - .as_ref() - .unwrap() - .project_beta - .project_id - .clone(); + let api = &test_env.api; + let beta_project_id = &test_env.dummy.project_beta.project_id; // Create report let report_create = Scopes::REPORT_CREATE; - let req_gen = || { - test::TestRequest::post().uri("/v3/report").set_json(json!({ - "report_type": "copyright", - "item_id": beta_project_id, - "item_type": "project", - "body": "This is a reupload of my mod, ", - })) + let req_gen = |pat: Option| async move { + api.create_report( + "copyright", + beta_project_id, + CommonItemType::Project, + "This is a reupload of my mod", + pat.as_deref(), + ) + .await }; ScopeTest::new(&test_env) .test(req_gen, report_create) @@ -867,24 +762,23 @@ pub async fn report_scopes() { // Get reports let report_read = Scopes::REPORT_READ; - let req_gen = || test::TestRequest::get().uri("/v3/report"); + let req_gen = + |pat: Option| async move { api.get_user_reports(pat.as_deref()).await }; let (_, success) = ScopeTest::new(&test_env) .test(req_gen, report_read) .await .unwrap(); let report_id = success[0]["id"].as_str().unwrap(); - let req_gen = || test::TestRequest::get().uri(&format!("/v3/report/{}", report_id)); + let req_gen = + |pat: Option| async move { api.get_report(report_id, pat.as_deref()).await }; ScopeTest::new(&test_env) .test(req_gen, report_read) .await .unwrap(); - let req_gen = || { - test::TestRequest::get().uri(&format!( - "/v3/reports?ids=[{}]", - urlencoding::encode(&format!("\"{}\"", report_id)) - )) + let req_gen = |pat: Option| async move { + api.get_reports(&[report_id], pat.as_deref()).await }; ScopeTest::new(&test_env) .test(req_gen, report_read) @@ -893,12 +787,13 @@ pub async fn report_scopes() { // Edit report let report_edit = Scopes::REPORT_WRITE; - let req_gen = || { - test::TestRequest::patch() - .uri(&format!("/v3/report/{}", report_id)) - .set_json(json!({ - "body": "This is a reupload of my mod, G8!", - })) + let req_gen = |pat: Option| async move { + api.edit_report( + report_id, + json!({ "body": "This is a reupload of my mod, G8!" }), + pat.as_deref(), + ) + .await }; ScopeTest::new(&test_env) .test(req_gen, report_edit) @@ -908,7 +803,8 @@ pub async fn report_scopes() { // Delete report // We use a moderator PAT here, as only moderators can delete reports let report_delete = Scopes::REPORT_DELETE; - let req_gen = || test::TestRequest::delete().uri(&format!("/v3/report/{}", report_id)); + let req_gen = + |pat: Option| async move { api.delete_report(report_id, pat.as_deref()).await }; ScopeTest::new(&test_env) .with_user_id(MOD_USER_ID_PARSED) .test(req_gen, report_delete) @@ -923,34 +819,23 @@ pub async fn report_scopes() { pub async fn thread_scopes() { // Test setup and dummy data with_test_environment_all(None, |test_env| async move { - let alpha_thread_id = &test_env - .dummy - .as_ref() - .unwrap() - .project_alpha - .thread_id - .clone(); - let beta_thread_id = &test_env - .dummy - .as_ref() - .unwrap() - .project_beta - .thread_id - .clone(); + let api = &test_env.api; + let alpha_thread_id = &test_env.dummy.project_alpha.thread_id; + let beta_thread_id = &test_env.dummy.project_beta.thread_id; // Thread read let thread_read = Scopes::THREAD_READ; - let req_gen = || test::TestRequest::get().uri(&format!("/v3/thread/{alpha_thread_id}")); + let req_gen = |pat: Option| async move { + api.get_thread(alpha_thread_id, pat.as_deref()).await + }; ScopeTest::new(&test_env) .test(req_gen, thread_read) .await .unwrap(); - let req_gen = || { - test::TestRequest::get().uri(&format!( - "/v3/threads?ids=[{}]", - urlencoding::encode(&format!("\"{}\"", "U")) - )) + let req_gen = |pat: Option| async move { + api.get_threads(&[alpha_thread_id.as_str()], pat.as_deref()) + .await }; ScopeTest::new(&test_env) .test(req_gen, thread_read) @@ -959,15 +844,14 @@ pub async fn thread_scopes() { // Thread write (to also push to moderator inbox) let thread_write = Scopes::THREAD_WRITE; - let req_gen = || { - test::TestRequest::post() - .uri(&format!("/v3/thread/{beta_thread_id}")) - .set_json(json!({ - "body": { - "type": "text", - "body": "test_thread_scopes Body" - } - })) + let req_gen = |pat: Option| async move { + api.write_to_thread( + beta_thread_id, + "text", + "test_thread_scopes Body", + pat.as_deref(), + ) + .await }; ScopeTest::new(&test_env) .with_user_id(USER_USER_ID_PARSED) @@ -977,7 +861,8 @@ pub async fn thread_scopes() { // Check moderation inbox // Uses moderator PAT, as only moderators can see the moderation inbox - let req_gen = || test::TestRequest::get().uri("/v3/thread/inbox"); + let req_gen = + |pat: Option| async move { api.get_moderation_inbox(pat.as_deref()).await }; let (_, success) = ScopeTest::new(&test_env) .with_user_id(MOD_USER_ID_PARSED) .test(req_gen, thread_read) @@ -987,7 +872,8 @@ pub async fn thread_scopes() { // Moderator 'read' thread // Uses moderator PAT, as only moderators can see the moderation inbox - let req_gen = || test::TestRequest::post().uri(&format!("/v3/thread/{thread_id}/read")); + let req_gen = + |pat: Option| async move { api.read_thread(thread_id, pat.as_deref()).await }; ScopeTest::new(&test_env) .with_user_id(MOD_USER_ID_PARSED) .test(req_gen, thread_read) @@ -996,16 +882,14 @@ pub async fn thread_scopes() { // Delete that message // First, get message id - let req_gen = test::TestRequest::get() - .uri(&format!("/v3/thread/{thread_id}")) - .append_pat(USER_USER_PAT) - .to_request(); - let resp = test_env.call(req_gen).await; + let resp = api.get_thread(thread_id, USER_USER_PAT).await; let success: serde_json::Value = test::read_body_json(resp).await; let thread_message_id = success["messages"][0]["id"].as_str().unwrap(); - let req_gen = - || test::TestRequest::delete().uri(&format!("/v3/message/{thread_message_id}")); + let req_gen = |pat: Option| async move { + api.delete_thread_message(thread_message_id, pat.as_deref()) + .await + }; ScopeTest::new(&test_env) .with_user_id(MOD_USER_ID_PARSED) .test(req_gen, thread_write) @@ -1019,16 +903,20 @@ pub async fn thread_scopes() { #[actix_rt::test] pub async fn pat_scopes() { with_test_environment_all(None, |test_env| async move { + let api = &test_env.api; // Pat create let pat_create = Scopes::PAT_CREATE; - let req_gen = || { - test::TestRequest::post() + let req_gen = |pat: Option| async move { + let req = test::TestRequest::post() .uri("/_internal/pat") .set_json(json!({ "scopes": 1, "name": "test_pat_scopes Name", "expires": Utc::now() + Duration::days(1), })) + .append_pat(pat.as_deref()) + .to_request(); + api.call(req).await }; let (_, success) = ScopeTest::new(&test_env) .test(req_gen, pat_create) @@ -1038,10 +926,13 @@ pub async fn pat_scopes() { // Pat write let pat_write = Scopes::PAT_WRITE; - let req_gen = || { - test::TestRequest::patch() + let req_gen = |pat: Option| async move { + let req = test::TestRequest::patch() .uri(&format!("/_internal/pat/{pat_id}")) .set_json(json!({})) + .append_pat(pat.as_deref()) + .to_request(); + api.call(req).await }; ScopeTest::new(&test_env) .test(req_gen, pat_write) @@ -1050,7 +941,13 @@ pub async fn pat_scopes() { // Pat read let pat_read = Scopes::PAT_READ; - let req_gen = || test::TestRequest::get().uri("/_internal/pat"); + let req_gen = |pat: Option| async move { + let req = test::TestRequest::get() + .uri("/_internal/pat") + .append_pat(pat.as_deref()) + .to_request(); + api.call(req).await + }; ScopeTest::new(&test_env) .test(req_gen, pat_read) .await @@ -1058,7 +955,13 @@ pub async fn pat_scopes() { // Pat delete let pat_delete = Scopes::PAT_DELETE; - let req_gen = || test::TestRequest::delete().uri(&format!("/_internal/pat/{pat_id}")); + let req_gen = |pat: Option| async move { + let req = test::TestRequest::delete() + .uri(&format!("/_internal/pat/{pat_id}")) + .append_pat(pat.as_deref()) + .to_request(); + api.call(req).await + }; ScopeTest::new(&test_env) .test(req_gen, pat_delete) .await @@ -1071,27 +974,20 @@ pub async fn pat_scopes() { #[actix_rt::test] pub async fn collections_scopes() { // Test setup and dummy data - with_test_environment_all(None, |test_env| async move { - let alpha_project_id = &test_env - .dummy - .as_ref() - .unwrap() - .project_alpha - .project_id - .clone(); - - let small_icon = DummyImage::SmallIcon; + with_test_environment(None, |test_env: TestEnvironment| async move { + let api = &test_env.api; + let alpha_project_id = &test_env.dummy.project_alpha.project_id; // Create collection let collection_create = Scopes::COLLECTION_CREATE; - let req_gen = || { - test::TestRequest::post() - .uri("/v3/collection") - .set_json(json!({ - "name": "Test Collection", - "description": "Test Collection Description", - "projects": [alpha_project_id] - })) + let req_gen = |pat: Option| async move { + api.create_collection( + "Test Collection", + "Test Collection Description", + &[alpha_project_id.as_str()], + pat.as_deref(), + ) + .await }; let (_, success) = ScopeTest::new(&test_env) .test(req_gen, collection_create) @@ -1102,13 +998,16 @@ pub async fn collections_scopes() { // Patch collection // Collections always initialize to public, so we do patch before Get testing let collection_write = Scopes::COLLECTION_WRITE; - let req_gen = || { - test::TestRequest::patch() - .uri(&format!("/v3/collection/{collection_id}")) - .set_json(json!({ + let req_gen = |pat: Option| async move { + api.edit_collection( + collection_id, + json!({ "name": "Test Collection patch", "status": "private", - })) + }), + pat.as_deref(), + ) + .await }; ScopeTest::new(&test_env) .test(req_gen, collection_write) @@ -1117,18 +1016,17 @@ pub async fn collections_scopes() { // Read collection let collection_read = Scopes::COLLECTION_READ; - let req_gen = || test::TestRequest::get().uri(&format!("/v3/collection/{}", collection_id)); + let req_gen = |pat: Option| async move { + api.get_collection(collection_id, pat.as_deref()).await + }; ScopeTest::new(&test_env) .with_failure_code(404) .test(req_gen, collection_read) .await .unwrap(); - let req_gen = || { - test::TestRequest::get().uri(&format!( - "/v3/collections?ids=[{}]", - urlencoding::encode(&format!("\"{}\"", collection_id)) - )) + let req_gen = |pat: Option| async move { + api.get_collections(&[collection_id], pat.as_deref()).await }; let (failure, success) = ScopeTest::new(&test_env) .with_failure_code(200) @@ -1138,8 +1036,9 @@ pub async fn collections_scopes() { assert_eq!(failure.as_array().unwrap().len(), 0); assert_eq!(success.as_array().unwrap().len(), 1); - let req_gen = - || test::TestRequest::get().uri(&format!("/v3/user/{USER_USER_ID}/collections")); + let req_gen = |pat: Option| async move { + api.get_user_collections(USER_USER_ID, pat.as_deref()).await + }; let (failure, success) = ScopeTest::new(&test_env) .with_failure_code(200) .test(req_gen, collection_read) @@ -1148,21 +1047,23 @@ pub async fn collections_scopes() { assert_eq!(failure.as_array().unwrap().len(), 0); assert_eq!(success.as_array().unwrap().len(), 1); - let req_gen = || { - test::TestRequest::patch() - .uri(&format!( - "/v3/collection/{collection_id}/icon?ext={ext}", - ext = small_icon.extension() - )) - .set_payload(Bytes::from(small_icon.bytes())) + let req_gen = |pat: Option| async move { + api.edit_collection_icon( + collection_id, + Some(DummyImage::SmallIcon.get_icon_data()), + pat.as_deref(), + ) + .await }; ScopeTest::new(&test_env) .test(req_gen, collection_write) .await .unwrap(); - let req_gen = - || test::TestRequest::delete().uri(&format!("/v3/collection/{collection_id}/icon")); + let req_gen = |pat: Option| async move { + api.edit_collection_icon(collection_id, None, pat.as_deref()) + .await + }; ScopeTest::new(&test_env) .test(req_gen, collection_write) .await @@ -1175,26 +1076,15 @@ pub async fn collections_scopes() { #[actix_rt::test] pub async fn organization_scopes() { // Test setup and dummy data - with_test_environment_all(None, |test_env| async move { - let beta_project_id = &test_env - .dummy - .as_ref() - .unwrap() - .project_beta - .project_id - .clone(); - - let icon = DummyImage::SmallIcon; + with_test_environment(None, |test_env: TestEnvironment| async move { + let api = &test_env.api; + let beta_project_id = &test_env.dummy.project_beta.project_id; // Create organization let organization_create = Scopes::ORGANIZATION_CREATE; - let req_gen = || { - test::TestRequest::post() - .uri("/v3/organization") - .set_json(json!({ - "name": "TestOrg", - "description": "TestOrg Description", - })) + let req_gen = |pat: Option| async move { + api.create_organization("TestOrg", "TestOrg Description", pat.as_deref()) + .await }; let (_, success) = ScopeTest::new(&test_env) .test(req_gen, organization_create) @@ -1204,33 +1094,38 @@ pub async fn organization_scopes() { // Patch organization let organization_edit = Scopes::ORGANIZATION_WRITE; - let req_gen = || { - test::TestRequest::patch() - .uri(&format!("/v3/organization/{organization_id}")) - .set_json(json!({ + let req_gen = |pat: Option| async move { + api.edit_organization( + organization_id, + json!({ "description": "TestOrg Patch Description", - })) + }), + pat.as_deref(), + ) + .await }; ScopeTest::new(&test_env) .test(req_gen, organization_edit) .await .unwrap(); - let req_gen = || { - test::TestRequest::patch() - .uri(&format!( - "/v3/organization/{organization_id}/icon?ext={ext}", - ext = icon.extension() - )) - .set_payload(Bytes::from(icon.bytes())) + let req_gen = |pat: Option| async move { + api.edit_organization_icon( + organization_id, + Some(DummyImage::SmallIcon.get_icon_data()), + pat.as_deref(), + ) + .await }; ScopeTest::new(&test_env) .test(req_gen, organization_edit) .await .unwrap(); - let req_gen = - || test::TestRequest::delete().uri(&format!("/v3/organization/{organization_id}/icon")); + let req_gen = |pat: Option| async move { + api.edit_organization_icon(organization_id, None, pat.as_deref()) + .await + }; ScopeTest::new(&test_env) .test(req_gen, organization_edit) .await @@ -1238,12 +1133,9 @@ pub async fn organization_scopes() { // add project let organization_project_edit = Scopes::PROJECT_WRITE | Scopes::ORGANIZATION_WRITE; - let req_gen = || { - test::TestRequest::post() - .uri(&format!("/v3/organization/{organization_id}/projects")) - .set_json(json!({ - "project_id": beta_project_id - })) + let req_gen = |pat: Option| async move { + api.organization_add_project(organization_id, beta_project_id, pat.as_deref()) + .await }; ScopeTest::new(&test_env) .with_failure_scopes(Scopes::all() ^ Scopes::ORGANIZATION_WRITE) @@ -1253,8 +1145,9 @@ pub async fn organization_scopes() { // Organization reads let organization_read = Scopes::ORGANIZATION_READ; - let req_gen = - || test::TestRequest::get().uri(&format!("/v3/organization/{organization_id}")); + let req_gen = |pat: Option| async move { + api.get_organization(organization_id, pat.as_deref()).await + }; let (failure, success) = ScopeTest::new(&test_env) .with_failure_code(200) .test(req_gen, organization_read) @@ -1263,11 +1156,9 @@ pub async fn organization_scopes() { assert!(failure["members"][0]["permissions"].is_null()); assert!(!success["members"][0]["permissions"].is_null()); - let req_gen = || { - test::TestRequest::get().uri(&format!( - "/v3/organizations?ids=[{}]", - urlencoding::encode(&format!("\"{}\"", organization_id)) - )) + let req_gen = |pat: Option| async move { + api.get_organizations(&[organization_id], pat.as_deref()) + .await }; let (failure, success) = ScopeTest::new(&test_env) @@ -1279,8 +1170,9 @@ pub async fn organization_scopes() { assert!(!success[0]["members"][0]["permissions"].is_null()); let organization_project_read = Scopes::PROJECT_READ | Scopes::ORGANIZATION_READ; - let req_gen = || { - test::TestRequest::get().uri(&format!("/v3/organization/{organization_id}/projects")) + let req_gen = |pat: Option| async move { + api.get_organization_projects(organization_id, pat.as_deref()) + .await }; let (failure, success) = ScopeTest::new(&test_env) .with_failure_code(200) @@ -1292,10 +1184,9 @@ pub async fn organization_scopes() { assert!(!success.as_array().unwrap().is_empty()); // remove project (now that we've checked) - let req_gen = || { - test::TestRequest::delete().uri(&format!( - "/v3/organization/{organization_id}/projects/{beta_project_id}" - )) + let req_gen = |pat: Option| async move { + api.organization_remove_project(organization_id, beta_project_id, pat.as_deref()) + .await }; ScopeTest::new(&test_env) .with_failure_scopes(Scopes::all() ^ Scopes::ORGANIZATION_WRITE) @@ -1305,8 +1196,10 @@ pub async fn organization_scopes() { // Delete organization let organization_delete = Scopes::ORGANIZATION_DELETE; - let req_gen = - || test::TestRequest::delete().uri(&format!("/v3/organization/{organization_id}")); + let req_gen = |pat: Option| async move { + api.delete_organization(organization_id, pat.as_deref()) + .await + }; ScopeTest::new(&test_env) .test(req_gen, organization_delete) .await diff --git a/tests/teams.rs b/tests/teams.rs index b25044e8..0980e18d 100644 --- a/tests/teams.rs +++ b/tests/teams.rs @@ -1,8 +1,4 @@ -use crate::common::{ - api_common::{ApiTeams, AppendsOptionalPat}, - database::*, -}; -use actix_web::test; +use crate::common::{api_common::ApiTeams, database::*}; use common::{ api_v3::ApiV3, environment::{with_test_environment, with_test_environment_all, TestEnvironment}, @@ -16,126 +12,248 @@ mod common; #[actix_rt::test] async fn test_get_team() { // Test setup and dummy data + // Perform get_team related tests for a project team + //TODO: This needs to consider organizations now as well with_test_environment_all(None, |test_env| async move { - let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; - let alpha_team_id = &test_env.dummy.as_ref().unwrap().project_alpha.team_id; - let zeta_organization_id = &test_env - .dummy - .as_ref() - .unwrap() - .organization_zeta - .organization_id; - let zeta_team_id = &test_env.dummy.as_ref().unwrap().organization_zeta.team_id; + let api = &test_env.api; + let alpha_project_id = &test_env.dummy.project_alpha.project_id; + let alpha_team_id = &test_env.dummy.project_alpha.team_id; - // Perform tests for an organization team and a project team - for (team_association_id, team_association, team_id) in [ - (alpha_project_id, "project", alpha_team_id), - (zeta_organization_id, "organization", zeta_team_id), - ] { - // A non-member of the team should get basic info but not be able to see private data - for uri in [ - format!("/v3/team/{team_id}/members"), - format!("/v3/{team_association}/{team_association_id}/members"), - ] { - let req = test::TestRequest::get() - .uri(&uri) - .append_pat(FRIEND_USER_PAT) - .to_request(); + // A non-member of the team should get basic info but not be able to see private data + let members = api + .get_team_members_deserialized_common(alpha_team_id, FRIEND_USER_PAT) + .await; + assert_eq!(members.len(), 1); + assert_eq!(members[0].user.id.0, USER_USER_ID_PARSED as u64); + assert!(members[0].permissions.is_none()); - let resp = test_env.call(req).await; - assert_eq!(resp.status(), 200); - let value: serde_json::Value = test::read_body_json(resp).await; - assert_eq!(value[0]["user"]["id"], USER_USER_ID); - assert!(value[0]["permissions"].is_null()); - } + let members = api + .get_project_members_deserialized_common(alpha_project_id, FRIEND_USER_PAT) + .await; + assert_eq!(members.len(), 1); + assert_eq!(members[0].user.id.0, USER_USER_ID_PARSED as u64); - // A non-accepted member of the team should: - // - not be able to see private data about the team, but see all members including themselves - // - should not appear in the team members list to enemy users - let req = test::TestRequest::post() - .uri(&format!("/v3/team/{team_id}/members")) - .append_pat(USER_USER_PAT) - .set_json(&json!({ - "user_id": FRIEND_USER_ID, - })) - .to_request(); - let resp = test_env.call(req).await; - assert_eq!(resp.status(), 204); + // A non-accepted member of the team should: + // - not be able to see private data about the team, but see all members including themselves + // - should not appear in the team members list to enemy users + let resp = api + .add_user_to_team(alpha_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT) + .await; + assert_eq!(resp.status(), 204); - for uri in [ - format!("/v3/team/{team_id}/members"), - format!("/v3/{team_association}/{team_association_id}/members"), - ] { - let req = test::TestRequest::get() - .uri(&uri) - .append_pat(FRIEND_USER_PAT) - .to_request(); - let resp = test_env.call(req).await; - assert_eq!(resp.status(), 200); - let value: serde_json::Value = test::read_body_json(resp).await; - let members = value.as_array().unwrap(); - assert!(members.len() == 2); // USER_USER_ID and FRIEND_USER_ID should be in the team - let user_user = members - .iter() - .find(|x| x["user"]["id"] == USER_USER_ID) - .unwrap(); - let friend_user = members - .iter() - .find(|x| x["user"]["id"] == FRIEND_USER_ID) - .unwrap(); - assert_eq!(user_user["user"]["id"], USER_USER_ID); - assert!(user_user["permissions"].is_null()); // Should not see private data of the team - assert_eq!(friend_user["user"]["id"], FRIEND_USER_ID); - assert!(friend_user["permissions"].is_null()); + // Team check directly + let members = api + .get_team_members_deserialized_common(alpha_team_id, FRIEND_USER_PAT) + .await; + assert!(members.len() == 2); // USER_USER_ID and FRIEND_USER_ID should be in the team + let user_user = members + .iter() + .find(|x| x.user.id.0 == USER_USER_ID_PARSED as u64) + .unwrap(); + let friend_user = members + .iter() + .find(|x| x.user.id.0 == FRIEND_USER_ID_PARSED as u64) + .unwrap(); + assert_eq!(user_user.user.id.0, USER_USER_ID_PARSED as u64); + assert!(user_user.permissions.is_none()); // Should not see private data of the team + assert_eq!(friend_user.user.id.0, FRIEND_USER_ID_PARSED as u64); + assert!(friend_user.permissions.is_none()); - let req = test::TestRequest::get() - .uri(&uri) - .append_pat(ENEMY_USER_PAT) - .to_request(); - let resp = test_env.call(req).await; - assert_eq!(resp.status(), 200); - let value: serde_json::Value = test::read_body_json(resp).await; - let members = value.as_array().unwrap(); - assert_eq!(members.len(), 1); // Only USER_USER_ID should be in the team - assert_eq!(members[0]["user"]["id"], USER_USER_ID); - assert!(members[0]["permissions"].is_null()); - } - // An accepted member of the team should appear in the team members list - // and should be able to see private data about the team - let req = test::TestRequest::post() - .uri(&format!("/v3/team/{team_id}/join")) - .append_pat(FRIEND_USER_PAT) - .to_request(); - let resp = test_env.call(req).await; - assert_eq!(resp.status(), 204); + // team check via association + let members = api + .get_project_members_deserialized_common(alpha_project_id, FRIEND_USER_PAT) + .await; + assert!(members.len() == 2); // USER_USER_ID and FRIEND_USER_ID should be in the team + let user_user = members + .iter() + .find(|x| x.user.id.0 == USER_USER_ID_PARSED as u64) + .unwrap(); + let friend_user = members + .iter() + .find(|x| x.user.id.0 == FRIEND_USER_ID_PARSED as u64) + .unwrap(); + assert_eq!(user_user.user.id.0, USER_USER_ID_PARSED as u64); + assert!(user_user.permissions.is_none()); // Should not see private data of the team + assert_eq!(friend_user.user.id.0, FRIEND_USER_ID_PARSED as u64); + assert!(friend_user.permissions.is_none()); - for uri in [ - format!("/v3/team/{team_id}/members"), - format!("/v3/{team_association}/{team_association_id}/members"), - ] { - let req = test::TestRequest::get() - .uri(&uri) - .append_pat(FRIEND_USER_PAT) - .to_request(); - let resp = test_env.call(req).await; - assert_eq!(resp.status(), 200); - let value: serde_json::Value = test::read_body_json(resp).await; - let members = value.as_array().unwrap(); - assert!(members.len() == 2); // USER_USER_ID and FRIEND_USER_ID should be in the team - let user_user = members - .iter() - .find(|x| x["user"]["id"] == USER_USER_ID) - .unwrap(); - let friend_user = members - .iter() - .find(|x| x["user"]["id"] == FRIEND_USER_ID) - .unwrap(); - assert_eq!(user_user["user"]["id"], USER_USER_ID); - assert!(!user_user["permissions"].is_null()); // SHOULD see private data of the team - assert_eq!(friend_user["user"]["id"], FRIEND_USER_ID); - assert!(!friend_user["permissions"].is_null()); - } - } + // enemy team check directly + let members = api + .get_team_members_deserialized_common(alpha_team_id, ENEMY_USER_PAT) + .await; + assert_eq!(members.len(), 1); // Only USER_USER_ID should be in the team + + // enemy team check via association + let members = api + .get_project_members_deserialized_common(alpha_project_id, ENEMY_USER_PAT) + .await; + assert_eq!(members.len(), 1); // Only USER_USER_ID should be in the team + + // An accepted member of the team should appear in the team members list + // and should be able to see private data about the team + let resp = api.join_team(alpha_team_id, FRIEND_USER_PAT).await; + assert_eq!(resp.status(), 204); + + // Team check directly + let members = api + .get_team_members_deserialized_common(alpha_team_id, FRIEND_USER_PAT) + .await; + assert!(members.len() == 2); // USER_USER_ID and FRIEND_USER_ID should be in the team + let user_user = members + .iter() + .find(|x| x.user.id.0 == USER_USER_ID_PARSED as u64) + .unwrap(); + let friend_user = members + .iter() + .find(|x| x.user.id.0 == FRIEND_USER_ID_PARSED as u64) + .unwrap(); + assert_eq!(user_user.user.id.0, USER_USER_ID_PARSED as u64); + assert!(user_user.permissions.is_some()); // SHOULD see private data of the team + assert_eq!(friend_user.user.id.0, FRIEND_USER_ID_PARSED as u64); + assert!(friend_user.permissions.is_some()); + + // team check via association + let members = api + .get_project_members_deserialized_common(alpha_project_id, FRIEND_USER_PAT) + .await; + assert!(members.len() == 2); // USER_USER_ID and FRIEND_USER_ID should be in the team + let user_user = members + .iter() + .find(|x| x.user.id.0 == USER_USER_ID_PARSED as u64) + .unwrap(); + let friend_user = members + .iter() + .find(|x| x.user.id.0 == FRIEND_USER_ID_PARSED as u64) + .unwrap(); + assert_eq!(user_user.user.id.0, USER_USER_ID_PARSED as u64); + assert!(user_user.permissions.is_some()); // SHOULD see private data of the team + assert_eq!(friend_user.user.id.0, FRIEND_USER_ID_PARSED as u64); + assert!(friend_user.permissions.is_some()); + }) + .await; +} + +#[actix_rt::test] +async fn test_get_team_organization() { + // Test setup and dummy data + // Perform get_team related tests for an organization team + //TODO: This needs to consider users in organizations now and how they perceive as well + with_test_environment(None, |test_env: TestEnvironment| async move { + let api = &test_env.api; + let zeta_organization_id = &test_env.dummy.organization_zeta.organization_id; + let zeta_team_id = &test_env.dummy.organization_zeta.team_id; + + // A non-member of the team should get basic info but not be able to see private data + let members = api + .get_team_members_deserialized_common(zeta_team_id, FRIEND_USER_PAT) + .await; + assert_eq!(members.len(), 1); + assert_eq!(members[0].user.id.0, USER_USER_ID_PARSED as u64); + assert!(members[0].permissions.is_none()); + + let members = api + .get_organization_members_deserialized_common(zeta_organization_id, FRIEND_USER_PAT) + .await; + assert_eq!(members.len(), 1); + assert_eq!(members[0].user.id.0, USER_USER_ID_PARSED as u64); + + // A non-accepted member of the team should: + // - not be able to see private data about the team, but see all members including themselves + // - should not appear in the team members list to enemy users + let resp = api + .add_user_to_team(zeta_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT) + .await; + assert_eq!(resp.status(), 204); + + // Team check directly + let members = api + .get_team_members_deserialized_common(zeta_team_id, FRIEND_USER_PAT) + .await; + assert!(members.len() == 2); // USER_USER_ID and FRIEND_USER_ID should be in the team + let user_user = members + .iter() + .find(|x| x.user.id.0 == USER_USER_ID_PARSED as u64) + .unwrap(); + let friend_user = members + .iter() + .find(|x| x.user.id.0 == FRIEND_USER_ID_PARSED as u64) + .unwrap(); + assert_eq!(user_user.user.id.0, USER_USER_ID_PARSED as u64); + assert!(user_user.permissions.is_none()); // Should not see private data of the team + assert_eq!(friend_user.user.id.0, FRIEND_USER_ID_PARSED as u64); + assert!(friend_user.permissions.is_none()); + + // team check via association + let members = api + .get_organization_members_deserialized_common(zeta_organization_id, FRIEND_USER_PAT) + .await; + + assert!(members.len() == 2); // USER_USER_ID and FRIEND_USER_ID should be in the team + let user_user = members + .iter() + .find(|x| x.user.id.0 == USER_USER_ID_PARSED as u64) + .unwrap(); + let friend_user = members + .iter() + .find(|x| x.user.id.0 == FRIEND_USER_ID_PARSED as u64) + .unwrap(); + assert_eq!(user_user.user.id.0, USER_USER_ID_PARSED as u64); + assert!(user_user.permissions.is_none()); // Should not see private data of the team + assert_eq!(friend_user.user.id.0, FRIEND_USER_ID_PARSED as u64); + assert!(friend_user.permissions.is_none()); + + // enemy team check directly + let members = api + .get_team_members_deserialized_common(zeta_team_id, ENEMY_USER_PAT) + .await; + assert_eq!(members.len(), 1); // Only USER_USER_ID should be in the team + + // enemy team check via association + let members = api + .get_organization_members_deserialized_common(zeta_organization_id, ENEMY_USER_PAT) + .await; + assert_eq!(members.len(), 1); // Only USER_USER_ID should be in the team + + // An accepted member of the team should appear in the team members list + // and should be able to see private data about the team + let resp = api.join_team(zeta_team_id, FRIEND_USER_PAT).await; + assert_eq!(resp.status(), 204); + + // Team check directly + let members = api + .get_team_members_deserialized_common(zeta_team_id, FRIEND_USER_PAT) + .await; + assert!(members.len() == 2); // USER_USER_ID and FRIEND_USER_ID should be in the team + let user_user = members + .iter() + .find(|x| x.user.id.0 == USER_USER_ID_PARSED as u64) + .unwrap(); + let friend_user = members + .iter() + .find(|x| x.user.id.0 == FRIEND_USER_ID_PARSED as u64) + .unwrap(); + assert_eq!(user_user.user.id.0, USER_USER_ID_PARSED as u64); + assert!(user_user.permissions.is_some()); // SHOULD see private data of the team + assert_eq!(friend_user.user.id.0, FRIEND_USER_ID_PARSED as u64); + assert!(friend_user.permissions.is_some()); + + // team check via association + let members = api + .get_organization_members_deserialized_common(zeta_organization_id, FRIEND_USER_PAT) + .await; + assert!(members.len() == 2); // USER_USER_ID and FRIEND_USER_ID should be in the team + let user_user = members + .iter() + .find(|x| x.user.id.0 == USER_USER_ID_PARSED as u64) + .unwrap(); + let friend_user = members + .iter() + .find(|x| x.user.id.0 == FRIEND_USER_ID_PARSED as u64) + .unwrap(); + assert_eq!(user_user.user.id.0, USER_USER_ID_PARSED as u64); + assert!(user_user.permissions.is_some()); // SHOULD see private data of the team + assert_eq!(friend_user.user.id.0, FRIEND_USER_ID_PARSED as u64); + assert!(friend_user.permissions.is_some()); }) .await; } @@ -143,69 +261,44 @@ async fn test_get_team() { #[actix_rt::test] async fn test_get_team_project_orgs() { // Test setup and dummy data - with_test_environment_all(None, |test_env| async move { - let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; - let alpha_team_id = &test_env.dummy.as_ref().unwrap().project_alpha.team_id; - let zeta_organization_id = &test_env - .dummy - .as_ref() - .unwrap() - .organization_zeta - .organization_id; - let zeta_team_id = &test_env.dummy.as_ref().unwrap().organization_zeta.team_id; + with_test_environment(None, |test_env: TestEnvironment| async move { + let alpha_project_id = &test_env.dummy.project_alpha.project_id; + let alpha_team_id = &test_env.dummy.project_alpha.team_id; + let zeta_organization_id = &test_env.dummy.organization_zeta.organization_id; + let zeta_team_id = &test_env.dummy.organization_zeta.team_id; // Attach alpha to zeta - let req = test::TestRequest::post() - .uri(&format!("/v3/organization/{zeta_organization_id}/projects")) - .append_pat(USER_USER_PAT) - .set_json(json!({ - "project_id": alpha_project_id, - })) - .to_request(); - let resp = test_env.call(req).await; + let resp = test_env + .api + .organization_add_project(zeta_organization_id, alpha_project_id, USER_USER_PAT) + .await; assert_eq!(resp.status(), 200); // Invite and add friend to zeta - let req = test::TestRequest::post() - .uri(&format!("/v3/team/{zeta_team_id}/members")) - .append_pat(USER_USER_PAT) - .set_json(json!({ - "user_id": FRIEND_USER_ID, - })) - .to_request(); - let resp = test_env.call(req).await; + let resp = test_env + .api + .add_user_to_team(zeta_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT) + .await; assert_eq!(resp.status(), 204); - let req = test::TestRequest::post() - .uri(&format!("/v3/team/{zeta_team_id}/join")) - .append_pat(FRIEND_USER_PAT) - .to_request(); - let resp = test_env.call(req).await; + let resp = test_env.api.join_team(zeta_team_id, FRIEND_USER_PAT).await; assert_eq!(resp.status(), 204); // The team members route from teams (on a project's team): // - the members of the project team specifically // - not the ones from the organization - let req = test::TestRequest::get() - .uri(&format!("/v3/team/{alpha_team_id}/members")) - .append_pat(FRIEND_USER_PAT) - .to_request(); - let resp = test_env.call(req).await; - assert_eq!(resp.status(), 200); - let value: serde_json::Value = test::read_body_json(resp).await; - let members = value.as_array().unwrap(); + let members = test_env + .api + .get_team_members_deserialized_common(alpha_team_id, FRIEND_USER_PAT) + .await; assert_eq!(members.len(), 1); // The team members route from project should show: // - the members of the project team including the ones from the organization - let req = test::TestRequest::get() - .uri(&format!("/v3/project/{alpha_project_id}/members")) - .append_pat(FRIEND_USER_PAT) - .to_request(); - let resp = test_env.call(req).await; - assert_eq!(resp.status(), 200); - let value: serde_json::Value = test::read_body_json(resp).await; - let members = value.as_array().unwrap(); + let members = test_env + .api + .get_project_members_deserialized_common(alpha_project_id, FRIEND_USER_PAT) + .await; assert_eq!(members.len(), 2); }) .await; @@ -218,7 +311,7 @@ async fn test_patch_project_team_member() { with_test_environment_all(None, |test_env| async move { let api = &test_env.api; - let alpha_team_id = &test_env.dummy.as_ref().unwrap().project_alpha.team_id; + let alpha_team_id = &test_env.dummy.project_alpha.team_id; // Edit team as admin/mod but not a part of the team should be OK let resp = api.edit_team_member(alpha_team_id, USER_USER_ID, json!({}), ADMIN_USER_PAT).await; @@ -286,117 +379,91 @@ async fn test_patch_project_team_member() { #[actix_rt::test] async fn test_patch_organization_team_member() { // Test setup and dummy data - with_test_environment_all(None, |test_env| async move { - let zeta_team_id = &test_env.dummy.as_ref().unwrap().organization_zeta.team_id; + with_test_environment(None, |test_env: TestEnvironment| async move { + let zeta_team_id = &test_env.dummy.organization_zeta.team_id; // Edit team as admin/mod but not a part of the team should be OK - let req = test::TestRequest::patch() - .uri(&format!("/v3/team/{zeta_team_id}/members/{USER_USER_ID}")) - .set_json(json!({})) - .append_pat(ADMIN_USER_PAT) - .to_request(); - let resp = test_env.call(req).await; + let resp = test_env + .api + .edit_team_member(zeta_team_id, USER_USER_ID, json!({}), ADMIN_USER_PAT) + .await; assert_eq!(resp.status(), 204); // As a non-owner with full permissions, attempt to edit the owner's permissions - let req = test::TestRequest::patch() - .uri(&format!("/v3/team/{zeta_team_id}/members/{USER_USER_ID}")) - .append_pat(ADMIN_USER_PAT) - .set_json(json!({ - "permissions": 0 - })) - .to_request(); - let resp = test_env.call(req).await; + let resp = test_env + .api + .edit_team_member(zeta_team_id, USER_USER_ID, json!({ "permissions": 0 }), ADMIN_USER_PAT) + .await; assert_eq!(resp.status(), 400); // Should not be able to add permissions to a user that the adding-user does not have // (true for both project and org) // first, invite friend - let req = test::TestRequest::post() - .uri(&format!("/v3/team/{zeta_team_id}/members")) - .append_pat(USER_USER_PAT) - .set_json(json!({ - "user_id": FRIEND_USER_ID, - "organization_permissions": (OrganizationPermissions::EDIT_MEMBER | OrganizationPermissions::EDIT_MEMBER_DEFAULT_PERMISSIONS).bits(), - })).to_request(); - let resp = test_env.call(req).await; + let resp = test_env + .api + .add_user_to_team(zeta_team_id, FRIEND_USER_ID, None, Some(OrganizationPermissions::EDIT_MEMBER | OrganizationPermissions::EDIT_MEMBER_DEFAULT_PERMISSIONS), USER_USER_PAT) + .await; assert_eq!(resp.status(), 204); // accept - let req = test::TestRequest::post() - .uri(&format!("/v3/team/{zeta_team_id}/join")) - .append_pat(FRIEND_USER_PAT) - .to_request(); - let resp = test_env.call(req).await; + let resp = test_env.api.join_team(zeta_team_id, FRIEND_USER_PAT).await; assert_eq!(resp.status(), 204); // try to add permissions- fails, as we do not have EDIT_DETAILS - let req = test::TestRequest::patch() - .uri(&format!("/v3/team/{zeta_team_id}/members/{FRIEND_USER_ID}")) - .append_pat(FRIEND_USER_PAT) - .set_json(json!({ - "organization_permissions": (OrganizationPermissions::EDIT_MEMBER | OrganizationPermissions::EDIT_DETAILS).bits() - })) - .to_request(); - let resp = test_env.call(req).await; - + let resp = test_env + .api + .edit_team_member(zeta_team_id, FRIEND_USER_ID, json!({ "organization_permissions": (OrganizationPermissions::EDIT_MEMBER | OrganizationPermissions::EDIT_DETAILS).bits() }), FRIEND_USER_PAT) + .await; assert_eq!(resp.status(), 400); // Cannot set payouts outside of 0 and 5000 for payout in [-1, 5001] { - let req = test::TestRequest::patch() - .uri(&format!("/v3/team/{zeta_team_id}/members/{FRIEND_USER_ID}")) - .append_pat(USER_USER_PAT) - .set_json(json!({ - "payouts_split": payout - })) - .to_request(); - let resp = test_env.call(req).await; + let resp = test_env + .api + .edit_team_member(zeta_team_id, FRIEND_USER_ID, json!({ "payouts_split": payout }), USER_USER_PAT) + .await; assert_eq!(resp.status(), 400); } // Successful patch - let req = test::TestRequest::patch() - .uri(&format!("/v3/team/{zeta_team_id}/members/{FRIEND_USER_ID}")) - .append_pat(FRIEND_USER_PAT) - .set_json(json!({ - "payouts_split": 51, - "organization_permissions": (OrganizationPermissions::EDIT_MEMBER).bits(), // reduces permissions - "permissions": (ProjectPermissions::EDIT_MEMBER).bits(), - "role": "very-cool-member", - "ordering": 5 - })) - .to_request(); - let resp = test_env.call(req).await; - + let resp = test_env + .api + .edit_team_member( + zeta_team_id, + FRIEND_USER_ID, + json!({ + "payouts_split": 51, + "organization_permissions": OrganizationPermissions::EDIT_MEMBER.bits(), // reduces permissions + "permissions": (ProjectPermissions::EDIT_MEMBER).bits(), + "role": "very-cool-member", + "ordering": 5 + }), + FRIEND_USER_PAT, + ) + .await; assert_eq!(resp.status(), 204); // Check results - let req = test::TestRequest::get() - .uri(&format!("/v3/team/{zeta_team_id}/members")) - .append_pat(FRIEND_USER_PAT) - .to_request(); - let resp = test_env.call(req).await; - assert_eq!(resp.status(), 200); - let value: serde_json::Value = test::read_body_json(resp).await; - let member = value - .as_array() - .unwrap() + let members = test_env + .api + .get_team_members_deserialized(zeta_team_id, FRIEND_USER_PAT) + .await; + let member = members .iter() - .find(|x| x["user"]["id"] == FRIEND_USER_ID) + .find(|x| x.user.id.0 == FRIEND_USER_ID_PARSED as u64) .unwrap(); - assert_eq!(member["payouts_split"], 51.0); + assert_eq!(member.payouts_split.unwrap(), Decimal::from_f64_retain(51.0_f64).unwrap()); assert_eq!( - member["organization_permissions"], - OrganizationPermissions::EDIT_MEMBER.bits() + member.organization_permissions, + Some(OrganizationPermissions::EDIT_MEMBER) ); assert_eq!( - member["permissions"], - ProjectPermissions::EDIT_MEMBER.bits() + member.permissions, + Some(ProjectPermissions::EDIT_MEMBER) ); - assert_eq!(member["role"], "very-cool-member"); - assert_eq!(member["ordering"], 5); + assert_eq!(member.role, "very-cool-member"); + assert_eq!(member.ordering, 5); }).await; } @@ -408,7 +475,7 @@ async fn transfer_ownership_v3() { with_test_environment(None, |test_env: TestEnvironment| async move { let api = &test_env.api; - let alpha_team_id = &test_env.dummy.as_ref().unwrap().project_alpha.team_id; + let alpha_team_id = &test_env.dummy.project_alpha.team_id; // Cannot set friend as owner (not a member) let resp = api @@ -514,10 +581,10 @@ async fn transfer_ownership_v3() { // let test_env = TestEnvironment::build(None).await; // let api = &test_env.api; -// let alpha_team_id = &test_env.dummy.as_ref().unwrap().project_alpha.team_id; -// let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; -// let zeta_organization_id = &test_env.dummy.as_ref().unwrap().zeta_organization_id; -// let zeta_team_id = &test_env.dummy.as_ref().unwrap().zeta_team_id; +// let alpha_team_id = &test_env.dummy.project_alpha.team_id; +// let alpha_project_id = &test_env.dummy.project_alpha.project_id; +// let zeta_organization_id = &test_env.dummy.zeta_organization_id; +// let zeta_team_id = &test_env.dummy.zeta_team_id; // // Link alpha team to zeta org // let resp = api.organization_add_project(zeta_organization_id, alpha_project_id, USER_USER_PAT).await; diff --git a/tests/user.rs b/tests/user.rs index 8426829e..3e4a1460 100644 --- a/tests/user.rs +++ b/tests/user.rs @@ -59,8 +59,8 @@ pub async fn get_user_projects_after_deleting_project_shows_removal() { #[actix_rt::test] pub async fn get_user_projects_after_joining_team_shows_team_projects() { with_test_environment_all(None, |test_env| async move { - let alpha_team_id = &test_env.dummy.as_ref().unwrap().project_alpha.team_id; - let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; + let alpha_team_id = &test_env.dummy.project_alpha.team_id; + let alpha_project_id = &test_env.dummy.project_alpha.project_id; let api = test_env.api; api.get_user_projects_deserialized_common(FRIEND_USER_ID, FRIEND_USER_PAT) .await; @@ -82,8 +82,8 @@ pub async fn get_user_projects_after_joining_team_shows_team_projects() { #[actix_rt::test] pub async fn get_user_projects_after_leaving_team_shows_no_team_projects() { with_test_environment_all(None, |test_env| async move { - let alpha_team_id = &test_env.dummy.as_ref().unwrap().project_alpha.team_id; - let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; + let alpha_team_id = &test_env.dummy.project_alpha.team_id; + let alpha_project_id = &test_env.dummy.project_alpha.project_id; let api = test_env.api; api.add_user_to_team(alpha_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT) .await; diff --git a/tests/v2/notifications.rs b/tests/v2/notifications.rs index d31f0ad6..b80bd2ab 100644 --- a/tests/v2/notifications.rs +++ b/tests/v2/notifications.rs @@ -8,13 +8,7 @@ use crate::common::{ #[actix_rt::test] pub async fn get_user_notifications_after_team_invitation_returns_notification() { with_test_environment(None, |test_env: TestEnvironment| async move { - let alpha_team_id = test_env - .dummy - .as_ref() - .unwrap() - .project_alpha - .team_id - .clone(); + let alpha_team_id = test_env.dummy.project_alpha.team_id.clone(); let api = test_env.api; api.add_user_to_team(&alpha_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT) .await; diff --git a/tests/v2/project.rs b/tests/v2/project.rs index ba03ce60..67f9386d 100644 --- a/tests/v2/project.rs +++ b/tests/v2/project.rs @@ -228,10 +228,10 @@ async fn test_add_remove_project() { #[actix_rt::test] async fn permissions_upload_version() { with_test_environment(None, |test_env: TestEnvironment| async move { - let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; - let alpha_version_id = &test_env.dummy.as_ref().unwrap().project_alpha.version_id; - let alpha_team_id = &test_env.dummy.as_ref().unwrap().project_alpha.team_id; - let alpha_file_hash = &test_env.dummy.as_ref().unwrap().project_alpha.file_hash; + let alpha_project_id = &test_env.dummy.project_alpha.project_id; + let alpha_version_id = &test_env.dummy.project_alpha.version_id; + let alpha_team_id = &test_env.dummy.project_alpha.team_id; + let alpha_file_hash = &test_env.dummy.project_alpha.file_hash; let api = &test_env.api; let basic_mod_different_file = TestFile::BasicModDifferent; @@ -331,7 +331,7 @@ pub async fn test_patch_v2() { with_test_environment(None, |test_env: TestEnvironment| async move { let api = &test_env.api; - let alpha_project_slug = &test_env.dummy.as_ref().unwrap().project_alpha.project_slug; + let alpha_project_slug = &test_env.dummy.project_alpha.project_slug; // Sucessful request to patch many fields. let resp = api @@ -437,8 +437,8 @@ async fn permissions_patch_project_v2() { pub async fn test_bulk_edit_links() { with_test_environment(None, |test_env: TestEnvironment| async move { let api = &test_env.api; - let alpha_project_id: &str = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; - let beta_project_id: &str = &test_env.dummy.as_ref().unwrap().project_beta.project_id; + let alpha_project_id: &str = &test_env.dummy.project_alpha.project_id; + let beta_project_id: &str = &test_env.dummy.project_beta.project_id; let resp = api .edit_project_bulk( diff --git a/tests/v2/scopes.rs b/tests/v2/scopes.rs index ff77670d..a0e3324f 100644 --- a/tests/v2/scopes.rs +++ b/tests/v2/scopes.rs @@ -1,29 +1,27 @@ +use crate::common::api_common::ApiProject; +use crate::common::api_common::ApiVersion; use crate::common::api_v2::request_data::get_public_project_creation_data; -use crate::common::api_v2::request_data::get_public_version_creation_data; use crate::common::api_v2::ApiV2; use crate::common::dummy_data::TestFile; use crate::common::environment::with_test_environment; use crate::common::environment::TestEnvironment; use crate::common::scopes::ScopeTest; -use actix_web::test; use labrinth::models::ids::base62_impl::parse_base62; use labrinth::models::pats::Scopes; use labrinth::models::projects::ProjectId; -use labrinth::util::actix::AppendsMultipart; // Project version creation scopes #[actix_rt::test] pub async fn project_version_create_scopes() { with_test_environment(None, |test_env: TestEnvironment| async move { + let api = &test_env.api; // Create project let create_project = Scopes::PROJECT_CREATE; - let req_gen = || { + let req_gen = |pat: Option| async move { let creation_data = get_public_project_creation_data("demo", Some(TestFile::BasicMod), None); - test::TestRequest::post() - .uri("/v2/project") - .set_multipart(creation_data.segment_data) + api.create_project(creation_data, pat.as_deref()).await }; let (_, success) = ScopeTest::new(&test_env) .test(req_gen, create_project) @@ -34,17 +32,16 @@ pub async fn project_version_create_scopes() { // Add version to project let create_version = Scopes::VERSION_CREATE; - let req_gen = || { - let creation_data = get_public_version_creation_data( + let req_gen = |pat: Option| async move { + api.add_public_version( project_id, "1.2.3.4", TestFile::BasicModDifferent, None, None, - ); - test::TestRequest::post() - .uri("/v2/version") - .set_multipart(creation_data.segment_data) + pat.as_deref(), + ) + .await }; ScopeTest::new(&test_env) .test(req_gen, create_version) diff --git a/tests/v2/teams.rs b/tests/v2/teams.rs index 3d664594..7caf9b72 100644 --- a/tests/v2/teams.rs +++ b/tests/v2/teams.rs @@ -17,7 +17,7 @@ async fn transfer_ownership_v2() { with_test_environment(None, |test_env: TestEnvironment| async move { let api = &test_env.api; - let alpha_team_id = &test_env.dummy.as_ref().unwrap().project_alpha.team_id; + let alpha_team_id = &test_env.dummy.project_alpha.team_id; // Cannot set friend as owner (not a member) let resp = api diff --git a/tests/v2/version.rs b/tests/v2/version.rs index 75fc98bc..bc719f68 100644 --- a/tests/v2/version.rs +++ b/tests/v2/version.rs @@ -9,6 +9,7 @@ use serde_json::json; use crate::common::api_common::{ApiProject, ApiVersion}; use crate::common::api_v2::ApiV2; +use crate::common::dummy_data::{DummyProjectAlpha, DummyProjectBeta}; use crate::common::environment::{with_test_environment, TestEnvironment}; use crate::common::{ database::{ENEMY_USER_PAT, USER_USER_PAT}, @@ -20,7 +21,7 @@ pub async fn test_patch_version() { with_test_environment(None, |test_env: TestEnvironment| async move { let api = &test_env.api; - let alpha_version_id = &test_env.dummy.as_ref().unwrap().project_alpha.version_id; + let alpha_version_id = &test_env.dummy.project_alpha.version_id; // // First, we do some patch requests that should fail. // // Failure because the user is not authorized. @@ -132,18 +133,18 @@ async fn version_updates() { // Test setup and dummy data with_test_environment(None, |test_env: TestEnvironment| async move { let api = &test_env.api; - - let alpha_project_id: &String = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; - let alpha_project_id_parsed = test_env - .dummy - .as_ref() - .unwrap() - .project_alpha - .project_id_parsed; - let alpha_version_id = &test_env.dummy.as_ref().unwrap().project_alpha.version_id; - let beta_version_id = &test_env.dummy.as_ref().unwrap().project_beta.version_id; - let alpha_version_hash = &test_env.dummy.as_ref().unwrap().project_alpha.file_hash; - let beta_version_hash = &test_env.dummy.as_ref().unwrap().project_beta.file_hash; + let DummyProjectAlpha { + project_id: alpha_project_id, + project_id_parsed: alpha_project_id_parsed, + version_id: alpha_version_id, + file_hash: alpha_version_hash, + .. + } = &test_env.dummy.project_alpha; + let DummyProjectBeta { + version_id: beta_version_id, + file_hash: beta_version_hash, + .. + } = &test_env.dummy.project_beta; // Quick test, using get version from hash let version = api @@ -224,7 +225,7 @@ async fn version_updates() { { let version = api .add_public_version_deserialized_common( - alpha_project_id_parsed, + *alpha_project_id_parsed, version_number, TestFile::build_random_jar(), None, diff --git a/tests/version.rs b/tests/version.rs index 97bb4980..5d762f8c 100644 --- a/tests/version.rs +++ b/tests/version.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use crate::common::api_common::ApiVersion; use crate::common::database::*; -use crate::common::dummy_data::TestFile; +use crate::common::dummy_data::{DummyProjectAlpha, DummyProjectBeta, TestFile}; use crate::common::{asserts::assert_status, get_json_val_str}; use actix_http::StatusCode; use actix_web::test; @@ -27,9 +27,15 @@ async fn test_get_version() { // Test setup and dummy data with_test_environment_all(None, |test_env| async move { let api = &test_env.api; - let alpha_project_id: &String = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; - let alpha_version_id = &test_env.dummy.as_ref().unwrap().project_alpha.version_id; - let beta_version_id = &test_env.dummy.as_ref().unwrap().project_beta.version_id; + let DummyProjectAlpha { + project_id: alpha_project_id, + version_id: alpha_version_id, + .. + } = &test_env.dummy.project_alpha; + let DummyProjectBeta { + version_id: beta_version_id, + .. + } = &test_env.dummy.project_beta; // Perform request on dummy data let version = api @@ -82,19 +88,18 @@ async fn version_updates() { None, |test_env: common::environment::TestEnvironment| async move { let api = &test_env.api; - - let alpha_project_id: &String = - &test_env.dummy.as_ref().unwrap().project_alpha.project_id; - let alpha_project_id_parsed = test_env - .dummy - .as_ref() - .unwrap() - .project_alpha - .project_id_parsed; - let alpha_version_id = &test_env.dummy.as_ref().unwrap().project_alpha.version_id; - let beta_version_id = &test_env.dummy.as_ref().unwrap().project_beta.version_id; - let alpha_version_hash = &test_env.dummy.as_ref().unwrap().project_alpha.file_hash; - let beta_version_hash = &test_env.dummy.as_ref().unwrap().project_beta.file_hash; + let DummyProjectAlpha { + project_id: alpha_project_id, + project_id_parsed: alpha_project_id_parsed, + version_id: alpha_version_id, + file_hash: alpha_version_hash, + .. + } = &test_env.dummy.project_alpha; + let DummyProjectBeta { + version_id: beta_version_id, + file_hash: beta_version_hash, + .. + } = &test_env.dummy.project_beta; // Quick test, using get version from hash let version = api @@ -179,7 +184,7 @@ async fn version_updates() { { let version = api .add_public_version_deserialized( - alpha_project_id_parsed, + *alpha_project_id_parsed, version_number, TestFile::build_random_jar(), None, @@ -382,17 +387,15 @@ pub async fn test_patch_version() { with_test_environment_all(None, |test_env| async move { let api = &test_env.api; - let alpha_version_id = &test_env.dummy.as_ref().unwrap().project_alpha.version_id; - let beta_project_id = &test_env.dummy.as_ref().unwrap().project_beta.project_id; - let beta_project_id_parsed = &test_env - .dummy - .as_ref() - .unwrap() - .project_beta - .project_id_parsed; + let alpha_version_id = &test_env.dummy.project_alpha.version_id; + let DummyProjectBeta { + project_id: beta_project_id, + project_id_parsed: beta_project_id_parsed, + .. + } = &test_env.dummy.project_beta; - // // First, we do some patch requests that should fail. - // // Failure because the user is not authorized. + // First, we do some patch requests that should fail. + // Failure because the user is not authorized. let resp = api .edit_version( alpha_version_id, @@ -510,8 +513,8 @@ pub async fn test_patch_version() { pub async fn test_project_versions() { with_test_environment_all(None, |test_env| async move { let api = &test_env.api; - let alpha_project_id: &String = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; - let alpha_version_id = &test_env.dummy.as_ref().unwrap().project_alpha.version_id; + let alpha_project_id: &String = &test_env.dummy.project_alpha.project_id; + let alpha_version_id = &test_env.dummy.project_alpha.version_id; let versions = api .get_project_versions_deserialized_common( @@ -534,7 +537,7 @@ pub async fn test_project_versions() { #[actix_rt::test] async fn can_create_version_with_ordering() { with_test_environment_all(None, |env| async move { - let alpha_project_id_parsed = env.dummy.as_ref().unwrap().project_alpha.project_id_parsed; + let alpha_project_id_parsed = env.dummy.project_alpha.project_id_parsed; let new_version_id = get_json_val_str( env.api @@ -562,7 +565,7 @@ async fn can_create_version_with_ordering() { #[actix_rt::test] async fn edit_version_ordering_works() { with_test_environment_all(None, |env| async move { - let alpha_version_id = env.dummy.as_ref().unwrap().project_alpha.version_id.clone(); + let alpha_version_id = env.dummy.project_alpha.version_id.clone(); let resp = env .api @@ -582,8 +585,8 @@ async fn edit_version_ordering_works() { #[actix_rt::test] async fn version_ordering_for_specified_orderings_orders_lower_order_first() { with_test_environment_all(None, |env| async move { - let alpha_project_id_parsed = env.dummy.as_ref().unwrap().project_alpha.project_id_parsed; - let alpha_version_id = env.dummy.as_ref().unwrap().project_alpha.version_id.clone(); + let alpha_project_id_parsed = env.dummy.project_alpha.project_id_parsed; + let alpha_version_id = env.dummy.project_alpha.version_id.clone(); let new_version_id = get_json_val_str( env.api .add_public_version_deserialized_common( @@ -616,8 +619,8 @@ async fn version_ordering_for_specified_orderings_orders_lower_order_first() { #[actix_rt::test] async fn version_ordering_when_unspecified_orders_oldest_first() { with_test_environment_all(None, |env| async move { - let alpha_project_id_parsed = env.dummy.as_ref().unwrap().project_alpha.project_id_parsed; - let alpha_version_id: String = env.dummy.as_ref().unwrap().project_alpha.version_id.clone(); + let alpha_project_id_parsed = env.dummy.project_alpha.project_id_parsed; + let alpha_version_id: String = env.dummy.project_alpha.version_id.clone(); let new_version_id = get_json_val_str( env.api .add_public_version_deserialized_common( @@ -647,8 +650,8 @@ async fn version_ordering_when_unspecified_orders_oldest_first() { #[actix_rt::test] async fn version_ordering_when_specified_orders_specified_before_unspecified() { with_test_environment_all(None, |env| async move { - let alpha_project_id_parsed = env.dummy.as_ref().unwrap().project_alpha.project_id_parsed; - let alpha_version_id = env.dummy.as_ref().unwrap().project_alpha.version_id.clone(); + let alpha_project_id_parsed = env.dummy.project_alpha.project_id_parsed; + let alpha_version_id = env.dummy.project_alpha.version_id.clone(); let new_version_id = get_json_val_str( env.api .add_public_version_deserialized_common(