Add extensive rust integration tests for bindings parameters

This commit is contained in:
PThorpe92
2025-05-07 21:05:22 -04:00
parent 3691779408
commit 9b8227dbf8
2 changed files with 153 additions and 2 deletions

View File

@@ -38,6 +38,10 @@ impl TempDatabase {
}
pub fn new_with_rusqlite(table_sql: &str) -> Self {
let _ = tracing_subscriber::fmt()
.with_max_level(tracing::Level::TRACE)
.finish()
.try_init();
let mut path = TempDir::new().unwrap().into_path();
path.push("test.db");
{

View File

@@ -3,7 +3,6 @@ use limbo_core::{OwnedValue, StepResult};
#[test]
fn test_statement_reset_bind() -> anyhow::Result<()> {
let _ = env_logger::try_init();
let tmp_db = TempDatabase::new_with_rusqlite("create table test (i integer);");
let conn = tmp_db.connect_limbo();
@@ -48,7 +47,6 @@ fn test_statement_reset_bind() -> anyhow::Result<()> {
#[test]
fn test_statement_bind() -> anyhow::Result<()> {
let _ = env_logger::try_init();
let tmp_db = TempDatabase::new_with_rusqlite("create table test (i integer);");
let conn = tmp_db.connect_limbo();
@@ -99,3 +97,152 @@ fn test_statement_bind() -> anyhow::Result<()> {
}
Ok(())
}
#[test]
fn test_insert_parameter_remap() -> anyhow::Result<()> {
// ─────────────────────── schema ──────────────────────────────
// Table a b c d
// INSERT lists: d , c , a , b
// VALUES list: 22 , ?1 , 7 , ?2
//
// Expected row on disk: a = 7 , b = ?2 , c = ?1 , d = 22
//
// We bind ?1 = 111 and ?2 = 222 and expect (7,222,111,22).
// ───────────────────────────────────────────────────────────────
let tmp_db = TempDatabase::new_with_rusqlite(
"create table test (a integer, b integer, c integer, d integer);",
);
let conn = tmp_db.connect_limbo();
// prepare INSERT with re-ordered columns and constants
let mut ins = conn.prepare("insert into test (d, c, a, b) values (22, ?, 7, ?);")?;
let args = [OwnedValue::Integer(111), OwnedValue::Integer(222)];
for (i, arg) in args.iter().enumerate() {
let idx = i + 1;
ins.bind_at(idx.try_into()?, arg.clone());
}
loop {
match ins.step()? {
StepResult::IO => tmp_db.io.run_once()?,
StepResult::Done | StepResult::Interrupt => break,
StepResult::Busy => panic!("database busy"),
_ => {}
}
}
let mut sel = conn.prepare("select a, b, c, d from test;")?;
loop {
match sel.step()? {
StepResult::Row => {
let row = sel.row().unwrap();
// insert_index = 3
// A = 7
assert_eq!(row.get::<&OwnedValue>(0).unwrap(), &OwnedValue::Integer(7));
// insert_index = 4
// B = 222
assert_eq!(
row.get::<&OwnedValue>(1).unwrap(),
&OwnedValue::Integer(222)
);
// insert_index = 2
// C = 111
assert_eq!(
row.get::<&OwnedValue>(2).unwrap(),
&OwnedValue::Integer(111)
);
// insert_index = 1
// D = 22
assert_eq!(row.get::<&OwnedValue>(3).unwrap(), &OwnedValue::Integer(22));
}
StepResult::IO => tmp_db.io.run_once()?,
StepResult::Done | StepResult::Interrupt => break,
StepResult::Busy => panic!("database busy"),
}
}
// exactly two distinct parameters were used
assert_eq!(ins.parameters().count(), 2);
Ok(())
}
#[test]
fn test_insert_parameter_remap_all_params() -> anyhow::Result<()> {
// ─────────────────────── schema ──────────────────────────────
// Table a b c d
// INSERT lists: d , a , c , b
// VALUES list: ?1 , ?2 , ?3 , ?4
//
// Expected row on disk: a = ?2 , b = ?4 , c = ?3 , d = ?1
//
// We bind ?1 = 999, ?2 = 111, ?3 = 333, ?4 = 444.
// The row should be (111, 444, 333, 999).
// ───────────────────────────────────────────────────────────────
let tmp_db = TempDatabase::new_with_rusqlite(
"create table test (a integer, b integer, c integer, d integer);",
);
let conn = tmp_db.connect_limbo();
let mut ins = conn.prepare("insert into test (d, a, c, b) values (?, ?, ?, ?);")?;
let values = [
OwnedValue::Integer(999), // ?1 → d
OwnedValue::Integer(111), // ?2 → a
OwnedValue::Integer(333), // ?3 → c
OwnedValue::Integer(444), // ?4 → b
];
for (i, value) in values.iter().enumerate() {
let idx = i + 1;
ins.bind_at(idx.try_into()?, value.clone());
}
// execute the insert (no rows returned)
loop {
match ins.step()? {
StepResult::IO => tmp_db.io.run_once()?,
StepResult::Done | StepResult::Interrupt => break,
StepResult::Busy => panic!("database busy"),
_ => {}
}
}
let mut sel = conn.prepare("select a, b, c, d from test;")?;
loop {
match sel.step()? {
StepResult::Row => {
let row = sel.row().unwrap();
// insert_index = 2
// A = 111
assert_eq!(
row.get::<&OwnedValue>(0).unwrap(),
&OwnedValue::Integer(111)
);
// insert_index = 4
// B = 444
assert_eq!(
row.get::<&OwnedValue>(1).unwrap(),
&OwnedValue::Integer(444)
);
// insert_index = 3
// C = 333
assert_eq!(
row.get::<&OwnedValue>(2).unwrap(),
&OwnedValue::Integer(333)
);
// insert_index = 1
// D = 999
assert_eq!(
row.get::<&OwnedValue>(3).unwrap(),
&OwnedValue::Integer(999)
);
}
StepResult::IO => tmp_db.io.run_once()?,
StepResult::Done | StepResult::Interrupt => break,
StepResult::Busy => panic!("database busy"),
}
}
assert_eq!(ins.parameters().count(), 4);
Ok(())
}