elif-orm 0.7.1

Production-ready ORM with migrations, database services, connection pooling, and query builder
Documentation
//! Comprehensive tests for transaction functionality
//!
//! These tests verify transaction behavior including commit, rollback,
//! isolation levels, and error handling.

#[cfg(test)]
mod tests {
    use crate::database::ManagedPool;
    use crate::error::ModelError;
    use crate::transaction::*;
    use std::sync::Arc;

    // Mock database setup for testing (these would be real in integration tests)
    async fn create_test_pool() -> Result<Arc<ManagedPool>, ModelError> {
        // This is a placeholder - in real tests you'd set up a test database
        // For now, we'll create a mock that shows the structure
        Err(ModelError::Connection(
            "Test database not configured".to_string(),
        ))
    }

    #[tokio::test]
    async fn test_transaction_config_builder() {
        let config = TransactionConfig {
            isolation_level: Some(IsolationLevel::Serializable),
            read_only: true,
            auto_retry: true,
            max_retries: 5,
        };

        assert_eq!(config.isolation_level, Some(IsolationLevel::Serializable));
        assert!(config.read_only);
        assert!(config.auto_retry);
        assert_eq!(config.max_retries, 5);
    }

    #[tokio::test]
    async fn test_isolation_level_serialization() {
        assert_eq!(IsolationLevel::ReadUncommitted.as_sql(), "READ UNCOMMITTED");
        assert_eq!(IsolationLevel::ReadCommitted.as_sql(), "READ COMMITTED");
        assert_eq!(IsolationLevel::RepeatableRead.as_sql(), "REPEATABLE READ");
        assert_eq!(IsolationLevel::Serializable.as_sql(), "SERIALIZABLE");
    }

    #[tokio::test]
    async fn test_transaction_lifecycle() {
        // This test would require a real database connection
        // For now we test the structure and error handling
        let result = create_test_pool().await;
        assert!(result.is_err());

        // Test that error is properly propagated
        match result {
            Err(ModelError::Connection(msg)) => {
                assert!(msg.contains("Test database not configured"));
            }
            _ => panic!("Expected connection error"),
        }
    }

    #[tokio::test]
    async fn test_serialization_failure_detection() {
        let err1 = ModelError::Database(
            "ERROR: could not serialize access due to concurrent update".to_string(),
        );
        assert!(is_serialization_failure(&err1));

        let err2 = ModelError::Transaction("ERROR: 40001 serialization_failure".to_string());
        assert!(is_serialization_failure(&err2));

        let err3 = ModelError::Database("ERROR: 40P01 deadlock_detected".to_string());
        assert!(is_serialization_failure(&err3));

        let err4 = ModelError::Validation("Invalid input".to_string());
        assert!(!is_serialization_failure(&err4));

        let err5 = ModelError::NotFound("users".to_string());
        assert!(!is_serialization_failure(&err5));
    }

    #[tokio::test]
    async fn test_transaction_config_presets() {
        // Test default configuration
        let default_config = TransactionConfig::default();
        assert!(default_config.isolation_level.is_none());
        assert!(!default_config.read_only);
        assert!(!default_config.auto_retry);
        assert_eq!(default_config.max_retries, 3);

        // Test read-only configuration
        let read_only_config = TransactionConfig {
            read_only: true,
            ..Default::default()
        };
        assert!(read_only_config.read_only);
        assert!(!read_only_config.auto_retry);

        // Test serializable with auto-retry configuration
        let serializable_config = TransactionConfig {
            isolation_level: Some(IsolationLevel::Serializable),
            auto_retry: true,
            max_retries: 5,
            ..Default::default()
        };
        assert_eq!(
            serializable_config.isolation_level,
            Some(IsolationLevel::Serializable)
        );
        assert!(serializable_config.auto_retry);
        assert_eq!(serializable_config.max_retries, 5);
    }

    #[tokio::test]
    async fn test_error_categorization() {
        // Test different error types for proper categorization
        let database_errors = vec![
            "ERROR: 40001 serialization_failure",
            "ERROR: 40P01 deadlock_detected",
            "ERROR: could not serialize access due to concurrent update",
            "ERROR: could not serialize access due to read/write dependencies",
        ];

        for error_msg in database_errors {
            let error = ModelError::Database(error_msg.to_string());
            assert!(
                is_serialization_failure(&error),
                "Failed to detect serialization error: {}",
                error_msg
            );
        }

        let non_serialization_errors = vec![
            "ERROR: 23505 duplicate key value violates unique constraint",
            "ERROR: 42703 column \"nonexistent\" does not exist",
            "ERROR: 42P01 relation \"missing_table\" does not exist",
        ];

        for error_msg in non_serialization_errors {
            let error = ModelError::Database(error_msg.to_string());
            assert!(
                !is_serialization_failure(&error),
                "Incorrectly detected serialization error: {}",
                error_msg
            );
        }
    }

    #[tokio::test]
    async fn test_transaction_state_management() {
        // Test transaction state transitions (without actual database)
        // This tests the logic of transaction state management

        // Test initial state
        let config = TransactionConfig::default();
        assert!(!config.read_only);
        assert!(config.isolation_level.is_none());

        // Test active state simulation
        let mut is_active = true;
        let mut is_committed = false;

        // Simulate commit
        if is_active && !is_committed {
            is_committed = true;
            is_active = false;
        }

        assert!(is_committed);
        assert!(!is_active);
    }

    #[tokio::test]
    async fn test_transaction_isolation_levels() {
        // Test all isolation levels
        let levels = vec![
            IsolationLevel::ReadUncommitted,
            IsolationLevel::ReadCommitted,
            IsolationLevel::RepeatableRead,
            IsolationLevel::Serializable,
        ];

        for level in levels {
            let config = TransactionConfig {
                isolation_level: Some(level),
                ..Default::default()
            };

            assert_eq!(config.isolation_level, Some(level));

            // Test SQL generation
            let sql = level.as_sql();
            assert!(!sql.is_empty());
            // All isolation levels contain either READ or SERIALIZABLE
            assert!(sql.contains("READ") || sql.contains("SERIALIZABLE"));
        }
    }
}