-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Expand file tree
/
Copy pathtest_schema_manager.py
More file actions
276 lines (236 loc) · 9.39 KB
/
test_schema_manager.py
File metadata and controls
276 lines (236 loc) · 9.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import io
import pytest
import json
import os
from unittest.mock import patch, MagicMock, PropertyMock
from a2ui.schema.manager import A2uiSchemaManager, A2uiCatalog, CatalogConfig
from a2ui.basic_catalog import BasicCatalog
from a2ui.basic_catalog.constants import BASIC_CATALOG_NAME
from a2ui.schema.constants import (
DEFAULT_WORKFLOW_RULES,
STRICT_WORKFLOW_RULES,
INLINE_CATALOG_NAME,
VERSION_0_8,
VERSION_0_9,
)
from a2ui.schema.constants import (
A2UI_SCHEMA_BLOCK_START,
A2UI_SCHEMA_BLOCK_END,
INLINE_CATALOGS_KEY,
SUPPORTED_CATALOG_IDS_KEY,
)
@pytest.fixture
def mock_importlib_resources():
with patch("importlib.resources.files") as mock_files:
yield mock_files
def test_schema_manager_init_valid_version(mock_importlib_resources):
mock_files = mock_importlib_resources
mock_traversable = MagicMock()
def files_side_effect(package):
if package == "a2ui.assets":
return mock_traversable
return MagicMock()
mock_files.side_effect = files_side_effect
# Mock file open calls for server_to_client and catalog
def joinpath_side_effect(path):
if path == VERSION_0_8:
return mock_traversable
mock_file = MagicMock()
if path == "server_to_client.json":
content = (
'{"$schema": "https://json-schema.org/draft/2020-12/schema", "version":'
f' "{VERSION_0_8}", "defs": "server_defs"}}'
)
elif path == "standard_catalog_definition.json":
content = (
'{"$schema": "https://json-schema.org/draft/2020-12/schema", "version":'
f' "{VERSION_0_8}", "components": {{"Text": {{}}}}}}'
)
else:
content = '{"$schema": "https://json-schema.org/draft/2020-12/schema"}'
mock_file.open.return_value.__enter__.return_value = io.StringIO(content)
return mock_file
mock_traversable.joinpath.side_effect = joinpath_side_effect
manager = A2uiSchemaManager(
VERSION_0_8, catalogs=[BasicCatalog.get_config(VERSION_0_8)]
)
assert manager._server_to_client_schema["defs"] == "server_defs"
# Basic catalog might have a URI-based ID if not explicitly matched
# So we check if any catalog exists
assert len(manager._supported_catalogs) >= 1
# The first one should be the basic one
catalog = manager._supported_catalogs[0]
assert catalog.catalog_schema["version"] == VERSION_0_8
assert "Text" in catalog.catalog_schema["components"]
def test_schema_manager_init_invalid_version():
with pytest.raises(ValueError, match="Unknown A2UI specification version"):
A2uiSchemaManager("invalid_version")
def test_schema_manager_fallback_local_assets(mock_importlib_resources):
# Force importlib to fail
# Note: A2UI_ASSET_PACKAGE is "a2ui.assets"
mock_importlib_resources.side_effect = FileNotFoundError("Package not found")
with (
patch("os.path.exists", return_value=True),
patch("builtins.open", new_callable=MagicMock) as mock_open,
):
def open_side_effect(path, *args, **kwargs):
path_str = str(path)
if "server_to_client" in path_str:
return io.StringIO(
'{"$schema": "https://json-schema.org/draft/2020-12/schema", "defs":'
' "local_server"}'
)
elif "standard_catalog" in path_str or "catalog" in path_str:
return io.StringIO(
'{"$schema": "https://json-schema.org/draft/2020-12/schema",'
' "catalogId": "basic", "components": {"LocalText": {}}}'
)
raise FileNotFoundError(path)
mock_open.side_effect = open_side_effect
manager = A2uiSchemaManager(
VERSION_0_8, catalogs=[BasicCatalog.get_config(VERSION_0_8)]
)
assert manager._server_to_client_schema["defs"] == "local_server"
assert len(manager._supported_catalogs) >= 1
catalog = manager._supported_catalogs[0]
assert "LocalText" in catalog.catalog_schema["components"]
# --- Tests for strict_output parameter ---
class TestStrictOutput:
"""Tests for the strict_output parameter on generate_system_prompt()."""
@pytest.fixture
def manager(self):
return A2uiSchemaManager(
VERSION_0_8,
catalogs=[BasicCatalog.get_config(VERSION_0_8)],
)
def test_default_uses_default_rules(self, manager):
"""Default behavior (strict_output not set) uses DEFAULT_WORKFLOW_RULES."""
prompt = manager.generate_system_prompt(
role_description="Test agent",
)
assert "NEVER use markdown tables" not in prompt
def test_strict_output_false_matches_default(self, manager):
"""Explicitly passing strict_output=False matches default behavior."""
default_prompt = manager.generate_system_prompt(
role_description="Test agent",
)
explicit_prompt = manager.generate_system_prompt(
role_description="Test agent",
strict_output=False,
)
assert default_prompt == explicit_prompt
def test_strict_uses_strict_rules(self, manager):
"""strict_output=True uses STRICT_WORKFLOW_RULES."""
prompt = manager.generate_system_prompt(
role_description="Test agent",
strict_output=True,
)
assert (
"Your primary output format is A2UI JSON blocks, NOT conversational text"
in prompt
)
def test_strict_contains_anti_markdown_rules(self, manager):
"""strict_output=True includes anti-markdown formatting rules."""
prompt = manager.generate_system_prompt(
role_description="Test agent",
strict_output=True,
)
assert "NEVER use markdown tables" in prompt
assert "NEVER use markdown bullet" in prompt
assert "NEVER use markdown headers" in prompt
def test_strict_contains_output_ordering(self, manager):
"""strict_output=True mandates A2UI-first output ordering."""
prompt = manager.generate_system_prompt(
role_description="Test agent",
strict_output=True,
)
assert "Output A2UI JSON block(s) FIRST" in prompt
assert "1-2 brief sentences" in prompt
def test_strict_contains_component_guidance(self, manager):
"""strict_output=True includes component usage guidance."""
prompt = manager.generate_system_prompt(
role_description="Test agent",
strict_output=True,
)
assert "Row with Card children" in prompt
assert "List with Card children" in prompt
assert "Divider" in prompt
assert "at least 3 different component types" in prompt
def test_strict_preserves_role_description(self, manager):
"""strict_output=True still includes the role description."""
role = "You are a helpful analytics dashboard agent."
prompt = manager.generate_system_prompt(
role_description=role,
strict_output=True,
)
assert role in prompt
def test_strict_preserves_ui_description(self, manager):
"""strict_output=True still includes the UI description."""
ui_desc = "Render sales data as Card grids."
prompt = manager.generate_system_prompt(
role_description="Test agent",
ui_description=ui_desc,
strict_output=True,
)
assert ui_desc in prompt
def test_strict_preserves_workflow_description(self, manager):
"""strict_output=True appends workflow_description after strict rules."""
workflow = "Always confirm before booking."
prompt = manager.generate_system_prompt(
role_description="Test agent",
workflow_description=workflow,
strict_output=True,
)
assert workflow in prompt
assert "NEVER use markdown tables" in prompt
def test_strict_with_schema(self, manager):
"""strict_output=True works with include_schema=True."""
prompt = manager.generate_system_prompt(
role_description="Test agent",
include_schema=True,
strict_output=True,
)
assert "NEVER use markdown tables" in prompt
def test_strict_output_differs_from_default(self, manager):
"""strict_output=True produces different output from default."""
default_prompt = manager.generate_system_prompt(
role_description="Test agent",
)
strict_prompt = manager.generate_system_prompt(
role_description="Test agent",
strict_output=True,
)
assert default_prompt != strict_prompt
def test_strict_rules_constant_contains_a2ui_tags(self):
"""STRICT_WORKFLOW_RULES references the correct A2UI tags."""
assert "<a2ui-json>" in STRICT_WORKFLOW_RULES
assert "</a2ui-json>" in STRICT_WORKFLOW_RULES
def test_strict_rules_has_top_down_ordering(self):
"""STRICT_WORKFLOW_RULES preserves the top-down ordering requirement."""
assert "root" in STRICT_WORKFLOW_RULES
assert (
"Parent components MUST appear before their child" in STRICT_WORKFLOW_RULES
)
def test_strict_works_with_v09(self):
"""strict_output=True works with v0.9 catalogs."""
manager = A2uiSchemaManager(
VERSION_0_9,
catalogs=[BasicCatalog.get_config(VERSION_0_9)],
)
prompt = manager.generate_system_prompt(
role_description="Test agent",
strict_output=True,
)
assert "NEVER use markdown tables" in prompt
assert "Your primary output format is A2UI JSON blocks" in prompt