111 lines
No EOL
3.7 KiB
Rust
111 lines
No EOL
3.7 KiB
Rust
/*
|
|
* This file is part of Sharenet.
|
|
*
|
|
* Sharenet is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
|
|
*
|
|
* You may obtain a copy of the license at:
|
|
* https://creativecommons.org/licenses/by-nc-sa/4.0/
|
|
*
|
|
* Copyright (c) 2024 Continuist <continuist02@gmail.com>
|
|
*/
|
|
|
|
use sqlx::PgPool;
|
|
use sqlx::postgres::PgPoolOptions;
|
|
use std::env;
|
|
use std::sync::Once;
|
|
use std::sync::Mutex;
|
|
use std::sync::Arc;
|
|
use std::sync::OnceLock;
|
|
|
|
static INIT: Once = Once::new();
|
|
static MIGRATION_LOCK: Mutex<()> = Mutex::new(());
|
|
|
|
/// Global test database pool that is initialized once for all tests
|
|
static TEST_POOL: OnceLock<Arc<PgPool>> = OnceLock::new();
|
|
|
|
/// Initialize the test database with migrations
|
|
/// This function ensures migrations are run only once across all tests
|
|
async fn initialize_test_database() -> Arc<PgPool> {
|
|
let database_url = env::var("DATABASE_URL")
|
|
.unwrap_or_else(|_| "postgres://postgres:password@localhost:5432/sharenet_test".to_string());
|
|
|
|
let pool = PgPoolOptions::new()
|
|
.max_connections(10) // Increased for concurrent tests
|
|
.connect(&database_url)
|
|
.await
|
|
.expect("Failed to connect to test database");
|
|
|
|
// Run migrations only once
|
|
let _lock = MIGRATION_LOCK.lock().unwrap();
|
|
sqlx::migrate!("../../migrations")
|
|
.run(&pool)
|
|
.await
|
|
.expect("Failed to run migrations");
|
|
|
|
Arc::new(pool)
|
|
}
|
|
|
|
/// Get a shared database pool for tests
|
|
/// This ensures all tests use the same database connection and migrations are run only once
|
|
pub async fn get_test_pool() -> Arc<PgPool> {
|
|
if let Some(pool) = TEST_POOL.get() {
|
|
pool.clone()
|
|
} else {
|
|
let pool = initialize_test_database().await;
|
|
let _ = TEST_POOL.set(pool.clone());
|
|
pool
|
|
}
|
|
}
|
|
|
|
/// Setup a test database connection with migrations already applied
|
|
/// This is the main function that test modules should use
|
|
pub async fn setup_test_db() -> PgPool {
|
|
let _pool_arc = get_test_pool().await;
|
|
let pool = PgPoolOptions::new()
|
|
.max_connections(5)
|
|
.connect(&get_test_database_url())
|
|
.await
|
|
.expect("Failed to connect to test database");
|
|
|
|
// Clean up any existing test data
|
|
cleanup_test_data(&pool).await;
|
|
|
|
pool
|
|
}
|
|
|
|
/// Clean up test data (does not drop tables, just clears data)
|
|
pub async fn cleanup_test_data(pool: &PgPool) {
|
|
let mut tx = pool.begin().await.expect("Failed to begin transaction");
|
|
|
|
// Delete in reverse order of foreign key dependencies
|
|
sqlx::query("DELETE FROM products").execute(&mut *tx).await.expect("Failed to delete products");
|
|
sqlx::query("DELETE FROM users").execute(&mut *tx).await.expect("Failed to delete users");
|
|
|
|
tx.commit().await.expect("Failed to commit cleanup transaction");
|
|
}
|
|
|
|
/// Get the test database URL
|
|
pub fn get_test_database_url() -> String {
|
|
env::var("DATABASE_URL")
|
|
.unwrap_or_else(|_| "postgres://postgres:password@localhost:5432/sharenet_test".to_string())
|
|
}
|
|
|
|
/// Generate unique test data to avoid conflicts between concurrent tests
|
|
pub fn unique_test_data(prefix: &str) -> (String, String) {
|
|
use uuid::Uuid;
|
|
let id = Uuid::new_v4().to_string()[..8].to_string();
|
|
(format!("{}_{}", prefix, id), format!("{}_test@example.com", prefix))
|
|
}
|
|
|
|
/// Initialize the test environment
|
|
/// This should be called once at the start of the test suite
|
|
pub async fn initialize_test_environment() {
|
|
INIT.call_once(|| {
|
|
// This will be called only once, even if multiple tests call this function
|
|
println!("Initializing test environment...");
|
|
});
|
|
|
|
// Ensure database is set up
|
|
let _pool = get_test_pool().await;
|
|
println!("Test environment initialized");
|
|
}
|