use oxisql_core::{Connection, Value};
use oxisql_sqlite_compat::SqliteConnection;
#[tokio::test]
async fn test_basic_rollback() {
let conn = SqliteConnection::open_memory()
.await
.expect("open_memory failed");
conn.execute("CREATE TABLE t (id INTEGER)", &[])
.await
.expect("CREATE TABLE failed");
let mut txn = conn.transaction().await.expect("BEGIN failed");
txn.execute("INSERT INTO t VALUES (1)", &[])
.await
.expect("INSERT failed");
txn.rollback().await.expect("ROLLBACK failed");
let rows = conn
.query("SELECT COUNT(*) FROM t", &[])
.await
.expect("SELECT failed");
let count = match rows.first().and_then(|r| r.get_by_index(0)) {
Some(Value::I64(n)) => *n,
other => panic!("unexpected COUNT(*) result: {other:?}"),
};
assert_eq!(
count, 0,
"rolled-back INSERT must leave 0 rows, got {count}"
);
}
#[tokio::test]
async fn test_commit_not_rolled_back() {
let conn = SqliteConnection::open_memory()
.await
.expect("open_memory failed");
conn.execute("CREATE TABLE t (id INTEGER)", &[])
.await
.expect("CREATE TABLE failed");
let mut txn = conn.transaction().await.expect("BEGIN failed");
txn.execute("INSERT INTO t VALUES (42)", &[])
.await
.expect("INSERT failed");
txn.commit().await.expect("COMMIT failed");
let rows = conn
.query("SELECT id FROM t", &[])
.await
.expect("SELECT failed");
assert_eq!(rows.len(), 1, "committed row must persist after COMMIT");
assert_eq!(
rows[0].get_by_index(0),
Some(&Value::I64(42)),
"committed value must be 42"
);
}
#[tokio::test]
async fn test_rollback_after_no_txn() {
let conn = SqliteConnection::open_memory()
.await
.expect("open_memory failed");
let result = conn.execute("ROLLBACK", &[]).await;
assert!(
result.is_err(),
"ROLLBACK outside a transaction must return Err, but got Ok"
);
}
#[tokio::test]
async fn test_multi_row_rollback() {
let conn = SqliteConnection::open_memory()
.await
.expect("open_memory failed");
conn.execute("CREATE TABLE nums (n INTEGER)", &[])
.await
.expect("CREATE TABLE failed");
let mut txn = conn.transaction().await.expect("BEGIN failed");
for i in 0i64..10 {
txn.execute("INSERT INTO nums VALUES ($1)", &[&i])
.await
.expect("INSERT failed");
}
txn.rollback().await.expect("ROLLBACK failed");
let rows = conn
.query("SELECT COUNT(*) FROM nums", &[])
.await
.expect("SELECT failed");
let count = match rows.first().and_then(|r| r.get_by_index(0)) {
Some(Value::I64(n)) => *n,
other => panic!("unexpected COUNT(*) result: {other:?}"),
};
assert_eq!(
count, 0,
"all 10 rows must be gone after rollback, got {count}"
);
}
#[tokio::test]
async fn test_read_after_rollback() {
let conn = SqliteConnection::open_memory()
.await
.expect("open_memory failed");
conn.execute("CREATE TABLE items (val INTEGER)", &[])
.await
.expect("CREATE TABLE failed");
for v in [10i64, 20, 30] {
conn.execute("INSERT INTO items VALUES ($1)", &[&v])
.await
.expect("sentinel INSERT failed");
}
let mut txn1 = conn.transaction().await.expect("BEGIN txn1 failed");
txn1.execute("INSERT INTO items VALUES (999)", &[])
.await
.expect("INSERT in txn1 failed");
txn1.rollback().await.expect("ROLLBACK txn1 failed");
let mut txn2 = conn
.transaction()
.await
.expect("BEGIN txn2 after rollback failed");
let rows = txn2
.query("SELECT val FROM items ORDER BY val", &[])
.await
.expect("SELECT within txn2 failed");
txn2.commit().await.expect("COMMIT txn2 failed");
assert_eq!(
rows.len(),
3,
"exactly 3 sentinel rows must remain after rollback, got {}",
rows.len()
);
let vals: Vec<i64> = rows
.iter()
.filter_map(|r| {
if let Some(Value::I64(n)) = r.get_by_index(0) {
Some(*n)
} else {
None
}
})
.collect();
assert_eq!(
vals,
vec![10, 20, 30],
"sentinel values must be intact: {vals:?}"
);
assert!(
!vals.contains(&999),
"rolled-back value 999 must not appear: {vals:?}"
);
}