Files
el/lang/tests/suite/src/test_json.el
2026-05-05 01:38:51 -05:00

278 lines
13 KiB
EmacsLisp

// tests/suite/test_json.el comprehensive tests for runtime/json.el
//
// Covers json_get, json_get_raw, json_set, json_array_len, json_array_get,
// json_array_get_string, json_escape_string, json_build_object, json_build_array,
// json_array_push, typed extractors, bytes_to_str, and nested JSON.
// json_get
fn test_json_get_basic(_: String) -> String {
let obj: String = "{\"name\":\"alice\",\"age\":\"30\"}"
assert_eq(json_get(obj, "name"), "alice", "get string field")
assert_eq(json_get(obj, "age"), "30", "get numeric-looking string field")
assert_eq(json_get(obj, "missing"), "", "missing key returns empty string")
return ""
}
fn test_json_get_types(_: String) -> String {
let obj: String = "{\"count\":42,\"flag\":true,\"ratio\":3.14}"
assert_eq(json_get(obj, "count"), "42", "get integer field")
assert_eq(json_get(obj, "flag"), "true", "get boolean field")
assert_eq(json_get(obj, "ratio"), "3.14", "get float field")
assert_eq(json_get(obj, "absent"), "", "absent key yields empty")
return ""
}
fn test_json_get_nested(_: String) -> String {
let obj: String = "{\"user\":{\"name\":\"bob\",\"role\":\"admin\"}}"
assert_eq(json_get(obj, "user.name"), "bob", "dot-path traversal: user.name")
assert_eq(json_get(obj, "user.role"), "admin", "dot-path traversal: user.role")
assert_eq(json_get(obj, "user.missing"), "", "missing nested key")
return ""
}
fn test_json_get_empty(_: String) -> String {
let empty_obj: String = "{}"
assert_eq(json_get(empty_obj, "key"), "", "empty object: any key is missing")
assert_eq(json_get(empty_obj, "a.b"), "", "empty object: nested path missing")
return ""
}
// json_get typed extractors
fn test_json_get_int_basic(_: String) -> String {
let obj: String = "{\"count\":42,\"negative\":-7,\"zero\":0}"
assert_int_eq(json_get_int(obj, "count"), 42, "get int field 42")
assert_int_eq(json_get_int(obj, "negative"), -7, "get negative int field")
assert_int_eq(json_get_int(obj, "zero"), 0, "get zero int field")
assert_int_eq(json_get_int(obj, "missing"), 0, "missing int field returns 0")
return ""
}
fn test_json_get_bool_basic(_: String) -> String {
let obj: String = "{\"ok\":true,\"fail\":false}"
assert_true(json_get_bool(obj, "ok"), "get true bool field")
assert_false(json_get_bool(obj, "fail"), "get false bool field")
assert_false(json_get_bool(obj, "missing"), "missing bool field returns false")
return ""
}
fn test_json_get_float_basic(_: String) -> String {
let obj: String = "{\"ratio\":3.14,\"negative\":-1.5}"
let ratio: Float = json_get_float(obj, "ratio")
assert_true(ratio > 3.13, "ratio > 3.13")
assert_true(ratio < 3.15, "ratio < 3.15")
let neg: Float = json_get_float(obj, "negative")
assert_true(neg < -1.4, "negative float")
assert_true(neg > -1.6, "negative float bound")
return ""
}
// json_set
fn test_json_set_basic(_: String) -> String {
let obj: String = "{\"name\":\"alice\"}"
let obj2: String = json_set(obj, "name", "\"bob\"")
assert_eq(json_get(obj2, "name"), "bob", "overwrites existing key")
let obj3: String = json_set(obj, "role", "\"admin\"")
assert_eq(json_get(obj3, "role"), "admin", "inserts new key")
assert_eq(json_get(obj3, "name"), "alice", "existing key unchanged")
return ""
}
fn test_json_set_numeric(_: String) -> String {
let obj: String = "{}"
let obj2: String = json_set(obj, "count", "42")
assert_eq(json_get(obj2, "count"), "42", "set integer value")
let obj3: String = json_set(obj2, "count", "100")
assert_eq(json_get(obj3, "count"), "100", "overwrites integer value")
return ""
}
fn test_json_set_chained(_: String) -> String {
let obj: String = "{}"
let obj1: String = json_set(obj, "a", "\"1\"")
let obj2: String = json_set(obj1, "b", "\"2\"")
let obj3: String = json_set(obj2, "c", "\"3\"")
assert_eq(json_get(obj3, "a"), "1", "chained set: a")
assert_eq(json_get(obj3, "b"), "2", "chained set: b")
assert_eq(json_get(obj3, "c"), "3", "chained set: c")
return ""
}
// json_array_len
fn test_json_array_len_basic(_: String) -> String {
assert_int_eq(json_array_len("[]"), 0, "empty array has length 0")
assert_int_eq(json_array_len("[1]"), 1, "single element array")
assert_int_eq(json_array_len("[1,2,3]"), 3, "three element array")
assert_int_eq(json_array_len("[\"a\",\"b\"]"), 2, "string array length")
assert_int_eq(json_array_len("[1,2,3,4,5]"), 5, "five element array")
return ""
}
// json_array_get / json_array_get_string
fn test_json_array_get_basic(_: String) -> String {
let arr: String = "[\"alpha\",\"beta\",\"gamma\"]"
assert_eq(json_array_get_string(arr, 0), "alpha", "first element")
assert_eq(json_array_get_string(arr, 1), "beta", "second element")
assert_eq(json_array_get_string(arr, 2), "gamma", "third element")
assert_eq(json_array_get_string(arr, 3), "", "out of bounds returns empty")
return ""
}
fn test_json_array_get_numbers(_: String) -> String {
let arr: String = "[10,20,30]"
assert_eq(json_array_get(arr, 0), "10", "first number as fragment")
assert_eq(json_array_get(arr, 1), "20", "second number as fragment")
assert_eq(json_array_get(arr, 2), "30", "third number as fragment")
assert_int_eq(str_to_int(json_array_get(arr, 0)), 10, "convert fragment to int")
return ""
}
fn test_json_array_get_objects(_: String) -> String {
let arr: String = "[{\"name\":\"alice\"},{\"name\":\"bob\"}]"
let first: String = json_array_get(arr, 0)
let second: String = json_array_get(arr, 1)
assert_eq(json_get(first, "name"), "alice", "first object name")
assert_eq(json_get(second, "name"), "bob", "second object name")
assert_int_eq(json_array_len(arr), 2, "array of objects has correct length")
return ""
}
// json_escape_string
fn test_json_escape_string_basic(_: String) -> String {
assert_eq(json_escape_string("hello"), "hello", "plain string unchanged")
assert_eq(json_escape_string(""), "", "empty string unchanged")
assert_contains(json_escape_string("say \"hello\""), "\\\"", "double quotes escaped")
assert_contains(json_escape_string("line1\nline2"), "\\n", "newline escaped")
assert_contains(json_escape_string("col1\tcol2"), "\\t", "tab escaped")
return ""
}
fn test_json_escape_string_backslash(_: String) -> String {
let escaped: String = json_escape_string("a\\b")
assert_contains(escaped, "\\\\", "backslash doubled")
assert_true(str_len(escaped) > 3, "escaped is longer than original")
return ""
}
fn test_json_escape_roundtrip(_: String) -> String {
// Build a JSON string with an escaped value and extract it back
let raw: String = "hello world"
let escaped: String = json_escape_string(raw)
let json_str: String = "{\"msg\":\"" + escaped + "\"}"
assert_eq(json_get(json_str, "msg"), raw, "roundtrip: escape then parse")
return ""
}
// json_build_object
fn test_json_build_object_basic(_: String) -> String {
let parts: [String] = el_list_empty()
let parts = el_list_append(parts, "name")
let parts = el_list_append(parts, "alice")
let parts = el_list_append(parts, "role")
let parts = el_list_append(parts, "admin")
let obj: String = json_build_object(parts)
assert_eq(json_get(obj, "name"), "alice", "build object: name field")
assert_eq(json_get(obj, "role"), "admin", "build object: role field")
assert_true(str_starts_with(obj, "{"), "build object starts with {")
assert_true(str_ends_with(obj, "}"), "build object ends with }")
return ""
}
fn test_json_build_object_empty(_: String) -> String {
let parts: [String] = el_list_empty()
let obj: String = json_build_object(parts)
assert_true(str_starts_with(obj, "{"), "empty build object starts with {")
assert_true(str_ends_with(obj, "}"), "empty build object ends with }")
return ""
}
// json_build_array
fn test_json_build_array_basic(_: String) -> String {
let items: [String] = el_list_empty()
let items = el_list_append(items, "\"alice\"")
let items = el_list_append(items, "\"bob\"")
let arr: String = json_build_array(items)
assert_int_eq(json_array_len(arr), 2, "built array has 2 elements")
assert_eq(json_array_get_string(arr, 0), "alice", "first element of built array")
assert_eq(json_array_get_string(arr, 1), "bob", "second element of built array")
return ""
}
fn test_json_build_array_empty(_: String) -> String {
let items: [String] = el_list_empty()
let arr: String = json_build_array(items)
assert_int_eq(json_array_len(arr), 0, "empty built array has 0 elements")
assert_eq(arr, "[]", "empty array is []")
return ""
}
fn test_json_build_array_numbers(_: String) -> String {
let items: [String] = el_list_empty()
let items = el_list_append(items, "1")
let items = el_list_append(items, "2")
let items = el_list_append(items, "3")
let arr: String = json_build_array(items)
assert_int_eq(json_array_len(arr), 3, "numeric array has 3 elements")
assert_eq(json_array_get(arr, 0), "1", "first numeric element")
assert_eq(json_array_get(arr, 2), "3", "last numeric element")
return ""
}
// json_array_push
fn test_json_array_push_basic(_: String) -> String {
let arr: String = "[]"
let arr2: String = json_array_push(arr, "\"hello\"")
assert_int_eq(json_array_len(arr2), 1, "after push: length 1")
assert_eq(json_array_get_string(arr2, 0), "hello", "pushed element accessible")
let arr3: String = json_array_push(arr2, "\"world\"")
assert_int_eq(json_array_len(arr3), 2, "after second push: length 2")
assert_eq(json_array_get_string(arr3, 1), "world", "second pushed element")
return ""
}
fn test_json_array_push_preserves_order(_: String) -> String {
let arr: String = "[\"a\",\"b\"]"
let arr2: String = json_array_push(arr, "\"c\"")
assert_eq(json_array_get_string(arr2, 0), "a", "first element preserved")
assert_eq(json_array_get_string(arr2, 1), "b", "second element preserved")
assert_eq(json_array_get_string(arr2, 2), "c", "pushed element at end")
assert_int_eq(json_array_len(arr2), 3, "total length after push")
return ""
}
// nested JSON
fn test_json_nested_set_get(_: String) -> String {
let obj: String = "{\"user\":{\"name\":\"alice\"},\"count\":1}"
assert_eq(json_get(obj, "user.name"), "alice", "deep get via dot path")
assert_eq(json_get(obj, "count"), "1", "top-level int field")
let obj2: String = json_set(obj, "count", "2")
assert_eq(json_get(obj2, "count"), "2", "updated top-level field")
assert_eq(json_get(obj2, "user.name"), "alice", "nested field still accessible after set")
return ""
}
fn test_json_array_of_objects(_: String) -> String {
let arr: String = "[{\"id\":1,\"name\":\"alice\"},{\"id\":2,\"name\":\"bob\"}]"
assert_int_eq(json_array_len(arr), 2, "array of objects: length 2")
let first: String = json_array_get(arr, 0)
let second: String = json_array_get(arr, 1)
assert_eq(json_get(first, "name"), "alice", "first object name")
assert_eq(json_get(second, "name"), "bob", "second object name")
assert_eq(json_get(first, "id"), "1", "first object id")
assert_eq(json_get(second, "id"), "2", "second object id")
return ""
}