Files
foxhunt/database/tests/unit_tests.rs
jgrusewski 030a15ee05 🔧 Emergency Fix: Resolve catastrophic _i32 suffix corruption (463→0 errors)
- Fixed systematic array indexing corruption: [0_i32] → [0]
- Fixed numeric literal suffixes across 835 files
- Fixed iterator patterns on RwLockReadGuard (.iter() required)
- Fixed float type annotations (365.25_f64 for sqrt)
- Fixed missing semicolons in position manager
- Fixed reference dereferencing in data loader

Root cause: Mass refactoring incorrectly added _i32 suffixes to array indices
Impact: Complete compilation failure (463 errors)
Resolution: Automated regex + targeted fixes
Result: 100% compilation success (0 errors)

Validated: cargo check --workspace passes
Ready for: Production deployment
2025-10-10 23:05:26 +02:00

592 lines
17 KiB
Rust

//! Unit tests for the database package
//!
//! These tests focus on testing logic without requiring a live database connection.
use database::{DatabaseError, error::ErrorSeverity, OrderDirection, QueryBuilder};
use config::database::{DatabaseConfig, PoolConfig, TransactionConfig};
mod error_tests {
use super::*;
#[test]
fn test_error_is_retryable_connection_pool() {
let error = DatabaseError::ConnectionPool {
message: "Pool exhausted".to_string(),
};
assert!(error.is_retryable(), "ConnectionPool errors should be retryable");
}
#[test]
fn test_error_is_retryable_connection() {
let error = DatabaseError::Connection {
message: "Connection refused".to_string(),
};
assert!(error.is_retryable(), "Connection errors should be retryable");
}
#[test]
fn test_error_is_retryable_timeout() {
let error = DatabaseError::Timeout {
operation: "query".to_string(),
timeout_secs: 30,
};
assert!(error.is_retryable(), "Timeout errors should be retryable");
}
#[test]
fn test_error_is_retryable_query_with_connection_message() {
let error = DatabaseError::Query {
query: "SELECT 1".to_string(),
message: "connection reset".to_string(),
};
assert!(error.is_retryable(), "Query errors with connection issues should be retryable");
}
#[test]
fn test_error_is_not_retryable_validation() {
let error = DatabaseError::Validation {
field: "email".to_string(),
message: "Invalid format".to_string(),
};
assert!(!error.is_retryable(), "Validation errors should not be retryable");
}
#[test]
fn test_error_is_not_retryable_constraint_violation() {
let error = DatabaseError::ConstraintViolation {
constraint: "unique_email".to_string(),
message: "Duplicate key".to_string(),
};
assert!(!error.is_retryable(), "Constraint violations should not be retryable");
}
#[test]
fn test_error_is_not_retryable_not_found() {
let error = DatabaseError::NotFound {
resource_type: "user".to_string(),
identifier: "123".to_string(),
};
assert!(!error.is_retryable(), "NotFound errors should not be retryable");
}
#[test]
fn test_error_category_connection_pool() {
let error = DatabaseError::ConnectionPool {
message: "Pool error".to_string(),
};
assert_eq!(error.category(), "connection_pool");
}
#[test]
fn test_error_category_connection() {
let error = DatabaseError::Connection {
message: "Connection error".to_string(),
};
assert_eq!(error.category(), "connection");
}
#[test]
fn test_error_category_query() {
let error = DatabaseError::Query {
query: "SELECT 1".to_string(),
message: "Query error".to_string(),
};
assert_eq!(error.category(), "query");
}
#[test]
fn test_error_category_transaction() {
let error = DatabaseError::Transaction {
message: "Transaction error".to_string(),
};
assert_eq!(error.category(), "transaction");
}
#[test]
fn test_error_category_configuration() {
let error = DatabaseError::Configuration {
message: "Config error".to_string(),
};
assert_eq!(error.category(), "configuration");
}
#[test]
fn test_error_category_migration() {
let error = DatabaseError::Migration {
message: "Migration error".to_string(),
};
assert_eq!(error.category(), "migration");
}
#[test]
fn test_error_category_serialization() {
let error = DatabaseError::Serialization {
message: "Serialization error".to_string(),
};
assert_eq!(error.category(), "serialization");
}
#[test]
fn test_error_category_timeout() {
let error = DatabaseError::Timeout {
operation: "query".to_string(),
timeout_secs: 30,
};
assert_eq!(error.category(), "timeout");
}
#[test]
fn test_error_category_validation() {
let error = DatabaseError::Validation {
field: "email".to_string(),
message: "Invalid".to_string(),
};
assert_eq!(error.category(), "validation");
}
#[test]
fn test_error_category_not_found() {
let error = DatabaseError::NotFound {
resource_type: "user".to_string(),
identifier: "123".to_string(),
};
assert_eq!(error.category(), "not_found");
}
#[test]
fn test_error_category_constraint_violation() {
let error = DatabaseError::ConstraintViolation {
constraint: "pk".to_string(),
message: "Violation".to_string(),
};
assert_eq!(error.category(), "constraint_violation");
}
#[test]
fn test_error_category_unknown() {
let error = DatabaseError::Unknown {
message: "Unknown error".to_string(),
};
assert_eq!(error.category(), "unknown");
}
#[test]
fn test_error_severity_critical() {
let config_error = DatabaseError::Configuration {
message: "Config".to_string(),
};
assert_eq!(config_error.severity(), ErrorSeverity::Critical);
let migration_error = DatabaseError::Migration {
message: "Migration".to_string(),
};
assert_eq!(migration_error.severity(), ErrorSeverity::Critical);
}
#[test]
fn test_error_severity_high() {
let pool_error = DatabaseError::ConnectionPool {
message: "Pool".to_string(),
};
assert_eq!(pool_error.severity(), ErrorSeverity::High);
let conn_error = DatabaseError::Connection {
message: "Connection".to_string(),
};
assert_eq!(conn_error.severity(), ErrorSeverity::High);
let tx_error = DatabaseError::Transaction {
message: "Transaction".to_string(),
};
assert_eq!(tx_error.severity(), ErrorSeverity::High);
}
#[test]
fn test_error_severity_medium() {
let constraint_error = DatabaseError::ConstraintViolation {
constraint: "pk".to_string(),
message: "Violation".to_string(),
};
assert_eq!(constraint_error.severity(), ErrorSeverity::Medium);
let query_error = DatabaseError::Query {
query: "SELECT 1".to_string(),
message: "Error".to_string(),
};
assert_eq!(query_error.severity(), ErrorSeverity::Medium);
let timeout_error = DatabaseError::Timeout {
operation: "query".to_string(),
timeout_secs: 30,
};
assert_eq!(timeout_error.severity(), ErrorSeverity::Medium);
let unknown_error = DatabaseError::Unknown {
message: "Unknown".to_string(),
};
assert_eq!(unknown_error.severity(), ErrorSeverity::Medium);
}
#[test]
fn test_error_severity_low() {
let validation_error = DatabaseError::Validation {
field: "email".to_string(),
message: "Invalid".to_string(),
};
assert_eq!(validation_error.severity(), ErrorSeverity::Low);
let not_found_error = DatabaseError::NotFound {
resource_type: "user".to_string(),
identifier: "123".to_string(),
};
assert_eq!(not_found_error.severity(), ErrorSeverity::Low);
let serialization_error = DatabaseError::Serialization {
message: "Serialization failed".to_string(),
};
assert_eq!(serialization_error.severity(), ErrorSeverity::Low);
}
#[test]
fn test_error_severity_display() {
assert_eq!(format!("{}", ErrorSeverity::Low), "LOW");
assert_eq!(format!("{}", ErrorSeverity::Medium), "MEDIUM");
assert_eq!(format!("{}", ErrorSeverity::High), "HIGH");
assert_eq!(format!("{}", ErrorSeverity::Critical), "CRITICAL");
}
#[test]
fn test_error_display_connection_pool() {
let error = DatabaseError::ConnectionPool {
message: "Test message".to_string(),
};
let display = format!("{}", error);
assert!(display.contains("Connection pool error"));
assert!(display.contains("Test message"));
}
#[test]
fn test_error_display_timeout() {
let error = DatabaseError::Timeout {
operation: "query_execution".to_string(),
timeout_secs: 30,
};
let display = format!("{}", error);
assert!(display.contains("Operation timeout"));
assert!(display.contains("query_execution"));
assert!(display.contains("30"));
}
#[test]
fn test_error_display_validation() {
let error = DatabaseError::Validation {
field: "email".to_string(),
message: "Invalid format".to_string(),
};
let display = format!("{}", error);
assert!(display.contains("Validation error"));
assert!(display.contains("email"));
assert!(display.contains("Invalid format"));
}
#[test]
fn test_error_display_not_found() {
let error = DatabaseError::NotFound {
resource_type: "user".to_string(),
identifier: "id=123".to_string(),
};
let display = format!("{}", error);
assert!(display.contains("Resource not found"));
assert!(display.contains("user"));
assert!(display.contains("id=123"));
}
#[test]
fn test_error_display_constraint_violation() {
let error = DatabaseError::ConstraintViolation {
constraint: "unique_email".to_string(),
message: "Duplicate key".to_string(),
};
let display = format!("{}", error);
assert!(display.contains("Constraint violation"));
assert!(display.contains("unique_email"));
assert!(display.contains("Duplicate key"));
}
}
mod query_builder_tests {
use super::*;
#[test]
fn test_query_builder_new() {
let builder = QueryBuilder::new();
assert_eq!(builder.sql(), "");
}
#[test]
fn test_query_builder_default() {
let builder = QueryBuilder::default();
assert_eq!(builder.sql(), "");
}
#[test]
fn test_select_builder_basic() {
let query = QueryBuilder::select(&["id", "name"])
.from("users")
.build()
.unwrap();
let sql = query.sql();
assert!(sql.contains("SELECT id, name"));
assert!(sql.contains("FROM users"));
}
#[test]
fn test_select_builder_with_where() {
let query = QueryBuilder::select(&["*"])
.from("users")
.where_eq("active", true)
.build()
.unwrap();
let sql = query.sql();
assert!(sql.contains("WHERE active = $1"));
}
#[test]
fn test_select_builder_with_order_by() {
let query = QueryBuilder::select(&["*"])
.from("users")
.order_by("created_at", OrderDirection::Desc)
.build()
.unwrap();
let sql = query.sql();
assert!(sql.contains("ORDER BY created_at DESC"));
}
#[test]
fn test_select_builder_with_limit_offset() {
let query = QueryBuilder::select(&["*"])
.from("users")
.limit(10)
.offset(20)
.build()
.unwrap();
let sql = query.sql();
assert!(sql.contains("LIMIT 10"));
assert!(sql.contains("OFFSET 20"));
}
#[test]
fn test_select_builder_with_group_by() {
let query = QueryBuilder::select(&["status", "COUNT(*)"])
.from("users")
.group_by("status")
.build()
.unwrap();
let sql = query.sql();
assert!(sql.contains("GROUP BY status"));
}
#[test]
fn test_select_builder_with_having() {
let query = QueryBuilder::select(&["status", "COUNT(*)"])
.from("users")
.group_by("status")
.having("COUNT(*) > 10")
.build()
.unwrap();
let sql = query.sql();
assert!(sql.contains("HAVING COUNT(*) > 10"));
}
#[test]
fn test_select_builder_with_inner_join() {
let query = QueryBuilder::select(&["u.name", "p.title"])
.from("users u")
.inner_join("posts p", "u.id = p.user_id")
.build()
.unwrap();
let sql = query.sql();
assert!(sql.contains("INNER JOIN posts p ON u.id = p.user_id"));
}
#[test]
fn test_select_builder_with_left_join() {
let query = QueryBuilder::select(&["u.name", "p.title"])
.from("users u")
.left_join("posts p", "u.id = p.user_id")
.build()
.unwrap();
let sql = query.sql();
assert!(sql.contains("LEFT JOIN posts p ON u.id = p.user_id"));
}
#[test]
fn test_select_builder_missing_from() {
let result = QueryBuilder::select(&["id", "name"]).build();
assert!(result.is_err(), "SELECT without FROM should fail");
}
#[test]
fn test_insert_builder_basic() {
let query = QueryBuilder::insert("users")
.values(&[("name", "John"), ("email", "john@example.com")])
.build()
.unwrap();
let sql = query.sql();
assert!(sql.contains("INSERT INTO users"));
assert!(sql.contains("(name, email)"));
assert!(sql.contains("VALUES ($1, $2)"));
}
#[test]
fn test_insert_builder_with_returning() {
let query = QueryBuilder::insert("users")
.values(&[("name", "John")])
.returning("id")
.build()
.unwrap();
let sql = query.sql();
assert!(sql.contains("RETURNING id"));
}
#[test]
fn test_insert_builder_with_on_conflict() {
let query = QueryBuilder::insert("users")
.values(&[("email", "test@example.com")])
.on_conflict("(email) DO NOTHING")
.build()
.unwrap();
let sql = query.sql();
assert!(sql.contains("ON CONFLICT (email) DO NOTHING"));
}
#[test]
fn test_update_builder_basic() {
let query = QueryBuilder::update("users")
.set("name", "Jane")
.where_eq("id", 1)
.build()
.unwrap();
let sql = query.sql();
assert!(sql.contains("UPDATE users"));
assert!(sql.contains("SET name = $1"));
assert!(sql.contains("WHERE id = $2"));
}
#[test]
fn test_update_builder_with_returning() {
let query = QueryBuilder::update("users")
.set("name", "Jane")
.where_eq("id", 1)
.returning("*")
.build()
.unwrap();
let sql = query.sql();
assert!(sql.contains("RETURNING *"));
}
#[test]
fn test_update_builder_missing_set() {
let result = QueryBuilder::update("users")
.where_eq("id", 1)
.build();
assert!(result.is_err(), "UPDATE without SET should fail");
}
#[test]
fn test_delete_builder_basic() {
let query = QueryBuilder::delete("users")
.where_eq("id", 1)
.build()
.unwrap();
let sql = query.sql();
assert!(sql.contains("DELETE FROM users"));
assert!(sql.contains("WHERE id = $1"));
}
#[test]
fn test_delete_builder_with_where_raw() {
let query = QueryBuilder::delete("users")
.where_raw("created_at < NOW() - INTERVAL '1 year'")
.build()
.unwrap();
let sql = query.sql();
assert!(sql.contains("WHERE created_at < NOW() - INTERVAL '1 year'"));
}
#[test]
fn test_delete_builder_with_returning() {
let query = QueryBuilder::delete("users")
.where_eq("id", 1)
.returning("name")
.build()
.unwrap();
let sql = query.sql();
assert!(sql.contains("RETURNING name"));
}
#[test]
fn test_order_direction_display() {
assert_eq!(format!("{}", OrderDirection::Asc), "ASC");
assert_eq!(format!("{}", OrderDirection::Desc), "DESC");
}
}
mod config_tests {
use super::*;
#[test]
fn test_database_config_new() {
let config = DatabaseConfig::new();
assert!(config.application_name.is_some());
assert!(!config.enable_query_logging);
assert!(!config.url.is_empty());
}
#[test]
fn test_database_config_validation_empty_url() {
let mut config = DatabaseConfig::new();
config.url = String::new();
let result = config.validate();
assert!(result.is_err(), "Empty URL should fail validation");
}
#[test]
fn test_pool_config_default() {
let config = PoolConfig::default();
assert!(config.max_connections > 0);
assert!(config.min_connections > 0);
assert!(config.min_connections <= config.max_connections);
assert!(config.health_check_enabled);
assert!(config.test_before_acquire);
}
#[test]
fn test_transaction_config_default() {
let config = TransactionConfig::default();
assert!(config.max_retries > 0);
assert!(config.enable_retry);
assert!(config.default_timeout_secs > 0);
assert!(config.max_savepoints > 0);
assert!(!config.isolation_level.is_empty());
}
}