#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::indexing_slicing)]
mod sqlite_compat {
use heliosdb_nano::{EmbeddedDatabase, Value};
fn db() -> EmbeddedDatabase {
EmbeddedDatabase::new_in_memory().expect("Failed to create test database")
}
fn to_i64(v: &Value) -> i64 {
match v {
Value::Int2(n) => *n as i64,
Value::Int4(n) => *n as i64,
Value::Int8(n) => *n,
other => panic!("Expected integer, got {:?}", other),
}
}
fn to_str(v: &Value) -> String {
match v {
Value::String(s) => s.clone(),
other => panic!("Expected string, got {:?}", other),
}
}
#[test]
fn question_placeholder_in_insert_and_select() {
let db = db();
db.execute("CREATE TABLE t (id INT PRIMARY KEY, name TEXT)").unwrap();
db.execute("INSERT INTO t (id, name) VALUES (1, 'alice')").unwrap();
let rows = db
.query("SELECT name FROM t WHERE id = 1", &[])
.unwrap();
assert_eq!(rows.len(), 1);
assert_eq!(to_str(&rows[0].values[0]), "alice");
}
#[test]
fn question_placeholder_string_literal_preserved() {
let db = db();
db.execute("CREATE TABLE t (msg TEXT)").unwrap();
db.execute("INSERT INTO t (msg) VALUES ('hello?world')").unwrap();
let rows = db.query("SELECT msg FROM t", &[]).unwrap();
assert_eq!(to_str(&rows[0].values[0]), "hello?world");
}
#[test]
fn autoincrement_mapped_to_bigserial() {
let db = db();
db.execute("CREATE TABLE notes (id INTEGER PRIMARY KEY AUTOINCREMENT, body TEXT)")
.unwrap();
db.execute("INSERT INTO notes (body) VALUES ('first')").unwrap();
db.execute("INSERT INTO notes (body) VALUES ('second')").unwrap();
let rows = db
.query("SELECT id, body FROM notes ORDER BY id", &[])
.unwrap();
assert_eq!(rows.len(), 2);
assert_eq!(to_i64(&rows[0].values[0]), 1);
assert_eq!(to_i64(&rows[1].values[0]), 2);
}
#[test]
fn datetime_now_resolves_to_current_timestamp() {
let db = db();
db.execute("CREATE TABLE events (id INT PRIMARY KEY, ts TIMESTAMP)").unwrap();
db.execute("INSERT INTO events (id, ts) VALUES (1, DATETIME('now'))").unwrap();
let rows = db.query("SELECT ts FROM events", &[]).unwrap();
assert_eq!(rows.len(), 1);
assert!(!matches!(rows[0].values[0], Value::Null));
}
#[test]
fn insert_or_ignore_skips_conflict() {
let db = db();
db.execute("CREATE TABLE kv (k TEXT PRIMARY KEY, v TEXT)").unwrap();
db.execute("INSERT INTO kv (k, v) VALUES ('a', '1')").unwrap();
db.execute("INSERT OR IGNORE INTO kv (k, v) VALUES ('a', '2')")
.unwrap();
let rows = db.query("SELECT v FROM kv WHERE k = 'a'", &[]).unwrap();
assert_eq!(rows.len(), 1);
assert_eq!(to_str(&rows[0].values[0]), "1");
}
#[test]
fn insert_or_replace_overwrites_existing_row() {
let db = db();
db.execute("CREATE TABLE files (path TEXT PRIMARY KEY, mtime REAL, size INT)")
.unwrap();
db.execute("INSERT INTO files (path, mtime, size) VALUES ('/a', 1.0, 100)")
.unwrap();
db.execute(
"INSERT OR REPLACE INTO files (path, mtime, size) VALUES ('/a', 2.0, 200)",
)
.unwrap();
let rows = db
.query("SELECT mtime, size FROM files WHERE path = '/a'", &[])
.unwrap();
assert_eq!(rows.len(), 1);
match &rows[0].values[1] {
Value::Int2(n) => assert_eq!(*n as i64, 200),
Value::Int4(n) => assert_eq!(*n as i64, 200),
Value::Int8(n) => assert_eq!(*n, 200),
other => panic!("Expected integer size, got {:?}", other),
}
}
#[test]
fn sqlite_master_lists_user_tables() {
let db = db();
db.execute("CREATE TABLE alpha (a INT PRIMARY KEY)").unwrap();
db.execute("CREATE TABLE beta (b INT PRIMARY KEY)").unwrap();
let rows = db
.query(
"SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name",
&[],
)
.unwrap();
let names: Vec<String> = rows.iter().map(|r| to_str(&r.values[0])).collect();
assert!(names.contains(&"alpha".to_string()), "names: {:?}", names);
assert!(names.contains(&"beta".to_string()), "names: {:?}", names);
}
#[test]
fn sqlite_master_filters_by_name() {
let db = db();
db.execute("CREATE TABLE messages (uuid TEXT PRIMARY KEY)").unwrap();
let rows = db
.query(
"SELECT 1 FROM sqlite_master WHERE type='table' AND name='messages'",
&[],
)
.unwrap();
assert_eq!(rows.len(), 1);
}
#[test]
fn pragma_table_info_returns_columns() {
let db = db();
db.execute(
"CREATE TABLE schema_probe (id INTEGER PRIMARY KEY, name TEXT NOT NULL, weight REAL)",
)
.unwrap();
let rows = db.query("PRAGMA table_info(schema_probe)", &[]).unwrap();
assert_eq!(rows.len(), 3);
assert_eq!(to_i64(&rows[0].values[0]), 0);
assert_eq!(to_str(&rows[0].values[1]), "id");
assert_eq!(to_i64(&rows[0].values[5]), 1, "id should be marked pk");
assert_eq!(to_str(&rows[1].values[1]), "name");
assert_eq!(to_i64(&rows[1].values[3]), 1, "name NOT NULL");
assert_eq!(to_str(&rows[2].values[1]), "weight");
assert_eq!(to_i64(&rows[2].values[5]), 0, "weight is not pk");
}
#[test]
fn pragma_no_op_tunables_are_accepted() {
let db = db();
db.execute("PRAGMA foreign_keys = ON").unwrap();
db.execute("PRAGMA journal_mode = WAL").unwrap();
db.execute("PRAGMA synchronous = NORMAL").unwrap();
db.execute("PRAGMA busy_timeout = 30000").unwrap();
}
#[test]
fn token_dashboard_schema_smoke() {
let db = db();
db.execute(
"CREATE TABLE tool_calls (
id INTEGER PRIMARY KEY AUTOINCREMENT,
message_uuid TEXT,
tool_name TEXT
)",
)
.unwrap();
db.execute(
"CREATE TABLE files (
path TEXT PRIMARY KEY,
mtime REAL,
size INT
)",
)
.unwrap();
db.execute("PRAGMA foreign_keys = ON").unwrap();
db.execute("INSERT OR REPLACE INTO files (path, mtime, size) VALUES ('/x', 1.0, 10)")
.unwrap();
db.execute("INSERT OR REPLACE INTO files (path, mtime, size) VALUES ('/x', 2.0, 20)")
.unwrap();
let rows = db
.query(
"SELECT name FROM sqlite_master WHERE type='table' AND name='files'",
&[],
)
.unwrap();
assert_eq!(rows.len(), 1);
let cols = db.query("PRAGMA table_info(files)", &[]).unwrap();
assert_eq!(cols.len(), 3);
let names: Vec<String> = cols.iter().map(|r| to_str(&r.values[1])).collect();
assert_eq!(names, vec!["path", "mtime", "size"]);
let final_rows = db
.query("SELECT size FROM files WHERE path='/x'", &[])
.unwrap();
assert_eq!(final_rows.len(), 1);
assert_eq!(to_i64(&final_rows[0].values[0]), 20);
}
}