Skip to content

Commit f504ffd

Browse files
committed
test(parser): add unit tests for payload_fixer module
1 parent e2053ff commit f504ffd

1 file changed

Lines changed: 104 additions & 0 deletions

File tree

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import logging
16+
17+
import pytest
18+
19+
from a2ui.parser.payload_fixer import parse_and_fix
20+
21+
22+
@pytest.mark.parametrize(
23+
"payload,expected",
24+
[
25+
(
26+
'[{"beginRendering": {"surfaceId": "s1", "root": "r1"}}]',
27+
[{"beginRendering": {"surfaceId": "s1", "root": "r1"}}],
28+
),
29+
(
30+
'{"beginRendering": {"surfaceId": "s1", "root": "r1"}}',
31+
[{"beginRendering": {"surfaceId": "s1", "root": "r1"}}],
32+
),
33+
("{\u201ckey\u201d: \u201cvalue\u201d}", [{"key": "value"}]),
34+
(
35+
"{\u201Couter\u201D: {\u201Cinner\u201D: true}}",
36+
[{"outer": {"inner": True}}],
37+
),
38+
("[]", []),
39+
("[1, 2,]", [1, 2]),
40+
],
41+
)
42+
def test_parse_and_fix_normalization(payload, expected):
43+
assert parse_and_fix(payload) == expected
44+
45+
46+
@pytest.mark.parametrize(
47+
"payload,expected",
48+
[
49+
('[{"key": "value",}]', [{"key": "value"}]),
50+
('[{"items": ["a", "b",]}]', [{"items": ["a", "b"]}]),
51+
('[{"a": 1, "b": [1, 2,]}]', [{"a": 1, "b": [1, 2]}]),
52+
('{"a": {"b": 1,}}', [{"a": {"b": 1}}]),
53+
('[{"a": [1, 2, 3,]}]', [{"a": [1, 2, 3]}]),
54+
],
55+
)
56+
def test_parse_and_fix_trailing_comma(payload, expected):
57+
assert parse_and_fix(payload) == expected
58+
59+
60+
def test_parse_and_fix_multiple_messages():
61+
payload = (
62+
'[{"beginRendering": {"surfaceId": "s1", "root": "r1"}}, {"surfaceUpdate":'
63+
' {"surfaceId": "s1", "components": []}}]'
64+
)
65+
result = parse_and_fix(payload)
66+
assert len(result) == 2
67+
assert "beginRendering" in result[0]
68+
assert "surfaceUpdate" in result[1]
69+
70+
71+
def test_parse_and_fix_single_object_wrapped_in_list():
72+
payload = '{"beginRendering": {"surfaceId": "s1", "root": "r1"}}'
73+
result = parse_and_fix(payload)
74+
assert isinstance(result, list)
75+
assert len(result) == 1
76+
77+
78+
def test_parse_and_fix_logs_warnings_on_trailing_comma_autofix(caplog):
79+
payload = '[{"key": "value",}]'
80+
with caplog.at_level(logging.WARNING, logger="a2ui.parser.payload_fixer"):
81+
result = parse_and_fix(payload)
82+
log_messages = [r.message for r in caplog.records]
83+
assert any("Initial A2UI payload validation failed" in msg for msg in log_messages)
84+
assert any(
85+
"Detected trailing commas in LLM output; applied autofix." in msg
86+
for msg in log_messages
87+
)
88+
assert result == [{"key": "value"}]
89+
90+
91+
@pytest.mark.parametrize(
92+
"payload",
93+
[
94+
"this is not json at all",
95+
"",
96+
"{",
97+
"[",
98+
'{"key": "value"',
99+
'[{"key": "value",]',
100+
],
101+
)
102+
def test_parse_and_fix_invalid_payload_raises_value_error(payload):
103+
with pytest.raises(ValueError, match="Failed to parse JSON"):
104+
parse_and_fix(payload)

0 commit comments

Comments
 (0)