//! Unit tests for the database package //! //! These tests focus on testing logic without requiring a live database connection. use config::database::{DatabaseConfig, PoolConfig, TransactionConfig}; use database::{error::ErrorSeverity, DatabaseError, OrderDirection, QueryBuilder}; 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()); } }