#![cfg(feature = "embedded")]
use reddb_client::embedded::EmbeddedClient;
use reddb_client::{JsonValue, Value};
fn open() -> EmbeddedClient {
EmbeddedClient::in_memory().expect("memory db")
}
fn make_users(c: &EmbeddedClient) {
c.query("CREATE TABLE users (id INTEGER, name TEXT, age INTEGER, score FLOAT)")
.expect("create users");
c.insert(
"users",
&JsonValue::object([
("id", JsonValue::number(1)),
("name", JsonValue::string("Alice")),
("age", JsonValue::number(30)),
("score", JsonValue::number(9.5)),
]),
)
.expect("insert alice");
c.insert(
"users",
&JsonValue::object([
("id", JsonValue::number(2)),
("name", JsonValue::string("Bob")),
("age", JsonValue::number(25)),
("score", JsonValue::number(7.1)),
]),
)
.expect("insert bob");
}
#[tokio::test]
async fn query_with_int_param_matches_one_row() {
let c = open();
make_users(&c);
let r = c
.query_with("SELECT * FROM users WHERE id = $1", &[Value::Int(2)])
.expect("query_with int");
assert_eq!(r.rows.len(), 1);
let name = r.rows[0]
.iter()
.find(|(k, _)| k == "name")
.map(|(_, v)| format!("{v}"))
.unwrap();
assert_eq!(name, "Bob");
}
#[tokio::test]
async fn query_with_text_and_null_params() {
let c = open();
make_users(&c);
let r = c
.query_with(
"SELECT * FROM users WHERE name = $1 AND $2 IS NULL",
&[Value::Text("Alice".into()), Value::Null],
)
.expect("query_with text+null");
assert_eq!(r.rows.len(), 1);
}
#[tokio::test]
async fn query_with_limit_param_bounds_results() {
let c = open();
make_users(&c);
let r = c
.query_with("SELECT * FROM users LIMIT $1", &[Value::Int(1)])
.expect("query_with limit");
assert_eq!(r.rows.len(), 1);
}
#[tokio::test]
async fn query_with_vector_param_serializes_to_engine_value() {
let v = Value::Vector(vec![0.1, 0.2, 0.3]);
let schema = v.into_schema_value();
match schema {
reddb_server::storage::schema::Value::Vector(v) => {
assert_eq!(v, vec![0.1f32, 0.2, 0.3]);
}
other => panic!("expected SV::Vector, got {other:?}"),
}
}
#[tokio::test]
async fn query_with_empty_params_routes_to_legacy_path() {
let c = open();
make_users(&c);
let r = c
.query_with("SELECT * FROM users", &[] as &[Value])
.expect("query_with empty");
assert_eq!(r.rows.len(), 2);
}
#[tokio::test]
async fn query_with_arity_mismatch_surfaces_error() {
let c = open();
make_users(&c);
let err = c
.query_with(
"SELECT * FROM users WHERE id = $1 AND name = $2",
&[Value::Int(1)],
)
.expect_err("arity mismatch should error");
let msg = err.to_string();
assert!(
msg.contains("parameters") || msg.contains("expects"),
"got {msg}"
);
}
#[tokio::test]
async fn query_with_intovalue_ergonomic_form() {
use reddb_client::IntoValue;
let c = open();
make_users(&c);
let params: Vec<Value> = vec![1i64.into_value(), "Alice".into_value()];
let r = c
.query_with("SELECT * FROM users WHERE id = $1 AND name = $2", ¶ms)
.expect("query_with intovalue");
assert_eq!(r.rows.len(), 1);
}
#[tokio::test]
async fn execute_with_inserts_then_query_with_tuple_selects() {
let c = open();
c.execute_with("CREATE TABLE exec_params (id INTEGER, name TEXT)", ())
.expect("create table");
let inserted = c
.execute_with(
"INSERT INTO exec_params (id, name) VALUES ($1, $2)",
(3i64, "Cara"),
)
.expect("insert with tuple params");
assert_eq!(inserted.affected, 1);
let selected = c
.query_with("SELECT * FROM exec_params WHERE id = $1", (3i64,))
.expect("select with tuple params");
assert_eq!(selected.rows.len(), 1);
}
#[test]
fn intoparams_supports_unit_slices_arrays_vecs_and_tuples_to_arity_8() {
use reddb_client::{IntoParams, IntoValue};
assert!(().into_params().is_empty());
assert_eq!((&[Value::Int(1)]).into_params(), vec![Value::Int(1)]);
assert_eq!([Value::Int(1), Value::Int(2)].into_params().len(), 2);
assert_eq!(vec![Value::Int(1)].into_params(), vec![Value::Int(1)]);
let values = (
1i64,
true,
2.5f64,
"text",
String::from("owned"),
vec![1u8, 2],
vec![0.1f32, 0.2],
Option::<i64>::None,
)
.into_params();
assert_eq!(values.len(), 8);
assert_eq!(values[0], 1i64.into_value());
assert_eq!(values[7], Value::Null);
}