Fixes failing tests (#813)

* fixes failing  tests

* fmt clippy

* updated dockerfile

* fixes failing tests; adds important fix from extracts_versions PR

* assert_eq -> assert_status, giving better error messages

* fixed random failure bug

* fmt, clippy, etc
This commit is contained in:
Wyatt Verchere
2024-01-05 08:20:56 -08:00
committed by GitHub
parent f5802fee31
commit 10eed05d87
37 changed files with 555 additions and 330 deletions

View File

@@ -1,4 +1,4 @@
FROM rust:1.68.0 as build
FROM rust:1.75.0 as build
ENV PKG_CONFIG_ALLOW_CROSS=1
WORKDIR /usr/src/labrinth

View File

@@ -47,8 +47,9 @@ impl FileHost for MockHost {
) -> Result<DeleteFileData, FileHostingError> {
let path = std::path::Path::new(&dotenvy::var("MOCK_FILE_PATH").unwrap())
.join(file_name.replace("../", ""));
std::fs::remove_file(path)?;
if path.exists() {
std::fs::remove_file(path)?;
}
Ok(DeleteFileData {
file_id: file_id.to_string(),
file_name: file_name.to_string(),

View File

@@ -57,6 +57,7 @@ pub struct LabrinthConfig {
pub fn app_setup(
pool: sqlx::Pool<Postgres>,
redis_pool: RedisPool,
search_config: search::SearchConfig,
clickhouse: &mut Client,
file_host: Arc<dyn file_hosting::FileHost + Send + Sync>,
maxmind: Arc<queue::maxmind::MaxMindIndexer>,
@@ -66,11 +67,6 @@ pub fn app_setup(
dotenvy::var("BIND_ADDR").unwrap()
);
let search_config = search::SearchConfig {
address: dotenvy::var("MEILISEARCH_ADDR").unwrap(),
key: dotenvy::var("MEILISEARCH_KEY").unwrap(),
};
let mut scheduler = scheduler::Scheduler::new();
// The interval in seconds at which the local database is indexed

View File

@@ -6,10 +6,10 @@ use labrinth::file_hosting::S3Host;
use labrinth::ratelimit::errors::ARError;
use labrinth::ratelimit::memory::{MemoryStore, MemoryStoreActor};
use labrinth::ratelimit::middleware::RateLimiter;
use labrinth::search;
use labrinth::util::env::parse_var;
use labrinth::{check_env_vars, clickhouse, database, file_hosting, queue};
use log::{error, info};
use std::sync::Arc;
#[derive(Clone)]
@@ -93,11 +93,13 @@ async fn main() -> std::io::Result<()> {
.build()
.expect("Failed to create prometheus metrics middleware");
let search_config = search::SearchConfig::new(None);
info!("Starting Actix HTTP server!");
let labrinth_config = labrinth::app_setup(
pool.clone(),
redis_pool.clone(),
search_config.clone(),
&mut clickhouse,
file_host.clone(),
maxmind_reader.clone(),

View File

@@ -17,7 +17,7 @@ pub struct Organization {
pub id: OrganizationId,
/// The slug of the organization
pub slug: String,
/// The title (and slug) of the organization
/// The title of the organization
pub name: String,
/// The associated team of the organization
pub team_id: TeamId,

View File

@@ -81,7 +81,7 @@ pub fn root_config(cfg: &mut web::ServiceConfig) {
pub enum ApiError {
#[error("Environment Error")]
Env(#[from] dotenvy::Error),
#[error("Error while uploading file")]
#[error("Error while uploading file: {0}")]
FileHosting(#[from] FileHostingError),
#[error("Database Error: {0}")]
Database(#[from] crate::database::models::DatabaseError),

View File

@@ -8,7 +8,7 @@ use sqlx::PgPool;
use validator::Validate;
use crate::{
auth::get_user_from_headers,
auth::{filter_visible_projects, get_user_from_headers},
database::{models::User, redis::RedisPool},
file_hosting::FileHost,
models::{
@@ -65,23 +65,12 @@ pub async fn projects_list(
let id_option = User::get(&info.into_inner().0, &**pool, &redis).await?;
if let Some(id) = id_option.map(|x| x.id) {
let user_id: UserId = id.into();
let can_view_private = user
.map(|y| y.role.is_mod() || y.id == user_id)
.unwrap_or(false);
let project_data = User::get_projects(id, &**pool, &redis).await?;
let response: Vec<_> =
crate::database::Project::get_many_ids(&project_data, &**pool, &redis)
.await?
.into_iter()
.filter(|x| can_view_private || x.inner.status.is_searchable())
.map(Project::from)
.collect();
Ok(HttpResponse::Ok().json(response))
let projects: Vec<_> =
crate::database::Project::get_many_ids(&project_data, &**pool, &redis).await?;
let projects = filter_visible_projects(projects, &user, &pool).await?;
Ok(HttpResponse::Ok().json(projects))
} else {
Err(ApiError::NotFound)
}

View File

@@ -107,11 +107,12 @@ pub async fn get_indexes(
config: &SearchConfig,
) -> Result<Vec<Index>, meilisearch_sdk::errors::Error> {
let client = config.make_client();
let projects_index = create_or_update_index(&client, "projects", None).await?;
let project_name = config.get_index_name("projects");
let project_filtered_name = config.get_index_name("projects_filtered");
let projects_index = create_or_update_index(&client, &project_name, None).await?;
let projects_filtered_index = create_or_update_index(
&client,
"projects_filtered",
&project_filtered_name,
Some(&[
"sort",
"words",
@@ -128,7 +129,7 @@ pub async fn get_indexes(
async fn create_or_update_index(
client: &Client,
name: &'static str,
name: &str,
custom_rules: Option<&'static [&'static str]>,
) -> Result<Index, meilisearch_sdk::errors::Error> {
info!("Updating/creating index.");
@@ -207,7 +208,6 @@ async fn create_or_update_index(
typo_tolerance: None, // We don't use typo tolerance right now
dictionary: None, // We don't use dictionary right now
};
if old_settings.synonyms != settings.synonyms
|| old_settings.stop_words != settings.stop_words
|| old_settings.ranking_rules != settings.ranking_rules
@@ -294,16 +294,23 @@ async fn update_and_add_to_index(
new_filterable_attributes.extend(additional_fields.iter().map(|s| s.to_string()));
new_displayed_attributes.extend(additional_fields.iter().map(|s| s.to_string()));
info!("add attributes.");
index
let filterable_task = index
.set_filterable_attributes(new_filterable_attributes)
.await?;
index
let displayable_task = index
.set_displayed_attributes(new_displayed_attributes)
.await?;
filterable_task
.wait_for_completion(client, None, Some(TIMEOUT))
.await?;
displayable_task
.wait_for_completion(client, None, Some(TIMEOUT))
.await?;
info!("Adding to index.");
add_to_index(client, index, projects).await?;
Ok(())
}
@@ -315,7 +322,6 @@ pub async fn add_projects(
) -> Result<(), IndexingError> {
let client = config.make_client();
for index in indices {
info!("adding projects part1 or 2.");
update_and_add_to_index(&client, index, &projects, &additional_fields).await?;
}
@@ -329,7 +335,6 @@ fn default_settings() -> Settings {
sorted_sortable.sort();
let mut sorted_attrs = DEFAULT_ATTRIBUTES_FOR_FACETING.to_vec();
sorted_attrs.sort();
Settings::new()
.with_distinct_attribute("project_id")
.with_displayed_attributes(sorted_display)

View File

@@ -59,16 +59,34 @@ impl actix_web::ResponseError for SearchError {
}
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct SearchConfig {
pub address: String,
pub key: String,
pub meta_namespace: String,
}
impl SearchConfig {
// Panics if the environment variables are not set,
// but these are already checked for on startup.
pub fn new(meta_namespace: Option<String>) -> Self {
let address = dotenvy::var("MEILISEARCH_ADDR").expect("MEILISEARCH_ADDR not set");
let key = dotenvy::var("MEILISEARCH_KEY").expect("MEILISEARCH_KEY not set");
Self {
address,
key,
meta_namespace: meta_namespace.unwrap_or_default(),
}
}
pub fn make_client(&self) -> Client {
Client::new(self.address.as_str(), Some(self.key.as_str()))
}
pub fn get_index_name(&self, index: &str) -> String {
format!("{}_{}", self.meta_namespace, index)
}
}
/// A project document used for uploading projects to MeiliSearch's indices.
@@ -172,13 +190,18 @@ pub struct ResultSearchProject {
pub loader_fields: HashMap<String, Vec<serde_json::Value>>,
}
pub fn get_sort_index(index: &str) -> Result<(&str, [&str; 1]), SearchError> {
pub fn get_sort_index(
config: &SearchConfig,
index: &str,
) -> Result<(String, [&'static str; 1]), SearchError> {
let projects_name = config.get_index_name("projects");
let projects_filtered_name = config.get_index_name("projects_filtered");
Ok(match index {
"relevance" => ("projects", ["downloads:desc"]),
"downloads" => ("projects_filtered", ["downloads:desc"]),
"follows" => ("projects", ["follows:desc"]),
"updated" => ("projects", ["date_modified:desc"]),
"newest" => ("projects", ["date_created:desc"]),
"relevance" => (projects_name, ["downloads:desc"]),
"downloads" => (projects_filtered_name, ["downloads:desc"]),
"follows" => (projects_name, ["follows:desc"]),
"updated" => (projects_name, ["date_modified:desc"]),
"newest" => (projects_name, ["date_created:desc"]),
i => return Err(SearchError::InvalidIndex(i.to_string())),
})
}
@@ -193,8 +216,7 @@ pub async fn search_for_project(
let index = info.index.as_deref().unwrap_or("relevance");
let limit = info.limit.as_deref().unwrap_or("10").parse()?;
let sort = get_sort_index(index)?;
let sort = get_sort_index(config, index)?;
let meilisearch_index = client.get_index(sort.0).await?;
let mut filter_string = String::new();

View File

@@ -35,7 +35,7 @@ impl ApiV2 {
pat: Option<&str>,
) -> LegacyProject {
let resp = self.get_project(id_or_slug, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
@@ -45,7 +45,7 @@ impl ApiV2 {
pat: Option<&str>,
) -> Vec<LegacyProject> {
let resp = self.get_user_projects(user_id_or_username, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
@@ -72,8 +72,7 @@ impl ApiV2 {
.append_pat(pat)
.to_request();
let resp = self.call(req).await;
let status = resp.status();
assert_eq!(status, 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
}
@@ -164,7 +163,7 @@ impl ApiProject for ApiV2 {
pat: Option<&str>,
) -> CommonProject {
let resp = self.get_project(id_or_slug, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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
@@ -214,7 +213,7 @@ impl ApiProject for ApiV2 {
pat: Option<&str>,
) -> Vec<CommonProject> {
let resp = self.get_user_projects(user_id_or_username, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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

View File

@@ -1,3 +1,4 @@
use actix_http::StatusCode;
use actix_web::{
dev::ServiceResponse,
test::{self, TestRequest},
@@ -12,6 +13,7 @@ use crate::common::{
models::{CommonCategoryData, CommonLoaderData},
Api, ApiTags, AppendsOptionalPat,
},
asserts::assert_status,
database::ADMIN_USER_PAT,
};
@@ -30,7 +32,7 @@ impl ApiV2 {
pub async fn get_side_types_deserialized(&self) -> Vec<String> {
let resp = self.get_side_types().await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
@@ -44,19 +46,19 @@ impl ApiV2 {
pub async fn get_game_versions_deserialized(&self) -> Vec<GameVersionQueryData> {
let resp = self.get_game_versions().await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
pub async fn get_loaders_deserialized(&self) -> Vec<LoaderData> {
let resp = self.get_loaders().await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
pub async fn get_categories_deserialized(&self) -> Vec<CategoryData> {
let resp = self.get_categories().await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
@@ -70,8 +72,7 @@ impl ApiV2 {
pub async fn get_donation_platforms_deserialized(&self) -> Vec<DonationPlatformQueryData> {
let resp = self.get_donation_platforms().await;
println!("Response: {:?}", resp.response().body());
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
}
@@ -88,7 +89,7 @@ impl ApiTags for ApiV2 {
async fn get_loaders_deserialized_common(&self) -> Vec<CommonLoaderData> {
let resp = self.get_loaders().await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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
@@ -106,7 +107,7 @@ impl ApiTags for ApiV2 {
async fn get_categories_deserialized_common(&self) -> Vec<CommonCategoryData> {
let resp = self.get_categories().await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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

View File

@@ -24,7 +24,7 @@ impl ApiV2 {
pat: Option<&str>,
) -> Vec<LegacyTeamMember> {
let resp = self.get_organization_members(id_or_title, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
@@ -34,7 +34,7 @@ impl ApiV2 {
pat: Option<&str>,
) -> Vec<LegacyTeamMember> {
let resp = self.get_team_members(team_id, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
@@ -44,7 +44,7 @@ impl ApiV2 {
pat: Option<&str>,
) -> Vec<LegacyNotification> {
let resp = self.get_user_notifications(user_id, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
}
@@ -65,7 +65,7 @@ impl ApiTeams for ApiV2 {
pat: Option<&str>,
) -> Vec<CommonTeamMember> {
let resp = self.get_team_members(id_or_title, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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.
@@ -102,7 +102,7 @@ impl ApiTeams for ApiV2 {
pat: Option<&str>,
) -> Vec<CommonTeamMember> {
let resp = self.get_project_members(id_or_title, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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.
@@ -127,7 +127,7 @@ impl ApiTeams for ApiV2 {
pat: Option<&str>,
) -> Vec<CommonTeamMember> {
let resp = self.get_organization_members(id_or_title, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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.

View File

@@ -33,7 +33,7 @@ pub fn url_encode_json_serialized_vec(elements: &[String]) -> String {
impl ApiV2 {
pub async fn get_version_deserialized(&self, id: &str, pat: Option<&str>) -> LegacyVersion {
let resp = self.get_version(id, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
@@ -44,7 +44,7 @@ impl ApiV2 {
pat: Option<&str>,
) -> LegacyVersion {
let resp = self.get_version_from_hash(hash, algorithm, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
@@ -55,7 +55,7 @@ impl ApiV2 {
pat: Option<&str>,
) -> HashMap<String, LegacyVersion> {
let resp = self.get_versions_from_hashes(hashes, algorithm, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
@@ -83,7 +83,7 @@ impl ApiV2 {
pat: Option<&str>,
) -> HashMap<String, LegacyVersion> {
let resp = self.update_individual_files(algorithm, hashes, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
}
@@ -135,7 +135,7 @@ impl ApiVersion for ApiV2 {
pat,
)
.await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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
@@ -153,7 +153,7 @@ impl ApiVersion for ApiV2 {
async fn get_version_deserialized_common(&self, id: &str, pat: Option<&str>) -> CommonVersion {
let resp = self.get_version(id, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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
@@ -212,7 +212,7 @@ impl ApiVersion for ApiV2 {
pat: Option<&str>,
) -> CommonVersion {
let resp = self.get_version_from_hash(hash, algorithm, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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
@@ -244,7 +244,7 @@ impl ApiVersion for ApiV2 {
pat: Option<&str>,
) -> HashMap<String, CommonVersion> {
let resp = self.get_versions_from_hashes(hashes, algorithm, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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
@@ -287,7 +287,7 @@ impl ApiVersion for ApiV2 {
let resp = self
.get_update_from_hash(hash, algorithm, loaders, game_versions, version_types, pat)
.await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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
@@ -337,7 +337,7 @@ impl ApiVersion for ApiV2 {
pat,
)
.await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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
@@ -420,7 +420,7 @@ impl ApiVersion for ApiV2 {
pat,
)
.await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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

View File

@@ -1,3 +1,4 @@
use actix_http::StatusCode;
use actix_web::{
dev::ServiceResponse,
test::{self, TestRequest},
@@ -6,7 +7,10 @@ use bytes::Bytes;
use labrinth::models::{collections::Collection, v3::projects::Project};
use serde_json::json;
use crate::common::api_common::{request_data::ImageData, Api, AppendsOptionalPat};
use crate::common::{
api_common::{request_data::ImageData, Api, AppendsOptionalPat},
asserts::assert_status,
};
use super::ApiV3;
@@ -40,7 +44,7 @@ impl ApiV3 {
pub async fn get_collection_deserialized(&self, id: &str, pat: Option<&str>) -> Collection {
let resp = self.get_collection(id, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
@@ -70,7 +74,7 @@ impl ApiV3 {
pat: Option<&str>,
) -> Vec<Project> {
let resp = self.get_collection_projects(id, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
@@ -145,7 +149,7 @@ impl ApiV3 {
pat: Option<&str>,
) -> Vec<Collection> {
let resp = self.get_user_collections(user_id_or_username, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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

View File

@@ -1,3 +1,4 @@
use actix_http::StatusCode;
use actix_web::{
dev::ServiceResponse,
test::{self, TestRequest},
@@ -6,7 +7,10 @@ use bytes::Bytes;
use labrinth::models::{organizations::Organization, users::UserId, v3::projects::Project};
use serde_json::json;
use crate::common::api_common::{request_data::ImageData, Api, AppendsOptionalPat};
use crate::common::{
api_common::{request_data::ImageData, Api, AppendsOptionalPat},
asserts::assert_status,
};
use super::ApiV3;
@@ -14,6 +18,7 @@ impl ApiV3 {
pub async fn create_organization(
&self,
organization_title: &str,
organization_slug: &str,
description: &str,
pat: Option<&str>,
) -> ServiceResponse {
@@ -22,6 +27,7 @@ impl ApiV3 {
.append_pat(pat)
.set_json(json!({
"name": organization_title,
"slug": organization_slug,
"description": description,
}))
.to_request();
@@ -42,7 +48,7 @@ impl ApiV3 {
pat: Option<&str>,
) -> Organization {
let resp = self.get_organization(id_or_title, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
@@ -80,7 +86,7 @@ impl ApiV3 {
pat: Option<&str>,
) -> Vec<Project> {
let resp = self.get_organization_projects(id_or_title, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}

View File

@@ -119,7 +119,7 @@ impl ApiProject for ApiV3 {
pat: Option<&str>,
) -> CommonProject {
let resp = self.get_project(id_or_slug, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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
@@ -169,7 +169,7 @@ impl ApiProject for ApiV3 {
pat: Option<&str>,
) -> Vec<CommonProject> {
let resp = self.get_user_projects(user_id_or_username, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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
@@ -383,10 +383,7 @@ impl ApiProject for ApiV3 {
.append_pat(pat)
.to_request();
let t = self.call(req).await;
println!("Status: {}", t.status());
println!("respone Body: {:?}", t.response().body());
t
self.call(req).await
}
async fn remove_gallery_item(
@@ -480,7 +477,7 @@ impl ApiProject for ApiV3 {
impl ApiV3 {
pub async fn get_project_deserialized(&self, id_or_slug: &str, pat: Option<&str>) -> Project {
let resp = self.get_project(id_or_slug, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
@@ -503,7 +500,7 @@ impl ApiV3 {
pat: Option<&str>,
) -> Organization {
let resp = self.get_project_organization(id_or_slug, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
@@ -530,8 +527,7 @@ impl ApiV3 {
.append_pat(pat)
.to_request();
let resp = self.call(req).await;
let status = resp.status();
assert_eq!(status, 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
@@ -598,7 +594,7 @@ impl ApiV3 {
pat,
)
.await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
}

View File

@@ -1,3 +1,4 @@
use actix_http::StatusCode;
use actix_web::{
dev::ServiceResponse,
test::{self, TestRequest},
@@ -13,6 +14,7 @@ use crate::common::{
models::{CommonCategoryData, CommonLoaderData},
Api, ApiTags, AppendsOptionalPat,
},
asserts::assert_status,
database::ADMIN_USER_PAT,
};
@@ -30,7 +32,7 @@ impl ApiTags for ApiV3 {
async fn get_loaders_deserialized_common(&self) -> Vec<CommonLoaderData> {
let resp = self.get_loaders().await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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
@@ -48,7 +50,7 @@ impl ApiTags for ApiV3 {
async fn get_categories_deserialized_common(&self) -> Vec<CommonCategoryData> {
let resp = self.get_categories().await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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
@@ -60,7 +62,7 @@ impl ApiTags for ApiV3 {
impl ApiV3 {
pub async fn get_loaders_deserialized(&self) -> Vec<LoaderData> {
let resp = self.get_loaders().await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
@@ -77,7 +79,7 @@ impl ApiV3 {
loader_field: &str,
) -> Vec<LoaderFieldEnumValue> {
let resp = self.get_loader_field_variants(loader_field).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
@@ -92,7 +94,7 @@ impl ApiV3 {
pub async fn get_games_deserialized(&self) -> Vec<GameData> {
let resp = self.get_games().await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
}

View File

@@ -24,7 +24,7 @@ impl ApiV3 {
pat: Option<&str>,
) -> Vec<TeamMember> {
let resp = self.get_organization_members(id_or_title, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
@@ -34,7 +34,7 @@ impl ApiV3 {
pat: Option<&str>,
) -> Vec<TeamMember> {
let resp = self.get_team_members(team_id, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
@@ -44,7 +44,7 @@ impl ApiV3 {
pat: Option<&str>,
) -> Vec<TeamMember> {
let resp = self.get_project_members(project_id, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
}
@@ -65,7 +65,7 @@ impl ApiTeams for ApiV3 {
pat: Option<&str>,
) -> Vec<CommonTeamMember> {
let resp = self.get_team_members(id_or_title, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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
@@ -103,7 +103,7 @@ impl ApiTeams for ApiV3 {
pat: Option<&str>,
) -> Vec<CommonTeamMember> {
let resp = self.get_project_members(id_or_title, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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
@@ -129,7 +129,7 @@ impl ApiTeams for ApiV3 {
pat: Option<&str>,
) -> Vec<CommonTeamMember> {
let resp = self.get_organization_members(id_or_title, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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

View File

@@ -60,7 +60,7 @@ impl ApiV3 {
pub async fn get_version_deserialized(&self, id: &str, pat: Option<&str>) -> Version {
let resp = self.get_version(id, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
@@ -88,7 +88,7 @@ impl ApiV3 {
pat: Option<&str>,
) -> HashMap<String, Version> {
let resp = self.update_individual_files(algorithm, hashes, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
test::read_body_json(resp).await
}
}
@@ -158,7 +158,7 @@ impl ApiVersion for ApiV3 {
async fn get_version_deserialized_common(&self, id: &str, pat: Option<&str>) -> CommonVersion {
let resp = self.get_version(id, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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
@@ -217,7 +217,7 @@ impl ApiVersion for ApiV3 {
pat: Option<&str>,
) -> CommonVersion {
let resp = self.get_version_from_hash(hash, algorithm, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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
@@ -249,7 +249,7 @@ impl ApiVersion for ApiV3 {
pat: Option<&str>,
) -> HashMap<String, CommonVersion> {
let resp = self.get_versions_from_hashes(hashes, algorithm, pat).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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
@@ -301,7 +301,7 @@ impl ApiVersion for ApiV3 {
let resp = self
.get_update_from_hash(hash, algorithm, loaders, game_versions, version_types, pat)
.await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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
@@ -361,7 +361,7 @@ impl ApiVersion for ApiV3 {
pat,
)
.await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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
@@ -444,7 +444,7 @@ impl ApiVersion for ApiV3 {
pat,
)
.await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// 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

View File

@@ -1,6 +1,6 @@
#![allow(dead_code)]
use labrinth::database::redis::RedisPool;
use labrinth::{database::redis::RedisPool, search};
use sqlx::{postgres::PgPoolOptions, PgPool};
use std::time::Duration;
use url::Url;
@@ -39,6 +39,7 @@ const TEMPLATE_DATABASE_NAME: &str = "labrinth_tests_template";
pub struct TemporaryDatabase {
pub pool: PgPool,
pub redis_pool: RedisPool,
pub search_config: labrinth::search::SearchConfig,
pub database_name: String,
}
@@ -84,10 +85,13 @@ impl TemporaryDatabase {
// Gets new Redis pool
let redis_pool = RedisPool::new(Some(temp_database_name.clone()));
// Create new meilisearch config
let search_config = search::SearchConfig::new(Some(temp_database_name.clone()));
Self {
pool,
database_name: temp_database_name,
redis_pool,
search_config,
}
}
@@ -168,10 +172,12 @@ impl TemporaryDatabase {
if !dummy_data_exists {
// Add dummy data
let name = generate_random_name("test_template_");
let db = TemporaryDatabase {
pool: pool.clone(),
database_name: TEMPLATE_DATABASE_NAME.to_string(),
redis_pool: RedisPool::new(Some(generate_random_name("test_template_"))),
redis_pool: RedisPool::new(Some(name.clone())),
search_config: search::SearchConfig::new(Some(name)),
};
let setup_api = TestEnvironment::<ApiV3>::build_setup_api(&db).await;
dummy_data::add_dummy_data(&setup_api, db.clone()).await;

View File

@@ -207,7 +207,7 @@ impl DummyData {
organization_zeta: DummyOrganizationZeta {
organization_id: organization_zeta.id.to_string(),
team_id: organization_zeta.team_id.to_string(),
organization_name: organization_zeta.name,
organization_slug: organization_zeta.slug,
},
oauth_client_alpha: DummyOAuthClientAlpha {
@@ -249,7 +249,7 @@ pub struct DummyProjectBeta {
#[derive(Clone)]
pub struct DummyOrganizationZeta {
pub organization_id: String,
pub organization_name: String,
pub organization_slug: String,
pub team_id: String,
}
@@ -390,7 +390,7 @@ pub async fn add_project_beta(api: &ApiV3) -> (Project, Version) {
.set_multipart(vec![json_segment.clone(), file_segment.clone()])
.to_request();
let resp = api.call(req).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
get_project_beta(api).await
}
@@ -401,13 +401,14 @@ pub async fn add_organization_zeta(api: &ApiV3) -> Organization {
.uri("/v3/organization")
.append_pat(USER_USER_PAT)
.set_json(json!({
"name": "zeta",
"name": "Zeta",
"slug": "zeta",
"description": "A dummy organization for testing with."
}))
.to_request();
let resp = api.call(req).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
get_organization_zeta(api).await
}

View File

@@ -27,6 +27,7 @@ pub async fn setup(db: &database::TemporaryDatabase) -> LabrinthConfig {
let pool = db.pool.clone();
let redis_pool = db.redis_pool.clone();
let search_config = db.search_config.clone();
let file_host: Arc<dyn file_hosting::FileHost + Send + Sync> =
Arc::new(file_hosting::MockHost::new());
let mut clickhouse = clickhouse::init_client().await.unwrap();
@@ -36,6 +37,7 @@ pub async fn setup(db: &database::TemporaryDatabase) -> LabrinthConfig {
labrinth::app_setup(
pool.clone(),
redis_pool.clone(),
search_config,
&mut clickhouse,
file_host.clone(),
maxmind_reader,

View File

@@ -356,12 +356,13 @@ impl<'a, A: Api> PermissionsTest<'a, A> {
.await;
if !self.allowed_failure_codes.contains(&resp.status().as_u16()) {
return Err(format!(
"Failure permissions test failed. Expected failure codes {} got {}",
"Failure permissions test failed. Expected failure codes {} got {}. Body: {:#?}",
self.allowed_failure_codes
.iter()
.map(|code| code.to_string())
.join(","),
resp.status().as_u16()
resp.status().as_u16(),
resp.response().body()
));
}
@@ -385,8 +386,9 @@ impl<'a, A: Api> PermissionsTest<'a, A> {
.await;
if !resp.status().is_success() {
return Err(format!(
"Success permissions test failed. Expected success, got {}",
resp.status().as_u16()
"Success permissions test failed. Expected success, got {}. Body: {:#?}",
resp.status().as_u16(),
resp.response().body()
));
}
@@ -1007,15 +1009,15 @@ async fn create_dummy_project(setup_api: &ApiV3) -> (String, String) {
async fn create_dummy_org(setup_api: &ApiV3) -> (String, String) {
// Create a very simple organization
let name = generate_random_name("test_org");
let slug = generate_random_name("test_org");
let resp = setup_api
.create_organization(&name, "Example description.", ADMIN_USER_PAT)
.create_organization("Example org", &slug, "Example description.", ADMIN_USER_PAT)
.await;
assert!(resp.status().is_success());
let organization = setup_api
.get_organization_deserialized(&name, ADMIN_USER_PAT)
.get_organization_deserialized(&slug, ADMIN_USER_PAT)
.await;
let organizaion_id = organization.id.to_string();
let team_id = organization.team_id.to_string();
@@ -1109,7 +1111,7 @@ async fn get_project_permissions(
.await;
let permissions = members
.iter()
.find(|member| &member.user.id.to_string() == user_id)
.find(|member| member.user.id.to_string() == user_id)
.and_then(|member| member.permissions);
let organization_members = match organization {
@@ -1123,7 +1125,7 @@ async fn get_project_permissions(
let organization_default_project_permissions = match organization_members {
Some(members) => members
.iter()
.find(|member| &member.user.id.to_string() == user_id)
.find(|member| member.user.id.to_string() == user_id)
.and_then(|member| member.permissions),
None => None,
};

View File

@@ -2,6 +2,7 @@
use std::{collections::HashMap, sync::Arc};
use actix_http::StatusCode;
use serde_json::json;
use crate::common::{
@@ -10,7 +11,7 @@ use crate::common::{
dummy_data::{TestFile, DUMMY_CATEGORIES},
};
use super::{api_v3::ApiV3, environment::TestEnvironment};
use super::{api_v3::ApiV3, asserts::assert_status, environment::TestEnvironment};
pub async fn setup_search_projects(test_env: &TestEnvironment<ApiV3>) -> Arc<HashMap<u64, u64>> {
// Test setup and dummy data
@@ -47,7 +48,7 @@ pub async fn setup_search_projects(test_env: &TestEnvironment<ApiV3>) -> Arc<Has
MOD_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
(project.id.0, id)
}
};
@@ -209,7 +210,7 @@ pub async fn setup_search_projects(test_env: &TestEnvironment<ApiV3>) -> Arc<Has
// Forcibly reset the search index
let resp = api.reset_search_index().await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
id_conversion
}

View File

@@ -1,3 +1,4 @@
use actix_http::StatusCode;
use actix_web::test;
use bytes::Bytes;
use common::api_common::ApiProject;
@@ -6,6 +7,8 @@ use common::api_v3::ApiV3;
use common::database::USER_USER_PAT;
use common::environment::{with_test_environment, TestEnvironment};
use crate::common::asserts::assert_status;
mod common;
#[actix_rt::test]
@@ -14,7 +17,7 @@ pub async fn error_404_body() {
// 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);
assert_status(&resp, StatusCode::NOT_FOUND);
let body = test::read_body(resp).await;
let empty_bytes = Bytes::from_static(b"");
assert_ne!(body, empty_bytes);

View File

@@ -1,10 +1,12 @@
use std::collections::HashSet;
use actix_http::StatusCode;
use common::api_v3::ApiV3;
use common::environment::{with_test_environment, TestEnvironment};
use serde_json::json;
use crate::common::api_common::ApiVersion;
use crate::common::asserts::assert_status;
use crate::common::database::*;
use crate::common::dummy_data::{DummyProjectAlpha, DummyProjectBeta, TestFile};
@@ -49,7 +51,7 @@ async fn creating_loader_fields() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// - Patch
let resp = api
.edit_version(
@@ -60,7 +62,7 @@ async fn creating_loader_fields() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// Cannot create a version with a loader field that isnt used by the loader
// TODO: - Create project
@@ -82,7 +84,7 @@ async fn creating_loader_fields() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// - Patch
let resp = api
.edit_version(
@@ -93,7 +95,7 @@ async fn creating_loader_fields() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// Cannot create a version without an applicable loader field that is not optional
// TODO: - Create project
@@ -115,7 +117,7 @@ async fn creating_loader_fields() {
)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// Cannot create a version without a loader field array that has a minimum of 1
// TODO: - Create project
@@ -136,7 +138,7 @@ async fn creating_loader_fields() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// TODO: Create a test for too many elements in the array when we have a LF that has a max (past max)
// Cannot create a version with a loader field array that has fewer than the minimum elements
@@ -159,7 +161,7 @@ async fn creating_loader_fields() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// - Patch
let resp = api
@@ -171,7 +173,7 @@ async fn creating_loader_fields() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// Cannot create an invalid data type for the loader field type (including bad variant for the type)
for bad_type_game_versions in [
@@ -199,7 +201,7 @@ async fn creating_loader_fields() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// - Patch
let resp = api
@@ -211,7 +213,7 @@ async fn creating_loader_fields() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
}
// Can create with optional loader fields (other tests have checked if we can create without them)
@@ -245,7 +247,7 @@ async fn creating_loader_fields() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
let v = api
.get_version_deserialized(alpha_version_id, USER_USER_PAT)
.await;
@@ -296,7 +298,7 @@ async fn creating_loader_fields() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
let v = api
.get_version_deserialized(alpha_version_id, USER_USER_PAT)
.await;

View File

@@ -1,11 +1,13 @@
use crate::common::{
api_common::{ApiProject, ApiTeams},
asserts::assert_status,
database::{
generate_random_name, ADMIN_USER_PAT, ENEMY_USER_ID_PARSED, ENEMY_USER_PAT,
FRIEND_USER_ID_PARSED, MOD_USER_ID, MOD_USER_PAT, USER_USER_ID, USER_USER_ID_PARSED,
},
dummy_data::{DummyImage, DummyOrganizationZeta, DummyProjectAlpha, DummyProjectBeta},
};
use actix_http::StatusCode;
use common::{
api_v3::ApiV3,
database::{FRIEND_USER_ID, FRIEND_USER_PAT, USER_USER_PAT},
@@ -27,45 +29,61 @@ async fn create_organization() {
let zeta_organization_slug = &test_env.dummy.organization_zeta.organization_id;
// Failed creations title:
// - too short title
// - too long title
for title in ["a", &"a".repeat(100)] {
let resp = api
.create_organization(title, "theta", "theta_description", USER_USER_PAT)
.await;
assert_status(&resp, StatusCode::BAD_REQUEST);
}
// Failed creations slug:
// - slug collision with zeta
// - too short slug
// - too long slug
// - not url safe slug
for title in [
for slug in [
zeta_organization_slug,
"a",
&"a".repeat(100),
"not url safe%&^!#$##!@#$%^&*()",
] {
let resp = api
.create_organization(title, "theta_description", USER_USER_PAT)
.create_organization("Theta Org", slug, "theta_description", USER_USER_PAT)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
}
// Failed creations description:
// - too short slug
// - too long slug
// - too short desc
// - too long desc
for description in ["a", &"a".repeat(300)] {
let resp = api
.create_organization("theta", description, USER_USER_PAT)
.create_organization("Theta Org", "theta", description, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
}
// Create 'theta' organization
let resp = api
.create_organization("theta", "not url safe%&^!#$##!@#$%^&", USER_USER_PAT)
.create_organization(
"Theta Org",
"theta",
"not url safe%&^!#$##!@#$%^&",
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// Get organization using slug
let theta = api
.get_organization_deserialized("theta", USER_USER_PAT)
.await;
assert_eq!(theta.name, "theta");
assert_eq!(theta.name, "Theta Org");
assert_eq!(theta.slug, "theta");
assert_eq!(theta.description, "not url safe%&^!#$##!@#$%^&");
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// Get created team
let members = api
@@ -95,7 +113,7 @@ async fn get_project_organization() {
let resp = api
.organization_add_project(zeta_organization_id, alpha_project_id, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// Get project organization
let zeta = api
@@ -115,9 +133,25 @@ async fn patch_organization() {
// Create 'theta' organization
let resp = api
.create_organization("theta", "theta_description", USER_USER_PAT)
.create_organization("Theta Org", "theta", "theta_description", USER_USER_PAT)
.await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// Failed patch to theta title:
// - too short title
// - too long title
for title in ["a", &"a".repeat(100)] {
let resp = api
.edit_organization(
"theta",
json!({
"name": title,
}),
USER_USER_PAT,
)
.await;
assert_status(&resp, StatusCode::BAD_REQUEST);
}
// Failed patch to zeta slug:
// - slug collision with theta
@@ -134,13 +168,13 @@ async fn patch_organization() {
.edit_organization(
zeta_organization_id,
json!({
"name": title,
"slug": title,
"description": "theta_description"
}),
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
}
// Failed patch to zeta description:
@@ -156,7 +190,7 @@ async fn patch_organization() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
}
// Successful patch to many fields
@@ -165,18 +199,20 @@ async fn patch_organization() {
zeta_organization_id,
json!({
"name": "new_title",
"slug": "new_slug",
"description": "not url safe%&^!#$##!@#$%^&" // not-URL-safe description should still work
}),
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Get project using new slug
let new_title = api
.get_organization_deserialized("new_title", USER_USER_PAT)
.get_organization_deserialized("new_slug", USER_USER_PAT)
.await;
assert_eq!(new_title.name, "new_title");
assert_eq!(new_title.slug, "new_slug");
assert_eq!(new_title.description, "not url safe%&^!#$##!@#$%^&");
})
.await;
@@ -185,7 +221,7 @@ async fn patch_organization() {
// add/remove icon
#[actix_rt::test]
async fn add_remove_icon() {
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
with_test_environment(Some(10), |test_env: TestEnvironment<ApiV3>| async move {
let api = &test_env.api;
let zeta_organization_id = &test_env.dummy.organization_zeta.organization_id;
@@ -205,7 +241,7 @@ async fn add_remove_icon() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Get project
let zeta_org = api
@@ -218,7 +254,7 @@ async fn add_remove_icon() {
let resp = api
.edit_organization_icon(zeta_organization_id, None, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Get project
let zeta_org = api
@@ -239,13 +275,13 @@ async fn delete_org() {
let resp = api
.delete_organization(zeta_organization_id, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Get organization, which should no longer exist
let resp = api
.get_organization(zeta_organization_id, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 404);
assert_status(&resp, StatusCode::NOT_FOUND);
})
.await;
}
@@ -264,7 +300,7 @@ async fn add_remove_organization_projects() {
.api
.organization_add_project(zeta_organization_id, alpha, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// Get organization projects
let projects = test_env
@@ -284,7 +320,7 @@ async fn add_remove_organization_projects() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// Get organization projects
let projects = test_env
@@ -338,11 +374,11 @@ async fn add_remove_organization_project_ownership_to_user() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Accept invites
let resp = test_env.api.join_team(team, FRIEND_USER_PAT).await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
}
// For each team, confirm there are two members, but only one owner of the project, and it is USER_USER_ID
@@ -362,7 +398,7 @@ async fn add_remove_organization_project_ownership_to_user() {
.api
.transfer_team_ownership(beta_team_id, FRIEND_USER_ID, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Confirm there are still two users, but now FRIEND_USER_ID is the owner
let members = test_env
@@ -383,7 +419,7 @@ async fn add_remove_organization_project_ownership_to_user() {
.api
.organization_add_project(zeta_organization_id, project_id, pat)
.await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// Get and confirm it has been added
let project = test_env.api.get_project_deserialized(project_id, pat).await;
@@ -416,7 +452,7 @@ async fn add_remove_organization_project_ownership_to_user() {
.api
.transfer_team_ownership(zeta_team_id, FRIEND_USER_ID, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Confirm there are no members of the alpha project OR the beta project
// - Friend was removed as a member of these projects when ownership was transferred to them
@@ -433,14 +469,14 @@ async fn add_remove_organization_project_ownership_to_user() {
.api
.add_user_to_team(alpha_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// As friend, can add user to alpha project, as they are not the org owner
let resp = test_env
.api
.add_user_to_team(alpha_team_id, USER_USER_ID, None, None, FRIEND_USER_PAT)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// At this point, friend owns the org
// Alpha member has user as a member, but not as an owner
@@ -457,7 +493,7 @@ async fn add_remove_organization_project_ownership_to_user() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// Set user's permissions within the project that it is a member of to none (for a later test)
let resp = test_env
@@ -471,7 +507,7 @@ async fn add_remove_organization_project_ownership_to_user() {
FRIEND_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Remove project from organization with a user that is an organization member, and a project member
// This should succeed
@@ -484,7 +520,7 @@ async fn add_remove_organization_project_ownership_to_user() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// Remove project from organization with a user that is an organization member, but not a project member
// This should succeed
@@ -497,7 +533,7 @@ async fn add_remove_organization_project_ownership_to_user() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// For each of alpha and beta, confirm:
// - There is one member of each project, the owner, USER_USER_ID
@@ -559,11 +595,11 @@ async fn delete_organization_means_all_projects_to_org_owner() {
.api
.add_user_to_team(zeta_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Accept invite
let resp = test_env.api.join_team(zeta_team_id, FRIEND_USER_PAT).await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Confirm there is only one owner of the project, and it is USER_USER_ID
let members = test_env
@@ -579,7 +615,7 @@ async fn delete_organization_means_all_projects_to_org_owner() {
.api
.organization_add_project(zeta_organization_id, alpha_project_id, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// Add beta to zeta organization
test_env
@@ -592,13 +628,13 @@ async fn delete_organization_means_all_projects_to_org_owner() {
.api
.add_user_to_team(beta_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Try to accept invite
// This returns a failure, because since beta and FRIEND are in the organizations,
// they can be added to the project without an invite
let resp = test_env.api.join_team(beta_team_id, FRIEND_USER_PAT).await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// Confirm there is NO owner of the project, as it is owned by the organization
let members = test_env
@@ -613,7 +649,7 @@ async fn delete_organization_means_all_projects_to_org_owner() {
.api
.transfer_team_ownership(zeta_team_id, FRIEND_USER_ID, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Confirm there is NO owner of the project, as it is owned by the organization
let members = test_env
@@ -628,7 +664,7 @@ async fn delete_organization_means_all_projects_to_org_owner() {
.api
.delete_organization(zeta_organization_id, FRIEND_USER_PAT)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Confirm there is only one owner of the alpha project, and it is now FRIEND_USER_ID
let members = test_env
@@ -708,7 +744,7 @@ async fn permissions_patch_organization() {
// Not covered by PATCH /organization
#[actix_rt::test]
async fn permissions_edit_details() {
with_test_environment(None, |test_env: TestEnvironment<ApiV3>| async move {
with_test_environment(Some(12), |test_env: TestEnvironment<ApiV3>| async move {
let zeta_organization_id = &test_env.dummy.organization_zeta.organization_id;
let zeta_team_id = &test_env.dummy.organization_zeta.team_id;
@@ -814,9 +850,9 @@ async fn permissions_manage_invites() {
let resp = api
.add_user_to_team(zeta_team_id, MOD_USER_ID, None, None, ADMIN_USER_PAT)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
let resp = api.join_team(zeta_team_id, MOD_USER_PAT).await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// remove existing member (requires remove_member)
let remove_member = OrganizationPermissions::REMOVE_MEMBER;
@@ -852,13 +888,13 @@ async fn permissions_add_remove_project() {
let resp = api
.add_user_to_team(alpha_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
let resp = api.join_team(alpha_team_id, FRIEND_USER_PAT).await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
let resp = api
.transfer_team_ownership(alpha_team_id, FRIEND_USER_ID, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Now, FRIEND_USER_ID owns the alpha project
// Add alpha project to zeta organization

View File

@@ -1,3 +1,4 @@
use actix_http::StatusCode;
use actix_web::test;
use chrono::{Duration, Utc};
use common::{database::*, environment::with_test_environment_all};
@@ -5,7 +6,7 @@ use common::{database::*, environment::with_test_environment_all};
use labrinth::models::pats::Scopes;
use serde_json::json;
use crate::common::api_common::AppendsOptionalPat;
use crate::common::{api_common::AppendsOptionalPat, asserts::assert_status};
mod common;
@@ -30,7 +31,7 @@ pub async fn pat_full_test() {
}))
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status().as_u16(), 200);
assert_status(&resp, StatusCode::OK);
let success: serde_json::Value = test::read_body_json(resp).await;
let id = success["id"].as_str().unwrap();
@@ -48,7 +49,7 @@ pub async fn pat_full_test() {
.uri("/_internal/pat")
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status().as_u16(), 200);
assert_status(&resp, StatusCode::OK);
let success: serde_json::Value = test::read_body_json(resp).await;
// Ensure access token is NOT returned for any PATs
@@ -87,7 +88,7 @@ pub async fn pat_full_test() {
}))
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status().as_u16(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
assert_eq!(mock_pat_test(access_token).await, 401); // No longer works
// Change scopes back, and set expiry to the past, and test again
@@ -100,7 +101,7 @@ pub async fn pat_full_test() {
}))
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status().as_u16(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Wait 1 second before testing again for expiry
tokio::time::sleep(Duration::seconds(1).to_std().unwrap()).await;
@@ -115,7 +116,7 @@ pub async fn pat_full_test() {
}))
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status().as_u16(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
assert_eq!(mock_pat_test(access_token).await, 200); // Works again
// Patching to a bad expiry should fail
@@ -127,7 +128,7 @@ pub async fn pat_full_test() {
}))
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status().as_u16(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// Similar to above with PAT creation, patching to a bad scope should fail
for i in 0..64 {
@@ -156,7 +157,7 @@ pub async fn pat_full_test() {
.uri(&format!("/_internal/pat/{}", id))
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status().as_u16(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
})
.await;
}
@@ -175,7 +176,7 @@ pub async fn bad_pats() {
}))
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status().as_u16(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// Name too short or too long should fail
for name in ["n", "this_name_is_too_long".repeat(16).as_str()] {
@@ -189,7 +190,7 @@ pub async fn bad_pats() {
}))
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status().as_u16(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
}
// Creating a PAT with an expiry in the past should fail
@@ -203,7 +204,7 @@ pub async fn bad_pats() {
}))
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status().as_u16(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// Make a PAT with each scope, with the result varying by whether that scope is restricted
for i in 0..64 {
@@ -238,7 +239,7 @@ pub async fn bad_pats() {
}))
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status().as_u16(), 200);
assert_status(&resp, StatusCode::OK);
let success: serde_json::Value = test::read_body_json(resp).await;
let id = success["id"].as_str().unwrap();
@@ -252,7 +253,7 @@ pub async fn bad_pats() {
}))
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status().as_u16(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
}
// Patching to a bad expiry should fail
@@ -264,7 +265,7 @@ pub async fn bad_pats() {
}))
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status().as_u16(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// Similar to above with PAT creation, patching to a bad scope should fail
for i in 0..64 {

View File

@@ -3,6 +3,7 @@ use std::collections::HashMap;
use actix_http::StatusCode;
use actix_web::test;
use common::api_v3::ApiV3;
use common::asserts::assert_status;
use common::database::*;
use common::dummy_data::DUMMY_CATEGORIES;
@@ -20,7 +21,9 @@ use serde_json::json;
use crate::common::api_common::models::CommonItemType;
use crate::common::api_common::request_data::ProjectCreationRequestData;
use crate::common::api_common::{ApiProject, ApiTeams, ApiVersion};
use crate::common::dummy_data::{DummyImage, DummyProjectAlpha, DummyProjectBeta, TestFile};
use crate::common::dummy_data::{
DummyImage, DummyOrganizationZeta, DummyProjectAlpha, DummyProjectBeta, TestFile,
};
mod common;
#[actix_rt::test]
@@ -42,10 +45,9 @@ async fn test_get_project() {
// Perform request on dummy data
let resp = api.get_project(alpha_project_id, USER_USER_PAT).await;
let status = resp.status();
assert_status(&resp, StatusCode::OK);
let body: serde_json::Value = test::read_body_json(resp).await;
assert_eq!(status, 200);
assert_eq!(body["id"], json!(alpha_project_id));
assert_eq!(body["slug"], json!(alpha_project_slug));
let versions = body["versions"].as_array().unwrap();
@@ -75,8 +77,7 @@ async fn test_get_project() {
// Make the request again, this time it should be cached
let resp = api.get_project(alpha_project_id, USER_USER_PAT).await;
let status = resp.status();
assert_eq!(status, 200);
assert_status(&resp, StatusCode::OK);
let body: serde_json::Value = test::read_body_json(resp).await;
assert_eq!(body["id"], json!(alpha_project_id));
@@ -84,11 +85,11 @@ async fn test_get_project() {
// Request should fail on non-existent project
let resp = api.get_project("nonexistent", USER_USER_PAT).await;
assert_eq!(resp.status(), 404);
assert_status(&resp, StatusCode::NOT_FOUND);
// Similarly, request should fail on non-authorized user, on a yet-to-be-approved or hidden project, with a 404 (hiding the existence of the project)
let resp = api.get_project(beta_project_id, ENEMY_USER_PAT).await;
assert_eq!(resp.status(), 404);
assert_status(&resp, StatusCode::NOT_FOUND);
})
.await;
}
@@ -170,8 +171,7 @@ async fn test_add_remove_project() {
)
.await;
let status = resp.status();
assert_eq!(status, 200);
assert_status(&resp, StatusCode::OK);
// Get the project we just made, and confirm that it's correct
let project = api
@@ -204,7 +204,7 @@ async fn test_add_remove_project() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// Reusing with the same slug and a different file should fail
let resp = api
@@ -220,7 +220,7 @@ async fn test_add_remove_project() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// Different slug, different file should succeed
let resp = api
@@ -236,7 +236,7 @@ async fn test_add_remove_project() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// Get
let project = api
@@ -246,7 +246,7 @@ async fn test_add_remove_project() {
// Remove the project
let resp = test_env.api.remove_project("demo", USER_USER_PAT).await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Confirm that the project is gone from the cache
let mut redis_pool = test_env.db.redis_pool.connect().await.unwrap();
@@ -269,7 +269,7 @@ async fn test_add_remove_project() {
// Old slug no longer works
let resp = api.get_project("demo", USER_USER_PAT).await;
assert_eq!(resp.status(), 404);
assert_status(&resp, StatusCode::NOT_FOUND);
})
.await;
}
@@ -293,7 +293,7 @@ pub async fn test_patch_project() {
ENEMY_USER_PAT,
)
.await;
assert_eq!(resp.status(), 401);
assert_status(&resp, StatusCode::UNAUTHORIZED);
// Failure because we are setting URL fields to invalid urls.
for url_type in ["issues", "source", "wiki", "discord"] {
@@ -308,7 +308,7 @@ pub async fn test_patch_project() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
}
// Failure because these are illegal requested statuses for a normal user.
@@ -322,7 +322,7 @@ pub async fn test_patch_project() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
}
// Failure because these should not be able to be set by a non-mod
@@ -336,7 +336,7 @@ pub async fn test_patch_project() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 401);
assert_status(&resp, StatusCode::UNAUTHORIZED);
// (should work for a mod, though)
let resp = api
@@ -348,7 +348,7 @@ pub async fn test_patch_project() {
MOD_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
}
// Failed patch to alpha slug:
@@ -372,7 +372,7 @@ pub async fn test_patch_project() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
}
// Not allowed to directly set status, as 'beta_project_slug' (the other project) is "processing" and cannot have its status changed like this.
@@ -385,7 +385,7 @@ pub async fn test_patch_project() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 401);
assert_status(&resp, StatusCode::UNAUTHORIZED);
// Sucessful request to patch many fields.
let resp = api
@@ -406,11 +406,11 @@ pub async fn test_patch_project() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Old slug no longer works
let resp = api.get_project(alpha_project_slug, USER_USER_PAT).await;
assert_eq!(resp.status(), 404);
assert_status(&resp, StatusCode::NOT_FOUND);
// New slug does work
let project = api.get_project_deserialized("newslug", USER_USER_PAT).await;
@@ -447,7 +447,7 @@ pub async fn test_patch_project() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
let project = api.get_project_deserialized("newslug", USER_USER_PAT).await;
assert_eq!(project.link_urls.len(), 3);
assert!(!project.link_urls.contains_key("issues"));
@@ -475,7 +475,7 @@ pub async fn test_patch_v3() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
let project = api
.get_project_deserialized(alpha_project_slug, USER_USER_PAT)
@@ -509,7 +509,7 @@ pub async fn test_bulk_edit_categories() {
ADMIN_USER_PAT,
)
.await;
assert_eq!(resp.status(), StatusCode::NO_CONTENT);
assert_status(&resp, StatusCode::NO_CONTENT);
let alpha_body = api
.get_project_deserialized_common(alpha_project_id, ADMIN_USER_PAT)
@@ -552,7 +552,7 @@ pub async fn test_bulk_edit_links() {
ADMIN_USER_PAT,
)
.await;
assert_eq!(resp.status(), StatusCode::NO_CONTENT);
assert_status(&resp, StatusCode::NO_CONTENT);
let alpha_body = api
.get_project_deserialized(alpha_project_id, ADMIN_USER_PAT)
@@ -597,7 +597,7 @@ async fn delete_project_with_report() {
ENEMY_USER_PAT, // Enemy makes a report
)
.await;
assert_eq!(resp.status(), StatusCode::OK);
assert_status(&resp, StatusCode::OK);
let value = test::read_body_json::<serde_json::Value, _>(resp).await;
let alpha_report_id = value["id"].as_str().unwrap();
@@ -608,7 +608,7 @@ async fn delete_project_with_report() {
ENEMY_USER_PAT, // Enemy makes a report
)
.await;
assert_eq!(resp.status(), StatusCode::OK);
assert_status(&resp, StatusCode::OK);
// Do the same for beta
let resp = api
@@ -620,13 +620,13 @@ async fn delete_project_with_report() {
ENEMY_USER_PAT, // Enemy makes a report
)
.await;
assert_eq!(resp.status(), StatusCode::OK);
assert_status(&resp, StatusCode::OK);
let value = test::read_body_json::<serde_json::Value, _>(resp).await;
let beta_report_id = value["id"].as_str().unwrap();
// Delete the project
let resp = api.remove_project(alpha_project_id, USER_USER_PAT).await;
assert_eq!(resp.status(), StatusCode::NO_CONTENT);
assert_status(&resp, StatusCode::NO_CONTENT);
// Confirm that the project is gone from the cache
let mut redis_pool = test_env.db.redis_pool.connect().await.unwrap();
@@ -654,7 +654,7 @@ async fn delete_project_with_report() {
ENEMY_USER_PAT, // Enemy makes a report
)
.await;
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
assert_status(&resp, StatusCode::NOT_FOUND);
// Confirm that report for beta still exists
let resp = api
@@ -663,7 +663,7 @@ async fn delete_project_with_report() {
ENEMY_USER_PAT, // Enemy makes a report
)
.await;
assert_eq!(resp.status(), StatusCode::OK);
assert_status(&resp, StatusCode::OK);
})
.await;
}
@@ -815,7 +815,7 @@ async fn permissions_patch_project_v3() {
// MOD_USER_PAT,
// )
// .await;
// assert_eq!(resp.status(), 204);
// assert_status(&resp, StatusCode::NO_CONTENT);
// // Schedule version
// let req_gen = |ctx: PermissionsTestContext| async move {
@@ -839,7 +839,7 @@ async fn permissions_patch_project_v3() {
// Not covered by PATCH /project
#[actix_rt::test]
async fn permissions_edit_details() {
with_test_environment_all(None, |test_env| async move {
with_test_environment_all(Some(10), |test_env| async move {
let DummyProjectAlpha {
project_id: alpha_project_id,
team_id: alpha_team_id,
@@ -1112,11 +1112,11 @@ async fn permissions_manage_invites() {
ADMIN_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Accept invite
let resp = api.join_team(alpha_team_id, MOD_USER_PAT).await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// remove existing member (requires remove_member)
let remove_member = ProjectPermissions::REMOVE_MEMBER;
@@ -1223,7 +1223,7 @@ async fn align_search_projects() {
let project_model = api
.get_project(&project.id.to_string(), USER_USER_PAT)
.await;
assert_eq!(project_model.status(), 200);
assert_status(&project_model, StatusCode::OK);
let mut project_model: Project = test::read_body_json(project_model).await;
// Body/description is huge- don't store it in search, so it's OK if they differ here
@@ -1233,9 +1233,24 @@ async fn align_search_projects() {
// Aggregate project loader fields will not match exactly,
// because the search will only return the matching version, whereas the project returns the aggregate.
// So, we remove them from both.
let project_model_mrpack_loaders: Vec<_> = project_model
.fields
.remove("mrpack_loaders")
.unwrap_or_default()
.into_iter()
.filter_map(|v| v.as_str().map(|v| v.to_string()))
.collect();
project_model.fields = HashMap::new();
project.fields = HashMap::new();
// For a similar reason we also remove the mrpack loaders from the additional categories of the search model
// (Becasue they are not returned by the search)
// TODO: get models to match *exactly* without an additional project fetch,
// including these fields removed here
project
.additional_categories
.retain(|x| !project_model_mrpack_loaders.contains(x));
let project_model = serde_json::to_value(project_model).unwrap();
let searched_project_serialized = serde_json::to_value(project).unwrap();
assert_eq!(project_model, searched_project_serialized);
@@ -1244,6 +1259,129 @@ async fn align_search_projects() {
.await
}
#[actix_rt::test]
async fn projects_various_visibility() {
// For testing the filter_visible_projects and is_visible_project
with_test_environment(
None,
|env: common::environment::TestEnvironment<ApiV3>| async move {
let DummyProjectAlpha {
project_id: alpha_project_id,
project_id_parsed: alpha_project_id_parsed,
..
} = &env.dummy.project_alpha;
let DummyProjectBeta {
project_id: beta_project_id,
project_id_parsed: beta_project_id_parsed,
..
} = &env.dummy.project_beta;
let DummyOrganizationZeta {
organization_id: zeta_organization_id,
team_id: zeta_team_id,
..
} = &env.dummy.organization_zeta;
// Invite friend to org zeta and accept it
let resp = env
.api
.add_user_to_team(
zeta_team_id,
FRIEND_USER_ID,
Some(ProjectPermissions::empty()),
None,
USER_USER_PAT,
)
.await;
assert_status(&resp, StatusCode::NO_CONTENT);
let resp = env.api.join_team(zeta_team_id, FRIEND_USER_PAT).await;
assert_status(&resp, StatusCode::NO_CONTENT);
let visible_pat_pairs = vec![
(&alpha_project_id_parsed, USER_USER_PAT, StatusCode::OK),
(&alpha_project_id_parsed, FRIEND_USER_PAT, StatusCode::OK),
(&alpha_project_id_parsed, ENEMY_USER_PAT, StatusCode::OK),
(&beta_project_id_parsed, USER_USER_PAT, StatusCode::OK),
(
&beta_project_id_parsed,
FRIEND_USER_PAT,
StatusCode::NOT_FOUND,
),
(
&beta_project_id_parsed,
ENEMY_USER_PAT,
StatusCode::NOT_FOUND,
),
];
// Tests get_project, a route that uses is_visible_project
for (project_id, pat, expected_status) in visible_pat_pairs {
let resp = env.api.get_project(&project_id.to_string(), pat).await;
assert_status(&resp, expected_status);
}
// Test get_user_projects, a route that uses filter_visible_projects
let visible_pat_pairs = vec![
(USER_USER_PAT, 2),
(FRIEND_USER_PAT, 1),
(ENEMY_USER_PAT, 1),
];
for (pat, expected_count) in visible_pat_pairs {
let projects = env
.api
.get_user_projects_deserialized_common(USER_USER_ID, pat)
.await;
assert_eq!(projects.len(), expected_count);
}
// Add projects to org zeta
let resp = env
.api
.organization_add_project(zeta_organization_id, alpha_project_id, USER_USER_PAT)
.await;
assert_status(&resp, StatusCode::OK);
let resp = env
.api
.organization_add_project(zeta_organization_id, beta_project_id, USER_USER_PAT)
.await;
assert_status(&resp, StatusCode::OK);
// Test get_project, a route that uses is_visible_project
let visible_pat_pairs = vec![
(&alpha_project_id_parsed, USER_USER_PAT, StatusCode::OK),
(&alpha_project_id_parsed, FRIEND_USER_PAT, StatusCode::OK),
(&alpha_project_id_parsed, ENEMY_USER_PAT, StatusCode::OK),
(&beta_project_id_parsed, USER_USER_PAT, StatusCode::OK),
(&beta_project_id_parsed, FRIEND_USER_PAT, StatusCode::OK),
(
&beta_project_id_parsed,
ENEMY_USER_PAT,
StatusCode::NOT_FOUND,
),
];
for (project_id, pat, expected_status) in visible_pat_pairs {
let resp = env.api.get_project(&project_id.to_string(), pat).await;
assert_status(&resp, expected_status);
}
// Test get_user_projects, a route that uses filter_visible_projects
let visible_pat_pairs = vec![
(USER_USER_PAT, 2),
(FRIEND_USER_PAT, 2),
(ENEMY_USER_PAT, 1),
];
for (pat, expected_count) in visible_pat_pairs {
let projects = env
.api
.get_user_projects_deserialized_common(USER_USER_ID, pat)
.await;
assert_eq!(projects.len(), expected_count);
}
},
)
.await;
}
// Route tests:
// TODO: Missing routes on projects
// TODO: using permissions/scopes, can we SEE projects existence that we are not allowed to? (ie 401 instead of 404)

View File

@@ -2,12 +2,14 @@ use std::collections::HashMap;
use crate::common::api_common::{ApiProject, ApiTeams, ApiUser, ApiVersion, AppendsOptionalPat};
use crate::common::dummy_data::{DummyImage, DummyProjectAlpha, DummyProjectBeta};
use actix_http::StatusCode;
use actix_web::test;
use chrono::{Duration, Utc};
use common::api_common::models::CommonItemType;
use common::api_common::Api;
use common::api_v3::request_data::get_public_project_creation_data;
use common::api_v3::ApiV3;
use common::asserts::assert_status;
use common::dummy_data::TestFile;
use common::environment::{with_test_environment, with_test_environment_all, TestEnvironment};
use common::{database::*, scopes::ScopeTest};
@@ -114,7 +116,7 @@ pub async fn notifications_scopes() {
.api
.add_user_to_team(alpha_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Notification get
let read_notifications = Scopes::NOTIFICATION_READ;
@@ -187,7 +189,7 @@ pub async fn notifications_scopes() {
.api
.add_user_to_team(alpha_team_id, MOD_USER_ID, None, None, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
let read_notifications = Scopes::NOTIFICATION_READ;
let req_gen = |pat: Option<String>| async move {
api.get_user_notifications(MOD_USER_ID, pat.as_deref())
@@ -386,7 +388,7 @@ pub async fn project_version_reads_scopes() {
.api
.edit_version(beta_version_id, json!({ "status": "draft" }), USER_USER_PAT)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
let req_gen = |pat: Option<String>| async move {
api.get_version_from_hash(beta_file_hash, "sha1", pat.as_deref())
@@ -1084,7 +1086,7 @@ pub async fn organization_scopes() {
// Create organization
let organization_create = Scopes::ORGANIZATION_CREATE;
let req_gen = |pat: Option<String>| async move {
api.create_organization("TestOrg", "TestOrg Description", pat.as_deref())
api.create_organization("Test Org", "TestOrg", "TestOrg Description", pat.as_deref())
.await
};
let (_, success) = ScopeTest::new(&test_env)

View File

@@ -1,4 +1,5 @@
use crate::common::{api_common::ApiTeams, database::*};
use crate::common::{api_common::ApiTeams, asserts::assert_status, database::*};
use actix_http::StatusCode;
use common::{
api_v3::ApiV3,
environment::{with_test_environment, with_test_environment_all, TestEnvironment},
@@ -39,7 +40,7 @@ async fn test_get_team() {
let resp = api
.add_user_to_team(alpha_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Team check directly
let members = api
@@ -92,7 +93,7 @@ async fn test_get_team() {
// An accepted member of the team should appear in the team members list
// and should be able to see private data about the team
let resp = api.join_team(alpha_team_id, FRIEND_USER_PAT).await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Team check directly
let members = api
@@ -163,7 +164,7 @@ async fn test_get_team_organization() {
let resp = api
.add_user_to_team(zeta_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Team check directly
let members = api
@@ -217,7 +218,7 @@ async fn test_get_team_organization() {
// An accepted member of the team should appear in the team members list
// and should be able to see private data about the team
let resp = api.join_team(zeta_team_id, FRIEND_USER_PAT).await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Team check directly
let members = api
@@ -272,17 +273,17 @@ async fn test_get_team_project_orgs() {
.api
.organization_add_project(zeta_organization_id, alpha_project_id, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// Invite and add friend to zeta
let resp = test_env
.api
.add_user_to_team(zeta_team_id, FRIEND_USER_ID, None, None, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
let resp = test_env.api.join_team(zeta_team_id, FRIEND_USER_PAT).await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// The team members route from teams (on a project's team):
// - the members of the project team specifically
@@ -315,19 +316,19 @@ async fn test_patch_project_team_member() {
// Edit team as admin/mod but not a part of the team should be OK
let resp = api.edit_team_member(alpha_team_id, USER_USER_ID, json!({}), ADMIN_USER_PAT).await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// 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);
assert_status(&resp, StatusCode::BAD_REQUEST);
// Should not be able to edit organization permissions of a project team
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);
assert_status(&resp, StatusCode::BAD_REQUEST);
// Should not be able to add permissions to a user that the adding-user does not have
// (true for both project and org)
@@ -336,24 +337,24 @@ async fn test_patch_project_team_member() {
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);
assert_status(&resp, StatusCode::NO_CONTENT);
// accept
let resp = api.join_team(alpha_team_id, FRIEND_USER_PAT).await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// try to add permissions
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);
assert_status(&resp, StatusCode::BAD_REQUEST);
// Cannot set payouts outside of 0 and 5000
for payout in [-1, 5001] {
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);
assert_status(&resp, StatusCode::BAD_REQUEST);
}
// Successful patch
@@ -363,7 +364,7 @@ async fn test_patch_project_team_member() {
"role": "membe2r",
"ordering": 5
}), FRIEND_USER_PAT).await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Check results
let members = api.get_team_members_deserialized_common(alpha_team_id, FRIEND_USER_PAT).await;
@@ -387,14 +388,14 @@ async fn test_patch_organization_team_member() {
.api
.edit_team_member(zeta_team_id, USER_USER_ID, json!({}), ADMIN_USER_PAT)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// As a non-owner with full permissions, attempt to edit the owner's permissions
let resp = test_env
.api
.edit_team_member(zeta_team_id, USER_USER_ID, json!({ "permissions": 0 }), ADMIN_USER_PAT)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// Should not be able to add permissions to a user that the adding-user does not have
// (true for both project and org)
@@ -404,18 +405,18 @@ async fn test_patch_organization_team_member() {
.api
.add_user_to_team(zeta_team_id, FRIEND_USER_ID, None, Some(OrganizationPermissions::EDIT_MEMBER | OrganizationPermissions::EDIT_MEMBER_DEFAULT_PERMISSIONS), USER_USER_PAT)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// accept
let resp = test_env.api.join_team(zeta_team_id, FRIEND_USER_PAT).await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// try to add permissions- fails, as we do not have EDIT_DETAILS
let resp = test_env
.api
.edit_team_member(zeta_team_id, FRIEND_USER_ID, json!({ "organization_permissions": (OrganizationPermissions::EDIT_MEMBER | OrganizationPermissions::EDIT_DETAILS).bits() }), FRIEND_USER_PAT)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// Cannot set payouts outside of 0 and 5000
for payout in [-1, 5001] {
@@ -423,7 +424,7 @@ async fn test_patch_organization_team_member() {
.api
.edit_team_member(zeta_team_id, FRIEND_USER_ID, json!({ "payouts_split": payout }), USER_USER_PAT)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
}
// Successful patch
@@ -442,7 +443,7 @@ async fn test_patch_organization_team_member() {
FRIEND_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Check results
let members = test_env
@@ -481,39 +482,39 @@ async fn transfer_ownership_v3() {
let resp = api
.transfer_team_ownership(alpha_team_id, FRIEND_USER_ID, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
let resp = api
.transfer_team_ownership(alpha_team_id, FRIEND_USER_ID, FRIEND_USER_PAT)
.await;
assert_eq!(resp.status(), 401);
assert_status(&resp, StatusCode::UNAUTHORIZED);
// 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);
assert_status(&resp, StatusCode::NO_CONTENT);
// 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);
assert_status(&resp, StatusCode::BAD_REQUEST);
// accept
let resp = api.join_team(alpha_team_id, FRIEND_USER_PAT).await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// 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);
assert_status(&resp, StatusCode::UNAUTHORIZED);
// 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);
assert_status(&resp, StatusCode::NO_CONTENT);
// Check
let members = api
@@ -542,7 +543,7 @@ async fn transfer_ownership_v3() {
let resp = api
.remove_from_team(alpha_team_id, FRIEND_USER_ID, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 401);
assert_status(&resp, StatusCode::UNAUTHORIZED);
// V3 only- confirm the owner can change their role without losing ownership
let resp = api
@@ -555,7 +556,7 @@ async fn transfer_ownership_v3() {
FRIEND_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
let members = api
.get_team_members_deserialized(alpha_team_id, USER_USER_PAT)
@@ -588,25 +589,25 @@ async fn transfer_ownership_v3() {
// // Link alpha team to zeta org
// let resp = api.organization_add_project(zeta_organization_id, alpha_project_id, USER_USER_PAT).await;
// assert_eq!(resp.status(), 200);
// assert_status(&resp, StatusCode::OK);
// // Invite friend to zeta team with all project default permissions
// let resp = api.add_user_to_team(&zeta_team_id, FRIEND_USER_ID, Some(ProjectPermissions::all()), Some(OrganizationPermissions::all()), USER_USER_PAT).await;
// assert_eq!(resp.status(), 204);
// assert_status(&resp, StatusCode::NO_CONTENT);
// // Accept invite to zeta team
// let resp = api.join_team(&zeta_team_id, FRIEND_USER_PAT).await;
// assert_eq!(resp.status(), 204);
// assert_status(&resp, StatusCode::NO_CONTENT);
// // Attempt, as friend, to edit details of alpha project (should succeed, org invite accepted)
// let resp = api.edit_project(alpha_project_id, json!({
// "title": "new name"
// }), FRIEND_USER_PAT).await;
// assert_eq!(resp.status(), 204);
// assert_status(&resp, StatusCode::NO_CONTENT);
// // Invite friend to alpha team with *no* project permissions
// let resp = api.add_user_to_team(&alpha_team_id, FRIEND_USER_ID, Some(ProjectPermissions::empty()), None, USER_USER_PAT).await;
// assert_eq!(resp.status(), 204);
// assert_status(&resp, StatusCode::NO_CONTENT);
// // Do not accept invite to alpha team
@@ -614,7 +615,7 @@ async fn transfer_ownership_v3() {
// let resp = api.edit_project(alpha_project_id, json!({
// "title": "new name"
// }), FRIEND_USER_PAT).await;
// assert_eq!(resp.status(), 401);
// assert_status(&resp, StatusCode::UNAUTHORIZED);
// test_env.cleanup().await;
// }

View File

@@ -1,4 +1,6 @@
use crate::common::api_common::ApiProject;
use crate::common::asserts::assert_status;
use actix_http::StatusCode;
use actix_web::test;
use bytes::Bytes;
@@ -13,7 +15,7 @@ pub async fn error_404_empty() {
// 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);
assert_status(&resp, StatusCode::NOT_FOUND);
let body = test::read_body(resp).await;
let empty_bytes = Bytes::from_static(b"");
assert_eq!(body, empty_bytes);

View File

@@ -3,6 +3,7 @@ use std::sync::Arc;
use crate::common::{
api_common::{ApiProject, ApiVersion, AppendsOptionalPat},
api_v2::{request_data::get_public_project_creation_data_json, ApiV2},
asserts::assert_status,
database::{
generate_random_name, ADMIN_USER_PAT, FRIEND_USER_ID, FRIEND_USER_PAT, USER_USER_PAT,
},
@@ -132,10 +133,8 @@ async fn test_add_remove_project() {
.append_pat(USER_USER_PAT)
.set_multipart(vec![json_segment.clone(), file_segment.clone()])
.to_request();
let resp = test_env.call(req).await;
let status = resp.status();
assert_eq!(status, 200);
let resp: actix_web::dev::ServiceResponse = test_env.call(req).await;
assert_status(&resp, StatusCode::OK);
// Get the project we just made, and confirm that it's correct
let project = api.get_project_deserialized("demo", USER_USER_PAT).await;
@@ -163,7 +162,7 @@ async fn test_add_remove_project() {
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// Reusing with the same slug and a different file should fail
let req = test::TestRequest::post()
@@ -176,7 +175,7 @@ async fn test_add_remove_project() {
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// Different slug, different file should succeed
let req = test::TestRequest::post()
@@ -189,7 +188,7 @@ async fn test_add_remove_project() {
.to_request();
let resp = test_env.call(req).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
// Get
let project = api.get_project_deserialized("demo", USER_USER_PAT).await;
@@ -197,7 +196,7 @@ async fn test_add_remove_project() {
// Remove the project
let resp = test_env.api.remove_project("demo", USER_USER_PAT).await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Confirm that the project is gone from the cache
let mut redis_conn = test_env.db.redis_pool.connect().await.unwrap();
@@ -220,7 +219,7 @@ async fn test_add_remove_project() {
// Old slug no longer works
let resp = api.get_project("demo", USER_USER_PAT).await;
assert_eq!(resp.status(), 404);
assert_status(&resp, StatusCode::NOT_FOUND);
})
.await;
}
@@ -344,7 +343,7 @@ pub async fn test_patch_v2() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
let project = api
.get_project_deserialized(alpha_project_slug, USER_USER_PAT)
@@ -456,7 +455,7 @@ pub async fn test_bulk_edit_links() {
ADMIN_USER_PAT,
)
.await;
assert_eq!(resp.status(), StatusCode::NO_CONTENT);
assert_status(&resp, StatusCode::NO_CONTENT);
let alpha_body = api
.get_project_deserialized(alpha_project_id, ADMIN_USER_PAT)
@@ -496,7 +495,7 @@ pub async fn test_bulk_edit_links() {
ADMIN_USER_PAT,
)
.await;
assert_eq!(resp.status(), StatusCode::NO_CONTENT);
assert_status(&resp, StatusCode::NO_CONTENT);
let alpha_body = api
.get_project_deserialized(alpha_project_id, ADMIN_USER_PAT)
@@ -568,7 +567,7 @@ pub async fn test_bulk_edit_links() {
ADMIN_USER_PAT,
)
.await;
assert_eq!(resp.status(), StatusCode::NO_CONTENT);
assert_status(&resp, StatusCode::NO_CONTENT);
let alpha_body = api
.get_project_deserialized(alpha_project_id, ADMIN_USER_PAT)

View File

@@ -2,11 +2,13 @@ use crate::common::api_common::Api;
use crate::common::api_common::ApiProject;
use crate::common::api_common::ApiVersion;
use crate::common::api_v2::ApiV2;
use crate::common::asserts::assert_status;
use crate::common::database::*;
use crate::common::dummy_data::TestFile;
use crate::common::dummy_data::DUMMY_CATEGORIES;
use crate::common::environment::with_test_environment;
use crate::common::environment::TestEnvironment;
use actix_http::StatusCode;
use futures::stream::StreamExt;
use labrinth::models::ids::base62_impl::parse_base62;
use serde_json::json;
@@ -53,7 +55,7 @@ async fn search_projects() {
MOD_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
(project.id.0, id)
}
};
@@ -282,7 +284,7 @@ async fn search_projects() {
// Forcibly reset the search index
let resp = api.reset_search_index().await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// Test searches
let stream = futures::stream::iter(pairs);

View File

@@ -1,9 +1,11 @@
use actix_http::StatusCode;
use labrinth::models::teams::ProjectPermissions;
use serde_json::json;
use crate::common::{
api_common::ApiTeams,
api_v2::ApiV2,
asserts::assert_status,
database::{
FRIEND_USER_ID, FRIEND_USER_ID_PARSED, FRIEND_USER_PAT, USER_USER_ID_PARSED, USER_USER_PAT,
},
@@ -23,35 +25,35 @@ async fn transfer_ownership_v2() {
let resp = api
.transfer_team_ownership(alpha_team_id, FRIEND_USER_ID, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
// 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);
assert_status(&resp, StatusCode::NO_CONTENT);
// 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);
assert_status(&resp, StatusCode::BAD_REQUEST);
// accept
let resp = api.join_team(alpha_team_id, FRIEND_USER_PAT).await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
// 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);
assert_status(&resp, StatusCode::UNAUTHORIZED);
// 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);
assert_status(&resp, StatusCode::NO_CONTENT);
// Check
let members = api
@@ -78,7 +80,7 @@ async fn transfer_ownership_v2() {
let resp = api
.remove_from_team(alpha_team_id, FRIEND_USER_ID, USER_USER_PAT)
.await;
assert_eq!(resp.status(), 401);
assert_status(&resp, StatusCode::UNAUTHORIZED);
// V2 only- confirm the owner changing the role to member does nothing
let resp = api
@@ -91,7 +93,7 @@ async fn transfer_ownership_v2() {
FRIEND_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
let members = api
.get_team_members_deserialized(alpha_team_id, USER_USER_PAT)
.await;

View File

@@ -1,3 +1,4 @@
use actix_http::StatusCode;
use actix_web::test;
use futures::StreamExt;
use labrinth::models::projects::VersionId;
@@ -9,6 +10,7 @@ use serde_json::json;
use crate::common::api_common::{ApiProject, ApiVersion};
use crate::common::api_v2::ApiV2;
use crate::common::asserts::assert_status;
use crate::common::dummy_data::{DummyProjectAlpha, DummyProjectBeta};
use crate::common::environment::{with_test_environment, TestEnvironment};
use crate::common::{
@@ -34,7 +36,7 @@ pub async fn test_patch_version() {
ENEMY_USER_PAT,
)
.await;
assert_eq!(resp.status(), 401);
assert_status(&resp, StatusCode::UNAUTHORIZED);
// Failure because these are illegal requested statuses for a normal user.
for req in ["unknown", "scheduled"] {
@@ -48,7 +50,7 @@ pub async fn test_patch_version() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
}
// Sucessful request to patch many fields.
@@ -72,7 +74,7 @@ pub async fn test_patch_version() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
let version = api
.get_version_deserialized(alpha_version_id, USER_USER_PAT)
@@ -100,7 +102,7 @@ pub async fn test_patch_version() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
let version = api
.get_version_deserialized(alpha_version_id, USER_USER_PAT)
@@ -117,7 +119,7 @@ pub async fn test_patch_version() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
let version = api
.get_version_deserialized(alpha_version_id, USER_USER_PAT)
@@ -260,12 +262,12 @@ async fn version_updates() {
)
.await;
if success {
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
let body: serde_json::Value = test::read_body_json(resp).await;
let id = body["id"].as_str().unwrap();
assert_eq!(id, &result_id.to_string());
} else {
assert_eq!(resp.status(), 404);
assert_status(&resp, StatusCode::NOT_FOUND);
}
// update_files

View File

@@ -61,7 +61,7 @@ async fn test_get_version() {
// Request should fail on non-existent version
let resp = api.get_version("false", USER_USER_PAT).await;
assert_eq!(resp.status(), 404);
assert_status(&resp, StatusCode::NOT_FOUND);
// Similarly, request should fail on non-authorized user, on a yet-to-be-approved or hidden project, with a 404 (hiding the existence of the project)
// TODO: beta version should already be draft in dummy data, but theres a bug in finding it that
@@ -74,9 +74,9 @@ async fn test_get_version() {
)
.await;
let resp = api.get_version(beta_version_id, USER_USER_PAT).await;
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
let resp = api.get_version(beta_version_id, ENEMY_USER_PAT).await;
assert_eq!(resp.status(), 404);
assert_status(&resp, StatusCode::NOT_FOUND);
})
.await;
}
@@ -219,12 +219,12 @@ async fn version_updates() {
)
.await;
if success {
assert_eq!(resp.status(), 200);
assert_status(&resp, StatusCode::OK);
let body: serde_json::Value = test::read_body_json(resp).await;
let id = body["id"].as_str().unwrap();
assert_eq!(id, &result_id.to_string());
} else {
assert_eq!(resp.status(), 404);
assert_status(&resp, StatusCode::NOT_FOUND);
}
// update_files
@@ -405,7 +405,7 @@ pub async fn test_patch_version() {
ENEMY_USER_PAT,
)
.await;
assert_eq!(resp.status(), 401);
assert_status(&resp, StatusCode::UNAUTHORIZED);
// Failure because these are illegal requested statuses for a normal user.
for req in ["unknown", "scheduled"] {
@@ -419,7 +419,7 @@ pub async fn test_patch_version() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 400);
assert_status(&resp, StatusCode::BAD_REQUEST);
}
// Sucessful request to patch many fields.
@@ -447,7 +447,7 @@ pub async fn test_patch_version() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
let version = api
.get_version_deserialized_common(alpha_version_id, USER_USER_PAT)
@@ -483,7 +483,7 @@ pub async fn test_patch_version() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
let version = api
.get_version_deserialized_common(alpha_version_id, USER_USER_PAT)
@@ -499,7 +499,7 @@ pub async fn test_patch_version() {
USER_USER_PAT,
)
.await;
assert_eq!(resp.status(), 204);
assert_status(&resp, StatusCode::NO_CONTENT);
let version = api
.get_version_deserialized_common(alpha_version_id, USER_USER_PAT)