#![cfg(test)]
use crate::storage::query::ast::QueryExpr;
use crate::storage::query::parser::parse;
use crate::storage::schema::Value;
fn parse_first_value(insert_sql: &str) -> Value {
let q = parse(insert_sql)
.unwrap_or_else(|err| panic!("parse error for `{}`: {}", insert_sql, err))
.query;
let QueryExpr::Insert(ins) = q else {
panic!("expected InsertQuery, got {:?}", insert_sql);
};
ins.values
.into_iter()
.next()
.and_then(|row| row.into_iter().next())
.unwrap_or_else(|| panic!("no value parsed for `{}`", insert_sql))
}
fn canonical_json_bytes(raw: &str) -> Vec<u8> {
let parsed = crate::utils::json::parse_json(raw)
.unwrap_or_else(|err| panic!("parse_json failed for `{}`: {}", raw, err));
let canonical = crate::serde_json::Value::from(parsed);
crate::json::to_vec(&canonical).expect("to_vec must succeed for valid JSON")
}
fn assert_bytewise_equivalent(raw: &str) {
let sql = format!("INSERT INTO t (body) VALUES ({})", raw);
let v = parse_first_value(&sql);
let Value::Json(bare_bytes) = v else {
panic!("expected Value::Json for `{}`, got {:?}", raw, v);
};
let expected = canonical_json_bytes(raw);
assert_eq!(bare_bytes, expected, "bytewise mismatch for `{}`", raw);
}
#[test]
fn happy_empty_object() {
assert_bytewise_equivalent(r#"{}"#);
}
#[test]
fn happy_single_key() {
assert_bytewise_equivalent(r#"{"a":1}"#);
}
#[test]
fn happy_multi_key() {
assert_bytewise_equivalent(r#"{"a":1,"b":2,"c":3}"#);
}
#[test]
fn happy_nested() {
assert_bytewise_equivalent(r#"{"outer":{"inner":{"deep":42}}}"#);
}
#[test]
fn happy_array_value() {
assert_bytewise_equivalent(r#"{"tags":["a","b","c"]}"#);
}
#[test]
fn happy_mixed_types() {
assert_bytewise_equivalent(r#"{"s":"x","i":1,"f":3.14,"b":true,"n":null,"a":[1,2]}"#);
}
#[test]
fn happy_unicode_emoji() {
assert_bytewise_equivalent(r#"{"emoji":"🚀","ja":"日本語"}"#);
}
#[test]
fn happy_surrogate_pair_via_escape() {
let raw = r#"{"face":"😀"}"#;
let sql = format!("INSERT INTO t (body) VALUES ({})", raw);
let v = parse_first_value(&sql);
let Value::Json(bytes) = v else {
panic!("expected Json, got {:?}", v);
};
assert!(!bytes.is_empty());
}
#[test]
fn happy_negative_number() {
assert_bytewise_equivalent(r#"{"n":-42}"#);
}
#[test]
fn happy_exponent_number() {
assert_bytewise_equivalent(r#"{"n":1.5e10}"#);
}
#[test]
fn happy_max_value_number() {
assert_bytewise_equivalent(r#"{"n":9007199254740992}"#);
}
#[test]
fn happy_empty_string_value() {
assert_bytewise_equivalent(r#"{"s":""}"#);
}
#[test]
fn happy_deeply_nested_4_levels() {
assert_bytewise_equivalent(r#"{"a":{"b":{"c":{"d":1}}}}"#);
}
#[test]
fn whitespace_compact() {
assert_bytewise_equivalent(r#"{"a":1}"#);
}
#[test]
fn whitespace_padded_around_colon() {
let raw = r#"{"a" : 1}"#;
let sql = format!("INSERT INTO t (body) VALUES ({})", raw);
let v = parse_first_value(&sql);
assert!(matches!(v, Value::Json(_)));
}
#[test]
fn whitespace_padded_around_comma() {
let raw = r#"{"a":1 , "b":2}"#;
let sql = format!("INSERT INTO t (body) VALUES ({})", raw);
assert!(matches!(parse_first_value(&sql), Value::Json(_)));
}
#[test]
fn whitespace_multiline() {
let raw = "{\n \"a\": 1,\n \"b\": 2\n}";
let sql = format!("INSERT INTO t (body) VALUES ({})", raw);
assert!(matches!(parse_first_value(&sql), Value::Json(_)));
}
#[test]
fn whitespace_tabs() {
let raw = "{\t\"a\":\t1\t}";
let sql = format!("INSERT INTO t (body) VALUES ({})", raw);
assert!(matches!(parse_first_value(&sql), Value::Json(_)));
}
#[test]
fn whitespace_no_space_after_lparen() {
let raw = r#"{"a":1}"#;
let sql = format!("INSERT INTO t (body) VALUES({})", raw); assert!(matches!(parse_first_value(&sql), Value::Json(_)));
}
#[test]
fn ctx_insert_values() {
let q = parse(r#"INSERT INTO logs (body) VALUES ({"level":"info"})"#)
.unwrap()
.query;
assert!(matches!(q, QueryExpr::Insert(_)));
}
#[test]
fn ctx_insert_document_form() {
let q = parse(r#"INSERT INTO logs DOCUMENT (body) VALUES ({"level":"info"})"#)
.unwrap()
.query;
assert!(matches!(q, QueryExpr::Insert(_)));
}
#[test]
fn ctx_update_set() {
let q = parse(r#"UPDATE logs SET body = {"level":"warn"} WHERE id = 1"#)
.unwrap()
.query;
assert!(matches!(q, QueryExpr::Update(_)));
}
#[test]
fn ctx_select_projection() {
let q = parse(r#"SELECT {"k":1} AS lit FROM dual"#).unwrap().query;
assert!(matches!(q, QueryExpr::Table(_)));
}
#[test]
fn ctx_where_compare() {
let q = parse(r#"SELECT * FROM logs WHERE body = {"level":"info"}"#)
.unwrap()
.query;
assert!(matches!(q, QueryExpr::Table(_)));
}
#[test]
fn ctx_function_arg() {
let q = parse(r#"SELECT JSON_EXTRACT({"a":1}, '$.a') FROM dual"#)
.unwrap()
.query;
assert!(matches!(q, QueryExpr::Table(_)));
}
#[test]
fn ctx_batched_insert_mixing_forms() {
let q = parse(r#"INSERT INTO logs (body) VALUES ({"a":1}), ('{"b":2}')"#)
.unwrap()
.query;
let QueryExpr::Insert(ins) = q else {
panic!("expected insert");
};
assert_eq!(ins.values.len(), 2);
assert!(matches!(ins.values[0][0], Value::Json(_)));
assert!(matches!(ins.values[1][0], Value::Text(_)));
}
#[test]
fn ctx_queue_push() {
let q = parse(r#"QUEUE PUSH tasks {"job":"hello","retries":3}"#)
.unwrap()
.query;
assert!(matches!(q, QueryExpr::QueueCommand(_)));
}
#[test]
fn err_unbalanced_open_brace() {
let r = parse(r#"INSERT INTO t (body) VALUES ({"a":1)"#);
assert!(r.is_err(), "expected error for unbalanced `{{`");
}
#[test]
fn err_unbalanced_close_brace() {
let r = parse(r#"INSERT INTO t (body) VALUES ({"a":1}})"#);
assert!(r.is_err());
}
#[test]
fn err_trailing_comma() {
let r = parse(r#"INSERT INTO t (body) VALUES ({"a":1,})"#);
assert!(r.is_err(), "trailing comma must error");
}
#[test]
fn err_single_quotes_inside_object() {
let r = parse(r#"INSERT INTO t (body) VALUES ({"a":'x'})"#);
assert!(
r.is_err(),
"single-quoted value inside JSON literal must error"
);
}
#[test]
fn err_missing_key_quotes() {
let v = parse_first_value(r#"INSERT INTO t (body) VALUES ({a:1})"#);
assert!(matches!(v, Value::Json(_)));
}
#[test]
fn duplicate_keys_last_wins() {
let v = parse_first_value(r#"INSERT INTO t (body) VALUES ({"a":1,"a":2})"#);
let Value::Json(bytes) = v else {
panic!("expected Json");
};
let s = std::str::from_utf8(&bytes).unwrap();
assert!(s.contains("\"a\""));
assert!(s.contains("2"));
}
#[test]
fn err_js_style_comment() {
let r = parse(r#"INSERT INTO t (body) VALUES ({"a":1//x})"#);
assert!(r.is_err());
}
#[test]
fn err_infinity_literal() {
let r = parse(r#"INSERT INTO t (body) VALUES ({"n":Infinity})"#);
assert!(r.is_err());
}
#[test]
fn err_nan_literal() {
let r = parse(r#"INSERT INTO t (body) VALUES ({"n":NaN})"#);
assert!(r.is_err());
}
#[test]
fn err_leading_zero_number() {
let r = parse(r#"INSERT INTO t (body) VALUES ({"n":01})"#);
assert!(r.is_err());
}
#[test]
fn raw_newline_in_string_documented_lenient() {
let raw = "{\"s\":\"a\nb\"}";
let sql = format!("INSERT INTO t (body) VALUES ({})", raw);
let v = parse_first_value(&sql);
assert!(matches!(v, Value::Json(_)));
}
#[test]
fn err_trailing_chars_after_object() {
let r = parse(r#"INSERT INTO t (body) VALUES ({"a":1}xyz)"#);
assert!(r.is_err());
}
#[test]
fn ok_mixed_quotes_in_string_value() {
assert_bytewise_equivalent(r#"{"path":"O'Brien"}"#);
}
#[test]
fn err_lone_open_brace_in_where_position() {
let r = parse(r#"SELECT * FROM t WHERE { = 1"#);
assert!(r.is_err());
}
#[test]
fn quoted_form_still_parses() {
let q = parse(r#"INSERT INTO logs DOCUMENT (body) VALUES ('{"a":1}')"#)
.unwrap()
.query;
let QueryExpr::Insert(ins) = q else {
panic!("expected Insert");
};
assert!(matches!(ins.values[0][0], Value::Text(_)));
}
#[test]
fn vector_literal_still_parses() {
let q = parse(r#"INSERT INTO emb VECTOR (dense) VALUES ([0.1, 0.2, 0.3])"#)
.unwrap()
.query;
let QueryExpr::Insert(ins) = q else {
panic!("expected Insert");
};
assert!(matches!(ins.values[0][0], Value::Vector(_)));
}
#[test]
fn in_list_paren_intact() {
let q = parse(r#"SELECT * FROM logs WHERE id IN (1, 2, 3)"#)
.unwrap()
.query;
assert!(matches!(q, QueryExpr::Table(_)));
}
#[test]
fn cypher_property_bag_still_works() {
let q = parse(r#"MATCH (n:User {name: 'alice'}) RETURN n"#)
.unwrap()
.query;
assert!(matches!(q, QueryExpr::Graph(_)));
}
#[test]
fn bare_and_quoted_produce_equivalent_bytes() {
let bare_v = parse_first_value(r#"INSERT INTO t (body) VALUES ({"a":1})"#);
let Value::Json(bare_bytes) = bare_v else {
panic!("expected Json");
};
let quoted_text = r#"{"a":1}"#;
let expected = canonical_json_bytes(quoted_text);
assert_eq!(bare_bytes, expected, "bytewise on-disk mismatch");
}
#[test]
fn dos_recursion_depth_exceeded() {
let mut s = String::new();
for _ in 0..200 {
s.push_str(r#"{"a":"#);
}
s.push('1');
for _ in 0..200 {
s.push('}');
}
let sql = format!("INSERT INTO t (body) VALUES ({})", s);
let r = parse(&sql);
let err = r.expect_err("expected depth-limit error");
assert!(
err.to_string().contains("JSON_LITERAL_MAX_DEPTH"),
"expected depth error, got: {}",
err
);
}
#[test]
fn dos_recursion_depth_exactly_at_limit_passes() {
let mut s = String::new();
for _ in 0..64 {
s.push_str(r#"{"a":"#);
}
s.push('1');
for _ in 0..64 {
s.push('}');
}
let sql = format!("INSERT INTO t (body) VALUES ({})", s);
parse(&sql).expect("64 levels must parse");
}
#[test]
fn dos_thousand_keys() {
let mut s = String::from("{");
for i in 0..1000 {
if i > 0 {
s.push(',');
}
s.push_str(&format!("\"k{}\":{}", i, i));
}
s.push('}');
let sql = format!("INSERT INTO t (body) VALUES ({})", s);
let v = parse_first_value(&sql);
assert!(matches!(v, Value::Json(_)));
}
#[test]
fn dos_payload_size_exceeded() {
use crate::storage::query::lexer::JSON_LITERAL_MAX_BYTES;
let pad = "x".repeat(JSON_LITERAL_MAX_BYTES + 16);
let raw = format!(r#"{{"k":"{}"}}"#, pad);
let sql = format!("INSERT INTO t (body) VALUES ({})", raw);
let r = parse(&sql);
let err = r.expect_err("expected size-limit error");
let msg = err.to_string();
assert!(
msg.contains("JSON_LITERAL_MAX_BYTES")
|| msg.contains("InputTooLarge")
|| msg.contains("max_input_bytes"),
"expected size-limit error, got: {}",
err
);
}
#[test]
fn dos_unbalanced_eof_no_panic() {
let r = parse(r#"INSERT INTO t (body) VALUES ({"a":"#);
assert!(r.is_err());
}
#[test]
fn dos_invalid_utf8_in_string() {
let r = parse(r#"INSERT INTO t (body) VALUES ({"k":"\uZZZZ"})"#);
assert!(r.is_err());
}
#[test]
fn dos_bom_at_start_of_literal() {
let raw = "{\u{FEFF}\"a\":1}";
let sql = format!("INSERT INTO t (body) VALUES ({})", raw);
let r = parse(&sql);
assert!(r.is_err());
}