use motedb::{types::Value, Database};
use tempfile::TempDir;
fn rows(result: motedb::StreamingQueryResult) -> Vec<Vec<Value>> {
use motedb::QueryResult;
match result.materialize().unwrap() {
QueryResult::Select { rows, .. } => rows,
_ => panic!("Expected Select result"),
}
}
#[test]
fn test_update_pk_to_duplicate_rejected() {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, val TEXT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 'a')").unwrap();
db.execute("INSERT INTO t VALUES (2, 'b')").unwrap();
let result = db.execute("UPDATE t SET id = 2 WHERE id = 1");
assert!(result.is_err(), "UPDATE PK to duplicate should be rejected");
let r = rows(db.execute("SELECT id FROM t ORDER BY id").unwrap());
assert_eq!(r.len(), 2);
assert_eq!(&r[0][0], &Value::Integer(1));
assert_eq!(&r[1][0], &Value::Integer(2));
}
#[test]
fn test_update_pk_to_new_value_works() {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, val TEXT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 'a')").unwrap();
db.execute("UPDATE t SET id = 99 WHERE id = 1").unwrap();
let r = rows(db.execute("SELECT id, val FROM t").unwrap());
assert_eq!(r.len(), 1);
assert_eq!(&r[0][0], &Value::Integer(99));
assert_eq!(&r[0][1], &Value::text("a".to_string()));
let r2 = rows(db.execute("SELECT * FROM t WHERE id = 1").unwrap());
assert!(r2.is_empty(), "Old PK should return no rows");
let r3 = rows(db.execute("SELECT * FROM t WHERE id = 99").unwrap());
assert_eq!(r3.len(), 1);
}
#[test]
fn test_update_nonexistent_column_rejected() {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, val TEXT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 'original')").unwrap();
let result = db.execute("UPDATE t SET nonexistent = 'oops' WHERE id = 1");
assert!(
result.is_err(),
"UPDATE with nonexistent column should be rejected"
);
let r = rows(db.execute("SELECT val FROM t WHERE id = 1").unwrap());
assert_eq!(r.len(), 1);
assert_eq!(&r[0][0], &Value::text("original".to_string()));
}
#[test]
fn test_delete_row_with_null_in_indexed_column() {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, name TEXT, age INT)")
.unwrap();
db.execute("CREATE INDEX idx_name ON t (name)").unwrap();
db.execute("INSERT INTO t VALUES (1, NULL, 20)").unwrap();
db.execute("INSERT INTO t VALUES (2, 'alice', 25)").unwrap();
db.execute("DELETE FROM t WHERE id = 1").unwrap();
let r = rows(db.execute("SELECT id FROM t").unwrap());
assert_eq!(r.len(), 1);
assert_eq!(&r[0][0], &Value::Integer(2));
let r2 = rows(db.execute("SELECT id FROM t WHERE name = 'alice'").unwrap());
assert_eq!(r2.len(), 1);
assert_eq!(&r2[0][0], &Value::Integer(2));
}
#[test]
fn test_float_range_queries_negative_positive() {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, val FLOAT)")
.unwrap();
let values = [-10.0, -5.0, 0.0, 5.0, 10.0];
for (i, v) in values.iter().enumerate() {
db.execute(&format!("INSERT INTO t VALUES ({}, {})", i + 1, v))
.unwrap();
}
let r = rows(
db.execute("SELECT id FROM t WHERE val BETWEEN -5.0 AND 5.0 ORDER BY id")
.unwrap(),
);
assert_eq!(r.len(), 3, "BETWEEN -5.0 AND 5.0 should return 3 rows");
assert_eq!(&r[0][0], &Value::Integer(2)); assert_eq!(&r[1][0], &Value::Integer(3)); assert_eq!(&r[2][0], &Value::Integer(4));
let r2 = rows(
db.execute("SELECT id FROM t WHERE val >= 0.0 ORDER BY id")
.unwrap(),
);
assert_eq!(r2.len(), 3, "val >= 0.0 should return 3 rows");
let r3 = rows(
db.execute("SELECT id FROM t WHERE val < 0.0 ORDER BY id")
.unwrap(),
);
assert_eq!(r3.len(), 2, "val < 0.0 should return 2 rows");
}
#[test]
fn test_modulo_on_floats() {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, val FLOAT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 10.5)").unwrap();
let r = rows(db.execute("SELECT 10 % 3").unwrap());
assert_eq!(r.len(), 1);
assert_eq!(&r[0][0], &Value::Integer(1));
let result = db.execute("SELECT 10.5 % 3.0");
match result {
Ok(r) => {
let r = rows(r);
if let Value::Float(f) = &r[0][0] {
assert!(
(f - 1.5).abs() < 0.01,
"10.5 % 3.0 should be 1.5, got {}",
f
);
}
}
Err(_) => {
}
}
}
#[test]
fn test_modulo_integer_by_zero() {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
db.execute("CREATE TABLE t (id INT PRIMARY KEY)").unwrap();
db.execute("INSERT INTO t VALUES (1)").unwrap();
let result = db.execute("SELECT 10 % 0");
assert!(result.is_err(), "Modulo by zero should return error");
}
#[test]
fn test_select_division_by_zero() {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
db.execute("CREATE TABLE t (id INT PRIMARY KEY)").unwrap();
db.execute("INSERT INTO t VALUES (1)").unwrap();
let result = db.execute("SELECT 10 / 0");
assert!(result.is_err(), "Division by zero should return error");
}
#[test]
fn test_select_float_division_by_zero() {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
db.execute("CREATE TABLE t (id INT PRIMARY KEY)").unwrap();
db.execute("INSERT INTO t VALUES (1)").unwrap();
let result = db.execute("SELECT 10.0 / 0.0");
assert!(
result.is_err(),
"Float division by zero should return error"
);
}
#[test]
fn test_drop_table_nonexistent() {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
let result = db.execute("DROP TABLE nonexistent");
assert!(result.is_err(), "DROP TABLE nonexistent should error");
}
#[test]
fn test_drop_table_twice() {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
db.execute("CREATE TABLE t (id INT PRIMARY KEY)").unwrap();
db.execute("DROP TABLE t").unwrap();
let result = db.execute("DROP TABLE t");
assert!(result.is_err(), "Second DROP TABLE should error");
}
#[test]
fn test_count_after_delete_reinsert() {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, val INT)")
.unwrap();
for i in 1..=100 {
db.execute(&format!("INSERT INTO t VALUES ({}, {})", i, i))
.unwrap();
}
for i in 1..=50 {
db.execute(&format!("DELETE FROM t WHERE id = {}", i))
.unwrap();
}
for i in 1..=50 {
db.execute(&format!("INSERT INTO t VALUES ({}, {})", i, i * 10))
.unwrap();
}
let r = rows(db.execute("SELECT COUNT(*) FROM t").unwrap());
assert_eq!(
&r[0][0],
&Value::Integer(100),
"COUNT(*) should be 100 after delete + re-insert"
);
}
#[test]
fn test_insert_null_into_non_null_column() {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, val INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, NULL)").unwrap();
let r = rows(db.execute("SELECT val FROM t WHERE id = 1").unwrap());
assert_eq!(r.len(), 1);
assert!(matches!(&r[0][0], Value::Null), "val should be NULL");
}
#[test]
fn test_coalesce_all_null() {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
db.execute("CREATE TABLE t (id INT PRIMARY KEY)").unwrap();
db.execute("INSERT INTO t VALUES (1)").unwrap();
let r = rows(db.execute("SELECT COALESCE(NULL, NULL, 42)").unwrap());
assert_eq!(r.len(), 1);
assert_eq!(&r[0][0], &Value::Integer(42));
}
#[test]
fn test_inverted_range_returns_empty() {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, val INT)")
.unwrap();
for i in 1..=10 {
db.execute(&format!("INSERT INTO t VALUES ({}, {})", i, i))
.unwrap();
}
let r = rows(
db.execute("SELECT * FROM t WHERE val BETWEEN 10 AND 1")
.unwrap(),
);
assert!(
r.len() <= 10,
"Inverted range should return 0 rows or be handled gracefully"
);
}
#[test]
fn test_update_with_expression_arithmetic() {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, val INT)")
.unwrap();
db.execute("INSERT INTO t VALUES (1, 10)").unwrap();
db.execute("UPDATE t SET val = val * 2 + 1 WHERE id = 1")
.unwrap();
let r = rows(db.execute("SELECT val FROM t WHERE id = 1").unwrap());
assert_eq!(&r[0][0], &Value::Integer(21), "val should be 10*2+1 = 21");
}
#[test]
fn test_delete_from_empty_table() {
let dir = TempDir::new().unwrap();
let db = Database::create(dir.path()).unwrap();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, val TEXT)")
.unwrap();
let result = db.execute("DELETE FROM t WHERE id = 999");
assert!(result.is_ok(), "DELETE from empty table should not error");
let r = rows(db.execute("SELECT COUNT(*) FROM t").unwrap());
assert_eq!(&r[0][0], &Value::Integer(0));
}