You've already forked AstralRinth
forked from xxxOFFxxx/AstralRinth
Expose utilities for setting up the database (#4657)
* Expose utilities for setting up the database * Expose migrator directly * Make some test utils publicly accessible * expose migrator * more test fixture utils * more test fixture utils * more test fixture utils * fix * fix lint
This commit is contained in:
@@ -3,24 +3,22 @@
|
||||
|
||||
-- Inserts 5 dummy users for testing, with slight differences
|
||||
-- 'Friend' and 'enemy' function like 'user', but we can use them to simulate 'other' users that may or may not be able to access certain things
|
||||
-- IDs 1-5, 1-5
|
||||
INSERT INTO users (id, username, email, role) VALUES (1, 'Admin', 'admin@modrinth.com', 'admin');
|
||||
INSERT INTO users (id, username, email, role)
|
||||
VALUES (2, 'Moderator', 'moderator@modrinth.com', 'moderator');
|
||||
INSERT INTO users (id, username, email, role) VALUES (3, 'User', 'user@modrinth.com', 'developer');
|
||||
INSERT INTO users (id, username, email, role)
|
||||
VALUES (4, 'Friend', 'friend@modrinth.com', 'developer');
|
||||
INSERT INTO users (id, username, email, role)
|
||||
VALUES (5, 'Enemy', 'enemy@modrinth.com', 'developer');
|
||||
VALUES ({{user_id::ADMIN}}, 'Admin', 'admin@modrinth.com', 'admin'),
|
||||
({{user_id::MODERATOR}}, 'Moderator', 'moderator@modrinth.com', 'moderator'),
|
||||
({{user_id::USER}}, 'User', 'user@modrinth.com', 'developer'),
|
||||
({{user_id::FRIEND}}, 'Friend', 'friend@modrinth.com', 'developer'),
|
||||
({{user_id::ENEMY}}, 'Enemy', 'enemy@modrinth.com', 'developer');
|
||||
|
||||
-- Full PATs for each user, with different scopes
|
||||
-- These are not legal PATs, as they contain all scopes- they mimic permissions of a logged in user
|
||||
-- IDs: 50-54, o p q r s
|
||||
INSERT INTO pats (id, user_id, name, access_token, scopes, expires) VALUES (50, 1, 'admin-pat', 'mrp_patadmin', $1, '2030-08-18 15:48:58.435729+00');
|
||||
INSERT INTO pats (id, user_id, name, access_token, scopes, expires) VALUES (51, 2, 'moderator-pat', 'mrp_patmoderator', $1, '2030-08-18 15:48:58.435729+00');
|
||||
INSERT INTO pats (id, user_id, name, access_token, scopes, expires) VALUES (52, 3, 'user-pat', 'mrp_patuser', $1, '2030-08-18 15:48:58.435729+00');
|
||||
INSERT INTO pats (id, user_id, name, access_token, scopes, expires) VALUES (53, 4, 'friend-pat', 'mrp_patfriend', $1, '2030-08-18 15:48:58.435729+00');
|
||||
INSERT INTO pats (id, user_id, name, access_token, scopes, expires) VALUES (54, 5, 'enemy-pat', 'mrp_patenemy', $1, '2030-08-18 15:48:58.435729+00');
|
||||
INSERT INTO pats (id, user_id, name, access_token, scopes, expires)
|
||||
VALUES (50, {{user_id::ADMIN}}, 'admin-pat', '{{pat::ADMIN}}', {{all_scopes}}, '2030-08-18 15:48:58.435729+00'),
|
||||
(51, {{user_id::MODERATOR}}, 'moderator-pat', '{{pat::MODERATOR}}', {{all_scopes}}, '2030-08-18 15:48:58.435729+00'),
|
||||
(52, {{user_id::USER}}, 'user-pat', '{{pat::USER}}', {{all_scopes}}, '2030-08-18 15:48:58.435729+00'),
|
||||
(53, {{user_id::FRIEND}}, 'friend-pat', '{{pat::FRIEND}}', {{all_scopes}}, '2030-08-18 15:48:58.435729+00'),
|
||||
(54, {{user_id::ENEMY}}, 'enemy-pat', '{{pat::ENEMY}}', {{all_scopes}}, '2030-08-18 15:48:58.435729+00');
|
||||
|
||||
INSERT INTO loaders (id, loader) VALUES (5, 'fabric');
|
||||
INSERT INTO loaders_project_types (joining_loader_id, joining_project_type_id) VALUES (5, 1);
|
||||
@@ -119,7 +117,7 @@ VALUES (
|
||||
1,
|
||||
'oauth_client_alpha',
|
||||
NULL,
|
||||
$1,
|
||||
{{all_scopes}},
|
||||
'4dbff86cc2ca1bae1e16468a05cb9881c97f1753bce3619034898faa1aabe429955a1bf8ec483d7421fe3c1646613a59ed5441fb0f321389f77f48a879c7b1f1',
|
||||
3
|
||||
);
|
||||
@@ -5,5 +5,6 @@ pub use models::DBImage;
|
||||
pub use models::DBProject;
|
||||
pub use models::DBVersion;
|
||||
pub use postgres_database::{
|
||||
ReadOnlyPgPool, check_for_migrations, connect_all, register_and_set_metrics,
|
||||
MIGRATOR, ReadOnlyPgPool, check_for_migrations, connect_all,
|
||||
register_and_set_metrics,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use eyre::Context;
|
||||
use prometheus::{IntGauge, Registry};
|
||||
use sqlx::migrate::MigrateDatabase;
|
||||
use sqlx::migrate::{MigrateDatabase, Migrator};
|
||||
use sqlx::postgres::{PgPool, PgPoolOptions};
|
||||
use sqlx::{Connection, PgConnection, Postgres};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
@@ -75,25 +76,36 @@ pub async fn connect_all() -> Result<(PgPool, ReadOnlyPgPool), sqlx::Error> {
|
||||
Ok((pool, ro))
|
||||
}
|
||||
}
|
||||
pub async fn check_for_migrations() -> Result<(), sqlx::Error> {
|
||||
let uri = dotenvy::var("DATABASE_URL").expect("`DATABASE_URL` not in .env");
|
||||
|
||||
pub async fn check_for_migrations() -> eyre::Result<()> {
|
||||
let uri =
|
||||
dotenvy::var("DATABASE_URL").wrap_err("`DATABASE_URL` not in .env")?;
|
||||
let uri = uri.as_str();
|
||||
if !Postgres::database_exists(uri).await? {
|
||||
if !Postgres::database_exists(uri)
|
||||
.await
|
||||
.wrap_err("failed to check if database exists")?
|
||||
{
|
||||
info!("Creating database...");
|
||||
Postgres::create_database(uri).await?;
|
||||
Postgres::create_database(uri)
|
||||
.await
|
||||
.wrap_err("failed to create database")?;
|
||||
}
|
||||
|
||||
info!("Applying migrations...");
|
||||
|
||||
let mut conn: PgConnection = PgConnection::connect(uri).await?;
|
||||
sqlx::migrate!()
|
||||
let mut conn: PgConnection = PgConnection::connect(uri)
|
||||
.await
|
||||
.wrap_err("failed to connect to database")?;
|
||||
MIGRATOR
|
||||
.run(&mut conn)
|
||||
.await
|
||||
.expect("Error while running database migrations!");
|
||||
.wrap_err("failed to run database migrations")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub static MIGRATOR: Migrator = sqlx::migrate!();
|
||||
|
||||
pub async fn register_and_set_metrics(
|
||||
pool: &PgPool,
|
||||
registry: &Registry,
|
||||
|
||||
@@ -37,6 +37,7 @@ pub mod routes;
|
||||
pub mod scheduler;
|
||||
pub mod search;
|
||||
pub mod sync;
|
||||
pub mod test;
|
||||
pub mod util;
|
||||
pub mod validate;
|
||||
|
||||
|
||||
81
apps/labrinth/src/test/db.rs
Normal file
81
apps/labrinth/src/test/db.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
use eyre::{Context, Result};
|
||||
use sqlx::{Executor, PgPool};
|
||||
|
||||
/// Static personal access token for use in [`AppendPat`].
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Pat(pub &'static str);
|
||||
|
||||
/// Dummy [`Pat`]s.
|
||||
#[allow(missing_docs, reason = "self-explanatory")]
|
||||
pub mod pat {
|
||||
use super::Pat;
|
||||
|
||||
pub const ADMIN: Pat = Pat("mrp_patadmin");
|
||||
pub const MODERATOR: Pat = Pat("mrp_patmoderator");
|
||||
pub const USER: Pat = Pat("mrp_patuser");
|
||||
pub const FRIEND: Pat = Pat("mrp_patfriend");
|
||||
pub const ENEMY: Pat = Pat("mrp_patenemy");
|
||||
}
|
||||
|
||||
/// See [`AppendPat::append_pat`].
|
||||
pub trait AppendPat {
|
||||
/// Appends a [`Pat`] authorization token to an
|
||||
/// [`actix_web::test::TestRequest`].
|
||||
#[must_use]
|
||||
fn append_pat(self, pat: Pat) -> Self;
|
||||
}
|
||||
|
||||
impl AppendPat for actix_web::test::TestRequest {
|
||||
fn append_pat(self, pat: Pat) -> Self {
|
||||
self.append_header(("Authorization", pat.0))
|
||||
}
|
||||
}
|
||||
|
||||
/// Dummy [`DBUserId`]s.
|
||||
///
|
||||
/// [`DBUserId`]: crate::database::models::DBUserId
|
||||
pub mod user_id {
|
||||
use crate::database::models::DBUserId;
|
||||
|
||||
pub const ADMIN: DBUserId = DBUserId(1);
|
||||
pub const MODERATOR: DBUserId = DBUserId(2);
|
||||
pub const USER: DBUserId = DBUserId(3);
|
||||
pub const FRIEND: DBUserId = DBUserId(4);
|
||||
pub const ENEMY: DBUserId = DBUserId(5);
|
||||
}
|
||||
|
||||
/// Initialize a database with dummy fixture data.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Errors if the fixture could not be applied.
|
||||
pub async fn add_dummy_data(db: &PgPool) -> Result<()> {
|
||||
db.execute(
|
||||
include_str!("../../fixtures/dummy_data.sql")
|
||||
//
|
||||
.replace("{{user_id::ADMIN}}", &user_id::ADMIN.0.to_string())
|
||||
.replace(
|
||||
"{{user_id::MODERATOR}}",
|
||||
&user_id::MODERATOR.0.to_string(),
|
||||
)
|
||||
.replace("{{user_id::USER}}", &user_id::USER.0.to_string())
|
||||
.replace("{{user_id::FRIEND}}", &user_id::FRIEND.0.to_string())
|
||||
.replace("{{user_id::ENEMY}}", &user_id::ENEMY.0.to_string())
|
||||
//
|
||||
.replace("{{pat::ADMIN}}", pat::ADMIN.0)
|
||||
.replace("{{pat::MODERATOR}}", pat::MODERATOR.0)
|
||||
.replace("{{pat::USER}}", pat::USER.0)
|
||||
.replace("{{pat::FRIEND}}", pat::FRIEND.0)
|
||||
.replace("{{pat::ENEMY}}", pat::ENEMY.0)
|
||||
//
|
||||
.replace(
|
||||
"{{all_scopes}}",
|
||||
&crate::models::pats::Scopes::all().bits().to_string(),
|
||||
)
|
||||
.as_str(),
|
||||
)
|
||||
.await
|
||||
.wrap_err("failed to add dummy data")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
1
apps/labrinth/src/test/mod.rs
Normal file
1
apps/labrinth/src/test/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod db;
|
||||
@@ -10,11 +10,9 @@ use labrinth::models::ids::ProjectId;
|
||||
use labrinth::models::{
|
||||
oauth_clients::OAuthClient,
|
||||
organizations::Organization,
|
||||
pats::Scopes,
|
||||
projects::{Project, Version},
|
||||
};
|
||||
use serde_json::json;
|
||||
use sqlx::Executor;
|
||||
use zip::{CompressionMethod, ZipWriter, write::FileOptions};
|
||||
|
||||
use super::{
|
||||
@@ -291,13 +289,7 @@ pub async fn add_dummy_data(api: &ApiV3, db: TemporaryDatabase) -> DummyData {
|
||||
// Adds basic dummy data to the database directly with sql (user, pats)
|
||||
let pool = &db.pool.clone();
|
||||
|
||||
pool.execute(
|
||||
include_str!("../fixtures/dummy_data.sql")
|
||||
.replace("$1", &Scopes::all().bits().to_string())
|
||||
.as_str(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
labrinth::test::db::add_dummy_data(pool).await.unwrap();
|
||||
|
||||
let (alpha_project, alpha_version) = add_project_alpha(api).await;
|
||||
let (beta_project, beta_version) = add_project_beta(api).await;
|
||||
|
||||
Reference in New Issue
Block a user