Skip to content

Commit 14e6d60

Browse files
committed
test(protocol): add catalog structural integrity validation
Update the Catalog definition schema in client_capabilities.json to allow standard JSON schema properties ($schema, $id, title, description, $defs). Add a new test step to run_tests.py that automatically validates the basic, minimal, and testing catalogs against this Catalog schema definition.
1 parent 7646779 commit 14e6d60

3 files changed

Lines changed: 75 additions & 12 deletions

File tree

samples/client/flutter/restaurant_finder/e2e_test/test/infra_test.dart

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,9 @@ void main() {
3030
print('Joke from AI:\n\n$result\n\n');
3131
});
3232

33-
test(
34-
'test can start restaurant_finder',
35-
() async {
36-
final restaurantFinderClient = TestRestaurantFinderClient();
37-
addTearDown(restaurantFinderClient.dispose);
38-
await restaurantFinderClient.startAndVerify();
39-
},
40-
timeout: const Timeout(Duration(minutes: 5)),
41-
);
33+
test('test can start restaurant_finder', () async {
34+
final restaurantFinderClient = TestRestaurantFinderClient();
35+
addTearDown(restaurantFinderClient.dispose);
36+
await restaurantFinderClient.startAndVerify();
37+
}, timeout: const Timeout(Duration(minutes: 5)));
4238
}

specification/v0_10/json/client_capabilities.json

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"call": {
4242
"type": "object",
4343
"properties": {
44-
"const": { "type": "string" }
44+
"const": {"type": "string"}
4545
},
4646
"required": ["const"]
4747
},
@@ -56,8 +56,8 @@
5656
},
5757
"required": {
5858
"type": "array",
59-
"items": { "type": "string" },
60-
"contains": { "const": "call" }
59+
"items": {"type": "string"},
60+
"contains": {"const": "call"}
6161
},
6262
"unevaluatedProperties": {
6363
"type": "boolean"
@@ -99,6 +99,11 @@
9999
"type": "object",
100100
"description": "A collection of component and function definitions.",
101101
"properties": {
102+
"$schema": {"type": "string"},
103+
"$id": {"type": "string"},
104+
"title": {"type": "string"},
105+
"description": {"type": "string"},
106+
"$defs": {"type": "object"},
102107
"catalogId": {
103108
"type": "string",
104109
"description": "Unique identifier for this catalog."

specification/v0_10/test/run_tests.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,63 @@ def validate_jsonl_example(jsonl_path):
183183
finally:
184184
cleanup_catalog_alias()
185185

186+
def validate_catalogs_structure():
187+
"""
188+
Validates the catalog files directly against the Catalog definition in
189+
client_capabilities.json schema.
190+
"""
191+
client_caps_path = os.path.join(SCHEMA_DIR, "client_capabilities.json")
192+
if not os.path.exists(client_caps_path):
193+
print(f"Error: client_capabilities.json not found at {client_caps_path}")
194+
return 0, 1
195+
196+
temp_validator_path = os.path.join(TEST_DIR, "temp_catalog_validator.json")
197+
198+
# We reference the absolute ID of client_capabilities.json which gets loaded as a reference
199+
validator_schema = {
200+
"$schema": "https://json-schema.org/draft/2020-12/schema",
201+
"$ref": "https://a2ui.org/specification/v0_10/client_capabilities.json#/$defs/Catalog"
202+
}
203+
204+
with open(temp_validator_path, 'w') as f:
205+
json.dump(validator_schema, f)
206+
207+
catalogs_to_validate = [
208+
("catalogs/basic/catalog.json", os.path.join(SPEC_DIR, "catalogs/basic/catalog.json")),
209+
("catalogs/minimal/catalog.json", os.path.join(SPEC_DIR, "catalogs/minimal/catalog.json")),
210+
("test/testing_catalog.json", os.path.join(TEST_DIR, "testing_catalog.json")),
211+
]
212+
213+
passed = 0
214+
failed = 0
215+
216+
print("\nValidating catalog structural integrity against client_capabilities.json...")
217+
218+
ref_schemas = {
219+
"client_capabilities.json": client_caps_path
220+
}
221+
222+
try:
223+
for name, path in catalogs_to_validate:
224+
if not os.path.exists(path):
225+
print(f" [FAIL] {name} (File not found)")
226+
failed += 1
227+
continue
228+
229+
is_valid, output = validate_ajv(temp_validator_path, path, ref_schemas)
230+
if is_valid:
231+
passed += 1
232+
# print(f" [PASS] {name}")
233+
else:
234+
failed += 1
235+
print(f" [FAIL] {name}")
236+
print(f" Output: {output.strip()}")
237+
238+
return passed, failed
239+
finally:
240+
if os.path.exists(temp_validator_path):
241+
os.remove(temp_validator_path)
242+
186243
def main():
187244
if not os.path.exists(CASES_DIR):
188245
print(f"No cases directory found at {CASES_DIR}")
@@ -206,6 +263,11 @@ def main():
206263
total_passed += p
207264
total_failed += f
208265

266+
# 3. Validate catalogs structural integrity
267+
p, f = validate_catalogs_structure()
268+
total_passed += p
269+
total_failed += f
270+
209271
print("\n" + "="*30)
210272
print(f"Total Passed: {total_passed}")
211273
print(f"Total Failed: {total_failed}")

0 commit comments

Comments
 (0)