"""Strict permission tests — every role × every route with exact expected codes. ALLOWED → status must be 200 or 302 (never 403/404/500) DENIED → status must be 403 (strict — not 302!) ANONYMOUS → protected routes get 302 (redirect to login) → public routes get 200 Dynamic routes (with path params like {slug}) are skipped here. """ import pytest from tests.permission_config import ROUTE_PERMISSIONS, DYNAMIC_ROUTES, ROLES def _generate_test_cases(): """Yield (role, method, path, expected) tuples.""" for (method, path), allowed_roles in ROUTE_PERMISSIONS.items(): if (method, path) in DYNAMIC_ROUTES: continue for role in ROLES: if role in allowed_roles or "authenticated" in allowed_roles: yield (role, method, path, "allow") else: yield (role, method, path, "deny") # Anonymous if "anonymous" in allowed_roles: yield ("anonymous", method, path, "allow") else: yield ("anonymous", method, path, "deny") @pytest.mark.parametrize("role,method,path,expected", list(_generate_test_cases())) def test_access(role_client, role, method, path, expected): client = role_client[role] resp = client.request(method, path) if expected == "allow": assert resp.status_code not in (403, 404), ( f"[{role}] {method} {path}: got {resp.status_code}, expected non-403/404" ) if resp.status_code == 500: pytest.fail(f"[{role}] {method} {path}: 500 Internal Server Error") else: if role == "anonymous": assert resp.status_code in (302, 403), ( f"[{role}] {method} {path}: got {resp.status_code}, expected 302/403" ) else: assert resp.status_code == 403, ( f"[{role}] {method} {path}: got {resp.status_code}, expected 403" )