use stoolap::Database;
#[test]
fn test_basic_commit() {
let db = Database::open("memory://txn_basic_commit").expect("Failed to create database");
db.execute(
"CREATE TABLE txn_test (id INTEGER PRIMARY KEY, value TEXT)",
(),
)
.expect("Failed to create table");
db.execute(
"INSERT INTO txn_test (id, value) VALUES (1, 'test value')",
(),
)
.expect("Failed to insert");
let value: String = db
.query_one("SELECT value FROM txn_test WHERE id = 1", ())
.expect("Failed to query");
assert_eq!(value, "test value");
}
#[test]
fn test_explicit_transaction_commit() {
let db = Database::open("memory://txn_explicit_commit").expect("Failed to create database");
db.execute(
"CREATE TABLE txn_test (id INTEGER PRIMARY KEY, value TEXT)",
(),
)
.expect("Failed to create table");
db.execute("BEGIN", ())
.expect("Failed to begin transaction");
db.execute("INSERT INTO txn_test (id, value) VALUES (1, 'first')", ())
.expect("Failed to insert first");
db.execute("INSERT INTO txn_test (id, value) VALUES (2, 'second')", ())
.expect("Failed to insert second");
db.execute("COMMIT", ()).expect("Failed to commit");
let count: i64 = db
.query_one("SELECT COUNT(*) FROM txn_test", ())
.expect("Failed to count");
assert_eq!(count, 2);
}
#[test]
fn test_explicit_transaction_rollback() {
let db = Database::open("memory://txn_explicit_rollback").expect("Failed to create database");
db.execute(
"CREATE TABLE txn_test (id INTEGER PRIMARY KEY, value TEXT)",
(),
)
.expect("Failed to create table");
db.execute("INSERT INTO txn_test (id, value) VALUES (1, 'initial')", ())
.expect("Failed to insert initial");
db.execute("BEGIN", ())
.expect("Failed to begin transaction");
db.execute(
"INSERT INTO txn_test (id, value) VALUES (2, 'should rollback')",
(),
)
.expect("Failed to insert in transaction");
db.execute("ROLLBACK", ()).expect("Failed to rollback");
let count: i64 = db
.query_one("SELECT COUNT(*) FROM txn_test", ())
.expect("Failed to count");
assert_eq!(count, 1, "After rollback, only initial row should exist");
let value: String = db
.query_one("SELECT value FROM txn_test WHERE id = 1", ())
.expect("Failed to query");
assert_eq!(value, "initial");
}
#[test]
fn test_update_in_transaction() {
let db = Database::open("memory://txn_update").expect("Failed to create database");
db.execute(
"CREATE TABLE txn_test (id INTEGER PRIMARY KEY, value TEXT)",
(),
)
.expect("Failed to create table");
db.execute(
"INSERT INTO txn_test (id, value) VALUES (1, 'original')",
(),
)
.expect("Failed to insert");
db.execute("BEGIN", ()).expect("Failed to begin");
db.execute("UPDATE txn_test SET value = 'modified' WHERE id = 1", ())
.expect("Failed to update");
db.execute("COMMIT", ()).expect("Failed to commit");
let value: String = db
.query_one("SELECT value FROM txn_test WHERE id = 1", ())
.expect("Failed to query");
assert_eq!(value, "modified");
}
#[test]
fn test_delete_in_transaction() {
let db = Database::open("memory://txn_delete").expect("Failed to create database");
db.execute(
"CREATE TABLE txn_test (id INTEGER PRIMARY KEY, value TEXT)",
(),
)
.expect("Failed to create table");
db.execute(
"INSERT INTO txn_test (id, value) VALUES (1, 'to delete')",
(),
)
.expect("Failed to insert");
db.execute(
"INSERT INTO txn_test (id, value) VALUES (2, 'keep this')",
(),
)
.expect("Failed to insert");
db.execute("BEGIN", ()).expect("Failed to begin");
db.execute("DELETE FROM txn_test WHERE id = 1", ())
.expect("Failed to delete");
db.execute("COMMIT", ()).expect("Failed to commit");
let count: i64 = db
.query_one("SELECT COUNT(*) FROM txn_test", ())
.expect("Failed to count");
assert_eq!(count, 1);
let value: String = db
.query_one("SELECT value FROM txn_test WHERE id = 2", ())
.expect("Failed to query");
assert_eq!(value, "keep this");
}
#[test]
fn test_multiple_operations_in_transaction() {
let db = Database::open("memory://txn_multi_ops").expect("Failed to create database");
db.execute(
"CREATE TABLE txn_test (id INTEGER PRIMARY KEY, value TEXT)",
(),
)
.expect("Failed to create table");
db.execute("INSERT INTO txn_test (id, value) VALUES (1, 'a')", ())
.expect("Failed to insert");
db.execute("INSERT INTO txn_test (id, value) VALUES (2, 'b')", ())
.expect("Failed to insert");
db.execute("BEGIN", ()).expect("Failed to begin");
db.execute("INSERT INTO txn_test (id, value) VALUES (3, 'c')", ())
.expect("Failed to insert");
db.execute("UPDATE txn_test SET value = 'updated' WHERE id = 1", ())
.expect("Failed to update");
db.execute("DELETE FROM txn_test WHERE id = 2", ())
.expect("Failed to delete");
db.execute("COMMIT", ()).expect("Failed to commit");
let count: i64 = db
.query_one("SELECT COUNT(*) FROM txn_test", ())
.expect("Failed to count");
assert_eq!(count, 2);
let value1: String = db
.query_one("SELECT value FROM txn_test WHERE id = 1", ())
.expect("Failed to query");
assert_eq!(value1, "updated");
let value3: String = db
.query_one("SELECT value FROM txn_test WHERE id = 3", ())
.expect("Failed to query");
assert_eq!(value3, "c");
}
#[test]
fn test_rollback_update() {
let db = Database::open("memory://txn_rollback_update").expect("Failed to create database");
db.execute(
"CREATE TABLE txn_test (id INTEGER PRIMARY KEY, value TEXT)",
(),
)
.expect("Failed to create table");
db.execute(
"INSERT INTO txn_test (id, value) VALUES (1, 'original')",
(),
)
.expect("Failed to insert");
db.execute("BEGIN", ()).expect("Failed to begin");
db.execute(
"UPDATE txn_test SET value = 'should not persist' WHERE id = 1",
(),
)
.expect("Failed to update");
db.execute("ROLLBACK", ()).expect("Failed to rollback");
let value: String = db
.query_one("SELECT value FROM txn_test WHERE id = 1", ())
.expect("Failed to query");
assert_eq!(
value, "original",
"Value should be unchanged after rollback"
);
}
#[test]
fn test_rollback_delete() {
let db = Database::open("memory://txn_rollback_delete").expect("Failed to create database");
db.execute(
"CREATE TABLE txn_test (id INTEGER PRIMARY KEY, value TEXT)",
(),
)
.expect("Failed to create table");
db.execute(
"INSERT INTO txn_test (id, value) VALUES (1, 'should exist')",
(),
)
.expect("Failed to insert");
db.execute("BEGIN", ()).expect("Failed to begin");
db.execute("DELETE FROM txn_test WHERE id = 1", ())
.expect("Failed to delete");
db.execute("ROLLBACK", ()).expect("Failed to rollback");
let count: i64 = db
.query_one("SELECT COUNT(*) FROM txn_test", ())
.expect("Failed to count");
assert_eq!(count, 1, "Row should still exist after rollback");
}
#[test]
fn test_nested_begin_is_noop() {
let db = Database::open("memory://txn_nested").expect("Failed to create database");
db.execute(
"CREATE TABLE txn_test (id INTEGER PRIMARY KEY, value TEXT)",
(),
)
.unwrap();
db.execute("BEGIN", ()).expect("First BEGIN should succeed");
db.execute("INSERT INTO txn_test (id, value) VALUES (1, 'first')", ())
.unwrap();
db.execute("BEGIN", ())
.expect("Nested BEGIN should succeed as no-op");
db.execute("INSERT INTO txn_test (id, value) VALUES (2, 'second')", ())
.unwrap();
db.execute("ROLLBACK", ()).expect("ROLLBACK should succeed");
let count: i64 = db
.query_one("SELECT COUNT(*) FROM txn_test", ())
.expect("Failed to count");
assert_eq!(
count, 0,
"Both rows should be rolled back since nested BEGIN was a no-op"
);
}
#[test]
fn test_commit_without_begin() {
let db = Database::open("memory://txn_commit_no_begin").expect("Failed to create database");
db.execute("CREATE TABLE txn_test (id INTEGER PRIMARY KEY)", ())
.unwrap();
let result = db.execute("COMMIT", ());
let _ = result;
}
#[test]
fn test_transaction_insert_partial_columns() {
let db = Database::open("memory://txn_partial_cols").expect("Failed to create database");
db.execute(
"CREATE TABLE customers (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL,
country TEXT NOT NULL
)",
(),
)
.expect("Failed to create table");
db.execute(
"INSERT INTO customers (name, email, country) VALUES ('Alice', 'alice@example.com', 'US')",
(),
)
.expect("Non-transaction partial column insert should work");
db.execute("BEGIN", ()).expect("Failed to begin");
db.execute(
"INSERT INTO customers (name, email, country) VALUES ('Bob', 'bob@example.com', 'UK')",
(),
)
.expect("Transaction partial column insert should work");
db.execute(
"INSERT INTO customers (name, email, country) VALUES ('Clara', 'clara@example.com', 'DE')",
(),
)
.expect("Second transaction insert should work");
db.execute("COMMIT", ()).expect("Failed to commit");
let count: i64 = db
.query_one("SELECT COUNT(*) FROM customers", ())
.expect("Failed to count");
assert_eq!(count, 3, "All 3 rows should be inserted");
let max_id: i64 = db
.query_one("SELECT MAX(id) FROM customers", ())
.expect("Failed to get max id");
assert_eq!(max_id, 3, "Auto-increment IDs should be 1, 2, 3");
}
#[test]
fn test_transaction_insert_with_defaults() {
let db = Database::open("memory://txn_defaults").expect("Failed to create database");
db.execute(
"CREATE TABLE products (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
name TEXT NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
category TEXT NOT NULL DEFAULT 'General'
)",
(),
)
.expect("Failed to create table");
db.execute("BEGIN", ()).expect("Failed to begin");
db.execute("INSERT INTO products (name) VALUES ('Widget')", ())
.expect("Transaction insert with defaults should work");
db.execute("COMMIT", ()).expect("Failed to commit");
let category: String = db
.query_one("SELECT category FROM products WHERE name = 'Widget'", ())
.expect("Failed to query");
assert_eq!(category, "General", "Default value should be applied");
let active: bool = db
.query_one("SELECT is_active FROM products WHERE name = 'Widget'", ())
.expect("Failed to query");
assert!(active, "Default boolean should be true");
}
#[test]
fn test_autocommit_mode() {
let db = Database::open("memory://txn_autocommit").expect("Failed to create database");
db.execute(
"CREATE TABLE txn_test (id INTEGER PRIMARY KEY, value TEXT)",
(),
)
.expect("Failed to create table");
db.execute("INSERT INTO txn_test (id, value) VALUES (1, 'auto1')", ())
.expect("Failed to insert");
db.execute("INSERT INTO txn_test (id, value) VALUES (2, 'auto2')", ())
.expect("Failed to insert");
let count: i64 = db
.query_one("SELECT COUNT(*) FROM txn_test", ())
.expect("Failed to count");
assert_eq!(count, 2);
}