Files
foxhunt/database/tests/comprehensive_database_tests.rs
jgrusewski 6bd5b18465 🔧 Wave 33: Test Compilation Improvements - 57 errors remaining
**Progress: 1,178 → 57 test errors (95% reduction)**

## Status Summary
-  Production code: Compiles cleanly (0 errors)
- ⚠️  Test code: 57 errors remain (massive improvement)
- ⚙️  All services build successfully
- 📊 Warning count: 253 (target: <20) - AGENTS WILL FIX

## Remaining Test Errors (57 total)
### Primary Issues:
1. 23× E0308 mismatched types
2. 17× E0433 undeclared Decimal
3. 15× E0433 compliance module not found
4. 6× E0624 private method access
5. Various import and type issues

## Next Phase: Wave 33-2
Launch 10+ parallel agents to:
- Fix remaining 57 test compilation errors
- Reduce 253 warnings to <20
- Achieve 95% test coverage
- Ensure all tests pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-01 21:24:28 +02:00

471 lines
14 KiB
Rust

//! Comprehensive test coverage for database module
//! Target: 95%+ coverage for database operations and error handling
use config::database::{DatabaseConfig, PoolConfig, TransactionConfig};
use database::*;
#[cfg(test)]
mod database_error_tests {
use super::*;
#[test]
fn test_database_error_types() {
let connection_error = DatabaseError::Connection {
message: "Failed to connect".to_string(),
};
assert!(connection_error.to_string().contains("Connection failed"));
let query_error = DatabaseError::Query {
query: "SELECT *".to_string(),
message: "Invalid SQL".to_string(),
};
assert!(query_error.to_string().contains("Query execution failed"));
let transaction_error = DatabaseError::Transaction {
message: "Commit failed".to_string(),
};
assert!(transaction_error.to_string().contains("Transaction error"));
let pool_error = DatabaseError::ConnectionPool {
message: "Pool exhausted".to_string(),
};
assert!(pool_error.to_string().contains("Connection pool error"));
}
#[test]
fn test_database_error_debug() {
let error = DatabaseError::Connection {
message: "Test".to_string(),
};
let debug_str = format!("{:?}", error);
assert!(debug_str.contains("Connection"));
}
#[test]
fn test_database_error_retryable() {
let timeout_error = DatabaseError::Timeout {
operation: "query".to_string(),
timeout_secs: 30,
};
assert!(timeout_error.is_retryable());
let validation_error = DatabaseError::Validation {
field: "email".to_string(),
message: "invalid format".to_string(),
};
assert!(!validation_error.is_retryable());
}
#[test]
fn test_database_error_category() {
let error = DatabaseError::Configuration {
message: "Bad config".to_string(),
};
assert_eq!(error.category(), "configuration");
let error = DatabaseError::Migration {
message: "Migration failed".to_string(),
};
assert_eq!(error.category(), "migration");
}
}
#[cfg(test)]
mod pool_config_tests {
use super::*;
#[test]
fn test_pool_config_defaults() {
let config = PoolConfig::default();
assert!(config.max_connections > 0);
assert!(config.min_connections > 0);
assert!(config.max_connections >= config.min_connections);
assert!(config.health_check_enabled);
}
#[test]
fn test_pool_config_validation() {
let config = PoolConfig {
max_connections: 20,
min_connections: 5,
acquire_timeout_secs: 30,
idle_timeout_secs: 600,
max_lifetime_secs: 1800,
test_before_acquire: true,
database_url: "postgresql://localhost/test".to_string(),
health_check_enabled: true,
health_check_interval_secs: 60,
};
// Validate logical constraints
assert!(config.max_connections > config.min_connections);
assert!(config.acquire_timeout_secs > 0);
assert!(config.idle_timeout_secs > 0);
assert!(config.max_lifetime_secs > config.idle_timeout_secs);
}
#[test]
fn test_pool_config_serialization() {
let config = PoolConfig {
max_connections: 10,
min_connections: 2,
acquire_timeout_secs: 15,
idle_timeout_secs: 300,
max_lifetime_secs: 900,
test_before_acquire: false,
database_url: "postgresql://localhost/test".to_string(),
health_check_enabled: false,
health_check_interval_secs: 30,
};
let serialized = serde_json::to_string(&config).expect("Serialization failed");
let deserialized: PoolConfig =
serde_json::from_str(&serialized).expect("Deserialization failed");
assert_eq!(config.max_connections, deserialized.max_connections);
assert_eq!(config.test_before_acquire, deserialized.test_before_acquire);
}
#[test]
fn test_pool_config_edge_cases() {
// Test minimum possible values
let min_config = PoolConfig {
max_connections: 1,
min_connections: 1,
acquire_timeout_secs: 1,
idle_timeout_secs: 1,
max_lifetime_secs: 1,
test_before_acquire: false,
database_url: "postgresql://localhost/test".to_string(),
health_check_enabled: false,
health_check_interval_secs: 1,
};
assert_eq!(min_config.max_connections, min_config.min_connections);
// Test large values
let large_config = PoolConfig {
max_connections: 1000,
min_connections: 100,
acquire_timeout_secs: 3600,
idle_timeout_secs: 7200,
max_lifetime_secs: 86400,
test_before_acquire: true,
database_url: "postgresql://localhost/test".to_string(),
health_check_enabled: true,
health_check_interval_secs: 300,
};
assert!(large_config.max_connections > 100);
}
}
#[cfg(test)]
mod transaction_config_tests {
use super::*;
#[test]
fn test_transaction_config_defaults() {
let config = TransactionConfig::default();
assert!(config.max_retries > 0);
assert!(config.retry_delay_ms > 0);
assert!(config.enable_retry);
}
#[test]
fn test_transaction_config_validation() {
let config = TransactionConfig {
isolation_level: "READ_COMMITTED".to_string(),
timeout: std::time::Duration::from_secs(30),
default_timeout_secs: 30,
enable_retry: true,
max_retries: 5,
retry_delay_ms: 100,
max_savepoints: 10,
};
assert!(config.max_retries > 0);
assert!(config.retry_delay_ms > 0);
assert!(!config.isolation_level.is_empty());
assert!(config.enable_retry);
}
#[test]
fn test_transaction_config_serialization() {
let config = TransactionConfig {
isolation_level: "SERIALIZABLE".to_string(),
timeout: std::time::Duration::from_secs(60),
default_timeout_secs: 60,
enable_retry: false,
max_retries: 3,
retry_delay_ms: 50,
max_savepoints: 5,
};
let serialized = serde_json::to_string(&config).expect("Serialization failed");
let deserialized: TransactionConfig =
serde_json::from_str(&serialized).expect("Deserialization failed");
assert_eq!(config.isolation_level, deserialized.isolation_level);
assert_eq!(config.enable_retry, deserialized.enable_retry);
}
#[test]
fn test_transaction_isolation_levels() {
let levels = vec![
"READ_UNCOMMITTED",
"READ_COMMITTED",
"REPEATABLE_READ",
"SERIALIZABLE",
];
for level in levels {
let config = TransactionConfig {
isolation_level: level.to_string(),
timeout: std::time::Duration::from_secs(30),
default_timeout_secs: 30,
enable_retry: true,
max_retries: 3,
retry_delay_ms: 100,
max_savepoints: 10,
};
assert_eq!(config.isolation_level, level);
}
}
}
#[cfg(test)]
mod query_builder_tests {
use super::*;
#[test]
fn test_query_builder_basic() {
let query = Database::select(&["id", "name", "value"])
.from("test_table")
.where_raw("active = true")
.build()
.expect("Query build failed");
let sql = query.sql();
assert!(sql.contains("SELECT"));
assert!(sql.contains("FROM test_table"));
assert!(sql.contains("WHERE"));
}
#[test]
fn test_query_builder_with_joins() {
let query = Database::select(&["a.id", "b.name"])
.from("table_a a")
.inner_join("table_b b", "a.id = b.a_id")
.where_raw("a.status = 'active'")
.build()
.expect("Query build failed");
let sql = query.sql();
assert!(sql.contains("INNER JOIN"));
assert!(sql.contains("a.id = b.a_id"));
}
#[test]
fn test_query_builder_with_order_and_limit() {
let query = Database::select(&["*"])
.from("orders")
.where_raw("amount > 1000")
.order_by("created_at", OrderDirection::Desc)
.limit(10)
.build()
.expect("Query build failed");
let sql = query.sql();
assert!(sql.contains("ORDER BY"));
assert!(sql.contains("LIMIT 10"));
}
#[test]
fn test_insert_builder() {
let query = Database::insert("users")
.values(&[("name", "Alice"), ("email", "alice@example.com")])
.build()
.expect("Query build failed");
let sql = query.sql();
assert!(sql.contains("INSERT INTO users"));
}
#[test]
fn test_update_builder() {
let query = Database::update("users")
.set("name", "Bob")
.where_eq("id", 1)
.build()
.expect("Query build failed");
let sql = query.sql();
assert!(sql.contains("UPDATE users"));
assert!(sql.contains("SET"));
assert!(sql.contains("WHERE"));
}
#[test]
fn test_delete_builder() {
let query = Database::delete("users")
.where_raw("id = 1")
.build()
.expect("Query build failed");
let sql = query.sql();
assert!(sql.contains("DELETE FROM users"));
assert!(sql.contains("WHERE"));
}
}
#[cfg(test)]
mod database_config_tests {
use super::*;
#[test]
fn test_database_config_new() {
let config = DatabaseConfig::new();
assert!(!config.url.is_empty());
assert!(config.max_connections > 0);
assert!(config.min_connections > 0);
assert!(config.max_connections >= config.min_connections);
}
#[test]
fn test_database_config_validation() {
let mut config = DatabaseConfig::new();
assert!(config.validate().is_ok());
config.url = String::new();
assert!(config.validate().is_err());
}
#[test]
fn test_database_config_serialization() {
let config = DatabaseConfig::new();
let serialized = serde_json::to_string(&config).expect("Serialization failed");
let deserialized: DatabaseConfig =
serde_json::from_str(&serialized).expect("Deserialization failed");
assert_eq!(config.url, deserialized.url);
}
#[test]
fn test_database_config_pool_and_transaction() {
let config = DatabaseConfig::new();
assert!(config.pool.max_connections > 0);
assert!(config.transaction.max_retries > 0);
}
}
#[cfg(test)]
mod edge_case_tests {
use super::*;
#[test]
fn test_zero_max_connections() {
let config = PoolConfig {
max_connections: 0, // Invalid but should handle gracefully
min_connections: 0,
acquire_timeout_secs: 30,
idle_timeout_secs: 600,
max_lifetime_secs: 1800,
test_before_acquire: false,
database_url: "postgresql://localhost/test".to_string(),
health_check_enabled: false,
health_check_interval_secs: 60,
};
assert_eq!(config.max_connections, 0);
// Real implementation should validate this
}
#[test]
fn test_negative_timeout() {
// Using i64 to test negative values
let timeout = -1;
assert!(timeout < 0);
// System should handle negative timeouts (convert to u64 or error)
}
#[test]
fn test_empty_database_url() {
let mut config = DatabaseConfig::new();
config.url = String::new();
assert!(config.url.is_empty());
// Should be caught by validation
assert!(config.validate().is_err());
}
#[test]
fn test_constraint_violation_error() {
let error = DatabaseError::ConstraintViolation {
constraint: "unique_email".to_string(),
message: "Email already exists".to_string(),
};
assert!(error.to_string().contains("Constraint violation"));
assert_eq!(error.category(), "constraint_violation");
}
#[test]
fn test_not_found_error() {
let error = DatabaseError::NotFound {
resource_type: "User".to_string(),
identifier: "id=123".to_string(),
};
assert!(error.to_string().contains("Resource not found"));
assert_eq!(error.category(), "not_found");
}
#[test]
fn test_serialization_error() {
let error = DatabaseError::Serialization {
message: "Failed to serialize JSON".to_string(),
};
assert!(error.to_string().contains("Serialization error"));
assert_eq!(error.category(), "serialization");
}
}
#[cfg(test)]
mod query_builder_advanced_tests {
use super::*;
#[test]
fn test_select_with_multiple_where_clauses() {
let query = Database::select(&["id"])
.from("products")
.where_raw("price > 100")
.where_raw("stock > 0")
.where_raw("category = 'electronics'")
.build()
.expect("Query build failed");
let sql = query.sql();
assert!(sql.contains("WHERE"));
// Should combine multiple where clauses
assert!(sql.contains("price > 100"));
}
#[test]
fn test_insert_returning() {
let query = Database::insert("users")
.values(&[("name", "Alice"), ("email", "alice@example.com")])
.returning("id")
.build()
.expect("Query build failed");
let sql = query.sql();
assert!(sql.contains("RETURNING"));
}
#[test]
fn test_update_with_multiple_sets() {
let query = Database::update("users")
.set("name", "Bob")
.set("email", "bob@example.com")
.where_eq("id", 1)
.build()
.expect("Query build failed");
let sql = query.sql();
assert!(sql.contains("UPDATE users"));
}
}