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
This commit is contained in:
Wyatt Verchere
2023-12-20 11:46:53 -08:00
committed by GitHub
parent d59c522f7f
commit 60c535e861
39 changed files with 1775 additions and 1436 deletions

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -9,8 +9,7 @@ use crate::models::v2::projects::LegacyVersion;
use crate::queue::session::AuthQueue; use crate::queue::session::AuthQueue;
use crate::routes::{v2_reroute, v3}; use crate::routes::{v2_reroute, v3};
use crate::search::SearchConfig; use crate::search::SearchConfig;
use actix_web::{delete, get, patch, post, web, HttpRequest, HttpResponse}; use actix_web::{delete, get, patch, web, HttpRequest, HttpResponse};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::PgPool; use sqlx::PgPool;
use validator::Validate; use validator::Validate;
@@ -24,7 +23,6 @@ pub fn config(cfg: &mut web::ServiceConfig) {
.service(version_get) .service(version_get)
.service(version_delete) .service(version_delete)
.service(version_edit) .service(version_edit)
.service(version_schedule)
.service(super::version_creation::upload_file_to_version), .service(super::version_creation::upload_file_to_version),
); );
} }
@@ -275,36 +273,3 @@ pub async fn version_delete(
.await .await
.or_else(v2_reroute::flatten_404_error) .or_else(v2_reroute::flatten_404_error)
} }
#[derive(Deserialize)]
pub struct SchedulingData {
pub time: DateTime<Utc>,
pub requested_status: VersionStatus,
}
#[post("{id}/schedule")]
pub async fn version_schedule(
req: HttpRequest,
info: web::Path<(VersionId,)>,
pool: web::Data<PgPool>,
redis: web::Data<RedisPool>,
scheduling_data: web::Json<SchedulingData>,
session_queue: web::Data<AuthQueue>,
) -> Result<HttpResponse, ApiError> {
// 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)
}

View File

@@ -27,7 +27,7 @@ use crate::util::img;
use crate::util::routes::read_from_payload; use crate::util::routes::read_from_payload;
use crate::util::validate::validation_errors_to_string; use crate::util::validate::validation_errors_to_string;
use actix_web::{web, HttpRequest, HttpResponse}; use actix_web::{web, HttpRequest, HttpResponse};
use chrono::{DateTime, Utc}; use chrono::Utc;
use futures::TryStreamExt; use futures::TryStreamExt;
use itertools::Itertools; use itertools::Itertools;
use serde::{Deserialize, Serialize}; 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}/gallery", web::delete().to(delete_gallery_item))
.route("{id}/follow", web::post().to(project_follow)) .route("{id}/follow", web::post().to(project_follow))
.route("{id}/follow", web::delete().to(project_unfollow)) .route("{id}/follow", web::delete().to(project_unfollow))
.route("{id}/schedule", web::post().to(project_schedule))
.service( .service(
web::scope("{project_id}") web::scope("{project_id}")
.route( .route(
@@ -1305,100 +1304,6 @@ pub async fn bulk_edit_project_categories(
Ok(()) Ok(())
} }
#[derive(Deserialize)]
pub struct SchedulingData {
pub time: DateTime<Utc>,
pub requested_status: ProjectStatus,
}
pub async fn project_schedule(
req: HttpRequest,
info: web::Path<(String,)>,
pool: web::Data<PgPool>,
redis: web::Data<RedisPool>,
session_queue: web::Data<AuthQueue>,
scheduling_data: web::Json<SchedulingData>,
) -> Result<HttpResponse, ApiError> {
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)] #[derive(Serialize, Deserialize)]
pub struct Extension { pub struct Extension {
pub ext: String, pub ext: String,

View File

@@ -25,7 +25,6 @@ use crate::search::SearchConfig;
use crate::util::img; use crate::util::img;
use crate::util::validate::validation_errors_to_string; use crate::util::validate::validation_errors_to_string;
use actix_web::{web, HttpRequest, HttpResponse}; use actix_web::{web, HttpRequest, HttpResponse};
use chrono::{DateTime, Utc};
use itertools::Itertools; use itertools::Itertools;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::PgPool; use sqlx::PgPool;
@@ -43,7 +42,6 @@ pub fn config(cfg: &mut web::ServiceConfig) {
.route("{id}", web::get().to(version_get)) .route("{id}", web::get().to(version_get))
.route("{id}", web::patch().to(version_edit)) .route("{id}", web::patch().to(version_edit))
.route("{id}", web::delete().to(version_delete)) .route("{id}", web::delete().to(version_delete))
.route("{id}/schedule", web::post().to(version_schedule))
.route( .route(
"{version_id}/file", "{version_id}/file",
web::post().to(super::version_creation::upload_file_to_version), 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<Utc>,
pub requested_status: VersionStatus,
}
pub async fn version_schedule(
req: HttpRequest,
info: web::Path<(models::ids::VersionId,)>,
pool: web::Data<PgPool>,
redis: web::Data<RedisPool>,
scheduling_data: web::Json<SchedulingData>,
session_queue: web::Data<AuthQueue>,
) -> Result<HttpResponse, ApiError> {
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( pub async fn version_delete(
req: HttpRequest, req: HttpRequest,
info: web::Path<(models::ids::VersionId,)>, info: web::Path<(models::ids::VersionId,)>,

View File

@@ -19,13 +19,7 @@ pub async fn analytics_revenue() {
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
let api = &test_env.api; let api = &test_env.api;
let alpha_project_id = test_env let alpha_project_id = test_env.dummy.project_alpha.project_id.clone();
.dummy
.as_ref()
.unwrap()
.project_alpha
.project_id
.clone();
let pool = test_env.db.pool.clone(); let pool = test_env.db.pool.clone();
@@ -143,27 +137,9 @@ fn to_f64_vec_rounded_up(d: Vec<Decimal>) -> Vec<f64> {
#[actix_rt::test] #[actix_rt::test]
pub async fn permissions_analytics_revenue() { pub async fn permissions_analytics_revenue() {
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
let alpha_project_id = test_env let alpha_project_id = test_env.dummy.project_alpha.project_id.clone();
.dummy let alpha_version_id = test_env.dummy.project_alpha.version_id.clone();
.as_ref() let alpha_team_id = test_env.dummy.project_alpha.team_id.clone();
.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 api = &test_env.api; let api = &test_env.api;

View File

@@ -12,7 +12,7 @@ use crate::common::{api_v2::ApiV2, api_v3::ApiV3, dummy_data::TestFile};
use super::{ use super::{
models::{CommonProject, CommonVersion}, models::{CommonProject, CommonVersion},
request_data::{ImageData, ProjectCreationRequestData}, request_data::{ImageData, ProjectCreationRequestData},
Api, ApiProject, ApiTags, ApiTeams, ApiVersion, Api, ApiProject, ApiTags, ApiTeams, ApiUser, ApiVersion,
}; };
#[derive(Clone)] #[derive(Clone)]
@@ -71,17 +71,28 @@ delegate_api_variant!(
[remove_project, ServiceResponse, project_slug_or_id: &str, pat: Option<&str>], [remove_project, ServiceResponse, project_slug_or_id: &str, pat: Option<&str>],
[get_project, ServiceResponse, id_or_slug: &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_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, ServiceResponse, user_id_or_username: &str, pat: Option<&str>],
[get_user_projects_deserialized_common, Vec<CommonProject>, user_id_or_username: &str, pat: Option<&str>], [get_user_projects_deserialized_common, Vec<CommonProject>, user_id_or_username: &str, pat: Option<&str>],
[edit_project, ServiceResponse, id_or_slug: &str, patch: serde_json::Value, 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_bulk, ServiceResponse, ids_or_slugs: &[&str], patch: serde_json::Value, pat: Option<&str>],
[edit_project_icon, ServiceResponse, id_or_slug: &str, icon: Option<ImageData>, pat: Option<&str>], [edit_project_icon, ServiceResponse, id_or_slug: &str, icon: Option<ImageData>, pat: Option<&str>],
[schedule_project, ServiceResponse, id_or_slug: &str, requested_status: &str, date : chrono::DateTime<chrono::Utc>, pat: Option<&str>],
[add_gallery_item, ServiceResponse, id_or_slug: &str, image: ImageData, featured: bool, title: Option<String>, description: Option<String>, ordering: Option<i32>, pat: Option<&str>], [add_gallery_item, ServiceResponse, id_or_slug: &str, image: ImageData, featured: bool, title: Option<String>, description: Option<String>, ordering: Option<i32>, pat: Option<&str>],
[remove_gallery_item, ServiceResponse, id_or_slug: &str, image_url: &str, 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<String, String>, pat: Option<&str>], [edit_gallery_item, ServiceResponse, id_or_slug: &str, image_url: &str, patch: HashMap<String, String>, pat: Option<&str>],
[create_report, ServiceResponse, report_type: &str, id: &str, item_type: crate::common::api_common::models::CommonItemType, body: &str, 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_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 { impl ApiTeams for GenericApi {
[get_team_members, ServiceResponse, team_id: &str, pat: Option<&str>], [get_team_members, ServiceResponse, team_id: &str, pat: Option<&str>],
[get_team_members_deserialized_common, Vec<crate::common::api_common::models::CommonTeamMember>, team_id: &str, pat: Option<&str>], [get_team_members_deserialized_common, Vec<crate::common::api_common::models::CommonTeamMember>, 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, ServiceResponse, id_or_slug: &str, pat: Option<&str>],
[get_project_members_deserialized_common, Vec<crate::common::api_common::models::CommonTeamMember>, id_or_slug: &str, pat: Option<&str>], [get_project_members_deserialized_common, Vec<crate::common::api_common::models::CommonTeamMember>, id_or_slug: &str, pat: Option<&str>],
[get_organization_members, ServiceResponse, id_or_title: &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>], [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, ServiceResponse, user_id: &str, pat: Option<&str>],
[get_user_notifications_deserialized_common, Vec<crate::common::api_common::models::CommonNotification>, user_id: &str, pat: Option<&str>], [get_user_notifications_deserialized_common, Vec<crate::common::api_common::models::CommonNotification>, 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_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<ProjectPermissions>, organization_permissions: Option<OrganizationPermissions>, pat: Option<&str>], [add_user_to_team, ServiceResponse, team_id: &str, user_id: &str, project_permissions: Option<ProjectPermissions>, organization_permissions: Option<OrganizationPermissions>, pat: Option<&str>],
[delete_notification, ServiceResponse, notification_id: &str, 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_version_deserialized_common, CommonVersion, id_or_slug: &str, pat: Option<&str>],
[get_versions, ServiceResponse, ids_or_slugs: Vec<String>, pat: Option<&str>], [get_versions, ServiceResponse, ids_or_slugs: Vec<String>, pat: Option<&str>],
[get_versions_deserialized_common, Vec<CommonVersion>, ids_or_slugs: Vec<String>, pat: Option<&str>], [get_versions_deserialized_common, Vec<CommonVersion>, ids_or_slugs: Vec<String>, 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>], [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, 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>], [get_version_from_hash_deserialized_common, CommonVersion, id_or_slug: &str, hash: &str, pat: Option<&str>],

View File

@@ -7,7 +7,6 @@ use self::models::{
use self::request_data::{ImageData, ProjectCreationRequestData}; use self::request_data::{ImageData, ProjectCreationRequestData};
use actix_web::dev::ServiceResponse; use actix_web::dev::ServiceResponse;
use async_trait::async_trait; use async_trait::async_trait;
use chrono::{DateTime, Utc};
use labrinth::{ use labrinth::{
models::{ models::{
projects::{ProjectId, VersionType}, projects::{ProjectId, VersionType},
@@ -27,7 +26,7 @@ pub trait ApiBuildable: Api {
} }
#[async_trait(?Send)] #[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 call(&self, req: actix_http::Request) -> ServiceResponse;
async fn reset_search_index(&self) -> ServiceResponse; async fn reset_search_index(&self) -> ServiceResponse;
} }
@@ -59,6 +58,12 @@ pub trait ApiProject {
id_or_slug: &str, id_or_slug: &str,
pat: Option<&str>, pat: Option<&str>,
) -> CommonProject; ) -> 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( async fn get_user_projects(
&self, &self,
user_id_or_username: &str, user_id_or_username: &str,
@@ -87,13 +92,6 @@ pub trait ApiProject {
icon: Option<ImageData>, icon: Option<ImageData>,
pat: Option<&str>, pat: Option<&str>,
) -> ServiceResponse; ) -> ServiceResponse;
async fn schedule_project(
&self,
id_or_slug: &str,
requested_status: &str,
date: DateTime<Utc>,
pat: Option<&str>,
) -> ServiceResponse;
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
async fn add_gallery_item( async fn add_gallery_item(
&self, &self,
@@ -127,6 +125,27 @@ pub trait ApiProject {
pat: Option<&str>, pat: Option<&str>,
) -> ServiceResponse; ) -> ServiceResponse;
async fn get_report(&self, id: &str, 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)] #[async_trait(?Send)]
@@ -145,6 +164,7 @@ pub trait ApiTeams {
team_id: &str, team_id: &str,
pat: Option<&str>, pat: Option<&str>,
) -> Vec<CommonTeamMember>; ) -> Vec<CommonTeamMember>;
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(&self, id_or_slug: &str, pat: Option<&str>) -> ServiceResponse;
async fn get_project_members_deserialized_common( async fn get_project_members_deserialized_common(
&self, &self,
@@ -187,11 +207,14 @@ pub trait ApiTeams {
user_id: &str, user_id: &str,
pat: Option<&str>, pat: Option<&str>,
) -> Vec<CommonNotification>; ) -> Vec<CommonNotification>;
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( async fn mark_notification_read(
&self, &self,
notification_id: &str, notification_id: &str,
pat: Option<&str>, pat: Option<&str>,
) -> ServiceResponse; ) -> ServiceResponse;
async fn mark_notifications_read(&self, ids: &[&str], pat: Option<&str>) -> ServiceResponse;
async fn add_user_to_team( async fn add_user_to_team(
&self, &self,
team_id: &str, team_id: &str,
@@ -205,6 +228,20 @@ pub trait ApiTeams {
notification_id: &str, notification_id: &str,
pat: Option<&str>, pat: Option<&str>,
) -> ServiceResponse; ) -> 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)] #[async_trait(?Send)]
@@ -227,34 +264,40 @@ pub trait ApiVersion {
modify_json: Option<json_patch::Patch>, modify_json: Option<json_patch::Patch>,
pat: Option<&str>, pat: Option<&str>,
) -> CommonVersion; ) -> 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( async fn get_version_deserialized_common(
&self, &self,
id_or_slug: &str, id_or_slug: &str,
pat: Option<&str>, pat: Option<&str>,
) -> CommonVersion; ) -> CommonVersion;
async fn get_versions(&self, ids_or_slugs: Vec<String>, pat: Option<&str>) -> ServiceResponse; async fn get_versions(&self, ids: Vec<String>, pat: Option<&str>) -> ServiceResponse;
async fn get_versions_deserialized_common( async fn get_versions_deserialized_common(
&self, &self,
ids_or_slugs: Vec<String>, ids: Vec<String>,
pat: Option<&str>, pat: Option<&str>,
) -> Vec<CommonVersion>; ) -> Vec<CommonVersion>;
async fn download_version_redirect(
&self,
hash: &str,
algorithm: &str,
pat: Option<&str>,
) -> ServiceResponse;
async fn edit_version( async fn edit_version(
&self, &self,
id_or_slug: &str, id: &str,
patch: serde_json::Value, patch: serde_json::Value,
pat: Option<&str>, pat: Option<&str>,
) -> ServiceResponse; ) -> ServiceResponse;
async fn get_version_from_hash( async fn get_version_from_hash(
&self, &self,
id_or_slug: &str,
hash: &str, hash: &str,
algorithm: &str,
pat: Option<&str>, pat: Option<&str>,
) -> ServiceResponse; ) -> ServiceResponse;
async fn get_version_from_hash_deserialized_common( async fn get_version_from_hash_deserialized_common(
&self, &self,
id_or_slug: &str,
hash: &str, hash: &str,
algorithm: &str,
pat: Option<&str>, pat: Option<&str>,
) -> CommonVersion; ) -> CommonVersion;
async fn get_versions_from_hashes( async fn get_versions_from_hashes(

View File

@@ -13,6 +13,7 @@ pub mod project;
pub mod request_data; pub mod request_data;
pub mod tags; pub mod tags;
pub mod team; pub mod team;
pub mod user;
pub mod version; pub mod version;
#[derive(Clone)] #[derive(Clone)]

View File

@@ -15,7 +15,6 @@ use actix_web::{
}; };
use async_trait::async_trait; use async_trait::async_trait;
use bytes::Bytes; use bytes::Bytes;
use chrono::{DateTime, Utc};
use labrinth::{ use labrinth::{
models::v2::{projects::LegacyProject, search::LegacySearchResults}, models::v2::{projects::LegacyProject, search::LegacySearchResults},
util::actix::AppendsMultipart, util::actix::AppendsMultipart,
@@ -173,6 +172,30 @@ impl ApiProject for ApiV2 {
serde_json::from_value(value).unwrap() 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( async fn get_user_projects(
&self, &self,
user_id_or_username: &str, user_id_or_username: &str,
@@ -275,7 +298,7 @@ impl ApiProject for ApiV2 {
pat: Option<&str>, pat: Option<&str>,
) -> ServiceResponse { ) -> ServiceResponse {
let req = test::TestRequest::post() let req = test::TestRequest::post()
.uri("/v3/report") .uri("/v2/report")
.append_pat(pat) .append_pat(pat)
.set_json(json!( .set_json(json!(
{ {
@@ -292,28 +315,123 @@ impl ApiProject for ApiV2 {
async fn get_report(&self, id: &str, pat: Option<&str>) -> ServiceResponse { async fn get_report(&self, id: &str, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::get() let req = test::TestRequest::get()
.uri(&format!("/v3/report/{id}", id = id)) .uri(&format!("/v2/report/{id}"))
.append_pat(pat) .append_pat(pat)
.to_request(); .to_request();
self.call(req).await 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, &self,
id_or_slug: &str, id: &str,
requested_status: &str, patch: serde_json::Value,
date: DateTime<Utc>, 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>, pat: Option<&str>,
) -> ServiceResponse { ) -> ServiceResponse {
let req = test::TestRequest::post() let req = test::TestRequest::post()
.uri(&format!("/v2/version/{id_or_slug}/schedule")) .uri(&format!("/v2/thread/{id}"))
.set_json(json!( .append_pat(pat)
{ .set_json(json!({
"requested_status": requested_status, "body" : {
"time": date, "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) .append_pat(pat)
.to_request(); .to_request();

View File

@@ -72,6 +72,22 @@ impl ApiTeams for ApiV2 {
test::read_body_json(resp).await 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 { async fn get_project_members(&self, id_or_title: &str, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::get() let req = test::TestRequest::get()
.uri(&format!("/v2/project/{id_or_title}/members")) .uri(&format!("/v2/project/{id_or_title}/members"))
@@ -192,6 +208,30 @@ impl ApiTeams for ApiV2 {
serde_json::from_value(value).unwrap() 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(&notification_ids)
))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn mark_notification_read( async fn mark_notification_read(
&self, &self,
notification_id: &str, notification_id: &str,
@@ -204,6 +244,22 @@ impl ApiTeams for ApiV2 {
self.call(req).await 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(&notification_ids)
))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn add_user_to_team( async fn add_user_to_team(
&self, &self,
team_id: &str, team_id: &str,
@@ -235,4 +291,20 @@ impl ApiTeams for ApiV2 {
.to_request(); .to_request();
self.call(req).await 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(&notification_ids)
))
.append_pat(pat)
.to_request();
self.call(req).await
}
} }

View File

@@ -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
}
}

View File

@@ -161,6 +161,22 @@ impl ApiVersion for ApiV2 {
serde_json::from_value(value).unwrap() 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( async fn edit_version(
&self, &self,
version_id: &str, version_id: &str,

View File

@@ -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<Project> {
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<ImageData>,
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<Collection> {
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<Project> = 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()
}
}

View File

@@ -9,6 +9,7 @@ use async_trait::async_trait;
use labrinth::LabrinthConfig; use labrinth::LabrinthConfig;
use std::rc::Rc; use std::rc::Rc;
pub mod collections;
pub mod oauth; pub mod oauth;
pub mod oauth_clients; pub mod oauth_clients;
pub mod organization; pub mod organization;
@@ -16,6 +17,7 @@ pub mod project;
pub mod request_data; pub mod request_data;
pub mod tags; pub mod tags;
pub mod team; pub mod team;
pub mod user;
pub mod version; pub mod version;
#[derive(Clone)] #[derive(Clone)]

View File

@@ -46,6 +46,22 @@ impl ApiV3 {
test::read_body_json(resp).await 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( pub async fn get_organization_projects(
&self, &self,
id_or_title: &str, id_or_title: &str,

View File

@@ -126,6 +126,30 @@ impl ApiProject for ApiV3 {
serde_json::from_value(value).unwrap() 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( async fn get_user_projects(
&self, &self,
user_id_or_username: &str, user_id_or_username: &str,
@@ -245,28 +269,53 @@ impl ApiProject for ApiV3 {
async fn get_report(&self, id: &str, pat: Option<&str>) -> ServiceResponse { async fn get_report(&self, id: &str, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::get() let req = test::TestRequest::get()
.uri(&format!("/v3/report/{id}", id = id)) .uri(&format!("/v3/report/{id}"))
.append_pat(pat) .append_pat(pat)
.to_request(); .to_request();
self.call(req).await 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, &self,
id_or_slug: &str, id: &str,
requested_status: &str, patch: serde_json::Value,
date: DateTime<Utc>,
pat: Option<&str>, pat: Option<&str>,
) -> ServiceResponse { ) -> ServiceResponse {
let req = test::TestRequest::post() let req = test::TestRequest::patch()
.uri(&format!("/v3/version/{id_or_slug}/schedule")) .uri(&format!("/v3/report/{id}"))
.set_json(json!( .append_pat(pat)
{ .set_json(patch)
"requested_status": requested_status, .to_request();
"time": date,
} 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) .append_pat(pat)
.to_request(); .to_request();
@@ -355,6 +404,76 @@ impl ApiProject for ApiV3 {
self.call(req).await 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 { impl ApiV3 {

View File

@@ -63,6 +63,22 @@ impl ApiTeams for ApiV3 {
serde_json::from_value(value).unwrap() 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 { async fn get_project_members(&self, id_or_title: &str, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::get() let req = test::TestRequest::get()
.uri(&format!("/v3/project/{id_or_title}/members")) .uri(&format!("/v3/project/{id_or_title}/members"))
@@ -185,6 +201,30 @@ impl ApiTeams for ApiV3 {
serde_json::from_value(value).unwrap() 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(&notification_ids)
))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn mark_notification_read( async fn mark_notification_read(
&self, &self,
notification_id: &str, notification_id: &str,
@@ -196,6 +236,23 @@ impl ApiTeams for ApiV3 {
.to_request(); .to_request();
self.call(req).await 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(&notification_ids)
))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn add_user_to_team( async fn add_user_to_team(
&self, &self,
team_id: &str, team_id: &str,
@@ -227,4 +284,20 @@ impl ApiTeams for ApiV3 {
.to_request(); .to_request();
self.call(req).await 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(&notification_ids)
))
.append_pat(pat)
.to_request();
self.call(req).await
}
} }

View File

@@ -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
}
}

View File

@@ -181,6 +181,22 @@ impl ApiVersion for ApiV3 {
self.call(req).await 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( async fn get_version_from_hash(
&self, &self,
hash: &str, hash: &str,

View File

@@ -168,21 +168,14 @@ impl TemporaryDatabase {
if !dummy_data_exists { if !dummy_data_exists {
// Add dummy data // Add dummy data
let temporary_test_env = let db = TemporaryDatabase {
TestEnvironment::<ApiV3>::build_with_db(TemporaryDatabase { pool: pool.clone(),
pool: pool.clone(), database_name: TEMPLATE_DATABASE_NAME.to_string(),
database_name: TEMPLATE_DATABASE_NAME.to_string(), redis_pool: RedisPool::new(Some(generate_random_name("test_template_"))),
redis_pool: RedisPool::new(Some(generate_random_name( };
"test_template_", let setup_api = TestEnvironment::<ApiV3>::build_setup_api(&db).await;
))), dummy_data::add_dummy_data(&setup_api, db.clone()).await;
}) db.pool.close().await;
.await;
dummy_data::add_dummy_data(
&temporary_test_env.setup_api,
temporary_test_env.db.clone(),
)
.await;
temporary_test_env.db.pool.close().await;
} }
pool.close().await; pool.close().await;
drop(pool); drop(pool);

View File

@@ -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. // Use .call(req) on it directly to make a test call as if test::call_service(req) were being used.
#[derive(Clone)] #[derive(Clone)]
pub struct TestEnvironment<A> { pub struct TestEnvironment<A> {
// test_app: Rc<dyn LocalService>, // Rc as it's not Send
pub db: TemporaryDatabase, pub db: TemporaryDatabase,
pub api: A, pub api: A,
pub setup_api: ApiV3, // Used for setting up tests only (ie: in ScopesTest) pub setup_api: ApiV3, // Used for setting up tests only (ie: in ScopesTest)
pub dummy: Option<dummy_data::DummyData>, pub dummy: dummy_data::DummyData,
} }
impl<A: ApiBuildable> TestEnvironment<A> { impl<A: ApiBuildable> TestEnvironment<A> {
async fn build(max_connections: Option<u32>) -> Self { async fn build(max_connections: Option<u32>) -> Self {
let db = TemporaryDatabase::create(max_connections).await; 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 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 { Self {
db, db,
api: A::build(labrinth_config.clone()).await, api,
setup_api: ApiV3::build(labrinth_config.clone()).await, setup_api,
dummy: None, dummy,
// test_app
} }
} }
pub async fn build_setup_api(db: &TemporaryDatabase) -> ApiV3 {
let labrinth_config = setup(db).await;
ApiV3::build(labrinth_config).await
}
} }
impl<A: Api> TestEnvironment<A> { impl<A: Api> TestEnvironment<A> {
@@ -108,7 +105,7 @@ impl<A: Api> TestEnvironment<A> {
let resp = self let resp = self
.api .api
.add_user_to_team( .add_user_to_team(
&self.dummy.as_ref().unwrap().project_alpha.team_id, &self.dummy.project_alpha.team_id,
FRIEND_USER_ID, FRIEND_USER_ID,
None, None,
None, None,

View File

@@ -1,5 +1,6 @@
#![allow(dead_code)] #![allow(dead_code)]
use actix_web::test::{self, TestRequest}; use actix_web::{dev::ServiceResponse, test};
use futures::Future;
use labrinth::models::pats::Scopes; use labrinth::models::pats::Scopes;
use super::{ use super::{
@@ -59,13 +60,14 @@ impl<'a, A: Api> ScopeTest<'a, A> {
// success_scopes : the scopes that we are testing that should succeed // success_scopes : the scopes that we are testing that should succeed
// returns a tuple of (failure_body, success_body) // returns a tuple of (failure_body, success_body)
// Should return a String error if on unexpected status code, allowing unwrapping in tests. // Should return a String error if on unexpected status code, allowing unwrapping in tests.
pub async fn test<T>( pub async fn test<T, Fut>(
&self, &self,
req_gen: T, req_gen: T,
success_scopes: Scopes, success_scopes: Scopes,
) -> Result<(serde_json::Value, serde_json::Value), String> ) -> Result<(serde_json::Value, serde_json::Value), String>
where where
T: Fn() -> TestRequest, T: Fn(Option<String>) -> Fut,
Fut: Future<Output = ServiceResponse>, // Ensure Fut is Send and 'static
{ {
// First, create a PAT with failure scopes // First, create a PAT with failure scopes
let failure_scopes = self let failure_scopes = self
@@ -79,11 +81,7 @@ impl<'a, A: Api> ScopeTest<'a, A> {
// Perform test twice, once with each PAT // Perform test twice, once with each PAT
// the first time, we expect a 401 (or known failure code) // the first time, we expect a 401 (or known failure code)
let req = req_gen() let resp = req_gen(Some(access_token_all_others.clone())).await;
.append_header(("Authorization", access_token_all_others.as_str()))
.to_request();
let resp = self.test_env.call(req).await;
if resp.status().as_u16() != self.expected_failure_code { if resp.status().as_u16() != self.expected_failure_code {
return Err(format!( return Err(format!(
"Expected failure code {}, got {} ({:#?})", "Expected failure code {}, got {} ({:#?})",
@@ -103,11 +101,7 @@ impl<'a, A: Api> ScopeTest<'a, A> {
}; };
// The second time, we expect a success code // The second time, we expect a success code
let req = req_gen() let resp = req_gen(Some(access_token.clone())).await;
.append_header(("Authorization", access_token.as_str()))
.to_request();
let resp = self.test_env.call(req).await;
if !(resp.status().is_success() || resp.status().is_redirection()) { if !(resp.status().is_success() || resp.status().is_redirection()) {
return Err(format!( return Err(format!(
"Expected success code, got {} ({:#?})", "Expected success code, got {} ({:#?})",

View File

@@ -7,7 +7,7 @@ use serde_json::json;
use crate::common::api_common::ApiVersion; use crate::common::api_common::ApiVersion;
use crate::common::database::*; use crate::common::database::*;
use crate::common::dummy_data::TestFile; use crate::common::dummy_data::{DummyProjectAlpha, DummyProjectBeta, TestFile};
// importing common module. // importing common module.
mod common; mod common;
@@ -17,33 +17,16 @@ mod common;
async fn creating_loader_fields() { async fn creating_loader_fields() {
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
let api = &test_env.api; let api = &test_env.api;
let DummyProjectAlpha {
let alpha_project_id = &test_env project_id: alpha_project_id,
.dummy project_id_parsed: alpha_project_id_parsed,
.as_ref() version_id: alpha_version_id,
.unwrap() ..
.project_alpha } = &test_env.dummy.project_alpha;
.project_id let DummyProjectBeta {
.clone(); project_id_parsed: beta_project_id_parsed,
let alpha_project_id_parsed = test_env ..
.dummy } = &test_env.dummy.project_beta;
.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();
// ALL THE FOLLOWING FOR CREATE AND PATCH // 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") // 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 // - Create version
let resp = api let resp = api
.add_public_version( .add_public_version(
alpha_project_id_parsed, *alpha_project_id_parsed,
"1.0.0", "1.0.0",
TestFile::build_random_jar(), TestFile::build_random_jar(),
None, None,
@@ -84,7 +67,7 @@ async fn creating_loader_fields() {
// - Create version // - Create version
let resp = api let resp = api
.add_public_version( .add_public_version(
alpha_project_id_parsed, *alpha_project_id_parsed,
"1.0.0", "1.0.0",
TestFile::build_random_jar(), TestFile::build_random_jar(),
None, None,
@@ -117,7 +100,7 @@ async fn creating_loader_fields() {
// - Create version // - Create version
let resp = api let resp = api
.add_public_version( .add_public_version(
alpha_project_id_parsed, *alpha_project_id_parsed,
"1.0.0", "1.0.0",
TestFile::build_random_jar(), TestFile::build_random_jar(),
None, None,
@@ -139,7 +122,7 @@ async fn creating_loader_fields() {
// - Create version // - Create version
let resp = api let resp = api
.add_public_version( .add_public_version(
alpha_project_id_parsed, *alpha_project_id_parsed,
"1.0.0", "1.0.0",
TestFile::build_random_jar(), TestFile::build_random_jar(),
None, None,
@@ -161,7 +144,7 @@ async fn creating_loader_fields() {
// - Create version // - Create version
let resp: actix_web::dev::ServiceResponse = api let resp: actix_web::dev::ServiceResponse = api
.add_public_version( .add_public_version(
alpha_project_id_parsed, *alpha_project_id_parsed,
"1.0.0", "1.0.0",
TestFile::build_random_jar(), TestFile::build_random_jar(),
None, None,
@@ -201,7 +184,7 @@ async fn creating_loader_fields() {
// - Create version // - Create version
let resp = api let resp = api
.add_public_version( .add_public_version(
alpha_project_id_parsed, *alpha_project_id_parsed,
"1.0.0", "1.0.0",
TestFile::build_random_jar(), TestFile::build_random_jar(),
None, None,
@@ -236,7 +219,7 @@ async fn creating_loader_fields() {
// - Create version // - Create version
let v = api let v = api
.add_public_version_deserialized( .add_public_version_deserialized(
alpha_project_id_parsed, *alpha_project_id_parsed,
"1.0.0", "1.0.0",
TestFile::build_random_jar(), TestFile::build_random_jar(),
None, None,
@@ -272,7 +255,7 @@ async fn creating_loader_fields() {
// - Create // - Create
let v = api let v = api
.add_public_version_deserialized( .add_public_version_deserialized(
alpha_project_id_parsed, *alpha_project_id_parsed,
"1.0.0", "1.0.0",
TestFile::build_random_jar(), TestFile::build_random_jar(),
None, 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) // 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 // First, add a new version
api.add_public_version_deserialized( api.add_public_version_deserialized(
alpha_project_id_parsed, *alpha_project_id_parsed,
"1.0.1", "1.0.1",
TestFile::build_random_jar(), TestFile::build_random_jar(),
None, None,
@@ -347,7 +330,7 @@ async fn creating_loader_fields() {
// Also, add one to the beta project // Also, add one to the beta project
api.add_public_version_deserialized( api.add_public_version_deserialized(
beta_project_id_parsed, *beta_project_id_parsed,
"1.0.1", "1.0.1",
TestFile::build_random_jar(), TestFile::build_random_jar(),
None, None,

View File

@@ -10,13 +10,7 @@ mod common;
#[actix_rt::test] #[actix_rt::test]
pub async fn get_user_notifications_after_team_invitation_returns_notification() { pub async fn get_user_notifications_after_team_invitation_returns_notification() {
with_test_environment_all(None, |test_env| async move { with_test_environment_all(None, |test_env| async move {
let alpha_team_id = test_env let alpha_team_id = test_env.dummy.project_alpha.team_id.clone();
.dummy
.as_ref()
.unwrap()
.project_alpha
.team_id
.clone();
let api = test_env.api; let api = test_env.api;
api.get_user_notifications_deserialized_common(FRIEND_USER_ID, FRIEND_USER_PAT) api.get_user_notifications_deserialized_common(FRIEND_USER_ID, FRIEND_USER_PAT)
.await; .await;

View File

@@ -24,7 +24,7 @@ async fn oauth_flow_happy_path() {
valid_redirect_uri: base_redirect_uri, valid_redirect_uri: base_redirect_uri,
client_id, client_id,
client_secret, client_secret,
} = env.dummy.as_ref().unwrap().oauth_client_alpha.clone(); } = &env.dummy.oauth_client_alpha;
// Initiate authorization // Initiate authorization
let redirect_uri = format!("{}?foo=bar", base_redirect_uri); let redirect_uri = format!("{}?foo=bar", base_redirect_uri);
@@ -32,7 +32,7 @@ async fn oauth_flow_happy_path() {
let resp = env let resp = env
.api .api
.oauth_authorize( .oauth_authorize(
&client_id, client_id,
Some("USER_READ NOTIFICATION_READ"), Some("USER_READ NOTIFICATION_READ"),
Some(&redirect_uri), Some(&redirect_uri),
Some(original_state), Some(original_state),
@@ -60,7 +60,7 @@ async fn oauth_flow_happy_path() {
auth_code.to_string(), auth_code.to_string(),
Some(redirect_uri.clone()), Some(redirect_uri.clone()),
client_id.to_string(), client_id.to_string(),
&client_secret, client_secret,
) )
.await; .await;
assert_status(&resp, StatusCode::OK); assert_status(&resp, StatusCode::OK);
@@ -82,7 +82,7 @@ async fn oauth_flow_happy_path() {
#[actix_rt::test] #[actix_rt::test]
async fn oauth_authorize_for_already_authorized_scopes_returns_auth_code() { async fn oauth_authorize_for_already_authorized_scopes_returns_auth_code() {
with_test_environment(None, |env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |env: TestEnvironment<ApiV3>| async move {
let DummyOAuthClientAlpha { client_id, .. } = env.dummy.unwrap().oauth_client_alpha; let DummyOAuthClientAlpha { client_id, .. } = env.dummy.oauth_client_alpha;
let resp = env let resp = env
.api .api
@@ -119,7 +119,7 @@ async fn get_oauth_token_with_already_used_auth_code_fails() {
client_id, client_id,
client_secret, client_secret,
.. ..
} = env.dummy.unwrap().oauth_client_alpha; } = env.dummy.oauth_client_alpha;
let resp = env let resp = env
.api .api
@@ -152,7 +152,7 @@ async fn authorize_with_broader_scopes_can_complete_flow() {
client_id, client_id,
client_secret, client_secret,
.. ..
} = env.dummy.as_ref().unwrap().oauth_client_alpha.clone(); } = env.dummy.oauth_client_alpha.clone();
let first_access_token = env let first_access_token = env
.api .api
@@ -209,7 +209,7 @@ async fn authorize_with_broader_scopes_can_complete_flow() {
#[actix_rt::test] #[actix_rt::test]
async fn oauth_authorize_with_broader_scopes_requires_user_accept() { async fn oauth_authorize_with_broader_scopes_requires_user_accept() {
with_test_environment(None, |env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |env: TestEnvironment<ApiV3>| 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 let resp = env
.api .api
.oauth_authorize(&client_id, Some("USER_READ"), None, None, USER_USER_PAT) .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] #[actix_rt::test]
async fn reject_authorize_ends_authorize_flow() { async fn reject_authorize_ends_authorize_flow() {
with_test_environment(None, |env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |env: TestEnvironment<ApiV3>| 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 let resp = env
.api .api
.oauth_authorize(&client_id, None, None, None, USER_USER_PAT) .oauth_authorize(&client_id, None, None, None, USER_USER_PAT)
@@ -256,7 +256,7 @@ async fn reject_authorize_ends_authorize_flow() {
#[actix_rt::test] #[actix_rt::test]
async fn accept_authorize_after_already_accepting_fails() { async fn accept_authorize_after_already_accepting_fails() {
with_test_environment(None, |env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |env: TestEnvironment<ApiV3>| 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 let resp = env
.api .api
.oauth_authorize(&client_id, None, None, None, USER_USER_PAT) .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_id,
client_secret, client_secret,
.. ..
} = env.dummy.as_ref().unwrap().oauth_client_alpha.clone(); } = &env.dummy.oauth_client_alpha;
let access_token = env let access_token = env
.api .api
.complete_full_authorize_flow( .complete_full_authorize_flow(
&client_id, client_id,
&client_secret, client_secret,
Some("NOTIFICATION_READ"), Some("NOTIFICATION_READ"),
None, None,
None, None,
@@ -295,7 +295,7 @@ async fn revoke_authorization_after_issuing_token_revokes_token() {
let resp = env let resp = env
.api .api
.revoke_oauth_authorization(&client_id, USER_USER_PAT) .revoke_oauth_authorization(client_id, USER_USER_PAT)
.await; .await;
assert_status(&resp, StatusCode::OK); assert_status(&resp, StatusCode::OK);

View File

@@ -98,8 +98,7 @@ async fn create_oauth_client_with_restricted_scopes_fails() {
#[actix_rt::test] #[actix_rt::test]
async fn get_oauth_client_for_client_creator_succeeds() { async fn get_oauth_client_for_client_creator_succeeds() {
with_test_environment(None, |env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |env: TestEnvironment<ApiV3>| async move {
let DummyOAuthClientAlpha { client_id, .. } = let DummyOAuthClientAlpha { client_id, .. } = env.dummy.oauth_client_alpha.clone();
env.dummy.as_ref().unwrap().oauth_client_alpha.clone();
let resp = env let resp = env
.api .api
@@ -116,8 +115,7 @@ async fn get_oauth_client_for_client_creator_succeeds() {
#[actix_rt::test] #[actix_rt::test]
async fn get_oauth_client_for_unrelated_user_fails() { async fn get_oauth_client_for_unrelated_user_fails() {
with_test_environment(None, |env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |env: TestEnvironment<ApiV3>| async move {
let DummyOAuthClientAlpha { client_id, .. } = let DummyOAuthClientAlpha { client_id, .. } = env.dummy.oauth_client_alpha.clone();
env.dummy.as_ref().unwrap().oauth_client_alpha.clone();
let resp = env let resp = env
.api .api
@@ -132,7 +130,7 @@ async fn get_oauth_client_for_unrelated_user_fails() {
#[actix_rt::test] #[actix_rt::test]
async fn can_delete_oauth_client() { async fn can_delete_oauth_client() {
with_test_environment(None, |env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |env: TestEnvironment<ApiV3>| 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; let resp = env.api.delete_oauth_client(&client_id, USER_USER_PAT).await;
assert_status(&resp, StatusCode::NO_CONTENT); assert_status(&resp, StatusCode::NO_CONTENT);
@@ -152,7 +150,7 @@ async fn delete_oauth_client_after_issuing_access_tokens_revokes_tokens() {
client_id, client_id,
client_secret, client_secret,
.. ..
} = env.dummy.as_ref().unwrap().oauth_client_alpha.clone(); } = env.dummy.oauth_client_alpha.clone();
let access_token = env let access_token = env
.api .api
.complete_full_authorize_flow( .complete_full_authorize_flow(
@@ -184,7 +182,7 @@ async fn can_list_user_oauth_authorizations() {
client_id, client_id,
client_secret, client_secret,
.. ..
} = env.dummy.as_ref().unwrap().oauth_client_alpha.clone(); } = env.dummy.oauth_client_alpha.clone();
env.api env.api
.complete_full_authorize_flow( .complete_full_authorize_flow(
&client_id, &client_id,

View File

@@ -18,12 +18,7 @@ mod common;
async fn create_organization() { async fn create_organization() {
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
let api = &test_env.api; let api = &test_env.api;
let zeta_organization_slug = &test_env let zeta_organization_slug = &test_env.dummy.organization_zeta.organization_id;
.dummy
.as_ref()
.unwrap()
.organization_zeta
.organization_id;
// Failed creations title: // Failed creations title:
// - slug collision with zeta // - slug collision with zeta
@@ -88,12 +83,7 @@ async fn patch_organization() {
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
let api = &test_env.api; let api = &test_env.api;
let zeta_organization_id = &test_env let zeta_organization_id = &test_env.dummy.organization_zeta.organization_id;
.dummy
.as_ref()
.unwrap()
.organization_zeta
.organization_id;
// Create 'theta' organization // Create 'theta' organization
let resp = api let resp = api
@@ -169,12 +159,7 @@ async fn patch_organization() {
async fn add_remove_icon() { async fn add_remove_icon() {
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
let api = &test_env.api; let api = &test_env.api;
let zeta_organization_id = &test_env let zeta_organization_id = &test_env.dummy.organization_zeta.organization_id;
.dummy
.as_ref()
.unwrap()
.organization_zeta
.organization_id;
// Get project // Get project
let resp = test_env let resp = test_env
@@ -221,12 +206,7 @@ async fn add_remove_icon() {
async fn delete_org() { async fn delete_org() {
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
let api = &test_env.api; let api = &test_env.api;
let zeta_organization_id = &test_env let zeta_organization_id = &test_env.dummy.organization_zeta.organization_id;
.dummy
.as_ref()
.unwrap()
.organization_zeta
.organization_id;
let resp = api let resp = api
.delete_organization(zeta_organization_id, USER_USER_PAT) .delete_organization(zeta_organization_id, USER_USER_PAT)
@@ -246,14 +226,9 @@ async fn delete_org() {
#[actix_rt::test] #[actix_rt::test]
async fn add_remove_organization_projects() { async fn add_remove_organization_projects() {
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
let alpha_project_id: &str = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; let alpha_project_id: &str = &test_env.dummy.project_alpha.project_id;
let alpha_project_slug: &str = &test_env.dummy.as_ref().unwrap().project_alpha.project_slug; let alpha_project_slug: &str = &test_env.dummy.project_alpha.project_slug;
let zeta_organization_id: &str = &test_env let zeta_organization_id: &str = &test_env.dummy.organization_zeta.organization_id;
.dummy
.as_ref()
.unwrap()
.organization_zeta
.organization_id;
// Add/remove project to organization, first by ID, then by slug // Add/remove project to organization, first by ID, then by slug
for alpha in [alpha_project_id, alpha_project_slug] { for alpha in [alpha_project_id, alpha_project_slug] {
@@ -331,13 +306,8 @@ async fn permissions_patch_organization() {
#[actix_rt::test] #[actix_rt::test]
async fn permissions_edit_details() { async fn permissions_edit_details() {
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
let zeta_organization_id = &test_env let zeta_organization_id = &test_env.dummy.organization_zeta.organization_id;
.dummy let zeta_team_id = &test_env.dummy.organization_zeta.team_id;
.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 api = &test_env.api;
let edit_details = OrganizationPermissions::EDIT_DETAILS; let edit_details = OrganizationPermissions::EDIT_DETAILS;
@@ -381,13 +351,8 @@ async fn permissions_manage_invites() {
with_test_environment_all(None, |test_env| async move { with_test_environment_all(None, |test_env| async move {
let api = &test_env.api; let api = &test_env.api;
let zeta_organization_id = &test_env let zeta_organization_id = &test_env.dummy.organization_zeta.organization_id;
.dummy let zeta_team_id = &test_env.dummy.organization_zeta.team_id;
.as_ref()
.unwrap()
.organization_zeta
.organization_id;
let zeta_team_id = &test_env.dummy.as_ref().unwrap().organization_zeta.team_id;
let manage_invites = OrganizationPermissions::MANAGE_INVITES; let manage_invites = OrganizationPermissions::MANAGE_INVITES;
@@ -472,15 +437,10 @@ async fn permissions_add_remove_project() {
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
let api = &test_env.api; let api = &test_env.api;
let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; let alpha_project_id = &test_env.dummy.project_alpha.project_id;
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;
let zeta_organization_id = &test_env let zeta_organization_id = &test_env.dummy.organization_zeta.organization_id;
.dummy let zeta_team_id = &test_env.dummy.organization_zeta.team_id;
.as_ref()
.unwrap()
.organization_zeta
.organization_id;
let zeta_team_id = &test_env.dummy.as_ref().unwrap().organization_zeta.team_id;
let add_project = OrganizationPermissions::ADD_PROJECT; let add_project = OrganizationPermissions::ADD_PROJECT;
@@ -557,13 +517,8 @@ async fn permissions_delete_organization() {
#[actix_rt::test] #[actix_rt::test]
async fn permissions_add_default_project_permissions() { async fn permissions_add_default_project_permissions() {
with_test_environment_all(None, |test_env| async move { with_test_environment_all(None, |test_env| async move {
let zeta_organization_id = &test_env let zeta_organization_id = &test_env.dummy.organization_zeta.organization_id;
.dummy let zeta_team_id = &test_env.dummy.organization_zeta.team_id;
.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 api = &test_env.api;

View File

@@ -60,6 +60,9 @@ pub async fn pat_full_test() {
let mock_pat_test = |token: &str| { let mock_pat_test = |token: &str| {
let token = token.to_string(); let token = token.to_string();
async { 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() let req = test::TestRequest::post()
.uri("/v3/collection") .uri("/v3/collection")
.append_header(("Authorization", token)) .append_header(("Authorization", token))

View File

@@ -2,7 +2,6 @@ use std::collections::HashMap;
use actix_http::StatusCode; use actix_http::StatusCode;
use actix_web::test; use actix_web::test;
use chrono::{Duration, Utc};
use common::api_v3::ApiV3; use common::api_v3::ApiV3;
use common::database::*; use common::database::*;
use common::dummy_data::DUMMY_CATEGORIES; 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::models::CommonItemType;
use crate::common::api_common::request_data::ProjectCreationRequestData; use crate::common::api_common::request_data::ProjectCreationRequestData;
use crate::common::api_common::{ApiProject, ApiTeams, ApiVersion, AppendsOptionalPat}; use crate::common::api_common::{ApiProject, ApiTeams, ApiVersion};
use crate::common::dummy_data::{DummyImage, TestFile}; use crate::common::dummy_data::{DummyImage, DummyProjectAlpha, DummyProjectBeta, TestFile};
mod common; mod common;
#[actix_rt::test] #[actix_rt::test]
async fn test_get_project() { async fn test_get_project() {
// Test setup and dummy data // Test setup and dummy data
with_test_environment_all(None, |test_env| async move { with_test_environment_all(None, |test_env| async move {
let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; let DummyProjectAlpha {
let beta_project_id = &test_env.dummy.as_ref().unwrap().project_beta.project_id; project_id: alpha_project_id,
let alpha_project_slug = &test_env.dummy.as_ref().unwrap().project_alpha.project_slug; project_slug: alpha_project_slug,
let alpha_version_id = &test_env.dummy.as_ref().unwrap().project_alpha.version_id; 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 // Perform request on dummy data
let req = test::TestRequest::get() let resp = api.get_project(alpha_project_id, USER_USER_PAT).await;
.uri(&format!("/v3/project/{alpha_project_id}"))
.append_pat(USER_USER_PAT)
.to_request();
let resp = test_env.call(req).await;
let status = resp.status(); let status = resp.status();
let body: serde_json::Value = test::read_body_json(resp).await; 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)); assert_eq!(cached_project["inner"]["slug"], json!(alpha_project_slug));
// Make the request again, this time it should be cached // Make the request again, this time it should be cached
let req = test::TestRequest::get() let resp = api.get_project(alpha_project_id, USER_USER_PAT).await;
.uri(&format!("/v3/project/{alpha_project_id}"))
.append_pat(USER_USER_PAT)
.to_request();
let resp = test_env.call(req).await;
let status = resp.status(); let status = resp.status();
assert_eq!(status, 200); assert_eq!(status, 200);
@@ -84,21 +83,11 @@ async fn test_get_project() {
assert_eq!(body["slug"], json!(alpha_project_slug)); assert_eq!(body["slug"], json!(alpha_project_slug));
// Request should fail on non-existent project // Request should fail on non-existent project
let req = test::TestRequest::get() let resp = api.get_project("nonexistent", USER_USER_PAT).await;
.uri("/v3/project/nonexistent")
.append_pat(USER_USER_PAT)
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status(), 404); 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) // 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() let resp = api.get_project(beta_project_id, ENEMY_USER_PAT).await;
.uri(&format!("/v3/project/{beta_project_id}"))
.append_pat(ENEMY_USER_PAT)
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status(), 404); assert_eq!(resp.status(), 404);
}) })
.await; .await;
@@ -290,8 +279,8 @@ pub async fn test_patch_project() {
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
let api = &test_env.api; 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;
let beta_project_slug = &test_env.dummy.as_ref().unwrap().project_beta.project_slug; let beta_project_slug = &test_env.dummy.project_beta.project_slug;
// First, we do some patch requests that should fail. // First, we do some patch requests that should fail.
// Failure because the user is not authorized. // Failure because the user is not authorized.
@@ -472,7 +461,7 @@ pub async fn test_patch_v3() {
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
let api = &test_env.api; 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. // Sucessful request to patch many fields.
let resp = api let resp = api
@@ -503,8 +492,8 @@ pub async fn test_patch_v3() {
pub async fn test_bulk_edit_categories() { pub async fn test_bulk_edit_categories() {
with_test_environment_all(None, |test_env| async move { with_test_environment_all(None, |test_env| async move {
let api = &test_env.api; let api = &test_env.api;
let alpha_project_id: &str = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; let alpha_project_id: &str = &test_env.dummy.project_alpha.project_id;
let beta_project_id: &str = &test_env.dummy.as_ref().unwrap().project_beta.project_id; let beta_project_id: &str = &test_env.dummy.project_beta.project_id;
let resp = api let resp = api
.edit_project_bulk( .edit_project_bulk(
@@ -544,8 +533,8 @@ pub async fn test_bulk_edit_categories() {
pub async fn test_bulk_edit_links() { pub async fn test_bulk_edit_links() {
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
let api = &test_env.api; let api = &test_env.api;
let alpha_project_id: &str = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; let alpha_project_id: &str = &test_env.dummy.project_alpha.project_id;
let beta_project_id: &str = &test_env.dummy.as_ref().unwrap().project_beta.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 // Sets links for issue, source, wiki, and patreon for all projects
// The first loop, sets issue, the second, clears it 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() { async fn delete_project_with_report() {
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
let api = &test_env.api; let api = &test_env.api;
let alpha_project_id: &str = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; let alpha_project_id: &str = &test_env.dummy.project_alpha.project_id;
let beta_project_id: &str = &test_env.dummy.as_ref().unwrap().project_beta.project_id; let beta_project_id: &str = &test_env.dummy.project_beta.project_id;
// Create a report for the project // Create a report for the project
let resp = api let resp = api
@@ -682,8 +671,8 @@ async fn delete_project_with_report() {
#[actix_rt::test] #[actix_rt::test]
async fn permissions_patch_project_v3() { async fn permissions_patch_project_v3() {
with_test_environment(Some(8), |test_env: TestEnvironment<ApiV3>| async move { with_test_environment(Some(8), |test_env: TestEnvironment<ApiV3>| async move {
let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; let alpha_project_id = &test_env.dummy.project_alpha.project_id;
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;
let api = &test_env.api; let api = &test_env.api;
// TODO: This should be a separate test from v3 // TODO: This should be a separate test from v3
@@ -797,47 +786,69 @@ async fn permissions_patch_project_v3() {
.await; .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<ApiV3>| 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 // Not covered by PATCH /project
#[actix_rt::test] #[actix_rt::test]
async fn permissions_edit_details() { async fn permissions_edit_details() {
with_test_environment_all(None, |test_env| async move { with_test_environment_all(None, |test_env| async move {
let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; let DummyProjectAlpha {
let alpha_team_id = &test_env.dummy.as_ref().unwrap().project_alpha.team_id; project_id: alpha_project_id,
let beta_project_id = &test_env.dummy.as_ref().unwrap().project_beta.project_id; team_id: alpha_team_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; } = &test_env.dummy.project_alpha;
let edit_details = ProjectPermissions::EDIT_DETAILS; let edit_details = ProjectPermissions::EDIT_DETAILS;
let api = &test_env.api; 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 // Icon edit
// Uses alpha project to delete this icon // Uses alpha project to delete this icon
let req_gen = |ctx: PermissionsTestContext| async move { let req_gen = |ctx: PermissionsTestContext| async move {
@@ -889,11 +900,7 @@ async fn permissions_edit_details() {
.await .await
.unwrap(); .unwrap();
// Get project, as we need the gallery image url // Get project, as we need the gallery image url
let req = test::TestRequest::get() let resp = api.get_project(alpha_project_id, USER_USER_PAT).await;
.uri(&format!("/v3/project/{alpha_project_id}"))
.append_pat(USER_USER_PAT)
.to_request();
let resp = test_env.call(req).await;
let project: serde_json::Value = test::read_body_json(resp).await; let project: serde_json::Value = test::read_body_json(resp).await;
let gallery_url = project["gallery"][0]["url"].as_str().unwrap(); let gallery_url = project["gallery"][0]["url"].as_str().unwrap();
@@ -940,10 +947,10 @@ async fn permissions_edit_details() {
#[actix_rt::test] #[actix_rt::test]
async fn permissions_upload_version() { async fn permissions_upload_version() {
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; let alpha_project_id = &test_env.dummy.project_alpha.project_id;
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;
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;
let alpha_file_hash = &test_env.dummy.as_ref().unwrap().project_alpha.file_hash; let alpha_file_hash = &test_env.dummy.project_alpha.file_hash;
let api = &test_env.api; let api = &test_env.api;
@@ -1038,8 +1045,8 @@ async fn permissions_upload_version() {
async fn permissions_manage_invites() { async fn permissions_manage_invites() {
// Add member, remove member, edit member // Add member, remove member, edit member
with_test_environment_all(None, |test_env| async move { 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_project_id = &test_env.dummy.project_alpha.project_id;
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;
let api = &test_env.api; let api = &test_env.api;
let manage_invites = ProjectPermissions::MANAGE_INVITES; let manage_invites = ProjectPermissions::MANAGE_INVITES;
@@ -1096,22 +1103,19 @@ async fn permissions_manage_invites() {
.unwrap(); .unwrap();
// re-add member for testing // re-add member for testing
let req = test::TestRequest::post() let resp = api
.uri(&format!("/v3/team/{}/members", alpha_team_id)) .add_user_to_team(
.append_pat(ADMIN_USER_PAT) alpha_team_id,
.set_json(json!({ MOD_USER_ID,
"user_id": MOD_USER_ID, Some(ProjectPermissions::empty()),
})) None,
.to_request(); ADMIN_USER_PAT,
let resp = test_env.call(req).await; )
.await;
assert_eq!(resp.status(), 204); assert_eq!(resp.status(), 204);
// Accept invite // Accept invite
let req = test::TestRequest::post() let resp = api.join_team(alpha_team_id, MOD_USER_PAT).await;
.uri(&format!("/v3/team/{}/join", alpha_team_id))
.append_pat(MOD_USER_PAT)
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status(), 204); assert_eq!(resp.status(), 204);
// remove existing member (requires remove_member) // remove existing member (requires remove_member)

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,4 @@
use crate::common::{ use crate::common::{api_common::ApiTeams, database::*};
api_common::{ApiTeams, AppendsOptionalPat},
database::*,
};
use actix_web::test;
use common::{ use common::{
api_v3::ApiV3, api_v3::ApiV3,
environment::{with_test_environment, with_test_environment_all, TestEnvironment}, environment::{with_test_environment, with_test_environment_all, TestEnvironment},
@@ -16,126 +12,248 @@ mod common;
#[actix_rt::test] #[actix_rt::test]
async fn test_get_team() { async fn test_get_team() {
// Test setup and dummy data // 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 { with_test_environment_all(None, |test_env| async move {
let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; 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.project_alpha.project_id;
let zeta_organization_id = &test_env let alpha_team_id = &test_env.dummy.project_alpha.team_id;
.dummy
.as_ref()
.unwrap()
.organization_zeta
.organization_id;
let zeta_team_id = &test_env.dummy.as_ref().unwrap().organization_zeta.team_id;
// Perform tests for an organization team and a project team // A non-member of the team should get basic info but not be able to see private data
for (team_association_id, team_association, team_id) in [ let members = api
(alpha_project_id, "project", alpha_team_id), .get_team_members_deserialized_common(alpha_team_id, FRIEND_USER_PAT)
(zeta_organization_id, "organization", zeta_team_id), .await;
] { assert_eq!(members.len(), 1);
// A non-member of the team should get basic info but not be able to see private data assert_eq!(members[0].user.id.0, USER_USER_ID_PARSED as u64);
for uri in [ assert!(members[0].permissions.is_none());
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; let members = api
assert_eq!(resp.status(), 200); .get_project_members_deserialized_common(alpha_project_id, FRIEND_USER_PAT)
let value: serde_json::Value = test::read_body_json(resp).await; .await;
assert_eq!(value[0]["user"]["id"], USER_USER_ID); assert_eq!(members.len(), 1);
assert!(value[0]["permissions"].is_null()); assert_eq!(members[0].user.id.0, USER_USER_ID_PARSED as u64);
}
// A non-accepted member of the team should: // A non-accepted member of the team should:
// - not be able to see private data about the team, but see all members including themselves // - 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 // - should not appear in the team members list to enemy users
let req = test::TestRequest::post() let resp = api
.uri(&format!("/v3/team/{team_id}/members")) .add_user_to_team(alpha_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT)
.append_pat(USER_USER_PAT) .await;
.set_json(&json!({ assert_eq!(resp.status(), 204);
"user_id": FRIEND_USER_ID,
}))
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status(), 204);
for uri in [ // Team check directly
format!("/v3/team/{team_id}/members"), let members = api
format!("/v3/{team_association}/{team_association_id}/members"), .get_team_members_deserialized_common(alpha_team_id, FRIEND_USER_PAT)
] { .await;
let req = test::TestRequest::get() assert!(members.len() == 2); // USER_USER_ID and FRIEND_USER_ID should be in the team
.uri(&uri) let user_user = members
.append_pat(FRIEND_USER_PAT) .iter()
.to_request(); .find(|x| x.user.id.0 == USER_USER_ID_PARSED as u64)
let resp = test_env.call(req).await; .unwrap();
assert_eq!(resp.status(), 200); let friend_user = members
let value: serde_json::Value = test::read_body_json(resp).await; .iter()
let members = value.as_array().unwrap(); .find(|x| x.user.id.0 == FRIEND_USER_ID_PARSED as u64)
assert!(members.len() == 2); // USER_USER_ID and FRIEND_USER_ID should be in the team .unwrap();
let user_user = members assert_eq!(user_user.user.id.0, USER_USER_ID_PARSED as u64);
.iter() assert!(user_user.permissions.is_none()); // Should not see private data of the team
.find(|x| x["user"]["id"] == USER_USER_ID) assert_eq!(friend_user.user.id.0, FRIEND_USER_ID_PARSED as u64);
.unwrap(); assert!(friend_user.permissions.is_none());
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());
let req = test::TestRequest::get() // team check via association
.uri(&uri) let members = api
.append_pat(ENEMY_USER_PAT) .get_project_members_deserialized_common(alpha_project_id, FRIEND_USER_PAT)
.to_request(); .await;
let resp = test_env.call(req).await; assert!(members.len() == 2); // USER_USER_ID and FRIEND_USER_ID should be in the team
assert_eq!(resp.status(), 200); let user_user = members
let value: serde_json::Value = test::read_body_json(resp).await; .iter()
let members = value.as_array().unwrap(); .find(|x| x.user.id.0 == USER_USER_ID_PARSED as u64)
assert_eq!(members.len(), 1); // Only USER_USER_ID should be in the team .unwrap();
assert_eq!(members[0]["user"]["id"], USER_USER_ID); let friend_user = members
assert!(members[0]["permissions"].is_null()); .iter()
} .find(|x| x.user.id.0 == FRIEND_USER_ID_PARSED as u64)
// An accepted member of the team should appear in the team members list .unwrap();
// and should be able to see private data about the team assert_eq!(user_user.user.id.0, USER_USER_ID_PARSED as u64);
let req = test::TestRequest::post() assert!(user_user.permissions.is_none()); // Should not see private data of the team
.uri(&format!("/v3/team/{team_id}/join")) assert_eq!(friend_user.user.id.0, FRIEND_USER_ID_PARSED as u64);
.append_pat(FRIEND_USER_PAT) assert!(friend_user.permissions.is_none());
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status(), 204);
for uri in [ // enemy team check directly
format!("/v3/team/{team_id}/members"), let members = api
format!("/v3/{team_association}/{team_association_id}/members"), .get_team_members_deserialized_common(alpha_team_id, ENEMY_USER_PAT)
] { .await;
let req = test::TestRequest::get() assert_eq!(members.len(), 1); // Only USER_USER_ID should be in the team
.uri(&uri)
.append_pat(FRIEND_USER_PAT) // enemy team check via association
.to_request(); let members = api
let resp = test_env.call(req).await; .get_project_members_deserialized_common(alpha_project_id, ENEMY_USER_PAT)
assert_eq!(resp.status(), 200); .await;
let value: serde_json::Value = test::read_body_json(resp).await; assert_eq!(members.len(), 1); // Only USER_USER_ID should be in the team
let members = value.as_array().unwrap();
assert!(members.len() == 2); // USER_USER_ID and FRIEND_USER_ID should be in the team // An accepted member of the team should appear in the team members list
let user_user = members // and should be able to see private data about the team
.iter() let resp = api.join_team(alpha_team_id, FRIEND_USER_PAT).await;
.find(|x| x["user"]["id"] == USER_USER_ID) assert_eq!(resp.status(), 204);
.unwrap();
let friend_user = members // Team check directly
.iter() let members = api
.find(|x| x["user"]["id"] == FRIEND_USER_ID) .get_team_members_deserialized_common(alpha_team_id, FRIEND_USER_PAT)
.unwrap(); .await;
assert_eq!(user_user["user"]["id"], USER_USER_ID); assert!(members.len() == 2); // USER_USER_ID and FRIEND_USER_ID should be in the team
assert!(!user_user["permissions"].is_null()); // SHOULD see private data of the team let user_user = members
assert_eq!(friend_user["user"]["id"], FRIEND_USER_ID); .iter()
assert!(!friend_user["permissions"].is_null()); .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<ApiV3>| 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; .await;
} }
@@ -143,69 +261,44 @@ async fn test_get_team() {
#[actix_rt::test] #[actix_rt::test]
async fn test_get_team_project_orgs() { async fn test_get_team_project_orgs() {
// Test setup and dummy data // Test setup and dummy data
with_test_environment_all(None, |test_env| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; let alpha_project_id = &test_env.dummy.project_alpha.project_id;
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;
let zeta_organization_id = &test_env let zeta_organization_id = &test_env.dummy.organization_zeta.organization_id;
.dummy let zeta_team_id = &test_env.dummy.organization_zeta.team_id;
.as_ref()
.unwrap()
.organization_zeta
.organization_id;
let zeta_team_id = &test_env.dummy.as_ref().unwrap().organization_zeta.team_id;
// Attach alpha to zeta // Attach alpha to zeta
let req = test::TestRequest::post() let resp = test_env
.uri(&format!("/v3/organization/{zeta_organization_id}/projects")) .api
.append_pat(USER_USER_PAT) .organization_add_project(zeta_organization_id, alpha_project_id, USER_USER_PAT)
.set_json(json!({ .await;
"project_id": alpha_project_id,
}))
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status(), 200); assert_eq!(resp.status(), 200);
// Invite and add friend to zeta // Invite and add friend to zeta
let req = test::TestRequest::post() let resp = test_env
.uri(&format!("/v3/team/{zeta_team_id}/members")) .api
.append_pat(USER_USER_PAT) .add_user_to_team(zeta_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT)
.set_json(json!({ .await;
"user_id": FRIEND_USER_ID,
}))
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status(), 204); assert_eq!(resp.status(), 204);
let req = test::TestRequest::post() let resp = test_env.api.join_team(zeta_team_id, FRIEND_USER_PAT).await;
.uri(&format!("/v3/team/{zeta_team_id}/join"))
.append_pat(FRIEND_USER_PAT)
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status(), 204); assert_eq!(resp.status(), 204);
// The team members route from teams (on a project's team): // The team members route from teams (on a project's team):
// - the members of the project team specifically // - the members of the project team specifically
// - not the ones from the organization // - not the ones from the organization
let req = test::TestRequest::get() let members = test_env
.uri(&format!("/v3/team/{alpha_team_id}/members")) .api
.append_pat(FRIEND_USER_PAT) .get_team_members_deserialized_common(alpha_team_id, FRIEND_USER_PAT)
.to_request(); .await;
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); assert_eq!(members.len(), 1);
// The team members route from project should show: // The team members route from project should show:
// - the members of the project team including the ones from the organization // - the members of the project team including the ones from the organization
let req = test::TestRequest::get() let members = test_env
.uri(&format!("/v3/project/{alpha_project_id}/members")) .api
.append_pat(FRIEND_USER_PAT) .get_project_members_deserialized_common(alpha_project_id, FRIEND_USER_PAT)
.to_request(); .await;
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(), 2); assert_eq!(members.len(), 2);
}) })
.await; .await;
@@ -218,7 +311,7 @@ async fn test_patch_project_team_member() {
with_test_environment_all(None, |test_env| async move { with_test_environment_all(None, |test_env| async move {
let api = &test_env.api; 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 // 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; 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] #[actix_rt::test]
async fn test_patch_organization_team_member() { async fn test_patch_organization_team_member() {
// Test setup and dummy data // Test setup and dummy data
with_test_environment_all(None, |test_env| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
let zeta_team_id = &test_env.dummy.as_ref().unwrap().organization_zeta.team_id; 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 // Edit team as admin/mod but not a part of the team should be OK
let req = test::TestRequest::patch() let resp = test_env
.uri(&format!("/v3/team/{zeta_team_id}/members/{USER_USER_ID}")) .api
.set_json(json!({})) .edit_team_member(zeta_team_id, USER_USER_ID, json!({}), ADMIN_USER_PAT)
.append_pat(ADMIN_USER_PAT) .await;
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status(), 204); assert_eq!(resp.status(), 204);
// As a non-owner with full permissions, attempt to edit the owner's permissions // As a non-owner with full permissions, attempt to edit the owner's permissions
let req = test::TestRequest::patch() let resp = test_env
.uri(&format!("/v3/team/{zeta_team_id}/members/{USER_USER_ID}")) .api
.append_pat(ADMIN_USER_PAT) .edit_team_member(zeta_team_id, USER_USER_ID, json!({ "permissions": 0 }), ADMIN_USER_PAT)
.set_json(json!({ .await;
"permissions": 0
}))
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status(), 400); assert_eq!(resp.status(), 400);
// Should not be able to add permissions to a user that the adding-user does not have // Should not be able to add permissions to a user that the adding-user does not have
// (true for both project and org) // (true for both project and org)
// first, invite friend // first, invite friend
let req = test::TestRequest::post() let resp = test_env
.uri(&format!("/v3/team/{zeta_team_id}/members")) .api
.append_pat(USER_USER_PAT) .add_user_to_team(zeta_team_id, FRIEND_USER_ID, None, Some(OrganizationPermissions::EDIT_MEMBER | OrganizationPermissions::EDIT_MEMBER_DEFAULT_PERMISSIONS), USER_USER_PAT)
.set_json(json!({ .await;
"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;
assert_eq!(resp.status(), 204); assert_eq!(resp.status(), 204);
// accept // accept
let req = test::TestRequest::post() let resp = test_env.api.join_team(zeta_team_id, FRIEND_USER_PAT).await;
.uri(&format!("/v3/team/{zeta_team_id}/join"))
.append_pat(FRIEND_USER_PAT)
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status(), 204); assert_eq!(resp.status(), 204);
// try to add permissions- fails, as we do not have EDIT_DETAILS // try to add permissions- fails, as we do not have EDIT_DETAILS
let req = test::TestRequest::patch() let resp = test_env
.uri(&format!("/v3/team/{zeta_team_id}/members/{FRIEND_USER_ID}")) .api
.append_pat(FRIEND_USER_PAT) .edit_team_member(zeta_team_id, FRIEND_USER_ID, json!({ "organization_permissions": (OrganizationPermissions::EDIT_MEMBER | OrganizationPermissions::EDIT_DETAILS).bits() }), FRIEND_USER_PAT)
.set_json(json!({ .await;
"organization_permissions": (OrganizationPermissions::EDIT_MEMBER | OrganizationPermissions::EDIT_DETAILS).bits()
}))
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status(), 400); assert_eq!(resp.status(), 400);
// Cannot set payouts outside of 0 and 5000 // Cannot set payouts outside of 0 and 5000
for payout in [-1, 5001] { for payout in [-1, 5001] {
let req = test::TestRequest::patch() let resp = test_env
.uri(&format!("/v3/team/{zeta_team_id}/members/{FRIEND_USER_ID}")) .api
.append_pat(USER_USER_PAT) .edit_team_member(zeta_team_id, FRIEND_USER_ID, json!({ "payouts_split": payout }), USER_USER_PAT)
.set_json(json!({ .await;
"payouts_split": payout
}))
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status(), 400); assert_eq!(resp.status(), 400);
} }
// Successful patch // Successful patch
let req = test::TestRequest::patch() let resp = test_env
.uri(&format!("/v3/team/{zeta_team_id}/members/{FRIEND_USER_ID}")) .api
.append_pat(FRIEND_USER_PAT) .edit_team_member(
.set_json(json!({ zeta_team_id,
"payouts_split": 51, FRIEND_USER_ID,
"organization_permissions": (OrganizationPermissions::EDIT_MEMBER).bits(), // reduces permissions json!({
"permissions": (ProjectPermissions::EDIT_MEMBER).bits(), "payouts_split": 51,
"role": "very-cool-member", "organization_permissions": OrganizationPermissions::EDIT_MEMBER.bits(), // reduces permissions
"ordering": 5 "permissions": (ProjectPermissions::EDIT_MEMBER).bits(),
})) "role": "very-cool-member",
.to_request(); "ordering": 5
let resp = test_env.call(req).await; }),
FRIEND_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204); assert_eq!(resp.status(), 204);
// Check results // Check results
let req = test::TestRequest::get() let members = test_env
.uri(&format!("/v3/team/{zeta_team_id}/members")) .api
.append_pat(FRIEND_USER_PAT) .get_team_members_deserialized(zeta_team_id, FRIEND_USER_PAT)
.to_request(); .await;
let resp = test_env.call(req).await; let member = members
assert_eq!(resp.status(), 200);
let value: serde_json::Value = test::read_body_json(resp).await;
let member = value
.as_array()
.unwrap()
.iter() .iter()
.find(|x| x["user"]["id"] == FRIEND_USER_ID) .find(|x| x.user.id.0 == FRIEND_USER_ID_PARSED as u64)
.unwrap(); .unwrap();
assert_eq!(member["payouts_split"], 51.0); assert_eq!(member.payouts_split.unwrap(), Decimal::from_f64_retain(51.0_f64).unwrap());
assert_eq!( assert_eq!(
member["organization_permissions"], member.organization_permissions,
OrganizationPermissions::EDIT_MEMBER.bits() Some(OrganizationPermissions::EDIT_MEMBER)
); );
assert_eq!( assert_eq!(
member["permissions"], member.permissions,
ProjectPermissions::EDIT_MEMBER.bits() Some(ProjectPermissions::EDIT_MEMBER)
); );
assert_eq!(member["role"], "very-cool-member"); assert_eq!(member.role, "very-cool-member");
assert_eq!(member["ordering"], 5); assert_eq!(member.ordering, 5);
}).await; }).await;
} }
@@ -408,7 +475,7 @@ async fn transfer_ownership_v3() {
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
let api = &test_env.api; 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) // Cannot set friend as owner (not a member)
let resp = api let resp = api
@@ -514,10 +581,10 @@ async fn transfer_ownership_v3() {
// let test_env = TestEnvironment::build(None).await; // let test_env = TestEnvironment::build(None).await;
// let api = &test_env.api; // 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;
// let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; // let alpha_project_id = &test_env.dummy.project_alpha.project_id;
// let zeta_organization_id = &test_env.dummy.as_ref().unwrap().zeta_organization_id; // let zeta_organization_id = &test_env.dummy.zeta_organization_id;
// let zeta_team_id = &test_env.dummy.as_ref().unwrap().zeta_team_id; // let zeta_team_id = &test_env.dummy.zeta_team_id;
// // Link alpha team to zeta org // // Link alpha team to zeta org
// let resp = api.organization_add_project(zeta_organization_id, alpha_project_id, USER_USER_PAT).await; // let resp = api.organization_add_project(zeta_organization_id, alpha_project_id, USER_USER_PAT).await;

View File

@@ -59,8 +59,8 @@ pub async fn get_user_projects_after_deleting_project_shows_removal() {
#[actix_rt::test] #[actix_rt::test]
pub async fn get_user_projects_after_joining_team_shows_team_projects() { pub async fn get_user_projects_after_joining_team_shows_team_projects() {
with_test_environment_all(None, |test_env| async move { 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_team_id = &test_env.dummy.project_alpha.team_id;
let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; let alpha_project_id = &test_env.dummy.project_alpha.project_id;
let api = test_env.api; let api = test_env.api;
api.get_user_projects_deserialized_common(FRIEND_USER_ID, FRIEND_USER_PAT) api.get_user_projects_deserialized_common(FRIEND_USER_ID, FRIEND_USER_PAT)
.await; .await;
@@ -82,8 +82,8 @@ pub async fn get_user_projects_after_joining_team_shows_team_projects() {
#[actix_rt::test] #[actix_rt::test]
pub async fn get_user_projects_after_leaving_team_shows_no_team_projects() { pub async fn get_user_projects_after_leaving_team_shows_no_team_projects() {
with_test_environment_all(None, |test_env| async move { 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_team_id = &test_env.dummy.project_alpha.team_id;
let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; let alpha_project_id = &test_env.dummy.project_alpha.project_id;
let api = test_env.api; let api = test_env.api;
api.add_user_to_team(alpha_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT) api.add_user_to_team(alpha_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT)
.await; .await;

View File

@@ -8,13 +8,7 @@ use crate::common::{
#[actix_rt::test] #[actix_rt::test]
pub async fn get_user_notifications_after_team_invitation_returns_notification() { pub async fn get_user_notifications_after_team_invitation_returns_notification() {
with_test_environment(None, |test_env: TestEnvironment<ApiV2>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV2>| async move {
let alpha_team_id = test_env let alpha_team_id = test_env.dummy.project_alpha.team_id.clone();
.dummy
.as_ref()
.unwrap()
.project_alpha
.team_id
.clone();
let api = test_env.api; let api = test_env.api;
api.add_user_to_team(&alpha_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT) api.add_user_to_team(&alpha_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT)
.await; .await;

View File

@@ -228,10 +228,10 @@ async fn test_add_remove_project() {
#[actix_rt::test] #[actix_rt::test]
async fn permissions_upload_version() { async fn permissions_upload_version() {
with_test_environment(None, |test_env: TestEnvironment<ApiV2>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV2>| async move {
let alpha_project_id = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; let alpha_project_id = &test_env.dummy.project_alpha.project_id;
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;
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;
let alpha_file_hash = &test_env.dummy.as_ref().unwrap().project_alpha.file_hash; let alpha_file_hash = &test_env.dummy.project_alpha.file_hash;
let api = &test_env.api; let api = &test_env.api;
let basic_mod_different_file = TestFile::BasicModDifferent; let basic_mod_different_file = TestFile::BasicModDifferent;
@@ -331,7 +331,7 @@ pub async fn test_patch_v2() {
with_test_environment(None, |test_env: TestEnvironment<ApiV2>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV2>| async move {
let api = &test_env.api; 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. // Sucessful request to patch many fields.
let resp = api let resp = api
@@ -437,8 +437,8 @@ async fn permissions_patch_project_v2() {
pub async fn test_bulk_edit_links() { pub async fn test_bulk_edit_links() {
with_test_environment(None, |test_env: TestEnvironment<ApiV2>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV2>| async move {
let api = &test_env.api; let api = &test_env.api;
let alpha_project_id: &str = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; let alpha_project_id: &str = &test_env.dummy.project_alpha.project_id;
let beta_project_id: &str = &test_env.dummy.as_ref().unwrap().project_beta.project_id; let beta_project_id: &str = &test_env.dummy.project_beta.project_id;
let resp = api let resp = api
.edit_project_bulk( .edit_project_bulk(

View File

@@ -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_project_creation_data;
use crate::common::api_v2::request_data::get_public_version_creation_data;
use crate::common::api_v2::ApiV2; use crate::common::api_v2::ApiV2;
use crate::common::dummy_data::TestFile; use crate::common::dummy_data::TestFile;
use crate::common::environment::with_test_environment; use crate::common::environment::with_test_environment;
use crate::common::environment::TestEnvironment; use crate::common::environment::TestEnvironment;
use crate::common::scopes::ScopeTest; use crate::common::scopes::ScopeTest;
use actix_web::test;
use labrinth::models::ids::base62_impl::parse_base62; use labrinth::models::ids::base62_impl::parse_base62;
use labrinth::models::pats::Scopes; use labrinth::models::pats::Scopes;
use labrinth::models::projects::ProjectId; use labrinth::models::projects::ProjectId;
use labrinth::util::actix::AppendsMultipart;
// Project version creation scopes // Project version creation scopes
#[actix_rt::test] #[actix_rt::test]
pub async fn project_version_create_scopes() { pub async fn project_version_create_scopes() {
with_test_environment(None, |test_env: TestEnvironment<ApiV2>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV2>| async move {
let api = &test_env.api;
// Create project // Create project
let create_project = Scopes::PROJECT_CREATE; let create_project = Scopes::PROJECT_CREATE;
let req_gen = || { let req_gen = |pat: Option<String>| async move {
let creation_data = let creation_data =
get_public_project_creation_data("demo", Some(TestFile::BasicMod), None); get_public_project_creation_data("demo", Some(TestFile::BasicMod), None);
test::TestRequest::post() api.create_project(creation_data, pat.as_deref()).await
.uri("/v2/project")
.set_multipart(creation_data.segment_data)
}; };
let (_, success) = ScopeTest::new(&test_env) let (_, success) = ScopeTest::new(&test_env)
.test(req_gen, create_project) .test(req_gen, create_project)
@@ -34,17 +32,16 @@ pub async fn project_version_create_scopes() {
// Add version to project // Add version to project
let create_version = Scopes::VERSION_CREATE; let create_version = Scopes::VERSION_CREATE;
let req_gen = || { let req_gen = |pat: Option<String>| async move {
let creation_data = get_public_version_creation_data( api.add_public_version(
project_id, project_id,
"1.2.3.4", "1.2.3.4",
TestFile::BasicModDifferent, TestFile::BasicModDifferent,
None, None,
None, None,
); pat.as_deref(),
test::TestRequest::post() )
.uri("/v2/version") .await
.set_multipart(creation_data.segment_data)
}; };
ScopeTest::new(&test_env) ScopeTest::new(&test_env)
.test(req_gen, create_version) .test(req_gen, create_version)

View File

@@ -17,7 +17,7 @@ async fn transfer_ownership_v2() {
with_test_environment(None, |test_env: TestEnvironment<ApiV2>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV2>| async move {
let api = &test_env.api; 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) // Cannot set friend as owner (not a member)
let resp = api let resp = api

View File

@@ -9,6 +9,7 @@ use serde_json::json;
use crate::common::api_common::{ApiProject, ApiVersion}; use crate::common::api_common::{ApiProject, ApiVersion};
use crate::common::api_v2::ApiV2; use crate::common::api_v2::ApiV2;
use crate::common::dummy_data::{DummyProjectAlpha, DummyProjectBeta};
use crate::common::environment::{with_test_environment, TestEnvironment}; use crate::common::environment::{with_test_environment, TestEnvironment};
use crate::common::{ use crate::common::{
database::{ENEMY_USER_PAT, USER_USER_PAT}, database::{ENEMY_USER_PAT, USER_USER_PAT},
@@ -20,7 +21,7 @@ pub async fn test_patch_version() {
with_test_environment(None, |test_env: TestEnvironment<ApiV2>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV2>| async move {
let api = &test_env.api; 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. // // First, we do some patch requests that should fail.
// // Failure because the user is not authorized. // // Failure because the user is not authorized.
@@ -132,18 +133,18 @@ async fn version_updates() {
// Test setup and dummy data // Test setup and dummy data
with_test_environment(None, |test_env: TestEnvironment<ApiV2>| async move { with_test_environment(None, |test_env: TestEnvironment<ApiV2>| async move {
let api = &test_env.api; let api = &test_env.api;
let DummyProjectAlpha {
let alpha_project_id: &String = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; project_id: alpha_project_id,
let alpha_project_id_parsed = test_env project_id_parsed: alpha_project_id_parsed,
.dummy version_id: alpha_version_id,
.as_ref() file_hash: alpha_version_hash,
.unwrap() ..
.project_alpha } = &test_env.dummy.project_alpha;
.project_id_parsed; let DummyProjectBeta {
let alpha_version_id = &test_env.dummy.as_ref().unwrap().project_alpha.version_id; version_id: beta_version_id,
let beta_version_id = &test_env.dummy.as_ref().unwrap().project_beta.version_id; file_hash: beta_version_hash,
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; } = &test_env.dummy.project_beta;
// Quick test, using get version from hash // Quick test, using get version from hash
let version = api let version = api
@@ -224,7 +225,7 @@ async fn version_updates() {
{ {
let version = api let version = api
.add_public_version_deserialized_common( .add_public_version_deserialized_common(
alpha_project_id_parsed, *alpha_project_id_parsed,
version_number, version_number,
TestFile::build_random_jar(), TestFile::build_random_jar(),
None, None,

View File

@@ -2,7 +2,7 @@ use std::collections::HashMap;
use crate::common::api_common::ApiVersion; use crate::common::api_common::ApiVersion;
use crate::common::database::*; 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 crate::common::{asserts::assert_status, get_json_val_str};
use actix_http::StatusCode; use actix_http::StatusCode;
use actix_web::test; use actix_web::test;
@@ -27,9 +27,15 @@ async fn test_get_version() {
// Test setup and dummy data // Test setup and dummy data
with_test_environment_all(None, |test_env| async move { with_test_environment_all(None, |test_env| async move {
let api = &test_env.api; let api = &test_env.api;
let alpha_project_id: &String = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; let DummyProjectAlpha {
let alpha_version_id = &test_env.dummy.as_ref().unwrap().project_alpha.version_id; project_id: alpha_project_id,
let beta_version_id = &test_env.dummy.as_ref().unwrap().project_beta.version_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 // Perform request on dummy data
let version = api let version = api
@@ -82,19 +88,18 @@ async fn version_updates() {
None, None,
|test_env: common::environment::TestEnvironment<ApiV3>| async move { |test_env: common::environment::TestEnvironment<ApiV3>| async move {
let api = &test_env.api; let api = &test_env.api;
let DummyProjectAlpha {
let alpha_project_id: &String = project_id: alpha_project_id,
&test_env.dummy.as_ref().unwrap().project_alpha.project_id; project_id_parsed: alpha_project_id_parsed,
let alpha_project_id_parsed = test_env version_id: alpha_version_id,
.dummy file_hash: alpha_version_hash,
.as_ref() ..
.unwrap() } = &test_env.dummy.project_alpha;
.project_alpha let DummyProjectBeta {
.project_id_parsed; version_id: beta_version_id,
let alpha_version_id = &test_env.dummy.as_ref().unwrap().project_alpha.version_id; file_hash: beta_version_hash,
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; } = &test_env.dummy.project_beta;
let beta_version_hash = &test_env.dummy.as_ref().unwrap().project_beta.file_hash;
// Quick test, using get version from hash // Quick test, using get version from hash
let version = api let version = api
@@ -179,7 +184,7 @@ async fn version_updates() {
{ {
let version = api let version = api
.add_public_version_deserialized( .add_public_version_deserialized(
alpha_project_id_parsed, *alpha_project_id_parsed,
version_number, version_number,
TestFile::build_random_jar(), TestFile::build_random_jar(),
None, None,
@@ -382,17 +387,15 @@ pub async fn test_patch_version() {
with_test_environment_all(None, |test_env| async move { with_test_environment_all(None, |test_env| async move {
let api = &test_env.api; 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;
let beta_project_id = &test_env.dummy.as_ref().unwrap().project_beta.project_id; let DummyProjectBeta {
let beta_project_id_parsed = &test_env project_id: beta_project_id,
.dummy project_id_parsed: beta_project_id_parsed,
.as_ref() ..
.unwrap() } = &test_env.dummy.project_beta;
.project_beta
.project_id_parsed;
// // First, we do some patch requests that should fail. // First, we do some patch requests that should fail.
// // Failure because the user is not authorized. // Failure because the user is not authorized.
let resp = api let resp = api
.edit_version( .edit_version(
alpha_version_id, alpha_version_id,
@@ -510,8 +513,8 @@ pub async fn test_patch_version() {
pub async fn test_project_versions() { pub async fn test_project_versions() {
with_test_environment_all(None, |test_env| async move { with_test_environment_all(None, |test_env| async move {
let api = &test_env.api; let api = &test_env.api;
let alpha_project_id: &String = &test_env.dummy.as_ref().unwrap().project_alpha.project_id; let alpha_project_id: &String = &test_env.dummy.project_alpha.project_id;
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;
let versions = api let versions = api
.get_project_versions_deserialized_common( .get_project_versions_deserialized_common(
@@ -534,7 +537,7 @@ pub async fn test_project_versions() {
#[actix_rt::test] #[actix_rt::test]
async fn can_create_version_with_ordering() { async fn can_create_version_with_ordering() {
with_test_environment_all(None, |env| async move { 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( let new_version_id = get_json_val_str(
env.api env.api
@@ -562,7 +565,7 @@ async fn can_create_version_with_ordering() {
#[actix_rt::test] #[actix_rt::test]
async fn edit_version_ordering_works() { async fn edit_version_ordering_works() {
with_test_environment_all(None, |env| async move { 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 let resp = env
.api .api
@@ -582,8 +585,8 @@ async fn edit_version_ordering_works() {
#[actix_rt::test] #[actix_rt::test]
async fn version_ordering_for_specified_orderings_orders_lower_order_first() { async fn version_ordering_for_specified_orderings_orders_lower_order_first() {
with_test_environment_all(None, |env| async move { 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 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 new_version_id = get_json_val_str( let new_version_id = get_json_val_str(
env.api env.api
.add_public_version_deserialized_common( .add_public_version_deserialized_common(
@@ -616,8 +619,8 @@ async fn version_ordering_for_specified_orderings_orders_lower_order_first() {
#[actix_rt::test] #[actix_rt::test]
async fn version_ordering_when_unspecified_orders_oldest_first() { async fn version_ordering_when_unspecified_orders_oldest_first() {
with_test_environment_all(None, |env| async move { 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 alpha_version_id: String = env.dummy.as_ref().unwrap().project_alpha.version_id.clone(); let alpha_version_id: String = env.dummy.project_alpha.version_id.clone();
let new_version_id = get_json_val_str( let new_version_id = get_json_val_str(
env.api env.api
.add_public_version_deserialized_common( .add_public_version_deserialized_common(
@@ -647,8 +650,8 @@ async fn version_ordering_when_unspecified_orders_oldest_first() {
#[actix_rt::test] #[actix_rt::test]
async fn version_ordering_when_specified_orders_specified_before_unspecified() { async fn version_ordering_when_specified_orders_specified_before_unspecified() {
with_test_environment_all(None, |env| async move { 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 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 new_version_id = get_json_val_str( let new_version_id = get_json_val_str(
env.api env.api
.add_public_version_deserialized_common( .add_public_version_deserialized_common(