use stoolap::api::Database;
fn create_test_db() -> Database {
Database::open_in_memory().expect("Failed to create in-memory database")
}
#[test]
fn test_semantic_cache_basic_operations() {
let db = create_test_db();
db.execute(
"CREATE TABLE orders (id INTEGER PRIMARY KEY, amount INTEGER, status TEXT)",
(),
)
.unwrap();
db.execute(
"INSERT INTO orders VALUES
(1, 100, 'pending'),
(2, 200, 'pending'),
(3, 300, 'shipped'),
(4, 400, 'delivered'),
(5, 500, 'pending')",
(),
)
.unwrap();
let count: i64 = db
.query_one("SELECT COUNT(*) FROM orders WHERE amount > 200", ())
.unwrap();
assert_eq!(count, 3);
let count: i64 = db
.query_one("SELECT COUNT(*) FROM orders WHERE amount > 300", ())
.unwrap();
assert_eq!(count, 2);
}
#[test]
fn test_semantic_cache_invalidation_on_insert() {
let db = create_test_db();
db.execute(
"CREATE TABLE products (id INTEGER PRIMARY KEY, price INTEGER)",
(),
)
.unwrap();
db.execute("INSERT INTO products VALUES (1, 100), (2, 200)", ())
.unwrap();
let count: i64 = db.query_one("SELECT COUNT(*) FROM products", ()).unwrap();
assert_eq!(count, 2);
db.execute("INSERT INTO products VALUES (3, 300)", ())
.unwrap();
let count: i64 = db.query_one("SELECT COUNT(*) FROM products", ()).unwrap();
assert_eq!(count, 3);
}
#[test]
fn test_semantic_cache_invalidation_on_update() {
let db = create_test_db();
db.execute(
"CREATE TABLE inventory (id INTEGER PRIMARY KEY, quantity INTEGER)",
(),
)
.unwrap();
db.execute("INSERT INTO inventory VALUES (1, 10), (2, 20), (3, 30)", ())
.unwrap();
let sum: i64 = db
.query_one("SELECT SUM(quantity) FROM inventory", ())
.unwrap();
assert_eq!(sum, 60);
db.execute("UPDATE inventory SET quantity = 100 WHERE id = 1", ())
.unwrap();
let sum: i64 = db
.query_one("SELECT SUM(quantity) FROM inventory", ())
.unwrap();
assert_eq!(sum, 150);
}
#[test]
fn test_semantic_cache_invalidation_on_delete() {
let db = create_test_db();
db.execute("CREATE TABLE logs (id INTEGER PRIMARY KEY, level TEXT)", ())
.unwrap();
db.execute(
"INSERT INTO logs VALUES (1, 'info'), (2, 'warn'), (3, 'error')",
(),
)
.unwrap();
let count: i64 = db.query_one("SELECT COUNT(*) FROM logs", ()).unwrap();
assert_eq!(count, 3);
db.execute("DELETE FROM logs WHERE level = 'info'", ())
.unwrap();
let count: i64 = db.query_one("SELECT COUNT(*) FROM logs", ()).unwrap();
assert_eq!(count, 2);
}
#[test]
fn test_semantic_cache_invalidation_on_truncate() {
let db = create_test_db();
db.execute(
"CREATE TABLE temp_data (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.unwrap();
db.execute("INSERT INTO temp_data VALUES (1, 1), (2, 2), (3, 3)", ())
.unwrap();
let count: i64 = db.query_one("SELECT COUNT(*) FROM temp_data", ()).unwrap();
assert_eq!(count, 3);
db.execute("TRUNCATE TABLE temp_data", ()).unwrap();
let count: i64 = db.query_one("SELECT COUNT(*) FROM temp_data", ()).unwrap();
assert_eq!(count, 0);
}
#[test]
fn test_semantic_cache_range_queries() {
let db = create_test_db();
db.execute(
"CREATE TABLE metrics (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.unwrap();
for i in 1..=1000 {
db.execute(
&format!("INSERT INTO metrics VALUES ({}, {})", i, i * 10),
(),
)
.unwrap();
}
let count1: i64 = db
.query_one("SELECT COUNT(*) FROM metrics WHERE value > 5000", ())
.unwrap();
assert_eq!(count1, 500);
let count2: i64 = db
.query_one("SELECT COUNT(*) FROM metrics WHERE value > 8000", ())
.unwrap();
assert_eq!(count2, 200); }
#[test]
fn test_semantic_cache_in_list_queries() {
let db = create_test_db();
db.execute(
"CREATE TABLE categories (id INTEGER PRIMARY KEY, name TEXT)",
(),
)
.unwrap();
db.execute(
"INSERT INTO categories VALUES
(1, 'A'), (2, 'B'), (3, 'C'), (4, 'D'), (5, 'E')",
(),
)
.unwrap();
let count1: i64 = db
.query_one(
"SELECT COUNT(*) FROM categories WHERE id IN (1, 2, 3, 4, 5)",
(),
)
.unwrap();
assert_eq!(count1, 5);
let count2: i64 = db
.query_one("SELECT COUNT(*) FROM categories WHERE id IN (2, 3)", ())
.unwrap();
assert_eq!(count2, 2);
}
#[test]
fn test_semantic_cache_and_conjunction() {
let db = create_test_db();
db.execute(
"CREATE TABLE events (
id INTEGER PRIMARY KEY,
event_type TEXT,
severity INTEGER
)",
(),
)
.unwrap();
for i in 1..=100 {
let event_type = if i % 2 == 0 { "error" } else { "info" };
let severity = i % 10;
db.execute(
&format!(
"INSERT INTO events VALUES ({}, '{}', {})",
i, event_type, severity
),
(),
)
.unwrap();
}
let count1: i64 = db
.query_one("SELECT COUNT(*) FROM events WHERE severity > 5", ())
.unwrap();
assert_eq!(count1, 40);
let count2: i64 = db
.query_one(
"SELECT COUNT(*) FROM events WHERE severity > 5 AND event_type = 'error'",
(),
)
.unwrap();
assert_eq!(count2, 20); }
#[test]
fn test_semantic_cache_table_isolation() {
let db = create_test_db();
db.execute(
"CREATE TABLE table_a (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.unwrap();
db.execute(
"CREATE TABLE table_b (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.unwrap();
db.execute("INSERT INTO table_a VALUES (1, 100), (2, 200)", ())
.unwrap();
db.execute("INSERT INTO table_b VALUES (1, 999), (2, 888)", ())
.unwrap();
let sum_a: i64 = db.query_one("SELECT SUM(value) FROM table_a", ()).unwrap();
assert_eq!(sum_a, 300);
let sum_b: i64 = db.query_one("SELECT SUM(value) FROM table_b", ()).unwrap();
assert_eq!(sum_b, 1887);
db.execute("INSERT INTO table_a VALUES (3, 300)", ())
.unwrap();
let sum_b: i64 = db.query_one("SELECT SUM(value) FROM table_b", ()).unwrap();
assert_eq!(sum_b, 1887);
}
#[test]
fn test_semantic_cache_complex_filters() {
let db = create_test_db();
db.execute(
"CREATE TABLE transactions (
id INTEGER PRIMARY KEY,
amount INTEGER,
fee INTEGER,
status TEXT
)",
(),
)
.unwrap();
for i in 1..=50 {
let status = if i % 3 == 0 {
"completed"
} else if i % 3 == 1 {
"pending"
} else {
"failed"
};
db.execute(
&format!(
"INSERT INTO transactions VALUES ({}, {}, {}, '{}')",
i,
i * 100,
i * 10,
status
),
(),
)
.unwrap();
}
let count: i64 = db
.query_one(
"SELECT COUNT(*) FROM transactions WHERE amount > 2000 AND fee < 300",
(),
)
.unwrap();
assert_eq!(count, 9);
}
#[test]
fn test_semantic_cache_between_queries() {
let db = create_test_db();
db.execute(
"CREATE TABLE readings (id INTEGER PRIMARY KEY, temperature INTEGER)",
(),
)
.unwrap();
for i in 1..=100 {
db.execute(&format!("INSERT INTO readings VALUES ({}, {})", i, i), ())
.unwrap();
}
let count: i64 = db
.query_one(
"SELECT COUNT(*) FROM readings WHERE temperature BETWEEN 30 AND 70",
(),
)
.unwrap();
assert_eq!(count, 41);
let count: i64 = db
.query_one(
"SELECT COUNT(*) FROM readings WHERE temperature BETWEEN 40 AND 60",
(),
)
.unwrap();
assert_eq!(count, 21); }
#[test]
fn test_semantic_cache_performance() {
let db = create_test_db();
db.execute(
"CREATE TABLE large_table (
id INTEGER PRIMARY KEY,
category INTEGER,
value INTEGER
)",
(),
)
.unwrap();
for batch in 0..100 {
let mut sql = String::from("INSERT INTO large_table VALUES ");
for i in 0..100 {
let id = batch * 100 + i;
if i > 0 {
sql.push_str(", ");
}
sql.push_str(&format!("({}, {}, {})", id, id % 10, id * 7));
}
db.execute(&sql, ()).unwrap();
}
let _: i64 = db
.query_one("SELECT COUNT(*) FROM large_table WHERE category = 5", ())
.unwrap();
for _ in 0..5 {
let count: i64 = db
.query_one("SELECT COUNT(*) FROM large_table WHERE category = 5", ())
.unwrap();
assert_eq!(count, 1000); }
}
#[test]
fn test_semantic_cache_equality_predicates() {
let db = create_test_db();
db.execute(
"CREATE TABLE users (id INTEGER PRIMARY KEY, status TEXT, level INTEGER)",
(),
)
.unwrap();
for i in 1..=100 {
let status = if i % 2 == 0 { "active" } else { "inactive" };
db.execute(
&format!("INSERT INTO users VALUES ({}, '{}', {})", i, status, i % 5),
(),
)
.unwrap();
}
let count: i64 = db
.query_one("SELECT COUNT(*) FROM users WHERE status = 'active'", ())
.unwrap();
assert_eq!(count, 50);
let count: i64 = db
.query_one(
"SELECT COUNT(*) FROM users WHERE status = 'active' AND level > 2",
(),
)
.unwrap();
assert_eq!(count, 20); }
#[test]
fn test_semantic_cache_null_handling() {
let db = create_test_db();
db.execute(
"CREATE TABLE nullable_data (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.unwrap();
db.execute(
"INSERT INTO nullable_data VALUES (1, 100), (2, NULL), (3, 300), (4, NULL), (5, 500)",
(),
)
.unwrap();
let count: i64 = db
.query_one(
"SELECT COUNT(*) FROM nullable_data WHERE value IS NOT NULL",
(),
)
.unwrap();
assert_eq!(count, 3);
let count: i64 = db
.query_one("SELECT COUNT(*) FROM nullable_data WHERE value IS NULL", ())
.unwrap();
assert_eq!(count, 2);
}
#[test]
fn test_semantic_cache_no_stale_data() {
let db = create_test_db();
db.execute(
"CREATE TABLE counter (id INTEGER PRIMARY KEY, count INTEGER)",
(),
)
.unwrap();
db.execute("INSERT INTO counter VALUES (1, 0)", ()).unwrap();
let count: i64 = db
.query_one("SELECT count FROM counter WHERE id = 1", ())
.unwrap();
assert_eq!(count, 0);
for expected in 1..=10 {
db.execute("UPDATE counter SET count = count + 1 WHERE id = 1", ())
.unwrap();
let count: i64 = db
.query_one("SELECT count FROM counter WHERE id = 1", ())
.unwrap();
assert_eq!(count, expected);
}
}
#[test]
fn test_semantic_cache_query_patterns() {
let db = create_test_db();
db.execute(
"CREATE TABLE sales (
id INTEGER PRIMARY KEY,
region TEXT,
amount INTEGER,
year INTEGER
)",
(),
)
.unwrap();
for i in 1..=200 {
let region = match i % 4 {
0 => "North",
1 => "South",
2 => "East",
_ => "West",
};
let year = 2020 + (i % 5);
db.execute(
&format!(
"INSERT INTO sales VALUES ({}, '{}', {}, {})",
i,
region,
i * 100,
year
),
(),
)
.unwrap();
}
let total: i64 = db
.query_one("SELECT SUM(amount) FROM sales WHERE year >= 2020", ())
.unwrap();
assert!(total > 0);
let recent: i64 = db
.query_one("SELECT SUM(amount) FROM sales WHERE year >= 2023", ())
.unwrap();
assert!(recent < total);
let north: i64 = db
.query_one("SELECT SUM(amount) FROM sales WHERE region = 'North'", ())
.unwrap();
assert!(north > 0);
let north_recent: i64 = db
.query_one(
"SELECT SUM(amount) FROM sales WHERE region = 'North' AND year >= 2023",
(),
)
.unwrap();
assert!(north_recent <= north);
}
#[test]
fn test_semantic_cache_stats_verification() {
let db = create_test_db();
db.execute(
"CREATE TABLE products (id INTEGER PRIMARY KEY, price INTEGER, quantity INTEGER)",
(),
)
.unwrap();
for i in 1..=100 {
db.execute(
&format!("INSERT INTO products VALUES ({}, {}, {})", i, i * 10, i * 5),
(),
)
.unwrap();
}
db.clear_semantic_cache().unwrap();
let stats_before = db.semantic_cache_stats().unwrap();
assert_eq!(stats_before.hits, 0);
assert_eq!(stats_before.misses, 0);
let rows: Vec<_> = db
.query("SELECT * FROM products WHERE price > 500", ())
.unwrap()
.collect::<Result<Vec<_>, _>>()
.unwrap();
assert_eq!(rows.len(), 50);
let stats_after_first = db.semantic_cache_stats().unwrap();
assert_eq!(
stats_after_first.misses, 1,
"First query should be a cache miss"
);
let rows2: Vec<_> = db
.query("SELECT * FROM products WHERE price > 500", ())
.unwrap()
.collect::<Result<Vec<_>, _>>()
.unwrap();
assert_eq!(rows2.len(), 50);
let stats_after_second = db.semantic_cache_stats().unwrap();
assert_eq!(
stats_after_second.exact_hits, 1,
"Second identical query should be an exact hit"
);
let rows3: Vec<_> = db
.query("SELECT * FROM products WHERE price > 800", ())
.unwrap()
.collect::<Result<Vec<_>, _>>()
.unwrap();
assert_eq!(rows3.len(), 20);
let stats_after_third = db.semantic_cache_stats().unwrap();
assert_eq!(
stats_after_third.subsumption_hits, 1,
"Stricter query should be a subsumption hit"
);
assert_eq!(stats_after_third.hits, 2, "Should have 2 total cache hits");
}
#[test]
fn test_semantic_cache_invalidation_clears_stats() {
let db = create_test_db();
db.execute(
"CREATE TABLE items (id INTEGER PRIMARY KEY, value INTEGER)",
(),
)
.unwrap();
for i in 1..=50 {
db.execute(&format!("INSERT INTO items VALUES ({}, {})", i, i * 10), ())
.unwrap();
}
db.clear_semantic_cache().unwrap();
let _: Vec<_> = db
.query("SELECT * FROM items WHERE value > 100", ())
.unwrap()
.collect::<Result<Vec<_>, _>>()
.unwrap();
let _: Vec<_> = db
.query("SELECT * FROM items WHERE value > 100", ())
.unwrap()
.collect::<Result<Vec<_>, _>>()
.unwrap();
let stats_before_insert = db.semantic_cache_stats().unwrap();
assert_eq!(stats_before_insert.exact_hits, 1);
db.execute("INSERT INTO items VALUES (51, 510)", ())
.unwrap();
let _: Vec<_> = db
.query("SELECT * FROM items WHERE value > 100", ())
.unwrap()
.collect::<Result<Vec<_>, _>>()
.unwrap();
let stats_after_insert = db.semantic_cache_stats().unwrap();
assert!(
stats_after_insert.misses > stats_before_insert.misses,
"Query after INSERT should be a cache miss due to invalidation"
);
}