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:
Wyatt Verchere
2023-12-01 19:15:00 -08:00
committed by GitHub
parent 2d92b08404
commit a70df067bc
119 changed files with 2897 additions and 1334 deletions

View File

@@ -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],

View File

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

View File

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

View 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>,
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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();

View File

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

View File

@@ -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": [],

View File

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

View File

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

View File

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

View File

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

View File

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