commit f758321c4c3e014e44d1d1219ef7cb0f300e5cf9
parent 8b1c6f9a8dd75115ea2e2033d95b9c45b3c6cc39
Author: triesap <tyson@radroots.org>
Date: Wed, 23 Sep 2026 20:35:35 +0000
test: characterize strict json boundaries
- Pin duplicate-key retention, first-wins lookup and re-encode behavior.
- Pin unchecked numeric, null, exponent and invalid-UTF-8 decoding.
- Pin stdio envelope duplicate, null, numeric and missing-field behavior.
- Pin query-analysis and JEV envelope duplicate, null and extra-field cases.
Diffstat:
3 files changed, 215 insertions(+), 2 deletions(-)
diff --git a/tests/test_hyf.mojo b/tests/test_hyf.mojo
@@ -9,7 +9,7 @@ from std.testing import (
)
from safe_tempdir import SafeTempDir
-from json import Value, loads, validate
+from json import Value, dumps, loads, validate
from fixture_assertions import (
assert_matches_scenario_response,
@@ -1388,3 +1388,113 @@ def test_gated_operation_descriptors_are_prepared_not_exposed() raises:
assert_true(not gated_operation_is_exposed("farm_update.interpret", False))
assert_true(gated_operation_is_exposed("buyer_request.match", True))
assert_true(not gated_operation_is_exposed("query_rewrite", True))
+
+
+# ── H009 strict JSON boundary characterization (current behavior) ────────────
+
+
+def test_strict_json_boundary_characterizes_duplicate_keys() raises:
+ # H009: pin the current duplicate-key behavior without declaring the final
+ # policy. The shared decoder accepts a repeated key, preserves every entry
+ # and resolves lookup to the first occurrence; a type-conflicting duplicate
+ # is likewise not rejected.
+ var value = loads('{"n":1,"m":2,"n":3}')
+ assert_equal(value.object_count(), 3)
+ assert_equal(value["n"].int_value(), 1)
+ assert_equal(dumps(value), '{"n":1,"m":2,"n":3}')
+ var conflicting = loads('{"n":1,"n":"x"}')
+ assert_equal(conflicting.object_count(), 2)
+ assert_equal(conflicting["n"].int_value(), 1)
+
+
+def test_strict_json_boundary_characterizes_numeric_and_null_values() raises:
+ # H009: current int_value() performs no type check. Exponents, floats, null
+ # and out-of-range integers decode to 0, and a string/bool/array value is
+ # interpreted without a type error. Pinned as current behavior, not as an
+ # approved contract.
+ var exponent = loads('{"n":1e2}')
+ assert_equal(exponent["n"].int_value(), 0)
+ var fractional = loads('{"n":1.5}')
+ assert_equal(fractional["n"].int_value(), 0)
+ var null_value = loads('{"n":null}')
+ assert_equal(null_value["n"].int_value(), 0)
+ var overflow = loads('{"n":9223372036854775808}')
+ assert_equal(overflow["n"].int_value(), 0)
+ var negative = loads('{"n":-1}')
+ assert_equal(negative["n"].int_value(), -1)
+ var plain = loads('{"n":7}')
+ assert_equal(plain["n"].int_value(), 7)
+ var string_value = loads('{"n":"7"}')
+ assert_true(string_value["n"].int_value() != 7)
+ var boolean = loads('{"n":true}')
+ assert_equal(boolean["n"].int_value(), 1)
+ var array = loads('{"n":[1,2]}')
+ assert_true(array["n"].int_value() != 0)
+
+
+def test_strict_json_boundary_accepts_invalid_utf8_in_a_string() raises:
+ # H009: the shared decoder currently accepts a string value that contains
+ # invalid UTF-8 bytes instead of rejecting the document.
+ var bytes = List[UInt8]()
+ for byte in '{"s":"'.as_bytes():
+ bytes.append(UInt8(Int(byte)))
+ bytes.append(255)
+ bytes.append(254)
+ for byte in '"}'.as_bytes():
+ bytes.append(UInt8(Int(byte)))
+ var raw = String(
+ unsafe_from_utf8=Span(ptr=bytes.unsafe_ptr(), length=len(bytes))
+ )
+ var value = loads(raw)
+ assert_equal(value.object_count(), 1)
+ assert_true(_has_key(value, "s"))
+
+
+def test_stdio_envelope_boundary_characterizes_duplicate_keys() raises:
+ # H009: the stdio request envelope currently accepts duplicated envelope
+ # and input keys and resolves each to its first occurrence.
+ var envelope = decode_request(
+ '{"version":1,"version":2,"request_id":"a","request_id":"b",'
+ '"capability":"query_rewrite","input":{"query":"eggs","query":"milk"}}'
+ )
+ assert_equal(envelope.version, 1)
+ assert_equal(envelope.request_id, "a")
+ assert_equal(envelope.input["query"].string_value(), "eggs")
+
+
+def test_stdio_envelope_boundary_rejects_null_and_numeric_fields() raises:
+ # H009: required envelope fields carry bounded, type-checked rejections for
+ # null, a non-integer number, a non-string and a missing field.
+ var cases = List[String]()
+ cases.append(
+ '{"version":null,"request_id":"a","capability":"query_rewrite",'
+ '"input":{"query":"eggs"}}'
+ )
+ cases.append(
+ '{"version":1.5,"request_id":"a","capability":"query_rewrite",'
+ '"input":{"query":"eggs"}}'
+ )
+ cases.append(
+ '{"request_id":"a","capability":"query_rewrite","input":{"query":"eggs"}}'
+ )
+ cases.append(
+ '{"version":1,"request_id":7,"capability":"query_rewrite",'
+ '"input":{"query":"eggs"}}'
+ )
+ cases.append(
+ '{"version":1,"request_id":"a","capability":null,'
+ '"input":{"query":"eggs"}}'
+ )
+ var messages = List[String]()
+ for index in range(len(cases)):
+ var message = ""
+ try:
+ _ = decode_request(cases[index])
+ except e:
+ message = String(e)
+ messages.append(message)
+ assert_true(messages[0].find("must be an integer") >= 0)
+ assert_true(messages[1].find("must be an integer") >= 0)
+ assert_true(messages[2].find("'version' is required") >= 0)
+ assert_true(messages[3].find("not a string") >= 0)
+ assert_true(messages[4].find("not a string") >= 0)
diff --git a/tests/test_jev.mojo b/tests/test_jev.mojo
@@ -170,6 +170,47 @@ def test_parse_jev_response_validates_answer_set() raises:
_ = parse_jev_response(missing, _bundle())
+def test_jev_envelope_boundary_characterizes_duplicate_and_null_fields() raises:
+ # H009: pin current JEV envelope boundary behavior. A duplicated model or
+ # answer key is accepted first-wins; a null answer object is rejected.
+ var duplicate_model = loads(
+ '{"model":"jev-1.13.0","model":"jev-other","answers":{'
+ '"supply_status":{"type":"choice","choice":"offered","probabilities":'
+ '{"offered":1.0,"forecast":0.0,"unclear":0.0},"confidence":1.0},'
+ '"seconds_ok":{"type":"noul","noul":0.9},'
+ '"culinary_fit":{"type":"score","score":2,"legend":{"0":"u","1":"l",'
+ '"2":"s"},"probabilities":{"0":0.0,"1":0.0,"2":1.0},"confidence":1.0}},'
+ '"usage":{"input_tokens":10,"output_tokens":5}}'
+ )
+ var answers = parse_jev_response(duplicate_model, _bundle())
+ assert_equal(len(answers), 3)
+ # A duplicated answer key is rejected at this boundary (unlike the raw JSON
+ # layer, which accepts duplicates): the answer set is not the declared one.
+ var duplicate_answer = loads(
+ '{"model":"jev-1.13.0","answers":{'
+ '"supply_status":{"type":"choice","choice":"offered","probabilities":'
+ '{"offered":1.0,"forecast":0.0,"unclear":0.0},"confidence":1.0},'
+ '"seconds_ok":{"type":"noul","noul":0.9},'
+ '"culinary_fit":{"type":"score","score":2,"legend":{"0":"u","1":"l",'
+ '"2":"s"},"confidence":1.0},'
+ '"supply_status":{"type":"noul","noul":0.1}}}'
+ )
+ var duplicate_message = ""
+ try:
+ _ = parse_jev_response(duplicate_answer, _bundle())
+ except e:
+ duplicate_message = String(e)
+ assert_true(duplicate_message.find("provider_answer_extra") >= 0)
+ var null_answer = loads(
+ '{"model":"jev-1.13.0","answers":{"supply_status":null,'
+ '"seconds_ok":{"type":"noul","noul":0.9},'
+ '"culinary_fit":{"type":"score","score":2,"legend":{"0":"u","1":"l",'
+ '"2":"s"},"confidence":1.0}}}'
+ )
+ with assert_raises():
+ _ = parse_jev_response(null_answer, _bundle())
+
+
from hyf_provider.jev_failures import map_jev_failure
diff --git a/tests/test_provider_adapter.mojo b/tests/test_provider_adapter.mojo
@@ -38,7 +38,7 @@ from bounded_call_helper import (
parse_bounded_report,
run_bounded_call,
)
-from strict_fixture import ExchangeScript, exchange_script
+from strict_fixture import ExchangeScript, exchange_script, json_escape
# H007 BC02: each bounded-call invocation carries its own correlation value, so
# a report produced for one call can never be accepted for another.
@@ -1204,5 +1204,67 @@ def test_maxlocal_wire_attempt_counts_for_non_retryable_status() raises:
guard.assert_clean()
+# ── H009 query-analysis JSON boundary characterization ──────────────────────
+
+
+def _analysis_envelope(content: String) raises -> Value:
+ return loads(
+ '{"choices":[{"message":{"content":' + json_escape(content) + "}}]}"
+ )
+
+
+def _analysis_content(fields: String) -> String:
+ return "{" + fields + "}"
+
+
+comptime ANALYSIS_TAIL = (
+ '"normalization_signals":[],"ranking_hints":[],'
+ '"extracted_filters":{"local_intent":true,"fulfillment":"unspecified",'
+ '"time_window":"unspecified"}'
+)
+
+
+def test_query_analysis_boundary_characterizes_duplicate_and_null_fields() raises:
+ # H009: pin current query-analysis boundary behavior. A duplicated field is
+ # accepted first-wins; a null string field and a non-array field are
+ # rejected as provider_schema_invalid.
+ var duplicate = _analysis_envelope(
+ _analysis_content(
+ '"original_text":"a","original_text":"b","normalized_text":"a",'
+ '"rewritten_text":"a","query_terms":["a"],'
+ + ANALYSIS_TAIL
+ )
+ )
+ var analysis = parse_query_analysis_from_chat_completion(duplicate)
+ assert_equal(analysis.original_text, "a")
+ assert_equal(len(analysis.query_terms), 1)
+ var null_text = _analysis_envelope(
+ _analysis_content(
+ '"original_text":null,"normalized_text":"a","rewritten_text":"a",'
+ '"query_terms":["a"],'
+ + ANALYSIS_TAIL
+ )
+ )
+ var message = ""
+ try:
+ _ = parse_query_analysis_from_chat_completion(null_text)
+ except e:
+ message = String(e)
+ assert_true(message.find("provider_schema_invalid") >= 0)
+ var numeric_terms = _analysis_envelope(
+ _analysis_content(
+ '"original_text":"a","normalized_text":"a","rewritten_text":"a",'
+ '"query_terms":7,'
+ + ANALYSIS_TAIL
+ )
+ )
+ var terms_message = ""
+ try:
+ _ = parse_query_analysis_from_chat_completion(numeric_terms)
+ except e:
+ terms_message = String(e)
+ assert_true(terms_message.find("provider_schema_invalid") >= 0)
+
+
def main() raises:
TestSuite.discover_tests[__functions_in_module()]().run()