From 7e982c365336c562ea88e3749ae4d0fdfd8da613 Mon Sep 17 00:00:00 2001 From: dal Date: Wed, 2 Apr 2025 14:28:29 -0600 Subject: [PATCH] delete testkit --- api/Cargo.toml | 1 - api/testkit/CLAUDE.md | 139 ----------------------------------------- api/testkit/Cargo.toml | 29 --------- api/testkit/build.rs | 104 ------------------------------ api/testkit/src/lib.rs | 90 -------------------------- 5 files changed, 363 deletions(-) delete mode 100644 api/testkit/CLAUDE.md delete mode 100644 api/testkit/Cargo.toml delete mode 100644 api/testkit/build.rs delete mode 100644 api/testkit/src/lib.rs diff --git a/api/Cargo.toml b/api/Cargo.toml index 135dce24a..0f6cafedd 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -9,7 +9,6 @@ members = [ "libs/sharing", "libs/sql_analyzer", "libs/search", - "testkit", ] resolver = "2" diff --git a/api/testkit/CLAUDE.md b/api/testkit/CLAUDE.md deleted file mode 100644 index 4ee7913fb..000000000 --- a/api/testkit/CLAUDE.md +++ /dev/null @@ -1,139 +0,0 @@ -# Testkit Library Usage Guide - -## Overview -The testkit library initializes database pools during build time and provides utilities for test isolation. The pools themselves are accessed directly from the database library. - -## Key Features -- Pre-initialized database connections at build time -- Environment variable management for test-specific configurations -- Test ID generation for test isolation - -## Usage - -### Basic Usage -```rust -use database::pool::get_pg_pool; -use testkit::test_id; -use anyhow::Result; - -#[tokio::test] -async fn test_database_operations() -> Result<()> { - // Create a unique test ID for isolation - let test_id = test_id(); - - // Pools are already initialized during build time - // Get the pool from the database library - let pool = get_pg_pool(); - - // Use the pool in your test - let conn = pool.get().await?; - - // Perform database operations... - - Ok(()) -} -``` - -### Environment Configuration -The testkit automatically loads environment variables from `.env.test` if available, otherwise from `.env` during the build process. Key environment variables include: - -- `TEST_DATABASE_URL` - PostgreSQL connection string for tests -- `TEST_POOLER_URL` - Connection string for the SQL pooler -- `TEST_REDIS_URL` - Redis connection string -- `TEST_DATABASE_POOL_SIZE` - Maximum connections in Diesel pool (default: 10) -- `TEST_SQLX_POOL_SIZE` - Maximum connections in SQLx pool (default: 10) - -### Accessing Database Pools -Access the pre-initialized database pools directly through the database library: - -```rust -// Get the Diesel PostgreSQL pool (AsyncPgConnection) -let pg_pool = database::pool::get_pg_pool(); - -// Get the SQLx PostgreSQL pool -let sqlx_pool = database::pool::get_sqlx_pool(); - -// Get the Redis pool -let redis_pool = database::pool::get_redis_pool(); -``` - -### Test ID Generation -For test isolation, you can generate unique IDs to tag test data: - -```rust -let test_id = testkit::test_id(); - -// Use this ID to mark test data for cleanup -diesel::insert_into(users::table) - .values(( - users::name.eq("Test User"), - users::email.eq("test@example.com"), - users::test_id.eq(test_id), // Use the test ID for later cleanup - )) - .execute(&mut conn) - .await?; -``` - -## Implementation Details - -### Pool Initialization -The testkit initializes pools during the build process using `build.rs`. This ensures pools are always available when your tests run, with no initialization overhead or risk of connection timing issues. - -### Error Handling -- Build-time initialization failures are reported as warnings but don't fail the build -- Unit tests that don't need database access will run fine even if pool initialization failed -- Integration tests that need database access will fail fast if the database isn't available - -### Database Reset (Optional Feature) -For clearing test data, use the `db_reset` feature: - -```rust -// Only available when the `db_reset` feature is enabled -#[cfg(feature = "db_reset")] -testkit::reset_test_database().await?; -``` - -## Best Practices - -1. **Direct Pool Access**: Always use `get_pg_pool()`, `get_sqlx_pool()`, or `get_redis_pool()` directly -2. **Use Test IDs**: Generate and use test IDs for proper test isolation -3. **Cleanup After Tests**: Always clean up test data after tests -4. **Connection Pooling**: Reuse connections from the pool instead of creating new ones -5. **Environment Variables**: Use the `.env.test` file for test-specific configuration -6. **Avoid Database in Unit Tests**: Unit tests should mock database operations - -## Example Integration Test Pattern - -```rust -#[tokio::test] -async fn test_example() -> Result<()> { - // Generate unique test ID - let test_id = testkit::test_id(); - - // Get pre-initialized database connection directly from database lib - let pool = database::pool::get_pg_pool(); - let mut conn = pool.get().await?; - - // Setup test data with test_id for tracking - let test_user = create_test_user(&mut conn, &test_id).await?; - - // Run the test against the test data - let result = your_function_under_test(test_user.id).await?; - assert_eq!(result.name, test_user.name); - - // Clean up test data using test_id - cleanup_test_data(&mut conn, &test_id).await?; - - Ok(()) -} - -async fn create_test_user(conn: &mut PgConnection, test_id: &str) -> Result { - // Insert user with test_id - // ... -} - -async fn cleanup_test_data(conn: &mut PgConnection, test_id: &str) -> Result<()> { - // Delete all data with matching test_id - // ... -} -``` \ No newline at end of file diff --git a/api/testkit/Cargo.toml b/api/testkit/Cargo.toml deleted file mode 100644 index feddb53c2..000000000 --- a/api/testkit/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "testkit" -version = "0.1.0" -edition = "2021" -description = "Test utilities for Buster API database pools" -build = "build.rs" - -[dependencies] -anyhow = { workspace = true } -tokio = { workspace = true } -diesel = { workspace = true } -diesel-async = { workspace = true } -sqlx = { workspace = true } -bb8-redis = { workspace = true } -uuid = { workspace = true } -dotenv = { workspace = true } -once_cell = { workspace = true } -futures = { workspace = true } -tracing = { workspace = true } -database = { path = "../libs/database" } - -[build-dependencies] -dotenv = { workspace = true } -tokio = { workspace = true, features = ["rt", "macros"] } -database = { path = "../libs/database" } - -[features] -default = [] -db_reset = [] # Use with caution - allows resetting the test database \ No newline at end of file diff --git a/api/testkit/build.rs b/api/testkit/build.rs deleted file mode 100644 index 344e170e8..000000000 --- a/api/testkit/build.rs +++ /dev/null @@ -1,104 +0,0 @@ -use std::env; -use std::fs; -use std::path::Path; - -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - - // Create default .env.test if it doesn't exist - ensure_test_env_exists(); - - // Load environment variables from .env - load_env_file(); - - // Try to initialize pools but don't fail the build if it fails - if let Err(e) = try_init_pools() { - println!("cargo:warning=Failed to initialize pools: {}", e); - println!("cargo:warning=This is not a build error - pools will be initialized when tests run"); - } else { - println!("cargo:warning=Successfully initialized database pools"); - } -} - -fn ensure_test_env_exists() { - let test_env_path = Path::new(".env.test"); - - // Only create if it doesn't exist - if !test_env_path.exists() { - println!("cargo:warning=Creating default .env.test file"); - - let default_content = r#" -# Test Environment Configuration -TEST_ENV=test -TEST_DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres -TEST_POOLER_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres -TEST_REDIS_URL=redis://localhost:6379 -TEST_DATABASE_POOL_SIZE=10 -TEST_SQLX_POOL_SIZE=10 -TEST_LOG=true -TEST_LOG_LEVEL=debug - "#.trim(); - - fs::write(test_env_path, default_content) - .expect("Failed to create default .env.test file"); - - println!("cargo:warning=Created default .env.test file"); - } -} - -fn load_env_file() { - // Try loading .env.test first, then fall back to .env - if Path::new(".env.test").exists() { - if let Ok(_) = dotenv::from_filename(".env.test") { - println!("cargo:warning=Loaded environment from .env.test"); - } - } else if let Ok(_) = dotenv::dotenv() { - println!("cargo:warning=Loaded environment from .env"); - } - - // Override DATABASE_URL to use TEST_DATABASE_URL for tests - if let Ok(test_db_url) = env::var("TEST_DATABASE_URL") { - env::set_var("DATABASE_URL", test_db_url); - println!("cargo:warning=Using TEST_DATABASE_URL for DATABASE_URL"); - } - - // Override POOLER_URL to use TEST_POOLER_URL for tests - if let Ok(test_pooler_url) = env::var("TEST_POOLER_URL") { - env::set_var("POOLER_URL", test_pooler_url); - println!("cargo:warning=Using TEST_POOLER_URL for POOLER_URL"); - } - - // Override REDIS_URL to use TEST_REDIS_URL for tests - if let Ok(test_redis_url) = env::var("TEST_REDIS_URL") { - env::set_var("REDIS_URL", test_redis_url); - println!("cargo:warning=Using TEST_REDIS_URL for REDIS_URL"); - } - - // Override pool sizes to prevent excessive connections in test environment - if let Ok(test_pool_size) = env::var("TEST_DATABASE_POOL_SIZE") { - env::set_var("DATABASE_POOL_SIZE", test_pool_size); - } - - if let Ok(test_sqlx_pool_size) = env::var("TEST_SQLX_POOL_SIZE") { - env::set_var("SQLX_POOL_SIZE", test_sqlx_pool_size); - } -} - -fn try_init_pools() -> Result<(), String> { - // Create a runtime for async operations - let runtime = match tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() { - Ok(rt) => rt, - Err(e) => return Err(e.to_string()), - }; - - // Try to initialize pools through the testkit's internal function - // This won't fail the build if it can't connect to the database - runtime.block_on(async { - match database::pool::init_pools().await { - Ok(_) => Ok(()), - Err(e) => Err(e.to_string()), - } - }) -} \ No newline at end of file diff --git a/api/testkit/src/lib.rs b/api/testkit/src/lib.rs deleted file mode 100644 index c2f02d4ff..000000000 --- a/api/testkit/src/lib.rs +++ /dev/null @@ -1,90 +0,0 @@ -use anyhow::Result; -use dotenv::dotenv; -use std::path::Path; -use std::sync::Once; -use once_cell::sync::OnceCell; - -static ENV_INIT: Once = Once::new(); -static POOL_INIT: OnceCell<()> = OnceCell::new(); - -/// Initialize the test environment by setting up .env.test -/// This should only be used internally or by build.rs -fn init_test_env() { - ENV_INIT.call_once(|| { - // Try loading .env.test first, then fall back to .env - if Path::new(".env.test").exists() { - dotenv::from_filename(".env.test").ok(); - } else { - dotenv().ok(); - } - - // Override DATABASE_URL to use TEST_DATABASE_URL for tests - if let Ok(test_db_url) = std::env::var("TEST_DATABASE_URL") { - std::env::set_var("DATABASE_URL", test_db_url); - } - - // Override POOLER_URL to use TEST_POOLER_URL for tests - if let Ok(test_pooler_url) = std::env::var("TEST_POOLER_URL") { - std::env::set_var("POOLER_URL", test_pooler_url); - } - - // Override REDIS_URL to use TEST_REDIS_URL for tests - if let Ok(test_redis_url) = std::env::var("TEST_REDIS_URL") { - std::env::set_var("REDIS_URL", test_redis_url); - } - - // Override pool sizes to prevent excessive connections in test environment - if let Ok(test_pool_size) = std::env::var("TEST_DATABASE_POOL_SIZE") { - std::env::set_var("DATABASE_POOL_SIZE", test_pool_size); - } - - if let Ok(test_sqlx_pool_size) = std::env::var("TEST_SQLX_POOL_SIZE") { - std::env::set_var("SQLX_POOL_SIZE", test_sqlx_pool_size); - } - }); -} - -// This function is only for internal use by build.rs - the pools should be -// initialized at build time, not during test runtime -#[doc(hidden)] -pub async fn _internal_init_pools() -> Result<()> { - // Setup the environment first - init_test_env(); - - // Only initialize pools once - if POOL_INIT.get().is_some() { - return Ok(()); - } - - // Use the database crate's init_pools function - let result = match database::pool::init_pools().await { - Ok(_) => { - // Success case - cache the result - let _ = POOL_INIT.set(()); - Ok(()) - }, - Err(e) => { - // Log the error but still cache the attempt to prevent repeated tries - tracing::error!("Failed to initialize test pools: {}", e); - let _ = POOL_INIT.set(()); - Err(e) - } - }; - - result -} - -// No need for pool accessor functions - users should access pools directly -// through the database library (database::pool::get_pg_pool(), etc.) - -/// Generate a unique test ID - useful for creating test resources -pub fn test_id() -> String { - uuid::Uuid::new_v4().to_string() -} - -/// Clean up a test database connection - useful in test teardown -pub async fn cleanup_connection() -> Result<()> { - // This is a placeholder for any future cleanup that might be needed - // Currently the pools handle their own cleanup - Ok(()) -} \ No newline at end of file