You've already forked AstralRinth
forked from didirus/AstralRinth
Batch inserts [MOD-555] (#726)
* Batch a bunch of inserts, but still more to do * Insert many for clickhouse (+ tests) * Batch the remaining ones except those requiring deduplication * Risky dedups * Bit o cleanup and formatting * cargo sqlx prepare * Add test around batch editing project categories * Add struct to satisfy clippy * Fix silly mistake that was caught by the tests! * Leave room for growth in dummy_data
This commit is contained in:
@@ -29,6 +29,7 @@ pub const USER_USER_PAT: &str = "mrp_patuser";
|
||||
pub const FRIEND_USER_PAT: &str = "mrp_patfriend";
|
||||
pub const ENEMY_USER_PAT: &str = "mrp_patenemy";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TemporaryDatabase {
|
||||
pub pool: PgPool,
|
||||
pub redis_pool: RedisPool,
|
||||
@@ -75,10 +76,14 @@ impl TemporaryDatabase {
|
||||
.await
|
||||
.expect("Connection to temporary database failed");
|
||||
|
||||
println!("Running migrations on temporary database");
|
||||
|
||||
// Performs migrations
|
||||
let migrations = sqlx::migrate!("./migrations");
|
||||
migrations.run(&pool).await.expect("Migrations failed");
|
||||
|
||||
println!("Migrations complete");
|
||||
|
||||
// Gets new Redis pool
|
||||
let redis_pool = RedisPool::new(Some(temp_database_name.clone()));
|
||||
|
||||
|
||||
@@ -13,6 +13,16 @@ use super::{
|
||||
environment::TestEnvironment,
|
||||
};
|
||||
|
||||
pub const DUMMY_CATEGORIES: &'static [&str] = &[
|
||||
"combat",
|
||||
"decoration",
|
||||
"economy",
|
||||
"food",
|
||||
"magic",
|
||||
"mobs",
|
||||
"optimization",
|
||||
];
|
||||
|
||||
pub struct DummyData {
|
||||
pub alpha_team_id: String,
|
||||
pub beta_team_id: String,
|
||||
|
||||
@@ -3,6 +3,19 @@
|
||||
use super::{database::TemporaryDatabase, dummy_data};
|
||||
use crate::common::setup;
|
||||
use actix_web::{dev::ServiceResponse, test, App};
|
||||
use futures::Future;
|
||||
|
||||
pub async fn with_test_environment<Fut>(f: impl FnOnce(TestEnvironment) -> Fut)
|
||||
where
|
||||
Fut: Future<Output = ()>,
|
||||
{
|
||||
let test_env = TestEnvironment::build_with_dummy().await;
|
||||
let db = test_env.db.clone();
|
||||
|
||||
f(test_env).await;
|
||||
|
||||
db.cleanup().await;
|
||||
}
|
||||
|
||||
// A complete test environment, with a test actix app and a database.
|
||||
// Must be called in an #[actix_rt::test] context. It also simulates a
|
||||
|
||||
@@ -27,10 +27,20 @@ INSERT INTO loaders (id, loader) VALUES (1, 'fabric');
|
||||
INSERT INTO loaders_project_types (joining_loader_id, joining_project_type_id) VALUES (1,1);
|
||||
INSERT INTO loaders_project_types (joining_loader_id, joining_project_type_id) VALUES (1,2);
|
||||
|
||||
INSERT INTO categories (id, category, project_type) VALUES (1, 'combat', 1);
|
||||
INSERT INTO categories (id, category, project_type) VALUES (2, 'decoration', 1);
|
||||
INSERT INTO categories (id, category, project_type) VALUES (3, 'economy', 1);
|
||||
INSERT INTO categories (id, category, project_type) VALUES
|
||||
(1, 'combat', 1),
|
||||
(2, 'decoration', 1),
|
||||
(3, 'economy', 1),
|
||||
(4, 'food', 1),
|
||||
(5, 'magic', 1),
|
||||
(6, 'mobs', 1),
|
||||
(7, 'optimization', 1);
|
||||
|
||||
INSERT INTO categories (id, category, project_type) VALUES (4, 'combat', 2);
|
||||
INSERT INTO categories (id, category, project_type) VALUES (5, 'decoration', 2);
|
||||
INSERT INTO categories (id, category, project_type) VALUES (6, 'economy', 2);
|
||||
INSERT INTO categories (id, category, project_type) VALUES
|
||||
(101, 'combat', 2),
|
||||
(102, 'decoration', 2),
|
||||
(103, 'economy', 2),
|
||||
(104, 'food', 2),
|
||||
(105, 'magic', 2),
|
||||
(106, 'mobs', 2),
|
||||
(107, 'optimization', 2);
|
||||
@@ -1,10 +1,14 @@
|
||||
use actix_http::StatusCode;
|
||||
use actix_web::dev::ServiceResponse;
|
||||
use actix_web::test;
|
||||
use common::environment::with_test_environment;
|
||||
use labrinth::database::models::project_item::{PROJECTS_NAMESPACE, PROJECTS_SLUGS_NAMESPACE};
|
||||
use labrinth::models::ids::base62_impl::parse_base62;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::database::*;
|
||||
|
||||
use crate::common::dummy_data::DUMMY_CATEGORIES;
|
||||
use crate::common::{actix::AppendsMultipart, environment::TestEnvironment};
|
||||
|
||||
// importing common module.
|
||||
@@ -403,7 +407,7 @@ pub async fn test_patch_project() {
|
||||
"title": "New successful title",
|
||||
"description": "New successful description",
|
||||
"body": "New successful body",
|
||||
"categories": ["combat"],
|
||||
"categories": [DUMMY_CATEGORIES[0]],
|
||||
"license_id": "MIT",
|
||||
"issues_url": "https://github.com",
|
||||
"discord_url": "https://discord.gg",
|
||||
@@ -441,7 +445,7 @@ pub async fn test_patch_project() {
|
||||
assert_eq!(body["title"], json!("New successful title"));
|
||||
assert_eq!(body["description"], json!("New successful description"));
|
||||
assert_eq!(body["body"], json!("New successful body"));
|
||||
assert_eq!(body["categories"], json!(["combat"]));
|
||||
assert_eq!(body["categories"], json!([DUMMY_CATEGORIES[0]]));
|
||||
assert_eq!(body["license"]["id"], json!("MIT"));
|
||||
assert_eq!(body["issues_url"], json!("https://github.com"));
|
||||
assert_eq!(body["discord_url"], json!("https://discord.gg"));
|
||||
@@ -457,5 +461,68 @@ pub async fn test_patch_project() {
|
||||
test_env.cleanup().await;
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
pub async fn test_bulk_edit_categories() {
|
||||
with_test_environment(|test_env| async move {
|
||||
let alpha_project_id = &test_env.dummy.as_ref().unwrap().alpha_project_id;
|
||||
let beta_project_id = &test_env.dummy.as_ref().unwrap().beta_project_id;
|
||||
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!(
|
||||
"/v2/projects?ids={}",
|
||||
urlencoding::encode(&format!("[\"{alpha_project_id}\",\"{beta_project_id}\"]"))
|
||||
))
|
||||
.append_header(("Authorization", ADMIN_USER_PAT))
|
||||
.set_json(json!({
|
||||
"categories": [DUMMY_CATEGORIES[0], DUMMY_CATEGORIES[3]],
|
||||
"add_categories": [DUMMY_CATEGORIES[1], DUMMY_CATEGORIES[2]],
|
||||
"remove_categories": [DUMMY_CATEGORIES[3]],
|
||||
"additional_categories": [DUMMY_CATEGORIES[4], DUMMY_CATEGORIES[6]],
|
||||
"add_additional_categories": [DUMMY_CATEGORIES[5]],
|
||||
"remove_additional_categories": [DUMMY_CATEGORIES[6]],
|
||||
}))
|
||||
.to_request();
|
||||
let resp = test_env.call(req).await;
|
||||
assert_eq!(resp.status(), StatusCode::NO_CONTENT);
|
||||
|
||||
let alpha_body = get_project_body(&test_env, &alpha_project_id, ADMIN_USER_PAT).await;
|
||||
assert_eq!(alpha_body["categories"], json!(DUMMY_CATEGORIES[0..=2]));
|
||||
assert_eq!(
|
||||
alpha_body["additional_categories"],
|
||||
json!(DUMMY_CATEGORIES[4..=5])
|
||||
);
|
||||
|
||||
let beta_body = get_project_body(&test_env, &beta_project_id, ADMIN_USER_PAT).await;
|
||||
assert_eq!(beta_body["categories"], alpha_body["categories"]);
|
||||
assert_eq!(
|
||||
beta_body["additional_categories"],
|
||||
alpha_body["additional_categories"],
|
||||
);
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn get_project(
|
||||
test_env: &TestEnvironment,
|
||||
project_slug: &str,
|
||||
user_pat: &str,
|
||||
) -> ServiceResponse {
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!("/v2/project/{project_slug}"))
|
||||
.append_header(("Authorization", user_pat))
|
||||
.to_request();
|
||||
test_env.call(req).await
|
||||
}
|
||||
|
||||
async fn get_project_body(
|
||||
test_env: &TestEnvironment,
|
||||
project_slug: &str,
|
||||
user_pat: &str,
|
||||
) -> serde_json::Value {
|
||||
let resp = get_project(test_env, project_slug, user_pat).await;
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
test::read_body_json(resp).await
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
Reference in New Issue
Block a user