#![cfg(any(feature = "sqlite", feature = "postgresql", feature = "mysql"))]
mod _test_common;
define_test_user_with_option_id!(TestUser, "test_transaction_users_1");
async fn test_transaction_commit_impl(
config: &_test_common::DbConfig,
) -> Result<(), Box<dyn std::error::Error>> {
#[cfg(feature = "postgresql")]
if matches!(config.0, ormer::DbType::PostgreSQL) {
println!("Skipping PostgreSQL test (known issue with Option<i64> primary key)");
return Ok(());
}
#[cfg(feature = "mysql")]
if matches!(config.0, ormer::DbType::MySQL) {
println!("Skipping MySQL test (known issue with Option<i64> primary key)");
return Ok(());
}
let db = _test_common::create_db_connection(config).await?;
db.create_table::<TestUser>().execute().await?;
let mut txn = db.begin().await?;
let user1 = TestUser {
id: None,
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
};
txn.insert(&user1).execute().await?;
txn.commit().await?;
let users: Vec<TestUser> = db.select::<TestUser>().collect::<Vec<TestUser>>().await?;
assert_eq!(users.len(), 1, "Should have 1 user after commit");
assert_eq!(users[0].name, "Alice");
db.drop_table::<TestUser>().execute().await?;
Ok(())
}
async fn test_transaction_rollback_impl(
config: &_test_common::DbConfig,
) -> Result<(), Box<dyn std::error::Error>> {
#[cfg(feature = "postgresql")]
if matches!(config.0, ormer::DbType::PostgreSQL) {
println!("Skipping PostgreSQL test (known issue with Option<i64> primary key)");
return Ok(());
}
#[cfg(feature = "mysql")]
if matches!(config.0, ormer::DbType::MySQL) {
println!("Skipping MySQL test (known issue with Option<i64> primary key)");
return Ok(());
}
let db = _test_common::create_db_connection(config).await?;
db.create_table::<TestUser>().execute().await?;
let initial_user = TestUser {
id: None,
name: "Initial".to_string(),
email: "initial@example.com".to_string(),
};
db.insert(&initial_user).execute().await?;
let mut txn = db.begin().await?;
let user1 = TestUser {
id: None,
name: "Should Rollback".to_string(),
email: "rollback@example.com".to_string(),
};
txn.insert(&user1).execute().await?;
txn.rollback().await?;
let users: Vec<TestUser> = db.select::<TestUser>().collect::<Vec<TestUser>>().await?;
assert_eq!(users.len(), 1, "Should have only 1 user after rollback");
assert_eq!(users[0].name, "Initial");
db.drop_table::<TestUser>().execute().await?;
Ok(())
}
async fn test_transaction_with_query_impl(
config: &_test_common::DbConfig,
) -> Result<(), Box<dyn std::error::Error>> {
#[cfg(feature = "postgresql")]
if matches!(config.0, ormer::DbType::PostgreSQL) {
println!("Skipping PostgreSQL test (known issue with Option<i64> primary key)");
return Ok(());
}
#[cfg(feature = "mysql")]
if matches!(config.0, ormer::DbType::MySQL) {
println!("Skipping MySQL test (known issue with Option<i64> primary key)");
return Ok(());
}
let db = _test_common::create_db_connection(config).await?;
db.create_table::<TestUser>().execute().await?;
let mut txn = db.begin().await?;
let user = TestUser {
id: None,
name: "Query Test".to_string(),
email: "query@example.com".to_string(),
};
txn.insert(&user).execute().await?;
let users: Vec<TestUser> = txn.select::<TestUser>().collect::<Vec<TestUser>>().await?;
assert_eq!(users.len(), 1, "Should see 1 user in transaction");
assert_eq!(users[0].name, "Query Test");
txn.commit().await?;
let users: Vec<TestUser> = db.select::<TestUser>().collect::<Vec<TestUser>>().await?;
assert_eq!(users.len(), 1, "Should have 1 user after commit");
db.drop_table::<TestUser>().execute().await?;
Ok(())
}
async fn test_transaction_with_update_impl(
config: &_test_common::DbConfig,
) -> Result<(), Box<dyn std::error::Error>> {
#[cfg(feature = "postgresql")]
if matches!(config.0, ormer::DbType::PostgreSQL) {
println!("Skipping PostgreSQL test (known issue with Option<i64> primary key)");
return Ok(());
}
#[cfg(feature = "mysql")]
if matches!(config.0, ormer::DbType::MySQL) {
println!("Skipping MySQL test (known issue with Option<i64> primary key)");
return Ok(());
}
let db = _test_common::create_db_connection(config).await?;
db.create_table::<TestUser>().execute().await?;
let user = TestUser {
id: None,
name: "Original".to_string(),
email: "original@example.com".to_string(),
};
db.insert(&user).execute().await?;
let txn = db.begin().await?;
#[allow(unused_imports)]
use ormer::WhereColumn;
txn.update::<TestUser>()
.filter(|w| w.name.eq("Original"))
.set(|w| w.name = w.name.set("Updated".to_string()))
.execute()
.await?;
txn.commit().await?;
let users: Vec<TestUser> = db
.select::<TestUser>()
.filter(|w| w.name.eq("Updated"))
.collect::<Vec<TestUser>>()
.await?;
assert_eq!(users.len(), 1, "Should have 1 updated user");
assert_eq!(users[0].email, "original@example.com");
db.drop_table::<TestUser>().execute().await?;
Ok(())
}
test_on_all_dbs_result!(test_transaction_commit_impl);
test_on_all_dbs_result!(test_transaction_rollback_impl);
test_on_all_dbs_result!(test_transaction_with_query_impl);
test_on_all_dbs_result!(test_transaction_with_update_impl);
#[cfg(feature = "sqlite")]
#[tokio::test]
async fn test_closure_transaction_and_savepoint_sqlite() -> Result<(), Box<dyn std::error::Error>> {
let db = ormer::Database::connect(ormer::DbType::Sqlite, ":memory:").await?;
db.create_table::<TestUser>().execute().await?;
let committed = TestUser {
id: None,
name: "Committed".to_string(),
email: "committed@example.com".to_string(),
};
db.transaction(|txn| {
Box::pin(async move {
txn.insert(&committed).execute().await?;
Ok(())
})
})
.await?;
let rolled_back = TestUser {
id: None,
name: "Rolled back".to_string(),
email: "rolled-back@example.com".to_string(),
};
let rolled_back_for_savepoint = rolled_back.clone();
let result = db
.transaction(|txn| {
Box::pin(async move {
txn.insert(&rolled_back).execute().await?;
Err::<(), _>(ormer::ormer_error!("rollback closure"))
})
})
.await;
assert!(result.is_err());
let savepoint_after = TestUser {
id: None,
name: "After savepoint".to_string(),
email: "after-savepoint@example.com".to_string(),
};
db.transaction(|txn| {
Box::pin(async move {
let nested = txn
.savepoint(|txn| {
Box::pin(async move {
txn.insert(&rolled_back_for_savepoint).execute().await?;
Err::<(), _>(ormer::ormer_error!("rollback savepoint"))
})
})
.await;
assert!(nested.is_err());
txn.insert(&savepoint_after).execute().await?;
Ok(())
})
})
.await?;
let users = db.select::<TestUser>().collect::<Vec<TestUser>>().await?;
assert_eq!(users.len(), 2);
assert!(users.iter().any(|user| user.name == "Committed"));
assert!(users.iter().any(|user| user.name == "After savepoint"));
assert!(!users.iter().any(|user| user.name == "Rolled back"));
db.drop_table::<TestUser>().execute().await?;
Ok(())
}