sharenet/backend/crates/integration-tests/src/test_setup.rs
continuist 61117b6fa6
Some checks are pending
CI/CD Pipeline / Test Backend (push) Waiting to run
CI/CD Pipeline / Test Frontend (push) Waiting to run
CI/CD Pipeline / Build and Push Docker Images (push) Blocked by required conditions
CI/CD Pipeline / Deploy to Production (push) Blocked by required conditions
Created test_setup.rs file that consolidates test setup code for interface tests, including db migrations
2025-06-28 01:57:12 -04:00

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");
}