use stoolap::Database;
use tempfile::tempdir;
#[test]
fn test_index_persistence() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let dsn = format!("file://{}", db_path.display());
{
let db = Database::open(&dsn).unwrap();
db.execute(
"CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT, price FLOAT)",
(),
)
.unwrap();
db.execute("CREATE INDEX idx_name ON products(name)", ())
.unwrap();
db.execute(
"INSERT INTO products (id, name, price) VALUES (1, 'Widget', 9.99)",
(),
)
.unwrap();
db.execute(
"INSERT INTO products (id, name, price) VALUES (2, 'Gadget', 19.99)",
(),
)
.unwrap();
db.close().unwrap();
}
{
let db = Database::open(&dsn).unwrap();
let count: i64 = db
.query_one("SELECT COUNT(*) FROM products WHERE name = 'Widget'", ())
.unwrap();
assert_eq!(count, 1, "Expected 1 row for Widget");
let count: i64 = db.query_one("SELECT COUNT(*) FROM products", ()).unwrap();
assert_eq!(count, 2, "Expected 2 products");
db.close().unwrap();
}
}
#[test]
fn test_unique_index_persistence() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let dsn = format!("file://{}", db_path.display());
{
let db = Database::open(&dsn).unwrap();
db.execute(
"CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT)",
(),
)
.unwrap();
db.execute("CREATE UNIQUE INDEX idx_email ON users(email)", ())
.unwrap();
db.execute(
"INSERT INTO users (id, email) VALUES (1, 'test@example.com')",
(),
)
.unwrap();
db.close().unwrap();
}
{
let db = Database::open(&dsn).unwrap();
let count: i64 = db.query_one("SELECT COUNT(*) FROM users", ()).unwrap();
assert_eq!(count, 1, "Expected 1 user");
let result = db.execute(
"INSERT INTO users (id, email) VALUES (2, 'test@example.com')",
(),
);
assert!(result.is_err(), "Duplicate insert should fail after reopen");
db.close().unwrap();
}
}
#[test]
fn test_update_persistence() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let dsn = format!("file://{}", db_path.display());
{
let db = Database::open(&dsn).unwrap();
db.execute(
"CREATE TABLE items (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.unwrap();
db.execute("INSERT INTO items (id, value) VALUES (1, 100)", ())
.unwrap();
db.execute("UPDATE items SET value = 200 WHERE id = 1", ())
.unwrap();
db.close().unwrap();
}
{
let db = Database::open(&dsn).unwrap();
let value: i64 = db
.query_one("SELECT value FROM items WHERE id = 1", ())
.unwrap();
assert_eq!(value, 200, "Expected updated value 200");
db.close().unwrap();
}
}
#[test]
fn test_delete_persistence() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let dsn = format!("file://{}", db_path.display());
{
let db = Database::open(&dsn).unwrap();
db.execute(
"CREATE TABLE items (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.unwrap();
db.execute(
"INSERT INTO items (id, value) VALUES (1, 100), (2, 200), (3, 300)",
(),
)
.unwrap();
db.execute("DELETE FROM items WHERE id = 2", ()).unwrap();
let count: i64 = db.query_one("SELECT COUNT(*) FROM items", ()).unwrap();
assert_eq!(count, 2, "Expected 2 items after delete");
db.close().unwrap();
}
{
let db = Database::open(&dsn).unwrap();
let count: i64 = db.query_one("SELECT COUNT(*) FROM items", ()).unwrap();
assert_eq!(count, 2, "Expected 2 items after reopen");
let rows = db.query("SELECT id FROM items ORDER BY id", ()).unwrap();
let ids: Vec<i64> = rows.map(|r| r.unwrap().get(0).unwrap()).collect();
assert_eq!(ids, vec![1, 3], "Expected ids 1 and 3");
db.close().unwrap();
}
}
#[test]
fn test_multiple_tables_persistence() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let dsn = format!("file://{}", db_path.display());
{
let db = Database::open(&dsn).unwrap();
db.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)", ())
.unwrap();
db.execute(
"CREATE TABLE orders (id INTEGER PRIMARY KEY, user_id INTEGER, amount INTEGER)",
(),
)
.unwrap();
db.execute(
"CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT, price FLOAT)",
(),
)
.unwrap();
db.execute(
"INSERT INTO users (id, name) VALUES (1, 'Alice'), (2, 'Bob')",
(),
)
.unwrap();
db.execute(
"INSERT INTO orders (id, user_id, amount) VALUES (1, 1, 100)",
(),
)
.unwrap();
db.execute(
"INSERT INTO products (id, name, price) VALUES (1, 'Widget', 9.99)",
(),
)
.unwrap();
db.close().unwrap();
}
{
let db = Database::open(&dsn).unwrap();
let users: i64 = db.query_one("SELECT COUNT(*) FROM users", ()).unwrap();
assert_eq!(users, 2, "Expected 2 users");
let orders: i64 = db.query_one("SELECT COUNT(*) FROM orders", ()).unwrap();
assert_eq!(orders, 1, "Expected 1 order");
let products: i64 = db.query_one("SELECT COUNT(*) FROM products", ()).unwrap();
assert_eq!(products, 1, "Expected 1 product");
db.close().unwrap();
}
}
#[test]
fn test_auto_increment_persistence() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let dsn = format!("file://{}", db_path.display());
{
let db = Database::open(&dsn).unwrap();
db.execute(
"CREATE TABLE items (id INTEGER PRIMARY KEY AUTO_INCREMENT, name TEXT)",
(),
)
.unwrap();
db.execute("INSERT INTO items (name) VALUES ('First')", ())
.unwrap();
db.execute("INSERT INTO items (name) VALUES ('Second')", ())
.unwrap();
let last_id: i64 = db.query_one("SELECT MAX(id) FROM items", ()).unwrap();
assert_eq!(last_id, 2, "Expected last id to be 2");
db.close().unwrap();
}
{
let db = Database::open(&dsn).unwrap();
db.execute("INSERT INTO items (name) VALUES ('Third')", ())
.unwrap();
let last_id: i64 = db.query_one("SELECT MAX(id) FROM items", ()).unwrap();
assert!(last_id >= 3, "Expected last id to be >= 3, got {}", last_id);
let count: i64 = db.query_one("SELECT COUNT(*) FROM items", ()).unwrap();
assert_eq!(count, 3, "Expected 3 items");
db.close().unwrap();
}
}
#[test]
fn test_transaction_rollback_not_persisted() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let dsn = format!("file://{}", db_path.display());
{
let db = Database::open(&dsn).unwrap();
db.execute(
"CREATE TABLE test (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.unwrap();
db.execute("INSERT INTO test (id, value) VALUES (1, 100)", ())
.unwrap();
let mut tx = db.begin().unwrap();
tx.execute("INSERT INTO test (id, value) VALUES (2, 200)", ())
.unwrap();
tx.rollback().unwrap();
let count: i64 = db.query_one("SELECT COUNT(*) FROM test", ()).unwrap();
assert_eq!(count, 1, "Should have only 1 row after rollback");
db.close().unwrap();
}
{
let db = Database::open(&dsn).unwrap();
let count: i64 = db.query_one("SELECT COUNT(*) FROM test", ()).unwrap();
assert_eq!(count, 1, "Should still have only 1 row after reopen");
db.close().unwrap();
}
}
#[test]
fn test_create_table_as_select_persistence() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let dsn = format!("file://{}", db_path.display());
{
let db = Database::open(&dsn).unwrap();
db.execute(
"CREATE TABLE source (id INTEGER PRIMARY KEY, name TEXT, value INTEGER)",
(),
)
.unwrap();
db.execute("INSERT INTO source (id, name, value) VALUES (1, 'A', 100), (2, 'B', 200), (3, 'C', 300)", ()).unwrap();
db.execute("CREATE TABLE derived AS SELECT id, name, value * 2 AS doubled FROM source WHERE value >= 200", ()).unwrap();
let count: i64 = db.query_one("SELECT COUNT(*) FROM derived", ()).unwrap();
assert_eq!(count, 2, "Expected 2 rows in derived table");
db.close().unwrap();
}
{
let db = Database::open(&dsn).unwrap();
let source_count: i64 = db.query_one("SELECT COUNT(*) FROM source", ()).unwrap();
assert_eq!(source_count, 3, "Source should have 3 rows");
let derived_count: i64 = db.query_one("SELECT COUNT(*) FROM derived", ()).unwrap();
assert_eq!(derived_count, 2, "Derived should have 2 rows");
let doubled_sum: i64 = db
.query_one("SELECT SUM(doubled) FROM derived", ())
.unwrap();
assert_eq!(
doubled_sum, 1000,
"Expected sum of doubled values to be 1000 (400 + 600)"
);
db.close().unwrap();
}
}
#[test]
fn test_truncate_persistence() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let dsn = format!("file://{}", db_path.display());
{
let db = Database::open(&dsn).unwrap();
db.execute(
"CREATE TABLE items (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.unwrap();
db.execute(
"INSERT INTO items (id, value) VALUES (1, 100), (2, 200), (3, 300)",
(),
)
.unwrap();
let before: i64 = db.query_one("SELECT COUNT(*) FROM items", ()).unwrap();
assert_eq!(before, 3, "Expected 3 items before truncate");
db.execute("TRUNCATE TABLE items", ()).unwrap();
let after: i64 = db.query_one("SELECT COUNT(*) FROM items", ()).unwrap();
assert_eq!(after, 0, "Expected 0 items after truncate");
db.close().unwrap();
}
{
let db = Database::open(&dsn).unwrap();
let count: i64 = db.query_one("SELECT COUNT(*) FROM items", ()).unwrap();
assert_eq!(count, 0, "Table should still be empty after reopen");
db.execute("INSERT INTO items (id, value) VALUES (10, 1000)", ())
.unwrap();
let new_count: i64 = db.query_one("SELECT COUNT(*) FROM items", ()).unwrap();
assert_eq!(new_count, 1, "Should have 1 row after new insert");
db.close().unwrap();
}
}
#[test]
fn test_multi_column_index_persistence() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let dsn = format!("file://{}", db_path.display());
{
let db = Database::open(&dsn).unwrap();
db.execute(
"CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER,
order_date TEXT,
amount FLOAT
)",
(),
)
.unwrap();
db.execute(
"CREATE INDEX idx_customer_date ON orders(customer_id, order_date)",
(),
)
.unwrap();
db.execute(
"INSERT INTO orders VALUES (1, 100, '2025-01-01', 99.99)",
(),
)
.unwrap();
db.execute(
"INSERT INTO orders VALUES (2, 100, '2025-01-02', 149.99)",
(),
)
.unwrap();
db.execute(
"INSERT INTO orders VALUES (3, 200, '2025-01-01', 199.99)",
(),
)
.unwrap();
db.close().unwrap();
}
{
let db = Database::open(&dsn).unwrap();
let count: i64 = db.query_one("SELECT COUNT(*) FROM orders", ()).unwrap();
assert_eq!(count, 3, "Expected 3 orders after reopen");
let count: i64 = db
.query_one(
"SELECT COUNT(*) FROM orders WHERE customer_id = 100 AND order_date = '2025-01-01'",
(),
)
.unwrap();
assert_eq!(count, 1, "Expected 1 order for customer 100 on 2025-01-01");
let count: i64 = db
.query_one("SELECT COUNT(*) FROM orders WHERE customer_id = 100", ())
.unwrap();
assert_eq!(count, 2, "Expected 2 orders for customer 100");
db.close().unwrap();
}
}
#[test]
fn test_unique_multi_column_index_persistence() {
let dir = tempdir().unwrap();
let db_path = dir.path().join("test.db");
let dsn = format!("file://{}", db_path.display());
{
let db = Database::open(&dsn).unwrap();
db.execute(
"CREATE TABLE reservations (
id INTEGER PRIMARY KEY,
room_id INTEGER,
date TEXT,
guest_name TEXT
)",
(),
)
.unwrap();
db.execute(
"CREATE UNIQUE INDEX idx_room_date ON reservations(room_id, date)",
(),
)
.unwrap();
db.execute(
"INSERT INTO reservations VALUES (1, 101, '2025-06-01', 'Alice')",
(),
)
.unwrap();
db.execute(
"INSERT INTO reservations VALUES (2, 101, '2025-06-02', 'Bob')",
(),
)
.unwrap();
db.execute(
"INSERT INTO reservations VALUES (3, 102, '2025-06-01', 'Charlie')",
(),
)
.unwrap();
db.close().unwrap();
}
{
let db = Database::open(&dsn).unwrap();
let count: i64 = db
.query_one("SELECT COUNT(*) FROM reservations", ())
.unwrap();
assert_eq!(count, 3, "Expected 3 reservations after reopen");
let result = db.execute(
"INSERT INTO reservations VALUES (4, 101, '2025-06-01', 'Eve')",
(),
);
assert!(
result.is_err(),
"Duplicate room_id + date should fail after reopen"
);
db.execute(
"INSERT INTO reservations VALUES (4, 101, '2025-06-03', 'Eve')",
(),
)
.unwrap();
let final_count: i64 = db
.query_one("SELECT COUNT(*) FROM reservations", ())
.unwrap();
assert_eq!(final_count, 4, "Expected 4 reservations after new insert");
db.close().unwrap();
}
}