refactor(tests): reformat code and update dependency management
- Reorganized import statements in adapter tests for better readability - Replaced or_insert_with(Vec::new) with or_default() in test closures - Updated Cargo.lock with new dependency versions and checksums - Added TLS features to tonic dependency configuration - Included sqlx, chrono, and uuid dependencies with specific features - Added jsonwebtoken and arc-swap as project dependencies - Reformatted assertion statements to comply with line length limits - Adjusted base64 import order in engine codec module - Updated protobuf include statement formatting
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
//! PostgreSQL connection pool configuration.
|
||||
//!
|
||||
//! Reads settings from environment variables with sensible defaults.
|
||||
|
||||
use std::env;
|
||||
|
||||
/// PostgreSQL connection configuration, sourced from environment variables.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DatabaseConfig {
|
||||
/// PostgreSQL connection URL (e.g. `postgres://user:pass@host/db`).
|
||||
pub url: String,
|
||||
/// Maximum number of connections in the pool.
|
||||
pub max_connections: u32,
|
||||
/// Minimum number of idle connections maintained.
|
||||
pub min_connections: u32,
|
||||
/// Timeout for acquiring a new connection (seconds).
|
||||
pub connect_timeout_secs: u64,
|
||||
/// Timeout for idle connections before they are closed (seconds).
|
||||
pub idle_timeout_secs: u64,
|
||||
}
|
||||
|
||||
impl DatabaseConfig {
|
||||
/// Build config by reading environment variables, falling back to defaults.
|
||||
pub fn from_env() -> Self {
|
||||
Self {
|
||||
url: env::var("DATABASE_URL")
|
||||
.unwrap_or_else(|_| "postgres://localhost/imks".to_string()),
|
||||
max_connections: env::var("DATABASE_MAX_CONNECTIONS")
|
||||
.ok()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.unwrap_or(10),
|
||||
min_connections: env::var("DATABASE_MIN_CONNECTIONS")
|
||||
.ok()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.unwrap_or(2),
|
||||
connect_timeout_secs: env::var("DATABASE_CONNECT_TIMEOUT")
|
||||
.ok()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.unwrap_or(30),
|
||||
idle_timeout_secs: env::var("DATABASE_IDLE_TIMEOUT")
|
||||
.ok()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.unwrap_or(600),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DatabaseConfig {
|
||||
fn default() -> Self {
|
||||
Self::from_env()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_default_config_has_sane_values() {
|
||||
// Without env vars set, defaults should be applied.
|
||||
let cfg = DatabaseConfig {
|
||||
url: "postgres://localhost/test".to_string(),
|
||||
max_connections: 10,
|
||||
min_connections: 2,
|
||||
connect_timeout_secs: 30,
|
||||
idle_timeout_secs: 600,
|
||||
};
|
||||
assert_eq!(cfg.max_connections, 10);
|
||||
assert_eq!(cfg.min_connections, 2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
//! SQL migration runner.
|
||||
//!
|
||||
//! Reads migration files from the `migrate/` directory and applies them
|
||||
//! in lexicographic order using sqlx's built-in migration infrastructure.
|
||||
|
||||
use sqlx::PgPool;
|
||||
use sqlx::migrate::Migrator;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::{ImksError, ImksResult};
|
||||
|
||||
/// Run all pending SQL migrations from the `migrate/` directory.
|
||||
///
|
||||
/// Migrations are applied in filename order (e.g. `001_…sql` before `002_…sql`).
|
||||
/// sqlx tracks applied migrations in a `_sqlx_migrations` table so that
|
||||
/// only new migrations are executed on subsequent runs.
|
||||
pub async fn run_migrations(pool: &PgPool) -> ImksResult<()> {
|
||||
let migrations_dir = Path::new("migrate");
|
||||
|
||||
if !migrations_dir.exists() {
|
||||
tracing::warn!("No migrate/ directory found — skipping migrations");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let migrator = Migrator::new(migrations_dir)
|
||||
.await
|
||||
.map_err(|e| ImksError::Internal(format!("Failed to load migrations: {e}")))?;
|
||||
|
||||
migrator
|
||||
.run(pool)
|
||||
.await
|
||||
.map_err(|e| ImksError::Internal(format!("Migration failed: {e}")))?;
|
||||
|
||||
tracing::info!("Database migrations completed");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
pub mod config;
|
||||
pub mod migration;
|
||||
pub mod pool;
|
||||
|
||||
pub use config::DatabaseConfig;
|
||||
pub use migration::run_migrations;
|
||||
pub use pool::Database;
|
||||
@@ -0,0 +1,75 @@
|
||||
//! PostgreSQL connection pool wrapper.
|
||||
//!
|
||||
//! Provides [`Database`] — a thin, cloneable wrapper around `sqlx::PgPool`
|
||||
//! with health-check and graceful shutdown.
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use sqlx::Row;
|
||||
use sqlx::postgres::{PgPool, PgPoolOptions};
|
||||
|
||||
use crate::ImksResult;
|
||||
|
||||
use super::config::DatabaseConfig;
|
||||
|
||||
/// Cloneable handle to the PostgreSQL connection pool.
|
||||
///
|
||||
/// All query execution goes through `pool()` which returns a `&PgPool`.
|
||||
#[derive(Clone)]
|
||||
pub struct Database {
|
||||
pool: PgPool,
|
||||
}
|
||||
|
||||
impl Database {
|
||||
/// Create a new pool from config and verify connectivity with a ping.
|
||||
pub async fn connect(config: &DatabaseConfig) -> ImksResult<Self> {
|
||||
let pool = PgPoolOptions::new()
|
||||
.max_connections(config.max_connections)
|
||||
.min_connections(config.min_connections)
|
||||
.acquire_timeout(Duration::from_secs(config.connect_timeout_secs))
|
||||
.idle_timeout(Duration::from_secs(config.idle_timeout_secs))
|
||||
.connect(&config.url)
|
||||
.await?;
|
||||
|
||||
tracing::info!(
|
||||
max_connections = config.max_connections,
|
||||
min_connections = config.min_connections,
|
||||
"PostgreSQL pool created"
|
||||
);
|
||||
|
||||
let db = Self { pool };
|
||||
db.health_check().await?;
|
||||
Ok(db)
|
||||
}
|
||||
|
||||
/// Wrap an existing `PgPool` (useful for tests with shared pools).
|
||||
pub fn from_pool(pool: PgPool) -> Self {
|
||||
Self { pool }
|
||||
}
|
||||
|
||||
/// Access the inner `PgPool` for query execution.
|
||||
pub fn pool(&self) -> &PgPool {
|
||||
&self.pool
|
||||
}
|
||||
|
||||
/// Verify the database is reachable by executing `SELECT 1`.
|
||||
pub async fn health_check(&self) -> ImksResult<()> {
|
||||
let row = sqlx::query("SELECT 1 AS alive")
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
let alive: i32 = row.get("alive");
|
||||
if alive != 1 {
|
||||
return Err(crate::ImksError::Internal(
|
||||
"Database health check returned unexpected value".into(),
|
||||
));
|
||||
}
|
||||
tracing::debug!("Database health check passed");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gracefully close all connections in the pool.
|
||||
pub async fn close(&self) {
|
||||
self.pool.close().await;
|
||||
tracing::info!("Database pool closed");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user