#[cfg(test)]
mod tests {
use crate::database::ManagedPool;
use crate::error::ModelError;
use crate::transaction::*;
use std::sync::Arc;
async fn create_test_pool() -> Result<Arc<ManagedPool>, ModelError> {
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() {
let result = create_test_pool().await;
assert!(result.is_err());
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() {
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);
let read_only_config = TransactionConfig {
read_only: true,
..Default::default()
};
assert!(read_only_config.read_only);
assert!(!read_only_config.auto_retry);
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() {
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() {
let config = TransactionConfig::default();
assert!(!config.read_only);
assert!(config.isolation_level.is_none());
let mut is_active = true;
let mut is_committed = false;
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() {
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));
let sql = level.as_sql();
assert!(!sql.is_empty());
assert!(sql.contains("READ") || sql.contains("SERIALIZABLE"));
}
}
}