Skip to content

Commit 97f20c9

Browse files
committed
test: add given-when-then comments to template coverage
1 parent 90906d0 commit 97f20c9

4 files changed

Lines changed: 188 additions & 0 deletions

File tree

‎models/tests/test_control_templates.py‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,12 @@ def _nested_template_value(depth: int) -> object:
4444

4545

4646
def test_control_definition_requires_template_fields_together() -> None:
47+
# Given: a rendered control that only includes template metadata without template values
4748
with pytest.raises(
4849
ValidationError,
4950
match="template and template_values must both be present or both absent",
5051
):
52+
# When: validating the control definition model
5153
ControlDefinition.model_validate(
5254
{
5355
"execution": "server",
@@ -63,13 +65,16 @@ def test_control_definition_requires_template_fields_together() -> None:
6365
"template": VALID_TEMPLATE,
6466
}
6567
)
68+
# Then: the model rejects the partial template-backed control shape
6669

6770

6871
def test_control_definition_rejects_template_values_without_template() -> None:
72+
# Given: a rendered control that only includes template values without template metadata
6973
with pytest.raises(
7074
ValidationError,
7175
match="template and template_values must both be present or both absent",
7276
):
77+
# When: validating the control definition model
7378
ControlDefinition.model_validate(
7479
{
7580
"execution": "server",
@@ -85,13 +90,16 @@ def test_control_definition_rejects_template_values_without_template() -> None:
8590
"template_values": {"pattern": "hello"},
8691
}
8792
)
93+
# Then: the model rejects the partial template-backed control shape
8894

8995

9096
def test_template_definition_rejects_invalid_parameter_name() -> None:
97+
# Given: a template definition with an invalid parameter name
9198
with pytest.raises(
9299
ValidationError,
93100
match=r"Parameter names must match \[a-zA-Z_\]\[a-zA-Z0-9_\]\*",
94101
):
102+
# When: validating the template definition model
95103
TemplateDefinition.model_validate(
96104
{
97105
"parameters": {
@@ -103,35 +111,43 @@ def test_template_definition_rejects_invalid_parameter_name() -> None:
103111
"definition_template": {},
104112
}
105113
)
114+
# Then: the invalid parameter name is rejected
106115

107116

108117
def test_template_definition_rejects_excessive_nesting() -> None:
118+
# Given: a template definition whose structure exceeds the nesting limit
109119
with pytest.raises(
110120
ValidationError,
111121
match="definition_template nesting depth exceeds maximum",
112122
):
123+
# When: validating the template definition model
113124
TemplateDefinition.model_validate(
114125
{
115126
"parameters": {},
116127
"definition_template": _nested_template_value(12),
117128
}
118129
)
130+
# Then: the model rejects the deeply nested template
119131

120132

121133
def test_template_definition_rejects_excessive_size() -> None:
134+
# Given: a template definition whose structure exceeds the size limit
122135
with pytest.raises(
123136
ValidationError,
124137
match="definition_template size exceeds maximum",
125138
):
139+
# When: validating the template definition model
126140
TemplateDefinition.model_validate(
127141
{
128142
"parameters": {},
129143
"definition_template": list(range(1001)),
130144
}
131145
)
146+
# Then: the model rejects the oversized template
132147

133148

134149
def test_create_control_request_parses_template_payload_as_template_input() -> None:
150+
# Given: a create request payload containing template-backed control input
135151
request = CreateControlRequest.model_validate(
136152
{
137153
"name": "template-control",
@@ -142,17 +158,21 @@ def test_create_control_request_parses_template_payload_as_template_input() -> N
142158
}
143159
)
144160

161+
# When: the request model parses the payload
162+
# Then: the control input is discriminated as template-backed input
145163
assert isinstance(request.data, TemplateControlInput)
146164

147165

148166
def test_create_control_request_rejects_mixed_raw_and_template_payload() -> None:
167+
# Given: a create request payload that mixes template and rendered control fields
149168
with pytest.raises(
150169
ValidationError,
151170
match=(
152171
"Template-backed control input cannot mix template fields with rendered "
153172
"control fields"
154173
),
155174
):
175+
# When: the request model parses the mixed payload
156176
CreateControlRequest.model_validate(
157177
{
158178
"name": "template-control",
@@ -163,9 +183,11 @@ def test_create_control_request_rejects_mixed_raw_and_template_payload() -> None
163183
},
164184
}
165185
)
186+
# Then: the mixed payload is rejected clearly
166187

167188

168189
def test_control_definition_can_round_trip_to_template_control_input() -> None:
190+
# Given: a stored template-backed control definition
169191
control = ControlDefinition.model_validate(
170192
{
171193
"execution": "server",
@@ -183,15 +205,18 @@ def test_control_definition_can_round_trip_to_template_control_input() -> None:
183205
}
184206
)
185207

208+
# When: converting the stored control back into template input
186209
template_input = control.to_template_control_input()
187210

211+
# Then: the template metadata and values round-trip unchanged
188212
assert template_input.template.parameters["pattern"].label == "Pattern"
189213
assert template_input.template.parameters["pattern"].type == "regex_re2"
190214
assert template_input.template.definition_template == VALID_TEMPLATE["definition_template"]
191215
assert template_input.template_values == {"pattern": "hello"}
192216

193217

194218
def test_control_definition_to_template_control_input_rejects_raw_control() -> None:
219+
# Given: a raw control definition without template metadata
195220
control = ControlDefinition.model_validate(
196221
{
197222
"execution": "server",
@@ -207,11 +232,14 @@ def test_control_definition_to_template_control_input_rejects_raw_control() -> N
207232
}
208233
)
209234

235+
# When: converting the raw control into template input
210236
with pytest.raises(ValueError, match="not template-backed"):
211237
control.to_template_control_input()
238+
# Then: the helper rejects the non-template-backed control
212239

213240

214241
def test_control_definition_runtime_ignores_template_metadata() -> None:
242+
# Given: a stored template-backed control definition with template metadata
215243
runtime_control = ControlDefinitionRuntime.model_validate(
216244
{
217245
"execution": "server",
@@ -229,5 +257,7 @@ def test_control_definition_runtime_ignores_template_metadata() -> None:
229257
}
230258
)
231259

260+
# When: validating it through the runtime-only model
261+
# Then: runtime parsing succeeds while ignoring template metadata
232262
assert runtime_control.execution == "server"
233263
assert runtime_control.action.decision == "deny"

‎sdks/python/tests/test_controls_api.py‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@
1313

1414
@pytest.mark.asyncio
1515
async def test_list_controls_passes_template_backed_filter() -> None:
16+
# Given: an SDK client stub and a template-backed list filter
1617
response = Mock()
1718
response.raise_for_status = Mock()
1819
response.json = Mock(return_value={"controls": [], "pagination": {}})
1920
client = SimpleNamespace(http_client=SimpleNamespace(get=AsyncMock(return_value=response)))
2021

22+
# When: listing controls through the SDK wrapper
2123
await agent_control.controls.list_controls(client, template_backed=True)
2224

25+
# Then: the filter is forwarded to the API request
2326
client.http_client.get.assert_awaited_once_with(
2427
"/api/v1/controls",
2528
params={"limit": 20, "template_backed": True},
@@ -28,6 +31,7 @@ async def test_list_controls_passes_template_backed_filter() -> None:
2831

2932
@pytest.mark.asyncio
3033
async def test_create_control_accepts_template_control_input() -> None:
34+
# Given: an SDK client stub and template-backed control input
3135
response = Mock()
3236
response.raise_for_status = Mock()
3337
response.json = Mock(return_value={"control_id": 123})
@@ -58,20 +62,24 @@ async def test_create_control_accepts_template_control_input() -> None:
5862
}
5963
)
6064

65+
# When: creating a control through the SDK wrapper
6166
await agent_control.controls.create_control(client, "templated", template_input)
6267

68+
# Then: the template values are serialized into the request body
6369
client.http_client.put.assert_awaited_once()
6470
_, kwargs = client.http_client.put.await_args
6571
assert kwargs["json"]["data"]["template_values"]["pattern"] == "hello"
6672

6773

6874
@pytest.mark.asyncio
6975
async def test_render_control_template_calls_preview_endpoint() -> None:
76+
# Given: an SDK client stub and template preview input
7077
response = Mock()
7178
response.raise_for_status = Mock()
7279
response.json = Mock(return_value={"control": {"execution": "server"}})
7380
client = SimpleNamespace(http_client=SimpleNamespace(post=AsyncMock(return_value=response)))
7481

82+
# When: rendering a control template through the SDK wrapper
7583
await agent_control.controls.render_control_template(
7684
client,
7785
template={
@@ -89,6 +97,7 @@ async def test_render_control_template_calls_preview_endpoint() -> None:
8997
template_values={},
9098
)
9199

100+
# Then: the SDK calls the preview endpoint with the expected payload
92101
client.http_client.post.assert_awaited_once_with(
93102
"/api/v1/control-templates/render",
94103
json={
@@ -111,6 +120,7 @@ async def test_render_control_template_calls_preview_endpoint() -> None:
111120

112121
@pytest.mark.asyncio
113122
async def test_validate_control_data_accepts_template_control_input() -> None:
123+
# Given: an SDK client stub and template-backed control input
114124
response = Mock()
115125
response.raise_for_status = Mock()
116126
response.json = Mock(return_value={"success": True})
@@ -141,8 +151,10 @@ async def test_validate_control_data_accepts_template_control_input() -> None:
141151
}
142152
)
143153

154+
# When: validating template-backed control input through the SDK wrapper
144155
await agent_control.controls.validate_control_data(client, template_input)
145156

157+
# Then: the template-backed payload is posted to the validate endpoint
146158
client.http_client.post.assert_awaited_once()
147159
_, kwargs = client.http_client.post.await_args
148160
assert kwargs["json"]["data"]["template_values"]["pattern"] == "hello"
@@ -156,6 +168,7 @@ async def test_validate_control_data_accepts_template_control_input() -> None:
156168

157169
@pytest.mark.asyncio
158170
async def test_set_control_data_accepts_template_control_input() -> None:
171+
# Given: an SDK client stub and template-backed control input
159172
response = Mock()
160173
response.raise_for_status = Mock()
161174
response.json = Mock(return_value={"success": True})
@@ -186,14 +199,17 @@ async def test_set_control_data_accepts_template_control_input() -> None:
186199
}
187200
)
188201

202+
# When: updating control data through the SDK wrapper
189203
await agent_control.controls.set_control_data(client, 123, template_input)
190204

205+
# Then: the template values are serialized into the request body
191206
client.http_client.put.assert_awaited_once()
192207
_, kwargs = client.http_client.put.await_args
193208
assert kwargs["json"]["data"]["template_values"]["pattern"] == "hello"
194209

195210

196211
def test_to_template_control_input_reshapes_stored_control_data() -> None:
212+
# Given: stored template-backed control data returned by the API
197213
template_input = agent_control.controls.to_template_control_input(
198214
{
199215
"execution": "server",
@@ -230,11 +246,15 @@ def test_to_template_control_input_reshapes_stored_control_data() -> None:
230246
}
231247
)
232248

249+
# When: reshaping the stored data into template input
250+
# Then: the result is template-backed input with the original values
233251
assert isinstance(template_input, TemplateControlInput)
234252
assert template_input.template_values == {"pattern": "hello"}
235253

236254

237255
def test_to_template_control_input_rejects_raw_control_data() -> None:
256+
# Given: raw control data without template metadata
257+
# When: reshaping it into template-backed control input
238258
with pytest.raises(ValueError, match="not template-backed"):
239259
agent_control.controls.to_template_control_input(
240260
{
@@ -250,3 +270,4 @@ def test_to_template_control_input_rejects_raw_control_data() -> None:
250270
"action": {"decision": "deny"},
251271
}
252272
)
273+
# Then: the helper rejects the raw control data

‎sdks/python/tests/test_local_evaluation.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,19 +305,22 @@ async def test_server_only_template_backed_controls_still_call_server(
305305
llm_payload,
306306
):
307307
"""Template metadata must not break routing for server-executed cached controls."""
308+
# Given: a cached server-executed control that includes template metadata
308309
controls = [
309310
add_template_metadata(
310311
make_control_dict(1, "templated_server_ctrl", execution="server"),
311312
),
312313
]
313314

315+
# Given: a client stub that returns a safe server evaluation response
314316
client = MagicMock(spec=AgentControlClient)
315317
mock_response = MagicMock()
316318
mock_response.json.return_value = {"is_safe": True, "confidence": 1.0}
317319
mock_response.raise_for_status = MagicMock()
318320
client.http_client = AsyncMock()
319321
client.http_client.post = AsyncMock(return_value=mock_response)
320322

323+
# When: evaluating with local controls enabled
321324
result = await check_evaluation_with_local(
322325
client=client,
323326
agent_name=agent_name,
@@ -326,6 +329,7 @@ async def test_server_only_template_backed_controls_still_call_server(
326329
controls=controls,
327330
)
328331

332+
# Then: the SDK still routes evaluation to the server
329333
client.http_client.post.assert_called_once()
330334
assert result.is_safe is True
331335

@@ -512,15 +516,18 @@ async def test_template_backed_local_control_executes_locally(
512516
llm_payload,
513517
):
514518
"""Template metadata should be ignored by runtime parsing in local evaluation."""
519+
# Given: a cached SDK-executed control that includes template metadata
515520
controls = [
516521
add_template_metadata(
517522
make_control_dict(1, "templated_local_ctrl", execution="sdk", pattern=r"test"),
518523
),
519524
]
525+
# Given: a client stub that would allow server calls if routing were wrong
520526
client = MagicMock(spec=AgentControlClient)
521527
client.http_client = AsyncMock()
522528
client.http_client.post = AsyncMock()
523529

530+
# When: evaluating with local controls enabled
524531
result = await check_evaluation_with_local(
525532
client=client,
526533
agent_name=agent_name,
@@ -529,6 +536,7 @@ async def test_template_backed_local_control_executes_locally(
529536
controls=controls,
530537
)
531538

539+
# Then: local evaluation succeeds and no server call is made
532540
client.http_client.post.assert_not_called()
533541
assert result.is_safe is False
534542
assert result.matches is not None

0 commit comments

Comments
 (0)