bd048ea23d
- 6 new test files: route discovery, strict permissions, role switch, CRUD operations, 500 error protection - permission_config.py: central permission definitions for all 110+ routes - Bug fixes: is_active checkbox in users_edit, int(id) 500 crash, missing validation in users_create/users_delete - Pre-commit hooks: lint + static routing tests - GitHub Actions CI: full pytest suite on push - Deploy gate: pytest runs before deploy, aborts on failure - All 3 deploy scripts: --skip-tests flag support
53 lines
1.9 KiB
Python
53 lines
1.9 KiB
Python
"""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"
|
||
)
|