use spg_engine::{Engine, QueryResult};
use spg_storage::Value;
fn one_row(r: QueryResult) -> Vec<Value<'static>> {
match r {
QueryResult::Rows { rows, .. } => {
assert_eq!(rows.len(), 1);
rows.into_iter().next().unwrap().values
}
_ => panic!("expected Rows"),
}
}
fn one_cell(eng: &mut Engine, sql: &str) -> Value<'static> {
let row = one_row(eng.execute(sql).unwrap());
assert_eq!(row.len(), 1, "{sql}");
row.into_iter().next().unwrap()
}
fn json_text(v: Value<'_>) -> String {
match v {
Value::Json(s) => s.into_owned(),
other => panic!("expected Value::Json, got {other:?}"),
}
}
#[test]
fn to_json_int_renders_bare_number() {
let mut e = Engine::new();
assert_eq!(json_text(one_cell(&mut e, "SELECT to_json(42)")), "42");
}
#[test]
fn to_json_bigint_renders_bare_number() {
let mut e = Engine::new();
assert_eq!(
json_text(one_cell(&mut e, "SELECT to_jsonb(9223372036854775807)")),
"9223372036854775807"
);
}
#[test]
fn to_json_text_quotes_and_escapes() {
let mut e = Engine::new();
let s = json_text(one_cell(&mut e, "SELECT to_json('hi\"there'::text)"));
assert_eq!(s, r#""hi\"there""#);
}
#[test]
fn to_json_text_escapes_newline_and_backslash() {
let mut e = Engine::new();
let s = json_text(one_cell(&mut e, "SELECT to_json('a\\nb'::text)"));
assert_eq!(s, r#""a\\nb""#);
}
#[test]
fn to_json_bool_renders_lowercase() {
let mut e = Engine::new();
assert_eq!(json_text(one_cell(&mut e, "SELECT to_json(true)")), "true");
assert_eq!(
json_text(one_cell(&mut e, "SELECT to_json(false)")),
"false"
);
}
#[test]
fn to_json_of_null_is_sql_null() {
let mut e = Engine::new();
assert!(matches!(
one_cell(&mut e, "SELECT to_json(NULL::INT)"),
Value::Null
));
assert!(matches!(
one_cell(&mut e, "SELECT to_jsonb(NULL::INT)"),
Value::Null
));
assert_eq!(
json_text(one_cell(&mut e, "SELECT to_jsonb('null'::JSON)")),
"null",
"a JSON null VALUE is not a NULL argument"
);
assert_eq!(
json_text(one_cell(&mut e, "SELECT jsonb_build_object('a', NULL)")),
r#"{"a": null}"#
);
}
#[test]
fn to_json_json_passes_through() {
let mut e = Engine::new();
let s = json_text(one_cell(&mut e, r#"SELECT to_jsonb('{"a": 1}'::json)"#));
assert_eq!(s, r#"{"a": 1}"#);
}
#[test]
fn json_build_object_basic_keys_and_values() {
let mut e = Engine::new();
let s = json_text(one_cell(
&mut e,
"SELECT json_build_object('a', 1, 'b', 'two', 'c', true)",
));
assert_eq!(s, r#"{"a" : 1, "b" : "two", "c" : true}"#);
}
#[test]
fn jsonb_build_object_empty_arg_list() {
let mut e = Engine::new();
assert_eq!(
json_text(one_cell(&mut e, "SELECT jsonb_build_object()")),
"{}"
);
}
#[test]
fn json_build_object_value_null_serialises_null() {
let mut e = Engine::new();
let s = json_text(one_cell(
&mut e,
"SELECT json_build_object('a', NULL, 'b', 1)",
));
assert_eq!(s, r#"{"a" : null, "b" : 1}"#);
}
#[test]
fn json_build_object_odd_args_errors() {
let mut e = Engine::new();
let r = e.execute("SELECT json_build_object('a', 1, 'b')");
assert!(r.is_err(), "odd-length arg list must error");
}
#[test]
fn json_build_object_null_key_errors() {
let mut e = Engine::new();
let r = e.execute("SELECT json_build_object(NULL, 1)");
assert!(r.is_err(), "NULL key must error");
}
#[test]
fn json_build_array_mixed_types() {
let mut e = Engine::new();
let s = json_text(one_cell(
&mut e,
"SELECT json_build_array(1, 'x', true, NULL)",
));
assert_eq!(s, r#"[1, "x", true, null]"#);
}
#[test]
fn jsonb_build_array_empty_arg_list() {
let mut e = Engine::new();
assert_eq!(
json_text(one_cell(&mut e, "SELECT jsonb_build_array()")),
"[]"
);
}
#[test]
fn jsonb_set_replaces_existing_object_key() {
let mut e = Engine::new();
let s = json_text(one_cell(
&mut e,
r#"SELECT jsonb_set('{"a": 1, "b": 2}', '{a}', '99')"#,
));
assert_eq!(s, r#"{"a": 99, "b": 2}"#);
}
#[test]
fn jsonb_set_replaces_nested_object_key() {
let mut e = Engine::new();
let s = json_text(one_cell(
&mut e,
r#"SELECT jsonb_set('{"a":{"b":{"c":1}}}', '{a,b,c}', '"x"')"#,
));
assert_eq!(s, r#"{"a": {"b": {"c": "x"}}}"#);
}
#[test]
fn jsonb_set_replaces_array_index() {
let mut e = Engine::new();
let s = json_text(one_cell(
&mut e,
r#"SELECT jsonb_set('[10,20,30]', '{1}', '99')"#,
));
assert_eq!(s, "[10, 99, 30]");
}
#[test]
fn jsonb_set_creates_missing_object_key_when_default_true() {
let mut e = Engine::new();
let s = json_text(one_cell(
&mut e,
r#"SELECT jsonb_set('{"a": 1}', '{b}', '2')"#,
));
assert_eq!(s, r#"{"a": 1, "b": 2}"#);
}
#[test]
fn jsonb_set_missing_with_create_missing_false_returns_unchanged() {
let mut e = Engine::new();
let s = json_text(one_cell(
&mut e,
r#"SELECT jsonb_set('{"a": 1}', '{b}', '2', false)"#,
));
assert_eq!(s, r#"{"a": 1}"#);
}
#[test]
fn jsonb_insert_into_array_before_index_by_default() {
let mut e = Engine::new();
let s = json_text(one_cell(
&mut e,
r#"SELECT jsonb_insert('[10,20,30]', '{1}', '99')"#,
));
assert_eq!(s, "[10, 99, 20, 30]");
}
#[test]
fn jsonb_insert_into_array_after_index_when_true() {
let mut e = Engine::new();
let s = json_text(one_cell(
&mut e,
r#"SELECT jsonb_insert('[10,20,30]', '{1}', '99', true)"#,
));
assert_eq!(s, "[10, 20, 99, 30]");
}
#[test]
fn jsonb_insert_creates_new_object_key() {
let mut e = Engine::new();
let s = json_text(one_cell(
&mut e,
r#"SELECT jsonb_insert('{"a": 1}', '{b}', '"new"')"#,
));
assert_eq!(s, r#"{"a": 1, "b": "new"}"#);
}
#[test]
fn jsonb_insert_errors_on_existing_object_key() {
let mut e = Engine::new();
let r = e.execute(r#"SELECT jsonb_insert('{"a": 1}', '{a}', '2')"#);
assert!(r.is_err(), "jsonb_insert on existing key must error");
}