You've already forked AstralRinth
forked from didirus/AstralRinth
Misc v3 linear tasks (#767)
* v3_reroute 404 error * hash change * fixed issue with error conversion * added new model confirmation tests + title name change * renaming, fields * owner; test changes * clippy prepare * fmt * merge fixes * clippy * working merge * revs * merge fixes
This commit is contained in:
@@ -11,6 +11,7 @@ use crate::common::{api_v2::ApiV2, api_v3::ApiV3, dummy_data::TestFile};
|
||||
|
||||
use super::{
|
||||
models::{CommonImageData, CommonProject, CommonVersion},
|
||||
request_data::ProjectCreationRequestData,
|
||||
Api, ApiProject, ApiTags, ApiTeams, ApiVersion,
|
||||
};
|
||||
|
||||
@@ -65,6 +66,8 @@ delegate_api_variant!(
|
||||
#[async_trait(?Send)]
|
||||
impl ApiProject for GenericApi {
|
||||
[add_public_project, (CommonProject, Vec<CommonVersion>), slug: &str, version_jar: Option<TestFile>, modify_json: Option<json_patch::Patch>, pat: &str],
|
||||
[get_public_project_creation_data_json, serde_json::Value, slug: &str, version_jar: Option<&TestFile>],
|
||||
[create_project, ServiceResponse, creation_data: ProjectCreationRequestData, pat: &str],
|
||||
[remove_project, ServiceResponse, project_slug_or_id: &str, pat: &str],
|
||||
[get_project, ServiceResponse, id_or_slug: &str, pat: &str],
|
||||
[get_project_deserialized_common, CommonProject, id_or_slug: &str, pat: &str],
|
||||
|
||||
@@ -4,6 +4,7 @@ use self::models::{
|
||||
CommonCategoryData, CommonImageData, CommonLoaderData, CommonNotification, CommonProject,
|
||||
CommonTeamMember, CommonVersion,
|
||||
};
|
||||
use self::request_data::ProjectCreationRequestData;
|
||||
use actix_web::dev::ServiceResponse;
|
||||
use async_trait::async_trait;
|
||||
use labrinth::{
|
||||
@@ -18,6 +19,7 @@ use super::dummy_data::TestFile;
|
||||
|
||||
pub mod generic;
|
||||
pub mod models;
|
||||
pub mod request_data;
|
||||
#[async_trait(?Send)]
|
||||
pub trait ApiBuildable: Api {
|
||||
async fn build(labrinth_config: LabrinthConfig) -> Self;
|
||||
@@ -38,6 +40,17 @@ pub trait ApiProject {
|
||||
modify_json: Option<json_patch::Patch>,
|
||||
pat: &str,
|
||||
) -> (CommonProject, Vec<CommonVersion>);
|
||||
async fn create_project(
|
||||
&self,
|
||||
creation_data: ProjectCreationRequestData,
|
||||
pat: &str,
|
||||
) -> ServiceResponse;
|
||||
async fn get_public_project_creation_data_json(
|
||||
&self,
|
||||
slug: &str,
|
||||
version_jar: Option<&TestFile>,
|
||||
) -> serde_json::Value;
|
||||
|
||||
async fn remove_project(&self, id_or_slug: &str, pat: &str) -> ServiceResponse;
|
||||
async fn get_project(&self, id_or_slug: &str, pat: &str) -> ServiceResponse;
|
||||
async fn get_project_deserialized_common(&self, id_or_slug: &str, pat: &str) -> CommonProject;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use labrinth::models::{
|
||||
notifications::{NotificationAction, NotificationBody, NotificationId},
|
||||
notifications::NotificationId,
|
||||
organizations::OrganizationId,
|
||||
projects::{
|
||||
Dependency, GalleryItem, License, ModeratorMessage, MonetizationStatus, ProjectId,
|
||||
ProjectStatus, VersionFile, VersionId, VersionStatus, VersionType,
|
||||
},
|
||||
teams::{OrganizationPermissions, ProjectPermissions, TeamId},
|
||||
teams::{ProjectPermissions, TeamId},
|
||||
threads::ThreadId,
|
||||
users::{User, UserId},
|
||||
};
|
||||
@@ -31,12 +31,7 @@ pub struct CommonProject {
|
||||
// For any tests that require those fields, we make a separate test with separate API functions tht do not use Common models.
|
||||
pub id: ProjectId,
|
||||
pub slug: Option<String>,
|
||||
pub team: TeamId,
|
||||
pub organization: Option<OrganizationId>,
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
pub body: String,
|
||||
pub body_url: Option<String>,
|
||||
pub published: DateTime<Utc>,
|
||||
pub updated: DateTime<Utc>,
|
||||
pub approved: Option<DateTime<Utc>>,
|
||||
@@ -67,7 +62,6 @@ pub struct CommonVersion {
|
||||
pub name: String,
|
||||
pub version_number: String,
|
||||
pub changelog: String,
|
||||
pub changelog_url: Option<String>,
|
||||
pub date_published: DateTime<Utc>,
|
||||
pub downloads: u32,
|
||||
pub version_type: VersionType,
|
||||
@@ -109,9 +103,7 @@ pub struct CommonTeamMember {
|
||||
pub user: User,
|
||||
pub role: String,
|
||||
|
||||
// TODO: Should these be removed from the Common?
|
||||
pub permissions: Option<ProjectPermissions>,
|
||||
pub organization_permissions: Option<OrganizationPermissions>,
|
||||
|
||||
pub accepted: bool,
|
||||
pub payouts_split: Option<Decimal>,
|
||||
@@ -124,13 +116,13 @@ pub struct CommonNotification {
|
||||
pub user_id: UserId,
|
||||
pub read: bool,
|
||||
pub created: DateTime<Utc>,
|
||||
pub body: NotificationBody,
|
||||
|
||||
// DEPRECATED: use body field instead
|
||||
#[serde(rename = "type")]
|
||||
pub type_: Option<String>,
|
||||
pub title: String,
|
||||
// Body is absent as one of the variants differs
|
||||
pub text: String,
|
||||
pub link: String,
|
||||
pub actions: Vec<NotificationAction>,
|
||||
pub actions: Vec<CommonNotificationAction>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CommonNotificationAction {
|
||||
pub action_route: (String, String),
|
||||
}
|
||||
|
||||
24
tests/common/api_common/request_data.rs
Normal file
24
tests/common/api_common/request_data.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
// The structures for project/version creation.
|
||||
// These are created differently, but are essentially the same between versions.
|
||||
|
||||
use labrinth::util::actix::MultipartSegment;
|
||||
|
||||
use crate::common::dummy_data::TestFile;
|
||||
|
||||
pub struct ProjectCreationRequestData {
|
||||
pub slug: String,
|
||||
pub jar: Option<TestFile>,
|
||||
pub segment_data: Vec<MultipartSegment>,
|
||||
}
|
||||
|
||||
pub struct VersionCreationRequestData {
|
||||
pub version: String,
|
||||
pub jar: Option<TestFile>,
|
||||
pub segment_data: Vec<MultipartSegment>,
|
||||
}
|
||||
|
||||
pub struct ImageData {
|
||||
pub filename: String,
|
||||
pub extension: String,
|
||||
pub icon: Vec<u8>,
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::common::{
|
||||
api_common::{
|
||||
models::{CommonImageData, CommonProject, CommonVersion},
|
||||
request_data::ProjectCreationRequestData,
|
||||
Api, ApiProject,
|
||||
},
|
||||
dummy_data::TestFile,
|
||||
@@ -20,7 +21,10 @@ use serde_json::json;
|
||||
|
||||
use crate::common::{asserts::assert_status, database::MOD_USER_PAT};
|
||||
|
||||
use super::{request_data::get_public_project_creation_data, ApiV2};
|
||||
use super::{
|
||||
request_data::{self, get_public_project_creation_data},
|
||||
ApiV2,
|
||||
};
|
||||
|
||||
impl ApiV2 {
|
||||
pub async fn get_project_deserialized(&self, id_or_slug: &str, pat: &str) -> LegacyProject {
|
||||
@@ -80,17 +84,13 @@ impl ApiProject for ApiV2 {
|
||||
let creation_data = get_public_project_creation_data(slug, version_jar, modify_json);
|
||||
|
||||
// Add a project.
|
||||
let req = TestRequest::post()
|
||||
.uri("/v2/project")
|
||||
.append_header(("Authorization", pat))
|
||||
.set_multipart(creation_data.segment_data)
|
||||
.to_request();
|
||||
let resp = self.call(req).await;
|
||||
let slug = creation_data.slug.clone();
|
||||
let resp = self.create_project(creation_data, pat).await;
|
||||
assert_status(&resp, StatusCode::OK);
|
||||
|
||||
// Approve as a moderator.
|
||||
let req = TestRequest::patch()
|
||||
.uri(&format!("/v2/project/{}", creation_data.slug))
|
||||
.uri(&format!("/v2/project/{}", slug))
|
||||
.append_header(("Authorization", MOD_USER_PAT))
|
||||
.set_json(json!(
|
||||
{
|
||||
@@ -101,13 +101,11 @@ impl ApiProject for ApiV2 {
|
||||
let resp = self.call(req).await;
|
||||
assert_status(&resp, StatusCode::NO_CONTENT);
|
||||
|
||||
let project = self
|
||||
.get_project_deserialized_common(&creation_data.slug, pat)
|
||||
.await;
|
||||
let project = self.get_project_deserialized_common(&slug, pat).await;
|
||||
|
||||
// Get project's versions
|
||||
let req = TestRequest::get()
|
||||
.uri(&format!("/v2/project/{}/version", creation_data.slug))
|
||||
.uri(&format!("/v2/project/{}/version", slug))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
let resp = self.call(req).await;
|
||||
@@ -116,6 +114,27 @@ impl ApiProject for ApiV2 {
|
||||
(project, versions)
|
||||
}
|
||||
|
||||
async fn get_public_project_creation_data_json(
|
||||
&self,
|
||||
slug: &str,
|
||||
version_jar: Option<&TestFile>,
|
||||
) -> serde_json::Value {
|
||||
request_data::get_public_project_creation_data_json(slug, version_jar)
|
||||
}
|
||||
|
||||
async fn create_project(
|
||||
&self,
|
||||
creation_data: ProjectCreationRequestData,
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let req = TestRequest::post()
|
||||
.uri("/v2/project")
|
||||
.append_header(("Authorization", pat))
|
||||
.set_multipart(creation_data.segment_data)
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
async fn remove_project(&self, project_slug_or_id: &str, pat: &str) -> ServiceResponse {
|
||||
let req = test::TestRequest::delete()
|
||||
.uri(&format!("/v2/project/{project_slug_or_id}"))
|
||||
@@ -137,7 +156,11 @@ impl ApiProject for ApiV2 {
|
||||
async fn get_project_deserialized_common(&self, id_or_slug: &str, pat: &str) -> CommonProject {
|
||||
let resp = self.get_project(id_or_slug, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let project: LegacyProject = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(project).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
async fn get_user_projects(&self, user_id_or_username: &str, pat: &str) -> ServiceResponse {
|
||||
@@ -155,7 +178,11 @@ impl ApiProject for ApiV2 {
|
||||
) -> Vec<CommonProject> {
|
||||
let resp = self.get_user_projects(user_id_or_username, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let projects: Vec<LegacyProject> = 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()
|
||||
}
|
||||
|
||||
async fn edit_project(
|
||||
|
||||
@@ -1,30 +1,15 @@
|
||||
#![allow(dead_code)]
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::dummy_data::{DummyImage, TestFile};
|
||||
use crate::common::{
|
||||
api_common::request_data::{ImageData, ProjectCreationRequestData, VersionCreationRequestData},
|
||||
dummy_data::{DummyImage, TestFile},
|
||||
};
|
||||
use labrinth::{
|
||||
models::projects::ProjectId,
|
||||
util::actix::{MultipartSegment, MultipartSegmentData},
|
||||
};
|
||||
|
||||
pub struct ProjectCreationRequestData {
|
||||
pub slug: String,
|
||||
pub jar: Option<TestFile>,
|
||||
pub segment_data: Vec<MultipartSegment>,
|
||||
}
|
||||
|
||||
pub struct VersionCreationRequestData {
|
||||
pub version: String,
|
||||
pub jar: Option<TestFile>,
|
||||
pub segment_data: Vec<MultipartSegment>,
|
||||
}
|
||||
|
||||
pub struct ImageData {
|
||||
pub filename: String,
|
||||
pub extension: String,
|
||||
pub icon: Vec<u8>,
|
||||
}
|
||||
|
||||
pub fn get_public_project_creation_data(
|
||||
slug: &str,
|
||||
version_jar: Option<TestFile>,
|
||||
|
||||
@@ -72,7 +72,11 @@ impl ApiTags for ApiV2 {
|
||||
async fn get_loaders_deserialized_common(&self) -> Vec<CommonLoaderData> {
|
||||
let resp = self.get_loaders().await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: Vec<LoaderData> = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
async fn get_categories(&self) -> ServiceResponse {
|
||||
@@ -86,6 +90,10 @@ impl ApiTags for ApiV2 {
|
||||
async fn get_categories_deserialized_common(&self) -> Vec<CommonCategoryData> {
|
||||
let resp = self.get_categories().await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: Vec<CategoryData> = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use actix_http::StatusCode;
|
||||
use actix_web::{dev::ServiceResponse, test};
|
||||
use async_trait::async_trait;
|
||||
use labrinth::models::teams::{OrganizationPermissions, ProjectPermissions};
|
||||
use labrinth::models::{
|
||||
teams::{OrganizationPermissions, ProjectPermissions},
|
||||
v2::{notifications::LegacyNotification, teams::LegacyTeamMember},
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::{
|
||||
@@ -14,6 +17,38 @@ use crate::common::{
|
||||
|
||||
use super::ApiV2;
|
||||
|
||||
impl ApiV2 {
|
||||
pub async fn get_organization_members_deserialized(
|
||||
&self,
|
||||
id_or_title: &str,
|
||||
pat: &str,
|
||||
) -> Vec<LegacyTeamMember> {
|
||||
let resp = self.get_organization_members(id_or_title, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
pub async fn get_team_members_deserialized(
|
||||
&self,
|
||||
team_id: &str,
|
||||
pat: &str,
|
||||
) -> Vec<LegacyTeamMember> {
|
||||
let resp = self.get_team_members(team_id, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
pub async fn get_user_notifications_deserialized(
|
||||
&self,
|
||||
user_id: &str,
|
||||
pat: &str,
|
||||
) -> Vec<LegacyNotification> {
|
||||
let resp = self.get_user_notifications(user_id, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl ApiTeams for ApiV2 {
|
||||
async fn get_team_members(&self, id_or_title: &str, pat: &str) -> ServiceResponse {
|
||||
@@ -31,6 +66,9 @@ impl ApiTeams for ApiV2 {
|
||||
) -> Vec<CommonTeamMember> {
|
||||
let resp = self.get_team_members(id_or_title, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
// TODO: Note, this does NOT deserialize to any other struct first, as currently TeamMember is the same in v2 and v3.
|
||||
// CommonTeamMember = TeamMember (v3)
|
||||
// This may yet change, so we should keep common struct.
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
@@ -49,6 +87,9 @@ impl ApiTeams for ApiV2 {
|
||||
) -> Vec<CommonTeamMember> {
|
||||
let resp = self.get_project_members(id_or_title, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
// TODO: Note, this does NOT deserialize to any other struct first, as currently TeamMember is the same in v2 and v3.
|
||||
// CommonTeamMember = TeamMember (v3)
|
||||
// This may yet change, so we should keep common struct.
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
@@ -67,6 +108,9 @@ impl ApiTeams for ApiV2 {
|
||||
) -> Vec<CommonTeamMember> {
|
||||
let resp = self.get_organization_members(id_or_title, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
// TODO: Note, this does NOT deserialize to any other struct first, as currently TeamMember is the same in v2 and v3.
|
||||
// CommonTeamMember = TeamMember (v3)
|
||||
// This may yet change, so we should keep common struct.
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
@@ -132,7 +176,11 @@ impl ApiTeams for ApiV2 {
|
||||
) -> Vec<CommonNotification> {
|
||||
let resp = self.get_user_notifications(user_id, pat).await;
|
||||
assert_status(&resp, StatusCode::OK);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: Vec<LegacyNotification> = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
async fn mark_notification_read(&self, notification_id: &str, pat: &str) -> ServiceResponse {
|
||||
|
||||
@@ -133,7 +133,11 @@ impl ApiVersion for ApiV2 {
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: LegacyVersion = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
async fn get_version(&self, id: &str, pat: &str) -> ServiceResponse {
|
||||
@@ -147,7 +151,11 @@ impl ApiVersion for ApiV2 {
|
||||
async fn get_version_deserialized_common(&self, id: &str, pat: &str) -> CommonVersion {
|
||||
let resp = self.get_version(id, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: LegacyVersion = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
async fn edit_version(
|
||||
@@ -186,7 +194,11 @@ impl ApiVersion for ApiV2 {
|
||||
) -> CommonVersion {
|
||||
let resp = self.get_version_from_hash(hash, algorithm, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: LegacyVersion = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
async fn get_versions_from_hashes(
|
||||
@@ -214,7 +226,11 @@ impl ApiVersion for ApiV2 {
|
||||
) -> HashMap<String, CommonVersion> {
|
||||
let resp = self.get_versions_from_hashes(hashes, algorithm, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: HashMap<String, LegacyVersion> = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
async fn get_update_from_hash(
|
||||
@@ -253,7 +269,11 @@ impl ApiVersion for ApiV2 {
|
||||
.get_update_from_hash(hash, algorithm, loaders, game_versions, version_types, pat)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: LegacyVersion = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
async fn update_files(
|
||||
@@ -299,7 +319,11 @@ impl ApiVersion for ApiV2 {
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: HashMap<String, LegacyVersion> = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
// TODO: Not all fields are tested currently in the V2 tests, only the v2-v3 relevant ones are
|
||||
@@ -378,7 +402,11 @@ impl ApiVersion for ApiV2 {
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: Vec<LegacyVersion> = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
async fn edit_version_ordering(
|
||||
@@ -415,6 +443,10 @@ impl ApiVersion for ApiV2 {
|
||||
) -> Vec<CommonVersion> {
|
||||
let resp = self.get_versions(version_ids, pat).await;
|
||||
assert_status(&resp, StatusCode::OK);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: Vec<LegacyVersion> = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ use bytes::Bytes;
|
||||
use labrinth::models::{organizations::Organization, v3::projects::Project};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::api_common::Api;
|
||||
use crate::common::api_common::{request_data::ImageData, Api};
|
||||
|
||||
use super::{request_data::ImageData, ApiV3};
|
||||
use super::ApiV3;
|
||||
|
||||
impl ApiV3 {
|
||||
pub async fn create_organization(
|
||||
@@ -21,7 +21,7 @@ impl ApiV3 {
|
||||
.uri("/v3/organization")
|
||||
.append_header(("Authorization", pat))
|
||||
.set_json(json!({
|
||||
"title": organization_title,
|
||||
"name": organization_title,
|
||||
"description": description,
|
||||
}))
|
||||
.to_request();
|
||||
|
||||
@@ -15,6 +15,7 @@ use serde_json::json;
|
||||
use crate::common::{
|
||||
api_common::{
|
||||
models::{CommonImageData, CommonProject, CommonVersion},
|
||||
request_data::ProjectCreationRequestData,
|
||||
Api, ApiProject,
|
||||
},
|
||||
asserts::assert_status,
|
||||
@@ -22,7 +23,10 @@ use crate::common::{
|
||||
dummy_data::TestFile,
|
||||
};
|
||||
|
||||
use super::{request_data::get_public_project_creation_data, ApiV3};
|
||||
use super::{
|
||||
request_data::{self, get_public_project_creation_data},
|
||||
ApiV3,
|
||||
};
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl ApiProject for ApiV3 {
|
||||
@@ -36,17 +40,13 @@ impl ApiProject for ApiV3 {
|
||||
let creation_data = get_public_project_creation_data(slug, version_jar, modify_json);
|
||||
|
||||
// Add a project.
|
||||
let req = TestRequest::post()
|
||||
.uri("/v3/project")
|
||||
.append_header(("Authorization", pat))
|
||||
.set_multipart(creation_data.segment_data)
|
||||
.to_request();
|
||||
let resp = self.call(req).await;
|
||||
let slug = creation_data.slug.clone();
|
||||
let resp = self.create_project(creation_data, pat).await;
|
||||
assert_status(&resp, StatusCode::OK);
|
||||
|
||||
// Approve as a moderator.
|
||||
let req = TestRequest::patch()
|
||||
.uri(&format!("/v3/project/{}", creation_data.slug))
|
||||
.uri(&format!("/v3/project/{}", slug))
|
||||
.append_header(("Authorization", MOD_USER_PAT))
|
||||
.set_json(json!(
|
||||
{
|
||||
@@ -57,12 +57,12 @@ impl ApiProject for ApiV3 {
|
||||
let resp = self.call(req).await;
|
||||
assert_status(&resp, StatusCode::NO_CONTENT);
|
||||
|
||||
let project = self.get_project(&creation_data.slug, pat).await;
|
||||
let project = self.get_project(&slug, pat).await;
|
||||
let project = test::read_body_json(project).await;
|
||||
|
||||
// Get project's versions
|
||||
let req = TestRequest::get()
|
||||
.uri(&format!("/v3/project/{}/version", creation_data.slug))
|
||||
.uri(&format!("/v3/project/{}/version", slug))
|
||||
.append_header(("Authorization", pat))
|
||||
.to_request();
|
||||
let resp = self.call(req).await;
|
||||
@@ -71,6 +71,27 @@ impl ApiProject for ApiV3 {
|
||||
(project, versions)
|
||||
}
|
||||
|
||||
async fn get_public_project_creation_data_json(
|
||||
&self,
|
||||
slug: &str,
|
||||
version_jar: Option<&TestFile>,
|
||||
) -> serde_json::Value {
|
||||
request_data::get_public_project_creation_data_json(slug, version_jar)
|
||||
}
|
||||
|
||||
async fn create_project(
|
||||
&self,
|
||||
creation_data: ProjectCreationRequestData,
|
||||
pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let req = TestRequest::post()
|
||||
.uri("/v3/project")
|
||||
.append_header(("Authorization", pat))
|
||||
.set_multipart(creation_data.segment_data)
|
||||
.to_request();
|
||||
self.call(req).await
|
||||
}
|
||||
|
||||
async fn remove_project(&self, project_slug_or_id: &str, pat: &str) -> ServiceResponse {
|
||||
let req = test::TestRequest::delete()
|
||||
.uri(&format!("/v3/project/{project_slug_or_id}"))
|
||||
@@ -92,7 +113,11 @@ impl ApiProject for ApiV3 {
|
||||
async fn get_project_deserialized_common(&self, id_or_slug: &str, pat: &str) -> CommonProject {
|
||||
let resp = self.get_project(id_or_slug, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let project: Project = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(project).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
async fn get_user_projects(&self, user_id_or_username: &str, pat: &str) -> ServiceResponse {
|
||||
@@ -110,7 +135,11 @@ impl ApiProject for ApiV3 {
|
||||
) -> Vec<CommonProject> {
|
||||
let resp = self.get_user_projects(user_id_or_username, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// 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()
|
||||
}
|
||||
|
||||
async fn edit_project(
|
||||
|
||||
@@ -1,30 +1,15 @@
|
||||
#![allow(dead_code)]
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::dummy_data::{DummyImage, TestFile};
|
||||
use crate::common::{
|
||||
api_common::request_data::{ImageData, ProjectCreationRequestData, VersionCreationRequestData},
|
||||
dummy_data::{DummyImage, TestFile},
|
||||
};
|
||||
use labrinth::{
|
||||
models::projects::ProjectId,
|
||||
util::actix::{MultipartSegment, MultipartSegmentData},
|
||||
};
|
||||
|
||||
pub struct ProjectCreationRequestData {
|
||||
pub slug: String,
|
||||
pub jar: Option<TestFile>,
|
||||
pub segment_data: Vec<MultipartSegment>,
|
||||
}
|
||||
|
||||
pub struct VersionCreationRequestData {
|
||||
pub version: String,
|
||||
pub jar: Option<TestFile>,
|
||||
pub segment_data: Vec<MultipartSegment>,
|
||||
}
|
||||
|
||||
pub struct ImageData {
|
||||
pub filename: String,
|
||||
pub extension: String,
|
||||
pub icon: Vec<u8>,
|
||||
}
|
||||
|
||||
pub fn get_public_project_creation_data(
|
||||
slug: &str,
|
||||
version_jar: Option<TestFile>,
|
||||
@@ -110,10 +95,10 @@ pub fn get_public_project_creation_data_json(
|
||||
let is_draft = version_jar.is_none();
|
||||
json!(
|
||||
{
|
||||
"title": format!("Test Project {slug}"),
|
||||
"name": format!("Test Project {slug}"),
|
||||
"slug": slug,
|
||||
"description": "A dummy project for testing with.",
|
||||
"body": "This project is approved, and versions are listed.",
|
||||
"summary": "A dummy project for testing with.",
|
||||
"description": "This project is approved, and versions are listed.",
|
||||
"initial_versions": initial_versions,
|
||||
"is_draft": is_draft,
|
||||
"categories": [],
|
||||
|
||||
@@ -3,8 +3,10 @@ use actix_web::{
|
||||
test::{self, TestRequest},
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use labrinth::database::models::loader_fields::LoaderFieldEnumValue;
|
||||
use labrinth::routes::v3::tags::{GameData, LoaderData};
|
||||
use labrinth::{
|
||||
database::models::loader_fields::LoaderFieldEnumValue, routes::v3::tags::CategoryData,
|
||||
};
|
||||
|
||||
use crate::common::{
|
||||
api_common::{
|
||||
@@ -29,7 +31,11 @@ impl ApiTags for ApiV3 {
|
||||
async fn get_loaders_deserialized_common(&self) -> Vec<CommonLoaderData> {
|
||||
let resp = self.get_loaders().await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: Vec<LoaderData> = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
async fn get_categories(&self) -> ServiceResponse {
|
||||
@@ -43,7 +49,11 @@ impl ApiTags for ApiV3 {
|
||||
async fn get_categories_deserialized_common(&self) -> Vec<CommonCategoryData> {
|
||||
let resp = self.get_categories().await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: Vec<CategoryData> = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use actix_http::StatusCode;
|
||||
use actix_web::{dev::ServiceResponse, test};
|
||||
use async_trait::async_trait;
|
||||
use labrinth::models::teams::{OrganizationPermissions, ProjectPermissions};
|
||||
use labrinth::models::{
|
||||
notifications::Notification,
|
||||
teams::{OrganizationPermissions, ProjectPermissions, TeamMember},
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::{
|
||||
@@ -14,6 +17,24 @@ use crate::common::{
|
||||
|
||||
use super::ApiV3;
|
||||
|
||||
impl ApiV3 {
|
||||
pub async fn get_organization_members_deserialized(
|
||||
&self,
|
||||
id_or_title: &str,
|
||||
pat: &str,
|
||||
) -> Vec<TeamMember> {
|
||||
let resp = self.get_organization_members(id_or_title, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
pub async fn get_team_members_deserialized(&self, team_id: &str, pat: &str) -> Vec<TeamMember> {
|
||||
let resp = self.get_team_members(team_id, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl ApiTeams for ApiV3 {
|
||||
async fn get_team_members(&self, id_or_title: &str, pat: &str) -> ServiceResponse {
|
||||
@@ -31,7 +52,11 @@ impl ApiTeams for ApiV3 {
|
||||
) -> Vec<CommonTeamMember> {
|
||||
let resp = self.get_team_members(id_or_title, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: Vec<TeamMember> = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
async fn get_project_members(&self, id_or_title: &str, pat: &str) -> ServiceResponse {
|
||||
@@ -49,7 +74,11 @@ impl ApiTeams for ApiV3 {
|
||||
) -> Vec<CommonTeamMember> {
|
||||
let resp = self.get_project_members(id_or_title, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: Vec<TeamMember> = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
async fn get_organization_members(&self, id_or_title: &str, pat: &str) -> ServiceResponse {
|
||||
@@ -67,7 +96,11 @@ impl ApiTeams for ApiV3 {
|
||||
) -> Vec<CommonTeamMember> {
|
||||
let resp = self.get_organization_members(id_or_title, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: Vec<TeamMember> = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
async fn join_team(&self, team_id: &str, pat: &str) -> ServiceResponse {
|
||||
@@ -132,7 +165,11 @@ impl ApiTeams for ApiV3 {
|
||||
) -> Vec<CommonNotification> {
|
||||
let resp = self.get_user_notifications(user_id, pat).await;
|
||||
assert_status(&resp, StatusCode::OK);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: Vec<Notification> = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
async fn mark_notification_read(&self, notification_id: &str, pat: &str) -> ServiceResponse {
|
||||
|
||||
@@ -138,7 +138,11 @@ impl ApiVersion for ApiV3 {
|
||||
)
|
||||
.await;
|
||||
assert_status(&resp, StatusCode::OK);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: Version = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
async fn get_version(&self, id: &str, pat: &str) -> ServiceResponse {
|
||||
@@ -152,7 +156,11 @@ impl ApiVersion for ApiV3 {
|
||||
async fn get_version_deserialized_common(&self, id: &str, pat: &str) -> CommonVersion {
|
||||
let resp = self.get_version(id, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: Version = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
async fn edit_version(
|
||||
@@ -191,7 +199,11 @@ impl ApiVersion for ApiV3 {
|
||||
) -> CommonVersion {
|
||||
let resp = self.get_version_from_hash(hash, algorithm, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: Version = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
async fn get_versions_from_hashes(
|
||||
@@ -219,7 +231,11 @@ impl ApiVersion for ApiV3 {
|
||||
) -> HashMap<String, CommonVersion> {
|
||||
let resp = self.get_versions_from_hashes(hashes, algorithm, pat).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: HashMap<String, Version> = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
async fn get_update_from_hash(
|
||||
@@ -267,7 +283,11 @@ impl ApiVersion for ApiV3 {
|
||||
.get_update_from_hash(hash, algorithm, loaders, game_versions, version_types, pat)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: Version = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
async fn update_files(
|
||||
@@ -323,7 +343,11 @@ impl ApiVersion for ApiV3 {
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: HashMap<String, Version> = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
// TODO: Not all fields are tested currently in the v3 tests, only the v2-v3 relevant ones are
|
||||
@@ -402,7 +426,11 @@ impl ApiVersion for ApiV3 {
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: Vec<Version> = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
|
||||
// TODO: remove redundancy in these functions
|
||||
@@ -440,6 +468,10 @@ impl ApiVersion for ApiV3 {
|
||||
) -> Vec<CommonVersion> {
|
||||
let resp = self.get_versions(version_ids, pat).await;
|
||||
assert_status(&resp, StatusCode::OK);
|
||||
test::read_body_json(resp).await
|
||||
// First, deserialize to the non-common format (to test the response is valid for this api version)
|
||||
let v: Vec<Version> = test::read_body_json(resp).await;
|
||||
// Then, deserialize to the common format
|
||||
let value = serde_json::to_value(v).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@ use std::io::{Cursor, Write};
|
||||
use actix_http::StatusCode;
|
||||
use actix_web::test::{self, TestRequest};
|
||||
use labrinth::models::{
|
||||
oauth_clients::OAuthClient, organizations::Organization, pats::Scopes, projects::ProjectId,
|
||||
oauth_clients::OAuthClient,
|
||||
organizations::Organization,
|
||||
pats::Scopes,
|
||||
projects::{Project, ProjectId, Version},
|
||||
};
|
||||
use serde_json::json;
|
||||
use sqlx::Executor;
|
||||
@@ -13,14 +16,7 @@ use zip::{write::FileOptions, CompressionMethod, ZipWriter};
|
||||
use crate::common::{api_common::Api, database::USER_USER_PAT};
|
||||
use labrinth::util::actix::{AppendsMultipart, MultipartSegment, MultipartSegmentData};
|
||||
|
||||
use super::{
|
||||
api_common::{
|
||||
models::{CommonProject, CommonVersion},
|
||||
ApiProject,
|
||||
},
|
||||
api_v3::ApiV3,
|
||||
database::TemporaryDatabase,
|
||||
};
|
||||
use super::{api_common::ApiProject, api_v3::ApiV3, database::TemporaryDatabase};
|
||||
|
||||
use super::{asserts::assert_status, database::USER_USER_ID, get_json_val_str};
|
||||
|
||||
@@ -174,16 +170,16 @@ pub struct DummyData {
|
||||
|
||||
impl DummyData {
|
||||
pub fn new(
|
||||
project_alpha: CommonProject,
|
||||
project_alpha_version: CommonVersion,
|
||||
project_beta: CommonProject,
|
||||
project_beta_version: CommonVersion,
|
||||
project_alpha: Project,
|
||||
project_alpha_version: Version,
|
||||
project_beta: Project,
|
||||
project_beta_version: Version,
|
||||
organization_zeta: Organization,
|
||||
oauth_client_alpha: OAuthClient,
|
||||
) -> Self {
|
||||
DummyData {
|
||||
project_alpha: DummyProjectAlpha {
|
||||
team_id: project_alpha.team.to_string(),
|
||||
team_id: project_alpha.team_id.to_string(),
|
||||
project_id: project_alpha.id.to_string(),
|
||||
project_slug: project_alpha.slug.unwrap(),
|
||||
project_id_parsed: project_alpha.id,
|
||||
@@ -193,7 +189,7 @@ impl DummyData {
|
||||
},
|
||||
|
||||
project_beta: DummyProjectBeta {
|
||||
team_id: project_beta.team.to_string(),
|
||||
team_id: project_beta.team_id.to_string(),
|
||||
project_id: project_beta.id.to_string(),
|
||||
project_slug: project_beta.slug.unwrap(),
|
||||
project_id_parsed: project_beta.id,
|
||||
@@ -205,7 +201,7 @@ impl DummyData {
|
||||
organization_zeta: DummyOrganizationZeta {
|
||||
organization_id: organization_zeta.id.to_string(),
|
||||
team_id: organization_zeta.team_id.to_string(),
|
||||
organization_title: organization_zeta.title,
|
||||
organization_name: organization_zeta.name,
|
||||
},
|
||||
|
||||
oauth_client_alpha: DummyOAuthClientAlpha {
|
||||
@@ -247,7 +243,7 @@ pub struct DummyProjectBeta {
|
||||
#[derive(Clone)]
|
||||
pub struct DummyOrganizationZeta {
|
||||
pub organization_id: String,
|
||||
pub organization_title: String,
|
||||
pub organization_name: String,
|
||||
pub team_id: String,
|
||||
}
|
||||
|
||||
@@ -311,7 +307,7 @@ pub async fn get_dummy_data(api: &ApiV3) -> DummyData {
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn add_project_alpha(api: &ApiV3) -> (CommonProject, CommonVersion) {
|
||||
pub async fn add_project_alpha(api: &ApiV3) -> (Project, Version) {
|
||||
let (project, versions) = api
|
||||
.add_public_project(
|
||||
"alpha",
|
||||
@@ -320,20 +316,29 @@ pub async fn add_project_alpha(api: &ApiV3) -> (CommonProject, CommonVersion) {
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
(project, versions.into_iter().next().unwrap())
|
||||
let alpha_project = api
|
||||
.get_project_deserialized(project.id.to_string().as_str(), USER_USER_PAT)
|
||||
.await;
|
||||
let alpha_version = api
|
||||
.get_version_deserialized(
|
||||
&versions.into_iter().next().unwrap().id.to_string(),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
(alpha_project, alpha_version)
|
||||
}
|
||||
|
||||
pub async fn add_project_beta(api: &ApiV3) -> (CommonProject, CommonVersion) {
|
||||
pub async fn add_project_beta(api: &ApiV3) -> (Project, Version) {
|
||||
// Adds dummy data to the database with sqlx (projects, versions, threads)
|
||||
// Generate test project data.
|
||||
let jar = TestFile::DummyProjectBeta;
|
||||
// TODO: this shouldnt be hardcoded (nor should other similar ones be)
|
||||
let json_data = json!(
|
||||
{
|
||||
"title": "Test Project Beta",
|
||||
"name": "Test Project Beta",
|
||||
"slug": "beta",
|
||||
"description": "A dummy project for testing with.",
|
||||
"body": "This project is not-yet-approved, and versions are draft.",
|
||||
"summary": "A dummy project for testing with.",
|
||||
"description": "This project is not-yet-approved, and versions are draft.",
|
||||
"initial_versions": [{
|
||||
"file_parts": [jar.filename()],
|
||||
"version_number": "1.2.3",
|
||||
@@ -390,7 +395,7 @@ pub async fn add_organization_zeta(api: &ApiV3) -> Organization {
|
||||
.uri("/v3/organization")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"title": "zeta",
|
||||
"name": "zeta",
|
||||
"description": "A dummy organization for testing with."
|
||||
}))
|
||||
.to_request();
|
||||
@@ -401,14 +406,14 @@ pub async fn add_organization_zeta(api: &ApiV3) -> Organization {
|
||||
get_organization_zeta(api).await
|
||||
}
|
||||
|
||||
pub async fn get_project_alpha(api: &ApiV3) -> (CommonProject, CommonVersion) {
|
||||
pub async fn get_project_alpha(api: &ApiV3) -> (Project, Version) {
|
||||
// Get project
|
||||
let req = TestRequest::get()
|
||||
.uri("/v3/project/alpha")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.to_request();
|
||||
let resp = api.call(req).await;
|
||||
let project: CommonProject = test::read_body_json(resp).await;
|
||||
let project: Project = test::read_body_json(resp).await;
|
||||
|
||||
// Get project's versions
|
||||
let req = TestRequest::get()
|
||||
@@ -416,13 +421,13 @@ pub async fn get_project_alpha(api: &ApiV3) -> (CommonProject, CommonVersion) {
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.to_request();
|
||||
let resp = api.call(req).await;
|
||||
let versions: Vec<CommonVersion> = test::read_body_json(resp).await;
|
||||
let versions: Vec<Version> = test::read_body_json(resp).await;
|
||||
let version = versions.into_iter().next().unwrap();
|
||||
|
||||
(project, version)
|
||||
}
|
||||
|
||||
pub async fn get_project_beta(api: &ApiV3) -> (CommonProject, CommonVersion) {
|
||||
pub async fn get_project_beta(api: &ApiV3) -> (Project, Version) {
|
||||
// Get project
|
||||
let req = TestRequest::get()
|
||||
.uri("/v3/project/beta")
|
||||
@@ -431,7 +436,7 @@ pub async fn get_project_beta(api: &ApiV3) -> (CommonProject, CommonVersion) {
|
||||
let resp = api.call(req).await;
|
||||
assert_status(&resp, StatusCode::OK);
|
||||
let project: serde_json::Value = test::read_body_json(resp).await;
|
||||
let project: CommonProject = serde_json::from_value(project).unwrap();
|
||||
let project: Project = serde_json::from_value(project).unwrap();
|
||||
|
||||
// Get project's versions
|
||||
let req = TestRequest::get()
|
||||
@@ -440,7 +445,7 @@ pub async fn get_project_beta(api: &ApiV3) -> (CommonProject, CommonVersion) {
|
||||
.to_request();
|
||||
let resp = api.call(req).await;
|
||||
assert_status(&resp, StatusCode::OK);
|
||||
let versions: Vec<CommonVersion> = test::read_body_json(resp).await;
|
||||
let versions: Vec<Version> = test::read_body_json(resp).await;
|
||||
let version = versions.into_iter().next().unwrap();
|
||||
|
||||
(project, version)
|
||||
|
||||
@@ -1018,7 +1018,11 @@ async fn create_dummy_project(setup_api: &ApiV3) -> (String, String) {
|
||||
.add_public_project(&slug, None, None, ADMIN_USER_PAT)
|
||||
.await;
|
||||
let project_id = project.id.to_string();
|
||||
let team_id = project.team.to_string();
|
||||
|
||||
let project = setup_api
|
||||
.get_project_deserialized(&project_id, ADMIN_USER_PAT)
|
||||
.await;
|
||||
let team_id = project.team_id.to_string();
|
||||
|
||||
(project_id, team_id)
|
||||
}
|
||||
|
||||
23
tests/error.rs
Normal file
23
tests/error.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use actix_web::test;
|
||||
use bytes::Bytes;
|
||||
use common::api_common::ApiProject;
|
||||
|
||||
use common::api_v3::ApiV3;
|
||||
use common::database::USER_USER_PAT;
|
||||
use common::environment::{with_test_environment, TestEnvironment};
|
||||
|
||||
mod common;
|
||||
|
||||
#[actix_rt::test]
|
||||
pub async fn error_404_body() {
|
||||
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
|
||||
// 3 errors should have 404 as non-blank body, for missing resources
|
||||
let api = &test_env.api;
|
||||
let resp = api.get_project("does-not-exist", USER_USER_PAT).await;
|
||||
assert_eq!(resp.status(), 404);
|
||||
let body = test::read_body(resp).await;
|
||||
let empty_bytes = Bytes::from_static(b"");
|
||||
assert_ne!(body, empty_bytes);
|
||||
})
|
||||
.await;
|
||||
}
|
||||
@@ -65,13 +65,13 @@ async fn create_organization() {
|
||||
let theta = api
|
||||
.get_organization_deserialized("theta", USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(theta.title, "theta");
|
||||
assert_eq!(theta.name, "theta");
|
||||
assert_eq!(theta.description, "not url safe%&^!#$##!@#$%^&");
|
||||
assert_eq!(resp.status(), 200);
|
||||
|
||||
// Get created team
|
||||
let members = api
|
||||
.get_organization_members_deserialized_common("theta", USER_USER_PAT)
|
||||
.get_organization_members_deserialized("theta", USER_USER_PAT)
|
||||
.await;
|
||||
|
||||
// Should only be one member, which is USER_USER_ID, and is the owner with full permissions
|
||||
@@ -81,6 +81,7 @@ async fn create_organization() {
|
||||
Some(OrganizationPermissions::all())
|
||||
);
|
||||
assert_eq!(members[0].role, "Owner");
|
||||
assert!(members[0].is_owner);
|
||||
})
|
||||
.await;
|
||||
}
|
||||
@@ -118,7 +119,7 @@ async fn patch_organization() {
|
||||
.edit_organization(
|
||||
zeta_organization_id,
|
||||
json!({
|
||||
"title": title,
|
||||
"name": title,
|
||||
"description": "theta_description"
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
@@ -148,7 +149,7 @@ async fn patch_organization() {
|
||||
.edit_organization(
|
||||
zeta_organization_id,
|
||||
json!({
|
||||
"title": "new_title",
|
||||
"name": "new_title",
|
||||
"description": "not url safe%&^!#$##!@#$%^&" // not-URL-safe description should still work
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
@@ -160,7 +161,7 @@ async fn patch_organization() {
|
||||
let new_title = api
|
||||
.get_organization_deserialized("new_title", USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(new_title.title, "new_title");
|
||||
assert_eq!(new_title.name, "new_title");
|
||||
assert_eq!(new_title.description, "not url safe%&^!#$##!@#$%^&");
|
||||
})
|
||||
.await;
|
||||
@@ -297,7 +298,7 @@ async fn permissions_patch_organization() {
|
||||
// For each permission covered by EDIT_DETAILS, ensure the permission is required
|
||||
let edit_details = OrganizationPermissions::EDIT_DETAILS;
|
||||
let test_pairs = [
|
||||
("title", json!("")), // generated in the test to not collide slugs
|
||||
("name", json!("")), // generated in the test to not collide slugs
|
||||
("description", json!("New description")),
|
||||
];
|
||||
|
||||
@@ -309,7 +310,7 @@ async fn permissions_patch_organization() {
|
||||
ctx.organization_id.unwrap()
|
||||
))
|
||||
.set_json(json!({
|
||||
key: if key == "title" {
|
||||
key: if key == "name" {
|
||||
json!(generate_random_name("randomslug"))
|
||||
} else {
|
||||
value.clone()
|
||||
|
||||
@@ -62,7 +62,7 @@ pub async fn pat_full_test() {
|
||||
.uri("/v3/collection")
|
||||
.append_header(("Authorization", token))
|
||||
.set_json(json!({
|
||||
"title": "Test Collection 1",
|
||||
"name": "Test Collection 1",
|
||||
"description": "Test Collection Description"
|
||||
}))
|
||||
.to_request();
|
||||
|
||||
168
tests/project.rs
168
tests/project.rs
@@ -17,10 +17,9 @@ use labrinth::models::teams::ProjectPermissions;
|
||||
use labrinth::util::actix::{AppendsMultipart, MultipartSegment, MultipartSegmentData};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::api_common::request_data::ProjectCreationRequestData;
|
||||
use crate::common::api_common::{ApiProject, ApiVersion};
|
||||
use crate::common::api_v3::request_data::get_public_project_creation_data_json;
|
||||
use crate::common::dummy_data::TestFile;
|
||||
|
||||
mod common;
|
||||
|
||||
#[actix_rt::test]
|
||||
@@ -109,8 +108,10 @@ async fn test_add_remove_project() {
|
||||
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
|
||||
let api = &test_env.api;
|
||||
|
||||
let mut json_data =
|
||||
get_public_project_creation_data_json("demo", Some(&TestFile::BasicMod));
|
||||
// Generate test project data.
|
||||
let mut json_data = api
|
||||
.get_public_project_creation_data_json("demo", Some(&TestFile::BasicMod))
|
||||
.await;
|
||||
|
||||
// Basic json
|
||||
let json_segment = MultipartSegment {
|
||||
@@ -167,12 +168,16 @@ async fn test_add_remove_project() {
|
||||
};
|
||||
|
||||
// Add a project- simple, should work.
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v3/project")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_multipart(vec![json_segment.clone(), file_segment.clone()])
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
let resp = api
|
||||
.create_project(
|
||||
ProjectCreationRequestData {
|
||||
slug: "demo".to_string(),
|
||||
segment_data: vec![json_segment.clone(), file_segment.clone()],
|
||||
jar: None, // File not needed at this point
|
||||
},
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
|
||||
let status = resp.status();
|
||||
assert_eq!(status, 200);
|
||||
@@ -195,42 +200,51 @@ async fn test_add_remove_project() {
|
||||
|
||||
// Reusing with a different slug and the same file should fail
|
||||
// Even if that file is named differently
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v3/project")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_multipart(vec![
|
||||
json_diff_slug_file_segment.clone(), // Different slug, different file name
|
||||
file_diff_name_segment.clone(), // Different file name, same content
|
||||
])
|
||||
.to_request();
|
||||
|
||||
let resp = test_env.call(req).await;
|
||||
let resp = api
|
||||
.create_project(
|
||||
ProjectCreationRequestData {
|
||||
slug: "demo".to_string(),
|
||||
segment_data: vec![
|
||||
json_diff_slug_file_segment.clone(),
|
||||
file_diff_name_segment.clone(),
|
||||
],
|
||||
jar: None, // File not needed at this point
|
||||
},
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
// Reusing with the same slug and a different file should fail
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v3/project")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_multipart(vec![
|
||||
json_diff_file_segment.clone(), // Same slug, different file name
|
||||
file_diff_name_content_segment.clone(), // Different file name, different content
|
||||
])
|
||||
.to_request();
|
||||
|
||||
let resp = test_env.call(req).await;
|
||||
let resp = api
|
||||
.create_project(
|
||||
ProjectCreationRequestData {
|
||||
slug: "demo".to_string(),
|
||||
segment_data: vec![
|
||||
json_diff_file_segment.clone(),
|
||||
file_diff_name_content_segment.clone(),
|
||||
],
|
||||
jar: None, // File not needed at this point
|
||||
},
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
// Different slug, different file should succeed
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/v3/project")
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_multipart(vec![
|
||||
json_diff_slug_file_segment.clone(), // Different slug, different file name
|
||||
file_diff_name_content_segment.clone(), // Different file name, same content
|
||||
])
|
||||
.to_request();
|
||||
|
||||
let resp = test_env.call(req).await;
|
||||
let resp = api
|
||||
.create_project(
|
||||
ProjectCreationRequestData {
|
||||
slug: "demo".to_string(),
|
||||
segment_data: vec![
|
||||
json_diff_slug_file_segment.clone(),
|
||||
file_diff_name_content_segment.clone(),
|
||||
],
|
||||
jar: None, // File not needed at this point
|
||||
},
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
|
||||
// Get
|
||||
@@ -283,7 +297,7 @@ pub async fn test_patch_project() {
|
||||
.edit_project(
|
||||
alpha_project_slug,
|
||||
json!({
|
||||
"title": "Test_Add_Project project - test 1",
|
||||
"name": "Test_Add_Project project - test 1",
|
||||
}),
|
||||
ENEMY_USER_PAT,
|
||||
)
|
||||
@@ -388,9 +402,6 @@ pub async fn test_patch_project() {
|
||||
alpha_project_slug,
|
||||
json!({
|
||||
"slug": "newslug",
|
||||
"title": "New successful title",
|
||||
"description": "New successful description",
|
||||
"body": "New successful body",
|
||||
"categories": [DUMMY_CATEGORIES[0]],
|
||||
"license_id": "MIT",
|
||||
"link_urls":
|
||||
@@ -404,7 +415,6 @@ pub async fn test_patch_project() {
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
println!("{:?}", resp.response().body());
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
// Old slug no longer works
|
||||
@@ -415,9 +425,6 @@ pub async fn test_patch_project() {
|
||||
let project = api.get_project_deserialized("newslug", USER_USER_PAT).await;
|
||||
|
||||
assert_eq!(project.slug.unwrap(), "newslug");
|
||||
assert_eq!(project.title, "New successful title");
|
||||
assert_eq!(project.description, "New successful description");
|
||||
assert_eq!(project.body, "New successful body");
|
||||
assert_eq!(project.categories, vec![DUMMY_CATEGORIES[0]]);
|
||||
assert_eq!(project.license.id, "MIT");
|
||||
|
||||
@@ -449,7 +456,6 @@ pub async fn test_patch_project() {
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
println!("{:?}", resp.response().body());
|
||||
assert_eq!(resp.status(), 204);
|
||||
let project = api.get_project_deserialized("newslug", USER_USER_PAT).await;
|
||||
assert_eq!(project.link_urls.len(), 3);
|
||||
@@ -458,6 +464,39 @@ pub async fn test_patch_project() {
|
||||
.await;
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
pub async fn test_patch_v3() {
|
||||
// Hits V3-specific patchable fields
|
||||
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;
|
||||
|
||||
// Sucessful request to patch many fields.
|
||||
let resp = api
|
||||
.edit_project(
|
||||
alpha_project_slug,
|
||||
json!({
|
||||
"name": "New successful title",
|
||||
"summary": "New successful summary",
|
||||
"description": "New successful description",
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
let project = api
|
||||
.get_project_deserialized(alpha_project_slug, USER_USER_PAT)
|
||||
.await;
|
||||
|
||||
assert_eq!(project.name, "New successful title");
|
||||
assert_eq!(project.summary, "New successful summary");
|
||||
assert_eq!(project.description, "New successful description");
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
pub async fn test_bulk_edit_categories() {
|
||||
with_test_environment_all(None, |test_env| async move {
|
||||
@@ -551,32 +590,31 @@ pub async fn test_bulk_edit_links() {
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn permissions_patch_project() {
|
||||
with_test_environment_all(Some(8), |test_env| async move {
|
||||
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;
|
||||
|
||||
// TODO: This should be a separate test from v3
|
||||
// - only a couple of these fields are v3-specific
|
||||
// once we have permissions/scope tests setup to not just take closures, we can split this up
|
||||
|
||||
// For each permission covered by EDIT_DETAILS, ensure the permission is required
|
||||
let edit_details = ProjectPermissions::EDIT_DETAILS;
|
||||
let test_pairs = [
|
||||
// Body, status, requested_status tested separately
|
||||
("slug", json!("")), // generated in the test to not collide slugs
|
||||
("title", json!("randomname")),
|
||||
("name", json!("randomname")),
|
||||
("description", json!("randomdescription")),
|
||||
("categories", json!(["combat", "economy"])),
|
||||
("additional_categories", json!(["decoration"])),
|
||||
("issues_url", json!("https://issues.com")),
|
||||
("source_url", json!("https://source.com")),
|
||||
("wiki_url", json!("https://wiki.com")),
|
||||
(
|
||||
"donation_urls",
|
||||
json!([{
|
||||
"id": "paypal",
|
||||
"platform": "Paypal",
|
||||
"url": "https://paypal.com"
|
||||
}]),
|
||||
"links",
|
||||
json!({
|
||||
"issues": "https://issues.com",
|
||||
"source": "https://source.com",
|
||||
}),
|
||||
),
|
||||
("discord_url", json!("https://discord.com")),
|
||||
("license_id", json!("MIT")),
|
||||
];
|
||||
|
||||
@@ -645,7 +683,7 @@ async fn permissions_patch_project() {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v3/project/{}", ctx.project_id.unwrap()))
|
||||
.set_json(json!({
|
||||
"body": "new body!",
|
||||
"description": "new description!",
|
||||
}))
|
||||
};
|
||||
PermissionsTest::new(&test_env)
|
||||
@@ -1027,7 +1065,7 @@ async fn project_permissions_consistency_test() {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v3/project/{}", ctx.project_id.unwrap()))
|
||||
.set_json(json!({
|
||||
"title": "Example title - changed.",
|
||||
"name": "Example title - changed.",
|
||||
}))
|
||||
};
|
||||
PermissionsTest::new(&test_env)
|
||||
@@ -1044,7 +1082,7 @@ async fn project_permissions_consistency_test() {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v3/project/{}", ctx.project_id.unwrap()))
|
||||
.set_json(json!({
|
||||
"title": "Example title - changed.",
|
||||
"name": "Example title - changed.",
|
||||
}))
|
||||
};
|
||||
PermissionsTest::new(&test_env)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use actix_web::test::{self, TestRequest};
|
||||
use bytes::Bytes;
|
||||
use chrono::{Duration, Utc};
|
||||
|
||||
use common::api_v3::request_data::{
|
||||
get_public_project_creation_data, get_public_version_creation_data,
|
||||
};
|
||||
@@ -207,8 +206,11 @@ pub async fn notifications_scopes() {
|
||||
|
||||
// Project version creation scopes
|
||||
#[actix_rt::test]
|
||||
pub async fn project_version_create_scopes() {
|
||||
pub async fn project_version_create_scopes_v3() {
|
||||
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
|
||||
// TODO: If possible, find a way to use generic api functions with the Permissions/Scopes test, then this can be recombined with the V2 version of this test
|
||||
// let api = &test_env.api;
|
||||
|
||||
// Create project
|
||||
let create_project = Scopes::PROJECT_CREATE;
|
||||
let req_gen = || {
|
||||
@@ -510,7 +512,7 @@ pub async fn project_write_scopes() {
|
||||
.uri(&format!("/v3/project/{beta_project_id}"))
|
||||
.set_json(json!(
|
||||
{
|
||||
"title": "test_project_version_write_scopes Title",
|
||||
"name": "test_project_version_write_scopes Title",
|
||||
}
|
||||
))
|
||||
};
|
||||
@@ -1081,7 +1083,7 @@ pub async fn collections_scopes() {
|
||||
test::TestRequest::post()
|
||||
.uri("/v3/collection")
|
||||
.set_json(json!({
|
||||
"title": "Test Collection",
|
||||
"name": "Test Collection",
|
||||
"description": "Test Collection Description",
|
||||
"projects": [alpha_project_id]
|
||||
}))
|
||||
@@ -1099,7 +1101,7 @@ pub async fn collections_scopes() {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v3/collection/{collection_id}"))
|
||||
.set_json(json!({
|
||||
"title": "Test Collection patch",
|
||||
"name": "Test Collection patch",
|
||||
"status": "private",
|
||||
}))
|
||||
};
|
||||
@@ -1182,7 +1184,7 @@ pub async fn organization_scopes() {
|
||||
test::TestRequest::post()
|
||||
.uri("/v3/organization")
|
||||
.set_json(json!({
|
||||
"title": "TestOrg",
|
||||
"name": "TestOrg",
|
||||
"description": "TestOrg Description",
|
||||
}))
|
||||
};
|
||||
|
||||
@@ -96,7 +96,7 @@ async fn search_projects() {
|
||||
let modify_json = serde_json::from_value(json!([
|
||||
{ "op": "add", "path": "/categories", "value": DUMMY_CATEGORIES[0..2] },
|
||||
{ "op": "add", "path": "/initial_versions/0/server_only", "value": true },
|
||||
{ "op": "add", "path": "/title", "value": "Mysterious Project" },
|
||||
{ "op": "add", "path": "/name", "value": "Mysterious Project" },
|
||||
]))
|
||||
.unwrap();
|
||||
project_creation_futures.push(create_async_future(
|
||||
@@ -112,7 +112,7 @@ async fn search_projects() {
|
||||
{ "op": "add", "path": "/categories", "value": DUMMY_CATEGORIES[0..3] },
|
||||
{ "op": "add", "path": "/initial_versions/0/server_only", "value": true },
|
||||
{ "op": "add", "path": "/initial_versions/0/game_versions", "value": ["1.20.4"] },
|
||||
{ "op": "add", "path": "/title", "value": "Mysterious Project" },
|
||||
{ "op": "add", "path": "/name", "value": "Mysterious Project" },
|
||||
{ "op": "add", "path": "/license_id", "value": "LicenseRef-All-Rights-Reserved" },
|
||||
]))
|
||||
.unwrap();
|
||||
@@ -240,7 +240,7 @@ async fn search_projects() {
|
||||
(json!([["server_only:true"]]), vec![0, 2, 3, 6, 7]),
|
||||
(json!([["open_source:true"]]), vec![0, 1, 2, 4, 5, 6, 7]),
|
||||
(json!([["license:MIT"]]), vec![1, 2, 4]),
|
||||
(json!([[r#"title:'Mysterious Project'"#]]), vec![2, 3]),
|
||||
(json!([[r#"name:'Mysterious Project'"#]]), vec![2, 3]),
|
||||
(json!([["author:user"]]), vec![0, 1, 2, 4, 5, 7]),
|
||||
(json!([["game_versions:1.20.5"]]), vec![4, 5]),
|
||||
// bug fix
|
||||
|
||||
317
tests/teams.rs
317
tests/teams.rs
@@ -1,7 +1,11 @@
|
||||
use crate::common::database::*;
|
||||
use crate::common::{api_common::ApiTeams, database::*};
|
||||
use actix_web::test;
|
||||
use common::environment::with_test_environment_all;
|
||||
use common::{
|
||||
api_v3::ApiV3,
|
||||
environment::{with_test_environment, with_test_environment_all, TestEnvironment},
|
||||
};
|
||||
use labrinth::models::teams::{OrganizationPermissions, ProjectPermissions};
|
||||
use rust_decimal::Decimal;
|
||||
use serde_json::json;
|
||||
|
||||
mod common;
|
||||
@@ -209,152 +213,69 @@ async fn test_get_team_project_orgs() {
|
||||
async fn test_patch_project_team_member() {
|
||||
// Test setup and dummy data
|
||||
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;
|
||||
|
||||
// Edit team as admin/mod but not a part of the team should be OK
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/members/{USER_USER_ID}"))
|
||||
.set_json(json!({}))
|
||||
.append_header(("Authorization", ADMIN_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
let resp = api.edit_team_member(alpha_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/roles
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/members/{USER_USER_ID}"))
|
||||
.append_header(("Authorization", ADMIN_USER_PAT))
|
||||
.set_json(json!({
|
||||
"role": "member"
|
||||
}))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/members/{USER_USER_ID}"))
|
||||
.append_header(("Authorization", ADMIN_USER_PAT))
|
||||
.set_json(json!({
|
||||
"permissions": 0
|
||||
}))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
|
||||
// As a non-owner with full permissions, attempt to edit the owner's permissions
|
||||
let resp = api.edit_team_member(alpha_team_id, USER_USER_ID, json!({
|
||||
"permissions": 0
|
||||
}), ADMIN_USER_PAT).await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
// Should not be able to edit organization permissions of a project team
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/members/{USER_USER_ID}"))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"organization_permissions": 0
|
||||
}))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
|
||||
let resp = api.edit_team_member(alpha_team_id, USER_USER_ID, json!({
|
||||
"organization_permissions": 0
|
||||
}), USER_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/{alpha_team_id}/members"))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"user_id": FRIEND_USER_ID,
|
||||
"permissions": (ProjectPermissions::EDIT_MEMBER | ProjectPermissions::EDIT_BODY).bits(),
|
||||
}))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
let resp = api.add_user_to_team(alpha_team_id, FRIEND_USER_ID,
|
||||
Some(ProjectPermissions::EDIT_MEMBER | ProjectPermissions::EDIT_BODY),
|
||||
None, USER_USER_PAT).await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
// accept
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/join"))
|
||||
.append_header(("Authorization", FRIEND_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
let resp = api.join_team(alpha_team_id, FRIEND_USER_PAT).await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
// try to add permissions
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/members/{FRIEND_USER_ID}"))
|
||||
.append_header(("Authorization", FRIEND_USER_PAT))
|
||||
.set_json(json!({
|
||||
"permissions": (ProjectPermissions::EDIT_MEMBER | ProjectPermissions::EDIT_DETAILS).bits()
|
||||
}))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
// Cannot set a user to Owner
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!(
|
||||
"/v3/team/{alpha_team_id}/members/{FRIEND_USER_ID}"
|
||||
))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"role": "Owner"
|
||||
}))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
let resp = api.edit_team_member(alpha_team_id, FRIEND_USER_ID, json!({
|
||||
"permissions": (ProjectPermissions::EDIT_MEMBER | ProjectPermissions::EDIT_DETAILS).bits()
|
||||
}), FRIEND_USER_PAT).await; // should this be friend_user_pat
|
||||
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/{alpha_team_id}/members/{FRIEND_USER_ID}"
|
||||
))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"payouts_split": payout
|
||||
}))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
|
||||
let resp = api.edit_team_member(alpha_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/{alpha_team_id}/members/{FRIEND_USER_ID}"
|
||||
))
|
||||
.append_header(("Authorization", FRIEND_USER_PAT))
|
||||
.set_json(json!({
|
||||
let resp = api.edit_team_member(alpha_team_id, FRIEND_USER_ID, json!({
|
||||
"payouts_split": 51,
|
||||
"permissions": ProjectPermissions::EDIT_MEMBER.bits(), // reduces permissions
|
||||
"role": "member",
|
||||
"role": "membe2r",
|
||||
"ordering": 5
|
||||
}))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
}), FRIEND_USER_PAT).await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
// Check results
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/members"))
|
||||
.append_header(("Authorization", 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()
|
||||
.iter()
|
||||
.find(|x| x["user"]["id"] == FRIEND_USER_ID)
|
||||
.unwrap();
|
||||
assert_eq!(member["payouts_split"], 51.0);
|
||||
assert_eq!(
|
||||
member["permissions"],
|
||||
ProjectPermissions::EDIT_MEMBER.bits()
|
||||
);
|
||||
assert_eq!(member["role"], "member");
|
||||
assert_eq!(member["ordering"], 5);
|
||||
|
||||
let members = api.get_team_members_deserialized_common(alpha_team_id, FRIEND_USER_PAT).await;
|
||||
let member = members.iter().find(|x| x.user.id.0 == FRIEND_USER_ID_PARSED as u64).unwrap();
|
||||
assert_eq!(member.payouts_split, Decimal::from_f64_retain(51.0));
|
||||
assert_eq!(member.permissions.unwrap(), ProjectPermissions::EDIT_MEMBER);
|
||||
assert_eq!(member.role, "membe2r");
|
||||
assert_eq!(member.ordering, 5);
|
||||
}).await;
|
||||
}
|
||||
|
||||
@@ -374,17 +295,7 @@ async fn test_patch_organization_team_member() {
|
||||
let resp = test_env.call(req).await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
// As a non-owner with full permissions, attempt to edit the owner's permissions/roles
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/team/{zeta_team_id}/members/{USER_USER_ID}"))
|
||||
.append_header(("Authorization", ADMIN_USER_PAT))
|
||||
.set_json(json!({
|
||||
"role": "member"
|
||||
}))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
// 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_header(("Authorization", ADMIN_USER_PAT))
|
||||
@@ -429,18 +340,6 @@ async fn test_patch_organization_team_member() {
|
||||
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
// Cannot set a user to Owner
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/team/{zeta_team_id}/members/{FRIEND_USER_ID}"))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"role": "Owner"
|
||||
}))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
// Cannot set payouts outside of 0 and 5000
|
||||
for payout in [-1, 5001] {
|
||||
let req = test::TestRequest::patch()
|
||||
@@ -462,7 +361,7 @@ async fn test_patch_organization_team_member() {
|
||||
"payouts_split": 51,
|
||||
"organization_permissions": (OrganizationPermissions::EDIT_MEMBER).bits(), // reduces permissions
|
||||
"permissions": (ProjectPermissions::EDIT_MEMBER).bits(),
|
||||
"role": "member",
|
||||
"role": "very-cool-member",
|
||||
"ordering": 5
|
||||
}))
|
||||
.to_request();
|
||||
@@ -493,7 +392,7 @@ async fn test_patch_organization_team_member() {
|
||||
member["permissions"],
|
||||
ProjectPermissions::EDIT_MEMBER.bits()
|
||||
);
|
||||
assert_eq!(member["role"], "member");
|
||||
assert_eq!(member["role"], "very-cool-member");
|
||||
assert_eq!(member["ordering"], 5);
|
||||
|
||||
}).await;
|
||||
@@ -501,104 +400,102 @@ async fn test_patch_organization_team_member() {
|
||||
|
||||
// trasnfer ownership (requires being owner, etc)
|
||||
#[actix_rt::test]
|
||||
async fn transfer_ownership() {
|
||||
async fn transfer_ownership_v3() {
|
||||
// Test setup and dummy data
|
||||
with_test_environment_all(None, |test_env| async move {
|
||||
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
|
||||
let api = &test_env.api;
|
||||
|
||||
let alpha_team_id = &test_env.dummy.as_ref().unwrap().project_alpha.team_id;
|
||||
|
||||
// Cannot set friend as owner (not a member)
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/owner"))
|
||||
.set_json(json!({
|
||||
"user_id": FRIEND_USER_ID
|
||||
}))
|
||||
.append_header(("Authorization", USER_USER_ID))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
let resp = api
|
||||
.transfer_team_ownership(alpha_team_id, FRIEND_USER_ID, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
let resp = api
|
||||
.transfer_team_ownership(alpha_team_id, FRIEND_USER_ID, FRIEND_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 401);
|
||||
|
||||
// first, invite friend
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/members"))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.set_json(json!({
|
||||
"user_id": FRIEND_USER_ID,
|
||||
}))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
let resp = api
|
||||
.add_user_to_team(alpha_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
// still cannot set friend as owner (not accepted)
|
||||
let resp = api
|
||||
.transfer_team_ownership(alpha_team_id, FRIEND_USER_ID, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
// accept
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/join"))
|
||||
.append_header(("Authorization", FRIEND_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
let resp = api.join_team(alpha_team_id, FRIEND_USER_PAT).await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
// Cannot set ourselves as owner
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/owner"))
|
||||
.set_json(json!({
|
||||
"user_id": FRIEND_USER_ID
|
||||
}))
|
||||
.append_header(("Authorization", FRIEND_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
// Cannot set ourselves as owner if we are not owner
|
||||
let resp = api
|
||||
.transfer_team_ownership(alpha_team_id, FRIEND_USER_ID, FRIEND_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 401);
|
||||
|
||||
// Can set friend as owner
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/owner"))
|
||||
.set_json(json!({
|
||||
"user_id": FRIEND_USER_ID
|
||||
}))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
let resp = api
|
||||
.transfer_team_ownership(alpha_team_id, FRIEND_USER_ID, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
// Check
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!("/v3/team/{alpha_team_id}/members"))
|
||||
.set_json(json!({
|
||||
"user_id": FRIEND_USER_ID
|
||||
}))
|
||||
.append_header(("Authorization", USER_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 friend_member = value
|
||||
.as_array()
|
||||
.unwrap()
|
||||
let members = api
|
||||
.get_team_members_deserialized(alpha_team_id, USER_USER_PAT)
|
||||
.await;
|
||||
let friend_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!(friend_member["role"], "Owner");
|
||||
assert_eq!(friend_member.role, "Member"); // her role does not actually change, but is_owner is set to true
|
||||
assert!(friend_member.is_owner);
|
||||
assert_eq!(
|
||||
friend_member["permissions"],
|
||||
ProjectPermissions::all().bits()
|
||||
friend_member.permissions.unwrap(),
|
||||
ProjectPermissions::all()
|
||||
);
|
||||
let user_member = value
|
||||
.as_array()
|
||||
.unwrap()
|
||||
|
||||
let user_member = members
|
||||
.iter()
|
||||
.find(|x| x["user"]["id"] == USER_USER_ID)
|
||||
.find(|x| x.user.id.0 == USER_USER_ID_PARSED as u64)
|
||||
.unwrap();
|
||||
assert_eq!(user_member["role"], "Member");
|
||||
assert_eq!(user_member["permissions"], ProjectPermissions::all().bits());
|
||||
assert_eq!(user_member.role, "Owner"); // We are the 'owner', but we are not actually the owner!
|
||||
assert!(!user_member.is_owner);
|
||||
assert_eq!(user_member.permissions.unwrap(), ProjectPermissions::all());
|
||||
|
||||
// Confirm that user, a user who still has full permissions, cannot then remove the owner
|
||||
let req = test::TestRequest::delete()
|
||||
.uri(&format!(
|
||||
"/v3/team/{alpha_team_id}/members/{FRIEND_USER_ID}"
|
||||
))
|
||||
.append_header(("Authorization", USER_USER_PAT))
|
||||
.to_request();
|
||||
|
||||
let resp = test_env.call(req).await;
|
||||
let resp = api
|
||||
.remove_from_team(alpha_team_id, FRIEND_USER_ID, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 401);
|
||||
|
||||
// V3 only- confirm the owner can change their role without losing ownership
|
||||
let resp = api
|
||||
.edit_team_member(
|
||||
alpha_team_id,
|
||||
FRIEND_USER_ID,
|
||||
json!({
|
||||
"role": "Member"
|
||||
}),
|
||||
FRIEND_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
let members = api
|
||||
.get_team_members_deserialized(alpha_team_id, USER_USER_PAT)
|
||||
.await;
|
||||
let friend_member = members
|
||||
.iter()
|
||||
.find(|x| x.user.id.0 == FRIEND_USER_ID_PARSED as u64)
|
||||
.unwrap();
|
||||
assert_eq!(friend_member.role, "Member");
|
||||
assert!(friend_member.is_owner);
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
22
tests/v2/error.rs
Normal file
22
tests/v2/error.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use crate::common::api_common::ApiProject;
|
||||
use actix_web::test;
|
||||
use bytes::Bytes;
|
||||
|
||||
use crate::common::database::USER_USER_PAT;
|
||||
use crate::common::{
|
||||
api_v2::ApiV2,
|
||||
environment::{with_test_environment, TestEnvironment},
|
||||
};
|
||||
#[actix_rt::test]
|
||||
pub async fn error_404_empty() {
|
||||
with_test_environment(None, |test_env: TestEnvironment<ApiV2>| async move {
|
||||
// V2 errors should have 404 as blank body, for missing resources
|
||||
let api = &test_env.api;
|
||||
let resp = api.get_project("does-not-exist", USER_USER_PAT).await;
|
||||
assert_eq!(resp.status(), 404);
|
||||
let body = test::read_body(resp).await;
|
||||
let empty_bytes = Bytes::from_static(b"");
|
||||
assert_eq!(body, empty_bytes);
|
||||
})
|
||||
.await;
|
||||
}
|
||||
31
tests/v2/notifications.rs
Normal file
31
tests/v2/notifications.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
use crate::common::{
|
||||
api_common::ApiTeams,
|
||||
api_v2::ApiV2,
|
||||
database::{FRIEND_USER_ID, FRIEND_USER_PAT, USER_USER_PAT},
|
||||
environment::{with_test_environment, TestEnvironment},
|
||||
};
|
||||
|
||||
#[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 api = test_env.api;
|
||||
api.add_user_to_team(&alpha_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT)
|
||||
.await;
|
||||
|
||||
let notifications = api
|
||||
.get_user_notifications_deserialized(FRIEND_USER_ID, FRIEND_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(1, notifications.len());
|
||||
|
||||
// Check to make sure type_ is correct
|
||||
assert_eq!(notifications[0].type_.as_ref().unwrap(), "team_invite");
|
||||
})
|
||||
.await;
|
||||
}
|
||||
@@ -5,15 +5,15 @@ use crate::common::{
|
||||
ApiV2,
|
||||
},
|
||||
database::{
|
||||
ADMIN_USER_PAT, ENEMY_USER_PAT, FRIEND_USER_ID, FRIEND_USER_PAT, MOD_USER_PAT,
|
||||
USER_USER_PAT,
|
||||
generate_random_name, ADMIN_USER_PAT, FRIEND_USER_ID, FRIEND_USER_PAT, USER_USER_PAT,
|
||||
},
|
||||
dummy_data::{TestFile, DUMMY_CATEGORIES},
|
||||
dummy_data::TestFile,
|
||||
environment::{with_test_environment, TestEnvironment},
|
||||
permissions::{PermissionsTest, PermissionsTestContext},
|
||||
};
|
||||
use actix_http::StatusCode;
|
||||
use actix_web::test;
|
||||
use futures::StreamExt;
|
||||
use itertools::Itertools;
|
||||
use labrinth::{
|
||||
database::models::project_item::PROJECTS_SLUGS_NAMESPACE,
|
||||
@@ -336,165 +336,102 @@ async fn permissions_upload_version() {
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
pub async fn test_patch_project() {
|
||||
pub async fn test_patch_v2() {
|
||||
// Hits V3-specific patchable fields
|
||||
// Other fields are tested in test_patch_project (the v2 version of that test)
|
||||
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 beta_project_slug = &test_env.dummy.as_ref().unwrap().project_beta.project_slug;
|
||||
|
||||
// First, we do some patch requests that should fail.
|
||||
// Failure because the user is not authorized.
|
||||
let resp = api
|
||||
.edit_project(
|
||||
alpha_project_slug,
|
||||
json!({
|
||||
"title": "Test_Add_Project project - test 1",
|
||||
}),
|
||||
ENEMY_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 401);
|
||||
|
||||
// Failure because we are setting URL fields to invalid urls.
|
||||
for url_type in ["issues_url", "source_url", "wiki_url", "discord_url"] {
|
||||
let resp = api
|
||||
.edit_project(
|
||||
alpha_project_slug,
|
||||
json!({
|
||||
url_type: "w.fake.url",
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
}
|
||||
|
||||
// Failure because these are illegal requested statuses for a normal user.
|
||||
for req in ["unknown", "processing", "withheld", "scheduled"] {
|
||||
let resp = api
|
||||
.edit_project(
|
||||
alpha_project_slug,
|
||||
json!({
|
||||
"requested_status": req,
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
}
|
||||
|
||||
// Failure because these should not be able to be set by a non-mod
|
||||
for key in ["moderation_message", "moderation_message_body"] {
|
||||
let resp = api
|
||||
.edit_project(
|
||||
alpha_project_slug,
|
||||
json!({
|
||||
key: "test",
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 401);
|
||||
|
||||
// (should work for a mod, though)
|
||||
let resp = api
|
||||
.edit_project(
|
||||
alpha_project_slug,
|
||||
json!({
|
||||
key: "test",
|
||||
}),
|
||||
MOD_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
}
|
||||
|
||||
// Failed patch to alpha slug:
|
||||
// - slug collision with beta
|
||||
// - too short slug
|
||||
// - too long slug
|
||||
// - not url safe slug
|
||||
// - not url safe slug
|
||||
for slug in [
|
||||
beta_project_slug,
|
||||
"a",
|
||||
&"a".repeat(100),
|
||||
"not url safe%&^!#$##!@#$%^&*()",
|
||||
] {
|
||||
let resp = api
|
||||
.edit_project(
|
||||
alpha_project_slug,
|
||||
json!({
|
||||
"slug": slug, // the other dummy project has this slug
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
}
|
||||
|
||||
// Not allowed to directly set status, as 'beta_project_slug' (the other project) is "processing" and cannot have its status changed like this.
|
||||
let resp = api
|
||||
.edit_project(
|
||||
beta_project_slug,
|
||||
json!({
|
||||
"status": "private"
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 401);
|
||||
|
||||
// Sucessful request to patch many fields.
|
||||
let resp = api
|
||||
.edit_project(
|
||||
alpha_project_slug,
|
||||
json!({
|
||||
"slug": "newslug",
|
||||
"title": "New successful title",
|
||||
"description": "New successful description",
|
||||
"body": "New successful body",
|
||||
"categories": [DUMMY_CATEGORIES[0]],
|
||||
"license_id": "MIT",
|
||||
"issues_url": "https://github.com",
|
||||
"discord_url": "https://discord.gg",
|
||||
"wiki_url": "https://wiki.com",
|
||||
"client_side": "unsupported",
|
||||
"server_side": "required",
|
||||
"donation_urls": [{
|
||||
"id": "patreon",
|
||||
"platform": "Patreon",
|
||||
"url": "https://patreon.com"
|
||||
}]
|
||||
}),
|
||||
USER_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
// Old slug no longer works
|
||||
let resp = api.get_project(alpha_project_slug, USER_USER_PAT).await;
|
||||
assert_eq!(resp.status(), 404);
|
||||
let project = api
|
||||
.get_project_deserialized(alpha_project_slug, USER_USER_PAT)
|
||||
.await;
|
||||
|
||||
// New slug does work
|
||||
let project = api.get_project_deserialized("newslug", USER_USER_PAT).await;
|
||||
assert_eq!(project.slug.unwrap(), "newslug");
|
||||
assert_eq!(project.title, "New successful title");
|
||||
assert_eq!(project.description, "New successful description");
|
||||
assert_eq!(project.body, "New successful body");
|
||||
assert_eq!(project.categories, vec![DUMMY_CATEGORIES[0]]);
|
||||
assert_eq!(project.license.id, "MIT");
|
||||
assert_eq!(project.issues_url, Some("https://github.com".to_string()));
|
||||
assert_eq!(project.discord_url, Some("https://discord.gg".to_string()));
|
||||
assert_eq!(project.wiki_url, Some("https://wiki.com".to_string()));
|
||||
// Note: the original V2 value of this was "optional",
|
||||
// but Required/Optional is no longer a carried combination in v3, as the changes made were lossy.
|
||||
// Now, the test Required/Unsupported combination is tested instead.
|
||||
// Setting Required/Optional in v2 will not work, this is known and accepteed.
|
||||
assert_eq!(project.client_side.as_str(), "unsupported");
|
||||
assert_eq!(project.server_side.as_str(), "required");
|
||||
assert_eq!(project.donation_urls.unwrap()[0].url, "https://patreon.com");
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn permissions_patch_project_v2() {
|
||||
with_test_environment(Some(8), |test_env: TestEnvironment<ApiV2>| async move {
|
||||
// TODO: This only includes v2 ones (as it should. See v3)
|
||||
// For each permission covered by EDIT_DETAILS, ensure the permission is required
|
||||
let edit_details = ProjectPermissions::EDIT_DETAILS;
|
||||
let test_pairs = [
|
||||
("description", json!("description")),
|
||||
("issues_url", json!("https://issues.com")),
|
||||
("source_url", json!("https://source.com")),
|
||||
("wiki_url", json!("https://wiki.com")),
|
||||
(
|
||||
"donation_urls",
|
||||
json!([{
|
||||
"id": "paypal",
|
||||
"platform": "Paypal",
|
||||
"url": "https://paypal.com"
|
||||
}]),
|
||||
),
|
||||
("discord_url", json!("https://discord.com")),
|
||||
];
|
||||
|
||||
futures::stream::iter(test_pairs)
|
||||
.map(|(key, value)| {
|
||||
let test_env = test_env.clone();
|
||||
async move {
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v2/project/{}", ctx.project_id.unwrap()))
|
||||
.set_json(json!({
|
||||
key: if key == "slug" {
|
||||
json!(generate_random_name("randomslug"))
|
||||
} else {
|
||||
value.clone()
|
||||
},
|
||||
}))
|
||||
};
|
||||
PermissionsTest::new(&test_env)
|
||||
.simple_project_permissions_test(edit_details, req_gen)
|
||||
.await
|
||||
.into_iter();
|
||||
}
|
||||
})
|
||||
.buffer_unordered(4)
|
||||
.collect::<Vec<_>>()
|
||||
.await;
|
||||
|
||||
// Edit body
|
||||
// Cannot bulk edit body
|
||||
let edit_body = ProjectPermissions::EDIT_BODY;
|
||||
let req_gen = |ctx: &PermissionsTestContext| {
|
||||
test::TestRequest::patch()
|
||||
.uri(&format!("/v2/project/{}", ctx.project_id.unwrap()))
|
||||
.set_json(json!({
|
||||
"body": "new body!", // new body
|
||||
}))
|
||||
};
|
||||
PermissionsTest::new(&test_env)
|
||||
.simple_project_permissions_test(edit_body, req_gen)
|
||||
.await
|
||||
.unwrap();
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
105
tests/v2/teams.rs
Normal file
105
tests/v2/teams.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
use labrinth::models::teams::ProjectPermissions;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::{
|
||||
api_common::ApiTeams,
|
||||
api_v2::ApiV2,
|
||||
database::{
|
||||
FRIEND_USER_ID, FRIEND_USER_ID_PARSED, FRIEND_USER_PAT, USER_USER_ID_PARSED, USER_USER_PAT,
|
||||
},
|
||||
environment::{with_test_environment, TestEnvironment},
|
||||
};
|
||||
|
||||
// trasnfer ownership (requires being owner, etc)
|
||||
#[actix_rt::test]
|
||||
async fn transfer_ownership_v2() {
|
||||
// Test setup and dummy data
|
||||
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;
|
||||
|
||||
// Cannot set friend as owner (not a member)
|
||||
let resp = api
|
||||
.transfer_team_ownership(alpha_team_id, FRIEND_USER_ID, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
// first, invite friend
|
||||
let resp = api
|
||||
.add_user_to_team(alpha_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
// still cannot set friend as owner (not accepted)
|
||||
let resp = api
|
||||
.transfer_team_ownership(alpha_team_id, FRIEND_USER_ID, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 400);
|
||||
|
||||
// accept
|
||||
let resp = api.join_team(alpha_team_id, FRIEND_USER_PAT).await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
// Cannot set ourselves as owner if we are not owner
|
||||
let resp = api
|
||||
.transfer_team_ownership(alpha_team_id, FRIEND_USER_ID, FRIEND_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 401);
|
||||
|
||||
// Can set friend as owner
|
||||
let resp = api
|
||||
.transfer_team_ownership(alpha_team_id, FRIEND_USER_ID, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
|
||||
// Check
|
||||
let members = api
|
||||
.get_team_members_deserialized(alpha_team_id, USER_USER_PAT)
|
||||
.await;
|
||||
let friend_member = members
|
||||
.iter()
|
||||
.find(|x| x.user.id.0 == FRIEND_USER_ID_PARSED as u64)
|
||||
.unwrap();
|
||||
assert_eq!(friend_member.role, "Owner");
|
||||
assert_eq!(
|
||||
friend_member.permissions.unwrap(),
|
||||
ProjectPermissions::all()
|
||||
);
|
||||
|
||||
let user_member = members
|
||||
.iter()
|
||||
.find(|x| x.user.id.0 == USER_USER_ID_PARSED as u64)
|
||||
.unwrap();
|
||||
assert_eq!(user_member.role, "Member");
|
||||
assert_eq!(user_member.permissions.unwrap(), ProjectPermissions::all());
|
||||
|
||||
// Confirm that user, a user who still has full permissions, cannot then remove the owner
|
||||
let resp = api
|
||||
.remove_from_team(alpha_team_id, FRIEND_USER_ID, USER_USER_PAT)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 401);
|
||||
|
||||
// V2 only- confirm the owner changing the role to member does nothing
|
||||
let resp = api
|
||||
.edit_team_member(
|
||||
alpha_team_id,
|
||||
FRIEND_USER_ID,
|
||||
json!({
|
||||
"role": "Member"
|
||||
}),
|
||||
FRIEND_USER_PAT,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 204);
|
||||
let members = api
|
||||
.get_team_members_deserialized(alpha_team_id, USER_USER_PAT)
|
||||
.await;
|
||||
let friend_member = members
|
||||
.iter()
|
||||
.find(|x| x.user.id.0 == FRIEND_USER_ID_PARSED as u64)
|
||||
.unwrap();
|
||||
assert_eq!(friend_member.role, "Owner");
|
||||
})
|
||||
.await;
|
||||
}
|
||||
@@ -8,9 +8,12 @@ mod common;
|
||||
|
||||
// Such V2 tests are exported here
|
||||
mod v2 {
|
||||
mod error;
|
||||
mod notifications;
|
||||
mod project;
|
||||
mod scopes;
|
||||
mod search;
|
||||
mod tags;
|
||||
mod teams;
|
||||
mod version;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user