from __future__ import annotations import unittest from http import HTTPStatus from api_access import ROUTES, required_role from backend.http import correlation_id, normalize_error_payload from tools.build_api_registry import build as build_api_registry class HttpGovernanceTests(unittest.TestCase): def test_runtime_registry_resolves_every_declared_route(self) -> None: for item in build_api_registry()["routes"]: path = ( item["path"] .replace("(\\d{6})", "000001") .replace("(\\d+)", "1") .replace("(.+)", "sample") ) resolved = ROUTES.resolve(item["method"], path) self.assertIsNotNone(resolved, f"{item['method']} {path}") self.assertEqual(resolved.feature, item["feature"]) self.assertEqual(resolved.access, item["access"]) def test_unknown_route_has_no_runtime_match(self) -> None: self.assertIsNone(ROUTES.resolve("GET", "/api/not-registered")) def test_compatibility_access_function_uses_runtime_registry(self) -> None: self.assertEqual(required_role("GET", "/api/screener/setup"), "member") self.assertEqual(required_role("POST", "/api/admin/settings"), "admin") self.assertEqual(required_role("GET", "/api/dashboard"), "authenticated") def test_errors_keep_legacy_field_and_add_stable_contract(self) -> None: result = normalize_error_payload( {"error": "invalid input"}, HTTPStatus.BAD_REQUEST, "request-123" ) self.assertEqual(result["error"], "invalid input") self.assertEqual(result["message"], "invalid input") self.assertEqual(result["code"], "bad_request") self.assertEqual(result["request_id"], "request-123") def test_correlation_id_rejects_header_injection(self) -> None: self.assertEqual(correlation_id("client-request-123"), "client-request-123") self.assertRegex(correlation_id("bad\r\nheader"), r"^[0-9a-f]{32}$") if __name__ == "__main__": unittest.main()