Skip to content

Commit ef13298

Browse files
committed
test: add negative template control failure coverage
1 parent a06c83f commit ef13298

1 file changed

Lines changed: 88 additions & 0 deletions

File tree

‎server/tests/test_control_templates.py‎

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,37 @@ def test_create_template_backed_control_persists_resolved_defaults_when_values_o
398398
}
399399

400400

401+
def test_create_template_backed_control_failure_does_not_persist_control(
402+
client: TestClient,
403+
) -> None:
404+
# Given: an invalid template-backed create payload and the current control list
405+
invalid_payload = _template_payload()
406+
invalid_payload["template_values"] = {"pattern": "["}
407+
control_name = f"invalid-template-control-{uuid.uuid4()}"
408+
409+
before_response = client.get("/api/v1/controls")
410+
assert before_response.status_code == 200, before_response.text
411+
before_controls = before_response.json()["controls"]
412+
before_ids = {control["id"] for control in before_controls}
413+
414+
# When: creating the invalid template-backed control
415+
response = client.put(
416+
"/api/v1/controls",
417+
json={"name": control_name, "data": invalid_payload},
418+
)
419+
420+
# Then: the request fails and no control is persisted
421+
assert response.status_code == 422, response.text
422+
assert response.json()["error_code"] == "TEMPLATE_PARAMETER_INVALID"
423+
424+
after_response = client.get("/api/v1/controls")
425+
assert after_response.status_code == 200, after_response.text
426+
after_controls = after_response.json()["controls"]
427+
after_ids = {control["id"] for control in after_controls}
428+
assert after_ids == before_ids
429+
assert all(control["name"] != control_name for control in after_controls)
430+
431+
401432
def test_template_backed_control_evaluates_after_policy_attachment(client: TestClient) -> None:
402433
# Given: a template-backed control attached to an agent through a policy
403434
control_id, control_name = _create_template_control_with_name(client)
@@ -746,6 +777,36 @@ def test_raw_control_can_be_replaced_with_template_backed_control(client: TestCl
746777
assert data["condition"]["evaluator"]["config"]["pattern"] == "hello"
747778

748779

780+
def test_raw_control_failed_template_replacement_does_not_mutate_raw_control(
781+
client: TestClient,
782+
) -> None:
783+
# Given: an existing raw control and invalid template-backed replacement input
784+
control_id = _create_raw_control(client)
785+
original_response = client.get(f"/api/v1/controls/{control_id}/data")
786+
assert original_response.status_code == 200, original_response.text
787+
original_data = original_response.json()["data"]
788+
789+
invalid_payload = _template_payload()
790+
invalid_payload["template_values"] = {"pattern": "["}
791+
792+
# When: replacing the raw control with invalid template-backed input
793+
response = client.put(
794+
f"/api/v1/controls/{control_id}/data",
795+
json={"data": invalid_payload},
796+
)
797+
798+
# Then: the conversion fails and the raw control stays unchanged
799+
assert response.status_code == 422, response.text
800+
assert response.json()["error_code"] == "TEMPLATE_PARAMETER_INVALID"
801+
802+
refreshed_response = client.get(f"/api/v1/controls/{control_id}/data")
803+
assert refreshed_response.status_code == 200, refreshed_response.text
804+
refreshed_data = refreshed_response.json()["data"]
805+
assert refreshed_data == original_data
806+
assert "template" not in refreshed_data
807+
assert "template_values" not in refreshed_data
808+
809+
749810
def test_template_update_preserves_enabled_value(client: TestClient) -> None:
750811
# Given: a template-backed control that has been disabled through the metadata patch API
751812
control_id = _create_template_control(client)
@@ -781,6 +842,33 @@ def test_template_update_preserves_enabled_value(client: TestClient) -> None:
781842
assert data["scope"]["step_names"] == ["updated-step"]
782843

783844

845+
def test_template_update_failure_does_not_mutate_existing_template_backed_control(
846+
client: TestClient,
847+
) -> None:
848+
# Given: an existing template-backed control and invalid updated template values
849+
control_id = _create_template_control(client)
850+
original_response = client.get(f"/api/v1/controls/{control_id}/data")
851+
assert original_response.status_code == 200, original_response.text
852+
original_data = original_response.json()["data"]
853+
854+
invalid_payload = _template_payload()
855+
invalid_payload["template_values"] = {"pattern": "["}
856+
857+
# When: updating the stored template-backed control with invalid input
858+
response = client.put(
859+
f"/api/v1/controls/{control_id}/data",
860+
json={"data": invalid_payload},
861+
)
862+
863+
# Then: the update fails and the stored control remains unchanged
864+
assert response.status_code == 422, response.text
865+
assert response.json()["error_code"] == "TEMPLATE_PARAMETER_INVALID"
866+
867+
refreshed_response = client.get(f"/api/v1/controls/{control_id}/data")
868+
assert refreshed_response.status_code == 200, refreshed_response.text
869+
assert refreshed_response.json()["data"] == original_data
870+
871+
784872
def test_template_update_preview_matches_persisted_rendered_control(client: TestClient) -> None:
785873
# Given: an existing template-backed control and updated template values
786874
control_id = _create_template_control(client)

0 commit comments

Comments
 (0)