Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions scripts/ci/backend_staging_smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,21 @@ def validate_bill_diff_payload(status: int, payload: Any) -> None:
require_non_empty_list(body, "bills:diff-full", "clauses")


def validate_bill_diff_unknown(status: int, payload: Any) -> None:
body = require_dict(payload, "bills:diff-unknown")
if is_api_gateway_not_found(status, body):
raise SmokeFailure("bills:diff-unknown: API Gateway returned Not Found; route is missing or unsynced")
if "error" not in body:
raise SmokeFailure(
"bills:diff-unknown: expected service-owned error body (key 'error'), got keys: "
+ (", ".join(sorted(body)) or "none")
)
if status == 404 and "not found" not in str(body["error"]).lower():
raise SmokeFailure(
f"bills:diff-unknown: 404 body is not a documented not-found message: {body['error']}"
)


def validate_members(_: int, payload: Any) -> None:
body = require_dict(payload, "members")
require_keys(body, "members", {"members"})
Expand Down Expand Up @@ -344,6 +359,17 @@ def validate_hansard_search_manifest(status: int, payload: Any) -> None:
deterministic_note="Route-reachability check omits from/to so the bills service returns its own HTTP 400 before diff data is required.",
fixture_note="No backfilled diff fixture required; API Gateway 404 is treated as a route exposure failure.",
),
SmokeCheck(
name="bills:diff-unknown",
method="GET",
path="/api/v1/bills/ZZ-9999/diff",
query={"from": "v1", "to": "v2"},
expected_statuses={404, 503},
validator=validate_bill_diff_unknown,
service="bills",
deterministic_note="Negative check — an unknown bill id with from/to set drives the bills service's own application-level 404 ('bill not found'), proving the route reaches the Lambda and is distinguished from an API Gateway route-missing 404.",
fixture_note="No backfilled diff data required; an unknown bill returns 404 before any version/diff lookup. HTTP 503 is tolerated while the bills index warms.",
),
SmokeCheck(
name="bills:diff-full",
method="GET",
Expand Down
23 changes: 23 additions & 0 deletions scripts/ci/test_backend_staging_smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,35 @@ def test_bill_diff_full_validator_requires_seeded_payload():
)


def test_bill_diff_unknown_validator_accepts_service_owned_404():
smoke = load_smoke_module()

smoke.validate_bill_diff_unknown(404, {"error": "bill not found"})
smoke.validate_bill_diff_unknown(404, {"error": "version not found"})
# The bills index can still be warming; a service-owned 503 proves route reachability.
smoke.validate_bill_diff_unknown(503, {"error": "bills index checksum mismatch"})


def test_bill_diff_unknown_validator_rejects_api_gateway_404():
smoke = load_smoke_module()

with pytest.raises(smoke.SmokeFailure, match="API Gateway returned Not Found"):
smoke.validate_bill_diff_unknown(404, {"message": "Not Found"})

with pytest.raises(smoke.SmokeFailure, match="service-owned error body"):
smoke.validate_bill_diff_unknown(404, {})

with pytest.raises(smoke.SmokeFailure, match="not a documented not-found message"):
smoke.validate_bill_diff_unknown(404, {"error": "internal error"})


def test_full_only_bill_diff_check_is_skipped_in_contract_mode():
smoke = load_smoke_module()

contract_checks = [check.name for check in smoke.CHECKS if not check.full_only]
full_checks = [check.name for check in smoke.CHECKS]

assert "bills:diff-route" in contract_checks
assert "bills:diff-unknown" in contract_checks
assert "bills:diff-full" not in contract_checks
assert "bills:diff-full" in full_checks
Loading