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::routes::{v2_reroute, v3};
use crate::search::SearchConfig;
use actix_web::{delete, get, patch, post, web, HttpRequest, HttpResponse};
use chrono::{DateTime, Utc};
use actix_web::{delete, get, patch, web, HttpRequest, HttpResponse};
use serde::{Deserialize, Serialize};
use sqlx::PgPool;
use validator::Validate;
@@ -24,7 +23,6 @@ pub fn config(cfg: &mut web::ServiceConfig) {
.service(version_get)
.service(version_delete)
.service(version_edit)
.service(version_schedule)
.service(super::version_creation::upload_file_to_version),
);
}
@@ -275,36 +273,3 @@ pub async fn version_delete(
.await
.or_else(v2_reroute::flatten_404_error)
}
#[derive(Deserialize)]
pub struct SchedulingData {
pub time: DateTime<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::validate::validation_errors_to_string;
use actix_web::{web, HttpRequest, HttpResponse};
use chrono::{DateTime, Utc};
use chrono::Utc;
use futures::TryStreamExt;
use itertools::Itertools;
use serde::{Deserialize, Serialize};
@@ -54,7 +54,6 @@ pub fn config(cfg: &mut web::ServiceConfig) {
.route("{id}/gallery", web::delete().to(delete_gallery_item))
.route("{id}/follow", web::post().to(project_follow))
.route("{id}/follow", web::delete().to(project_unfollow))
.route("{id}/schedule", web::post().to(project_schedule))
.service(
web::scope("{project_id}")
.route(
@@ -1305,100 +1304,6 @@ pub async fn bulk_edit_project_categories(
Ok(())
}
#[derive(Deserialize)]
pub struct SchedulingData {
pub time: DateTime<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)]
pub struct Extension {
pub ext: String,

View File

@@ -25,7 +25,6 @@ use crate::search::SearchConfig;
use crate::util::img;
use crate::util::validate::validation_errors_to_string;
use actix_web::{web, HttpRequest, HttpResponse};
use chrono::{DateTime, Utc};
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use sqlx::PgPool;
@@ -43,7 +42,6 @@ pub fn config(cfg: &mut web::ServiceConfig) {
.route("{id}", web::get().to(version_get))
.route("{id}", web::patch().to(version_edit))
.route("{id}", web::delete().to(version_delete))
.route("{id}/schedule", web::post().to(version_schedule))
.route(
"{version_id}/file",
web::post().to(super::version_creation::upload_file_to_version),
@@ -828,108 +826,6 @@ pub async fn version_list(
}
}
#[derive(Deserialize)]
pub struct SchedulingData {
pub time: DateTime<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(
req: HttpRequest,
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 {
let api = &test_env.api;
let alpha_project_id = test_env
.dummy
.as_ref()
.unwrap()
.project_alpha
.project_id
.clone();
let alpha_project_id = test_env.dummy.project_alpha.project_id.clone();
let pool = test_env.db.pool.clone();
@@ -143,27 +137,9 @@ fn to_f64_vec_rounded_up(d: Vec<Decimal>) -> Vec<f64> {
#[actix_rt::test]
pub async fn permissions_analytics_revenue() {
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
let alpha_project_id = test_env
.dummy
.as_ref()
.unwrap()
.project_alpha
.project_id
.clone();
let alpha_version_id = test_env
.dummy
.as_ref()
.unwrap()
.project_alpha
.version_id
.clone();
let alpha_team_id = test_env
.dummy
.as_ref()
.unwrap()
.project_alpha
.team_id
.clone();
let alpha_project_id = test_env.dummy.project_alpha.project_id.clone();
let alpha_version_id = test_env.dummy.project_alpha.version_id.clone();
let alpha_team_id = test_env.dummy.project_alpha.team_id.clone();
let api = &test_env.api;

View File

@@ -12,7 +12,7 @@ use crate::common::{api_v2::ApiV2, api_v3::ApiV3, dummy_data::TestFile};
use super::{
models::{CommonProject, CommonVersion},
request_data::{ImageData, ProjectCreationRequestData},
Api, ApiProject, ApiTags, ApiTeams, ApiVersion,
Api, ApiProject, ApiTags, ApiTeams, ApiUser, ApiVersion,
};
#[derive(Clone)]
@@ -71,17 +71,28 @@ delegate_api_variant!(
[remove_project, ServiceResponse, project_slug_or_id: &str, pat: Option<&str>],
[get_project, ServiceResponse, id_or_slug: &str, pat: Option<&str>],
[get_project_deserialized_common, CommonProject, id_or_slug: &str, pat: Option<&str>],
[get_projects, ServiceResponse, ids_or_slugs: &[&str], pat: Option<&str>],
[get_project_dependencies, ServiceResponse, id_or_slug: &str, pat: Option<&str>],
[get_user_projects, ServiceResponse, user_id_or_username: &str, pat: Option<&str>],
[get_user_projects_deserialized_common, Vec<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_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>],
[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>],
[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>],
[create_report, ServiceResponse, report_type: &str, id: &str, item_type: crate::common::api_common::models::CommonItemType, body: &str, pat: Option<&str>],
[get_report, ServiceResponse, id: &str, pat: Option<&str>],
[get_reports, ServiceResponse, ids: &[&str], pat: Option<&str>],
[get_user_reports, ServiceResponse, pat: Option<&str>],
[edit_report, ServiceResponse, id: &str, patch: serde_json::Value, pat: Option<&str>],
[delete_report, ServiceResponse, id: &str, pat: Option<&str>],
[get_thread, ServiceResponse, id: &str, pat: Option<&str>],
[get_threads, ServiceResponse, ids: &[&str], pat: Option<&str>],
[write_to_thread, ServiceResponse, id: &str, r#type : &str, message: &str, pat: Option<&str>],
[get_moderation_inbox, ServiceResponse, pat: Option<&str>],
[read_thread, ServiceResponse, id: &str, pat: Option<&str>],
[delete_thread_message, ServiceResponse, id: &str, pat: Option<&str>],
}
);
@@ -100,6 +111,7 @@ delegate_api_variant!(
impl ApiTeams for GenericApi {
[get_team_members, ServiceResponse, team_id: &str, pat: Option<&str>],
[get_team_members_deserialized_common, Vec<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_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>],
@@ -110,9 +122,23 @@ delegate_api_variant!(
[transfer_team_ownership, ServiceResponse, team_id: &str, user_id: &str, pat: Option<&str>],
[get_user_notifications, ServiceResponse, user_id: &str, pat: Option<&str>],
[get_user_notifications_deserialized_common, Vec<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_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>],
[delete_notification, ServiceResponse, notification_id: &str, pat: Option<&str>],
[delete_notifications, ServiceResponse, ids: &[&str], pat: Option<&str>],
}
);
delegate_api_variant!(
#[async_trait(?Send)]
impl ApiUser for GenericApi {
[get_user, ServiceResponse, id_or_username: &str, pat: Option<&str>],
[get_current_user, ServiceResponse, pat: Option<&str>],
[edit_user, ServiceResponse, id_or_username: &str, patch: serde_json::Value, pat: Option<&str>],
[delete_user, ServiceResponse, id_or_username: &str, pat: Option<&str>],
}
);
@@ -125,6 +151,7 @@ delegate_api_variant!(
[get_version_deserialized_common, CommonVersion, id_or_slug: &str, pat: Option<&str>],
[get_versions, ServiceResponse, ids_or_slugs: Vec<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>],
[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>],

View File

@@ -7,7 +7,6 @@ use self::models::{
use self::request_data::{ImageData, ProjectCreationRequestData};
use actix_web::dev::ServiceResponse;
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use labrinth::{
models::{
projects::{ProjectId, VersionType},
@@ -27,7 +26,7 @@ pub trait ApiBuildable: Api {
}
#[async_trait(?Send)]
pub trait Api: ApiProject + ApiTags + ApiTeams + ApiVersion {
pub trait Api: ApiProject + ApiTags + ApiTeams + ApiUser + ApiVersion {
async fn call(&self, req: actix_http::Request) -> ServiceResponse;
async fn reset_search_index(&self) -> ServiceResponse;
}
@@ -59,6 +58,12 @@ pub trait ApiProject {
id_or_slug: &str,
pat: Option<&str>,
) -> CommonProject;
async fn get_projects(&self, ids_or_slugs: &[&str], pat: Option<&str>) -> ServiceResponse;
async fn get_project_dependencies(
&self,
id_or_slug: &str,
pat: Option<&str>,
) -> ServiceResponse;
async fn get_user_projects(
&self,
user_id_or_username: &str,
@@ -87,13 +92,6 @@ pub trait ApiProject {
icon: Option<ImageData>,
pat: Option<&str>,
) -> 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)]
async fn add_gallery_item(
&self,
@@ -127,6 +125,27 @@ pub trait ApiProject {
pat: Option<&str>,
) -> ServiceResponse;
async fn get_report(&self, id: &str, pat: Option<&str>) -> ServiceResponse;
async fn get_reports(&self, ids: &[&str], pat: Option<&str>) -> ServiceResponse;
async fn get_user_reports(&self, pat: Option<&str>) -> ServiceResponse;
async fn edit_report(
&self,
id: &str,
patch: serde_json::Value,
pat: Option<&str>,
) -> ServiceResponse;
async fn delete_report(&self, id: &str, pat: Option<&str>) -> ServiceResponse;
async fn get_thread(&self, id: &str, pat: Option<&str>) -> ServiceResponse;
async fn get_threads(&self, ids: &[&str], pat: Option<&str>) -> ServiceResponse;
async fn write_to_thread(
&self,
id: &str,
r#type: &str,
message: &str,
pat: Option<&str>,
) -> ServiceResponse;
async fn get_moderation_inbox(&self, pat: Option<&str>) -> ServiceResponse;
async fn read_thread(&self, id: &str, pat: Option<&str>) -> ServiceResponse;
async fn delete_thread_message(&self, id: &str, pat: Option<&str>) -> ServiceResponse;
}
#[async_trait(?Send)]
@@ -145,6 +164,7 @@ pub trait ApiTeams {
team_id: &str,
pat: Option<&str>,
) -> Vec<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_deserialized_common(
&self,
@@ -187,11 +207,14 @@ pub trait ApiTeams {
user_id: &str,
pat: Option<&str>,
) -> 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(
&self,
notification_id: &str,
pat: Option<&str>,
) -> ServiceResponse;
async fn mark_notifications_read(&self, ids: &[&str], pat: Option<&str>) -> ServiceResponse;
async fn add_user_to_team(
&self,
team_id: &str,
@@ -205,6 +228,20 @@ pub trait ApiTeams {
notification_id: &str,
pat: Option<&str>,
) -> ServiceResponse;
async fn delete_notifications(&self, ids: &[&str], pat: Option<&str>) -> ServiceResponse;
}
#[async_trait(?Send)]
pub trait ApiUser {
async fn get_user(&self, id_or_username: &str, pat: Option<&str>) -> ServiceResponse;
async fn get_current_user(&self, pat: Option<&str>) -> ServiceResponse;
async fn edit_user(
&self,
id_or_username: &str,
patch: serde_json::Value,
pat: Option<&str>,
) -> ServiceResponse;
async fn delete_user(&self, id_or_username: &str, pat: Option<&str>) -> ServiceResponse;
}
#[async_trait(?Send)]
@@ -227,34 +264,40 @@ pub trait ApiVersion {
modify_json: Option<json_patch::Patch>,
pat: Option<&str>,
) -> CommonVersion;
async fn get_version(&self, id_or_slug: &str, pat: Option<&str>) -> ServiceResponse;
async fn get_version(&self, id: &str, pat: Option<&str>) -> ServiceResponse;
async fn get_version_deserialized_common(
&self,
id_or_slug: &str,
pat: Option<&str>,
) -> CommonVersion;
async fn get_versions(&self, ids_or_slugs: Vec<String>, pat: Option<&str>) -> ServiceResponse;
async fn get_versions(&self, ids: Vec<String>, pat: Option<&str>) -> ServiceResponse;
async fn get_versions_deserialized_common(
&self,
ids_or_slugs: Vec<String>,
ids: Vec<String>,
pat: Option<&str>,
) -> Vec<CommonVersion>;
async fn download_version_redirect(
&self,
hash: &str,
algorithm: &str,
pat: Option<&str>,
) -> ServiceResponse;
async fn edit_version(
&self,
id_or_slug: &str,
id: &str,
patch: serde_json::Value,
pat: Option<&str>,
) -> ServiceResponse;
async fn get_version_from_hash(
&self,
id_or_slug: &str,
hash: &str,
algorithm: &str,
pat: Option<&str>,
) -> ServiceResponse;
async fn get_version_from_hash_deserialized_common(
&self,
id_or_slug: &str,
hash: &str,
algorithm: &str,
pat: Option<&str>,
) -> CommonVersion;
async fn get_versions_from_hashes(

View File

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

View File

@@ -15,7 +15,6 @@ use actix_web::{
};
use async_trait::async_trait;
use bytes::Bytes;
use chrono::{DateTime, Utc};
use labrinth::{
models::v2::{projects::LegacyProject, search::LegacySearchResults},
util::actix::AppendsMultipart,
@@ -173,6 +172,30 @@ impl ApiProject for ApiV2 {
serde_json::from_value(value).unwrap()
}
async fn get_projects(&self, ids_or_slugs: &[&str], pat: Option<&str>) -> ServiceResponse {
let ids_or_slugs = serde_json::to_string(ids_or_slugs).unwrap();
let req = test::TestRequest::get()
.uri(&format!(
"/v2/projects?ids={encoded}",
encoded = urlencoding::encode(&ids_or_slugs)
))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn get_project_dependencies(
&self,
id_or_slug: &str,
pat: Option<&str>,
) -> ServiceResponse {
let req = TestRequest::get()
.uri(&format!("/v2/project/{id_or_slug}/dependencies"))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn get_user_projects(
&self,
user_id_or_username: &str,
@@ -275,7 +298,7 @@ impl ApiProject for ApiV2 {
pat: Option<&str>,
) -> ServiceResponse {
let req = test::TestRequest::post()
.uri("/v3/report")
.uri("/v2/report")
.append_pat(pat)
.set_json(json!(
{
@@ -292,28 +315,123 @@ impl ApiProject for ApiV2 {
async fn get_report(&self, id: &str, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::get()
.uri(&format!("/v3/report/{id}", id = id))
.uri(&format!("/v2/report/{id}"))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn schedule_project(
async fn get_reports(&self, ids: &[&str], pat: Option<&str>) -> ServiceResponse {
let ids_str = serde_json::to_string(ids).unwrap();
let req = test::TestRequest::get()
.uri(&format!(
"/v2/reports?ids={encoded}",
encoded = urlencoding::encode(&ids_str)
))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn get_user_reports(&self, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::get()
.uri("/v2/report")
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn delete_report(&self, id: &str, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::delete()
.uri(&format!("/v2/report/{id}"))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn edit_report(
&self,
id_or_slug: &str,
requested_status: &str,
date: DateTime<Utc>,
id: &str,
patch: serde_json::Value,
pat: Option<&str>,
) -> ServiceResponse {
let req = test::TestRequest::patch()
.uri(&format!("/v2/report/{id}"))
.append_pat(pat)
.set_json(patch)
.to_request();
self.call(req).await
}
async fn get_thread(&self, id: &str, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::get()
.uri(&format!("/v3/thread/{id}"))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn get_threads(&self, ids: &[&str], pat: Option<&str>) -> ServiceResponse {
let ids_str = serde_json::to_string(ids).unwrap();
let req = test::TestRequest::get()
.uri(&format!(
"/v3/threads?ids={encoded}",
encoded = urlencoding::encode(&ids_str)
))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn write_to_thread(
&self,
id: &str,
r#type: &str,
content: &str,
pat: Option<&str>,
) -> ServiceResponse {
let req = test::TestRequest::post()
.uri(&format!("/v2/version/{id_or_slug}/schedule"))
.set_json(json!(
{
"requested_status": requested_status,
"time": date,
.uri(&format!("/v2/thread/{id}"))
.append_pat(pat)
.set_json(json!({
"body" : {
"type": r#type,
"body": content,
}
))
}))
.to_request();
self.call(req).await
}
async fn get_moderation_inbox(&self, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::get()
.uri("/v2/thread/inbox")
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn read_thread(&self, id: &str, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::post()
.uri(&format!("/v2/thread/{id}/read"))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn delete_thread_message(&self, id: &str, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::delete()
.uri(&format!("/v2/message/{id}"))
.append_pat(pat)
.to_request();

View File

@@ -72,6 +72,22 @@ impl ApiTeams for ApiV2 {
test::read_body_json(resp).await
}
async fn get_teams_members(
&self,
ids_or_titles: &[&str],
pat: Option<&str>,
) -> ServiceResponse {
let ids_or_titles = serde_json::to_string(ids_or_titles).unwrap();
let req = test::TestRequest::get()
.uri(&format!(
"/v2/teams?ids={}",
urlencoding::encode(&ids_or_titles)
))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn get_project_members(&self, id_or_title: &str, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::get()
.uri(&format!("/v2/project/{id_or_title}/members"))
@@ -192,6 +208,30 @@ impl ApiTeams for ApiV2 {
serde_json::from_value(value).unwrap()
}
async fn get_notification(&self, notification_id: &str, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::get()
.uri(&format!("/v2/notification/{notification_id}"))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn get_notifications(
&self,
notification_ids: &[&str],
pat: Option<&str>,
) -> ServiceResponse {
let notification_ids = serde_json::to_string(notification_ids).unwrap();
let req = test::TestRequest::get()
.uri(&format!(
"/v2/notifications?ids={}",
urlencoding::encode(&notification_ids)
))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn mark_notification_read(
&self,
notification_id: &str,
@@ -204,6 +244,22 @@ impl ApiTeams for ApiV2 {
self.call(req).await
}
async fn mark_notifications_read(
&self,
notification_ids: &[&str],
pat: Option<&str>,
) -> ServiceResponse {
let notification_ids = serde_json::to_string(notification_ids).unwrap();
let req = test::TestRequest::patch()
.uri(&format!(
"/v2/notifications?ids={}",
urlencoding::encode(&notification_ids)
))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn add_user_to_team(
&self,
team_id: &str,
@@ -235,4 +291,20 @@ impl ApiTeams for ApiV2 {
.to_request();
self.call(req).await
}
async fn delete_notifications(
&self,
notification_ids: &[&str],
pat: Option<&str>,
) -> ServiceResponse {
let notification_ids = serde_json::to_string(notification_ids).unwrap();
let req = test::TestRequest::delete()
.uri(&format!(
"/v2/notifications?ids={}",
urlencoding::encode(&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()
}
async fn download_version_redirect(
&self,
hash: &str,
algorithm: &str,
pat: Option<&str>,
) -> ServiceResponse {
let req = test::TestRequest::get()
.uri(&format!("/v2/version_file/{hash}/download",))
.set_json(json!({
"algorithm": algorithm,
}))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn edit_version(
&self,
version_id: &str,

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 std::rc::Rc;
pub mod collections;
pub mod oauth;
pub mod oauth_clients;
pub mod organization;
@@ -16,6 +17,7 @@ pub mod project;
pub mod request_data;
pub mod tags;
pub mod team;
pub mod user;
pub mod version;
#[derive(Clone)]

View File

@@ -46,6 +46,22 @@ impl ApiV3 {
test::read_body_json(resp).await
}
pub async fn get_organizations(
&self,
ids_or_titles: &[&str],
pat: Option<&str>,
) -> ServiceResponse {
let ids_or_titles = serde_json::to_string(ids_or_titles).unwrap();
let req = test::TestRequest::get()
.uri(&format!(
"/v3/organizations?ids={}",
urlencoding::encode(&ids_or_titles)
))
.append_pat(pat)
.to_request();
self.call(req).await
}
pub async fn get_organization_projects(
&self,
id_or_title: &str,

View File

@@ -126,6 +126,30 @@ impl ApiProject for ApiV3 {
serde_json::from_value(value).unwrap()
}
async fn get_projects(&self, ids_or_slugs: &[&str], pat: Option<&str>) -> ServiceResponse {
let ids_or_slugs = serde_json::to_string(ids_or_slugs).unwrap();
let req = test::TestRequest::get()
.uri(&format!(
"/v2/projects?ids={encoded}",
encoded = urlencoding::encode(&ids_or_slugs)
))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn get_project_dependencies(
&self,
id_or_slug: &str,
pat: Option<&str>,
) -> ServiceResponse {
let req = TestRequest::get()
.uri(&format!("/v2/project/{id_or_slug}/dependencies"))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn get_user_projects(
&self,
user_id_or_username: &str,
@@ -245,28 +269,53 @@ impl ApiProject for ApiV3 {
async fn get_report(&self, id: &str, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::get()
.uri(&format!("/v3/report/{id}", id = id))
.uri(&format!("/v3/report/{id}"))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn schedule_project(
async fn get_reports(&self, ids: &[&str], pat: Option<&str>) -> ServiceResponse {
let ids_str = serde_json::to_string(ids).unwrap();
let req = test::TestRequest::get()
.uri(&format!(
"/v3/reports?ids={encoded}",
encoded = urlencoding::encode(&ids_str)
))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn get_user_reports(&self, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::get()
.uri("/v3/report")
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn edit_report(
&self,
id_or_slug: &str,
requested_status: &str,
date: DateTime<Utc>,
id: &str,
patch: serde_json::Value,
pat: Option<&str>,
) -> ServiceResponse {
let req = test::TestRequest::post()
.uri(&format!("/v3/version/{id_or_slug}/schedule"))
.set_json(json!(
{
"requested_status": requested_status,
"time": date,
}
))
let req = test::TestRequest::patch()
.uri(&format!("/v3/report/{id}"))
.append_pat(pat)
.set_json(patch)
.to_request();
self.call(req).await
}
async fn delete_report(&self, id: &str, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::delete()
.uri(&format!("/v3/report/{id}"))
.append_pat(pat)
.to_request();
@@ -355,6 +404,76 @@ impl ApiProject for ApiV3 {
self.call(req).await
}
async fn get_thread(&self, id: &str, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::get()
.uri(&format!("/v3/thread/{id}"))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn get_threads(&self, ids: &[&str], pat: Option<&str>) -> ServiceResponse {
let ids_str = serde_json::to_string(ids).unwrap();
let req = test::TestRequest::get()
.uri(&format!(
"/v3/threads?ids={encoded}",
encoded = urlencoding::encode(&ids_str)
))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn write_to_thread(
&self,
id: &str,
r#type: &str,
content: &str,
pat: Option<&str>,
) -> ServiceResponse {
let req = test::TestRequest::post()
.uri(&format!("/v3/thread/{id}"))
.append_pat(pat)
.set_json(json!({
"body": {
"type": r#type,
"body": content
}
}))
.to_request();
self.call(req).await
}
async fn get_moderation_inbox(&self, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::get()
.uri("/v3/thread/inbox")
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn read_thread(&self, id: &str, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::post()
.uri(&format!("/v3/thread/{id}/read"))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn delete_thread_message(&self, id: &str, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::delete()
.uri(&format!("/v3/message/{id}"))
.append_pat(pat)
.to_request();
self.call(req).await
}
}
impl ApiV3 {

View File

@@ -63,6 +63,22 @@ impl ApiTeams for ApiV3 {
serde_json::from_value(value).unwrap()
}
async fn get_teams_members(
&self,
ids_or_titles: &[&str],
pat: Option<&str>,
) -> ServiceResponse {
let ids_or_titles = serde_json::to_string(ids_or_titles).unwrap();
let req = test::TestRequest::get()
.uri(&format!(
"/v3/teams?ids={}",
urlencoding::encode(&ids_or_titles)
))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn get_project_members(&self, id_or_title: &str, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::get()
.uri(&format!("/v3/project/{id_or_title}/members"))
@@ -185,6 +201,30 @@ impl ApiTeams for ApiV3 {
serde_json::from_value(value).unwrap()
}
async fn get_notification(&self, notification_id: &str, pat: Option<&str>) -> ServiceResponse {
let req = test::TestRequest::get()
.uri(&format!("/v3/notification/{notification_id}"))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn get_notifications(
&self,
notification_ids: &[&str],
pat: Option<&str>,
) -> ServiceResponse {
let notification_ids = serde_json::to_string(notification_ids).unwrap();
let req = test::TestRequest::get()
.uri(&format!(
"/v3/notifications?ids={}",
urlencoding::encode(&notification_ids)
))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn mark_notification_read(
&self,
notification_id: &str,
@@ -196,6 +236,23 @@ impl ApiTeams for ApiV3 {
.to_request();
self.call(req).await
}
async fn mark_notifications_read(
&self,
notification_ids: &[&str],
pat: Option<&str>,
) -> ServiceResponse {
let notification_ids = serde_json::to_string(notification_ids).unwrap();
let req = test::TestRequest::patch()
.uri(&format!(
"/v3/notifications?ids={}",
urlencoding::encode(&notification_ids)
))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn add_user_to_team(
&self,
team_id: &str,
@@ -227,4 +284,20 @@ impl ApiTeams for ApiV3 {
.to_request();
self.call(req).await
}
async fn delete_notifications(
&self,
notification_ids: &[&str],
pat: Option<&str>,
) -> ServiceResponse {
let notification_ids = serde_json::to_string(notification_ids).unwrap();
let req = test::TestRequest::delete()
.uri(&format!(
"/v3/notifications?ids={}",
urlencoding::encode(&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
}
async fn download_version_redirect(
&self,
hash: &str,
algorithm: &str,
pat: Option<&str>,
) -> ServiceResponse {
let req = test::TestRequest::get()
.uri(&format!("/v3/version_file/{hash}/download",))
.set_json(json!({
"algorithm": algorithm,
}))
.append_pat(pat)
.to_request();
self.call(req).await
}
async fn get_version_from_hash(
&self,
hash: &str,

View File

@@ -168,21 +168,14 @@ impl TemporaryDatabase {
if !dummy_data_exists {
// Add dummy data
let temporary_test_env =
TestEnvironment::<ApiV3>::build_with_db(TemporaryDatabase {
pool: pool.clone(),
database_name: TEMPLATE_DATABASE_NAME.to_string(),
redis_pool: RedisPool::new(Some(generate_random_name(
"test_template_",
))),
})
.await;
dummy_data::add_dummy_data(
&temporary_test_env.setup_api,
temporary_test_env.db.clone(),
)
.await;
temporary_test_env.db.pool.close().await;
let db = TemporaryDatabase {
pool: pool.clone(),
database_name: TEMPLATE_DATABASE_NAME.to_string(),
redis_pool: RedisPool::new(Some(generate_random_name("test_template_"))),
};
let setup_api = TestEnvironment::<ApiV3>::build_setup_api(&db).await;
dummy_data::add_dummy_data(&setup_api, db.clone()).await;
db.pool.close().await;
}
pool.close().await;
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.
#[derive(Clone)]
pub struct TestEnvironment<A> {
// test_app: Rc<dyn LocalService>, // Rc as it's not Send
pub db: TemporaryDatabase,
pub api: A,
pub setup_api: ApiV3, // Used for setting up tests only (ie: in ScopesTest)
pub dummy: Option<dummy_data::DummyData>,
pub dummy: dummy_data::DummyData,
}
impl<A: ApiBuildable> TestEnvironment<A> {
async fn build(max_connections: Option<u32>) -> Self {
let db = TemporaryDatabase::create(max_connections).await;
let mut test_env = Self::build_with_db(db).await;
let dummy = dummy_data::get_dummy_data(&test_env.setup_api).await;
test_env.dummy = Some(dummy);
test_env
}
pub async fn build_with_db(db: TemporaryDatabase) -> Self {
let labrinth_config = setup(&db).await;
let api = A::build(labrinth_config.clone()).await;
let setup_api = ApiV3::build(labrinth_config).await;
let dummy = dummy_data::get_dummy_data(&setup_api).await;
Self {
db,
api: A::build(labrinth_config.clone()).await,
setup_api: ApiV3::build(labrinth_config.clone()).await,
dummy: None,
// test_app
api,
setup_api,
dummy,
}
}
pub async fn build_setup_api(db: &TemporaryDatabase) -> ApiV3 {
let labrinth_config = setup(db).await;
ApiV3::build(labrinth_config).await
}
}
impl<A: Api> TestEnvironment<A> {
@@ -108,7 +105,7 @@ impl<A: Api> TestEnvironment<A> {
let resp = self
.api
.add_user_to_team(
&self.dummy.as_ref().unwrap().project_alpha.team_id,
&self.dummy.project_alpha.team_id,
FRIEND_USER_ID,
None,
None,

View File

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

View File

@@ -7,7 +7,7 @@ use serde_json::json;
use crate::common::api_common::ApiVersion;
use crate::common::database::*;
use crate::common::dummy_data::TestFile;
use crate::common::dummy_data::{DummyProjectAlpha, DummyProjectBeta, TestFile};
// importing common module.
mod common;
@@ -17,33 +17,16 @@ mod common;
async fn creating_loader_fields() {
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
let api = &test_env.api;
let alpha_project_id = &test_env
.dummy
.as_ref()
.unwrap()
.project_alpha
.project_id
.clone();
let alpha_project_id_parsed = test_env
.dummy
.as_ref()
.unwrap()
.project_alpha
.project_id_parsed;
let beta_project_id_parsed = test_env
.dummy
.as_ref()
.unwrap()
.project_beta
.project_id_parsed;
let alpha_version_id = &test_env
.dummy
.as_ref()
.unwrap()
.project_alpha
.version_id
.clone();
let DummyProjectAlpha {
project_id: alpha_project_id,
project_id_parsed: alpha_project_id_parsed,
version_id: alpha_version_id,
..
} = &test_env.dummy.project_alpha;
let DummyProjectBeta {
project_id_parsed: beta_project_id_parsed,
..
} = &test_env.dummy.project_beta;
// ALL THE FOLLOWING FOR CREATE AND PATCH
// Cannot create a version with an extra argument that cannot be tied to a loader field ("invalid loader field")
@@ -51,7 +34,7 @@ async fn creating_loader_fields() {
// - Create version
let resp = api
.add_public_version(
alpha_project_id_parsed,
*alpha_project_id_parsed,
"1.0.0",
TestFile::build_random_jar(),
None,
@@ -84,7 +67,7 @@ async fn creating_loader_fields() {
// - Create version
let resp = api
.add_public_version(
alpha_project_id_parsed,
*alpha_project_id_parsed,
"1.0.0",
TestFile::build_random_jar(),
None,
@@ -117,7 +100,7 @@ async fn creating_loader_fields() {
// - Create version
let resp = api
.add_public_version(
alpha_project_id_parsed,
*alpha_project_id_parsed,
"1.0.0",
TestFile::build_random_jar(),
None,
@@ -139,7 +122,7 @@ async fn creating_loader_fields() {
// - Create version
let resp = api
.add_public_version(
alpha_project_id_parsed,
*alpha_project_id_parsed,
"1.0.0",
TestFile::build_random_jar(),
None,
@@ -161,7 +144,7 @@ async fn creating_loader_fields() {
// - Create version
let resp: actix_web::dev::ServiceResponse = api
.add_public_version(
alpha_project_id_parsed,
*alpha_project_id_parsed,
"1.0.0",
TestFile::build_random_jar(),
None,
@@ -201,7 +184,7 @@ async fn creating_loader_fields() {
// - Create version
let resp = api
.add_public_version(
alpha_project_id_parsed,
*alpha_project_id_parsed,
"1.0.0",
TestFile::build_random_jar(),
None,
@@ -236,7 +219,7 @@ async fn creating_loader_fields() {
// - Create version
let v = api
.add_public_version_deserialized(
alpha_project_id_parsed,
*alpha_project_id_parsed,
"1.0.0",
TestFile::build_random_jar(),
None,
@@ -272,7 +255,7 @@ async fn creating_loader_fields() {
// - Create
let v = api
.add_public_version_deserialized(
alpha_project_id_parsed,
*alpha_project_id_parsed,
"1.0.0",
TestFile::build_random_jar(),
None,
@@ -325,7 +308,7 @@ async fn creating_loader_fields() {
// Now that we've created a version, we need to make sure that the Project's loader fields are updated (aggregate)
// First, add a new version
api.add_public_version_deserialized(
alpha_project_id_parsed,
*alpha_project_id_parsed,
"1.0.1",
TestFile::build_random_jar(),
None,
@@ -347,7 +330,7 @@ async fn creating_loader_fields() {
// Also, add one to the beta project
api.add_public_version_deserialized(
beta_project_id_parsed,
*beta_project_id_parsed,
"1.0.1",
TestFile::build_random_jar(),
None,

View File

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

View File

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

View File

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

View File

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

View File

@@ -60,6 +60,9 @@ pub async fn pat_full_test() {
let mock_pat_test = |token: &str| {
let token = token.to_string();
async {
// This uses a route directly instead of an api call because it doesn't relaly matter and we
// want it to succeed no matter what.
// This is an arbitrary request.
let req = test::TestRequest::post()
.uri("/v3/collection")
.append_header(("Authorization", token))

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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