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
63 lines
2.4 KiB
Python
63 lines
2.4 KiB
Python
"""Auto-discover all routes and verify they're covered in permission_config.
|
|
|
|
This test runs without a server — it inspects the FastAPI app directly.
|
|
When a new route is added, this test will FAIL until it's added to
|
|
permission_config.ROUTE_PERMISSIONS.
|
|
"""
|
|
|
|
from tests.permission_config import ROUTE_PERMISSIONS, DYNAMIC_ROUTES, EXCLUDED_ROUTES
|
|
|
|
|
|
def collect_app_routes():
|
|
"""Return set of (method, path) from the main app and all routers."""
|
|
from app.main import app
|
|
routes = set()
|
|
for route in app.routes:
|
|
if hasattr(route, "methods") and hasattr(route, "path"):
|
|
for method in route.methods:
|
|
method = method.upper()
|
|
if method in ("GET", "POST", "PUT", "DELETE", "PATCH"):
|
|
routes.add((method, route.path))
|
|
return routes
|
|
|
|
|
|
def test_all_routes_covered():
|
|
"""Every app route must have a permission entry."""
|
|
app_routes = collect_app_routes() - EXCLUDED_ROUTES
|
|
config_routes = set(ROUTE_PERMISSIONS.keys())
|
|
missing = app_routes - config_routes
|
|
assert not missing, (
|
|
f"Routes missing from permission_config.ROUTE_PERMISSIONS:\n"
|
|
+ "\n".join(f" {m} {p}" for m, p in sorted(missing))
|
|
+ "\n\nAdd them to py_service/tests/permission_config.py"
|
|
)
|
|
|
|
|
|
def test_no_stale_permissions():
|
|
"""No stale entries in permission config."""
|
|
app_routes = collect_app_routes() - EXCLUDED_ROUTES
|
|
config_routes = set(ROUTE_PERMISSIONS.keys())
|
|
stale = config_routes - app_routes
|
|
assert not stale, (
|
|
f"ROUTE_PERMISSIONS has stale entries (route no longer exists):\n"
|
|
+ "\n".join(f" {m} {p}" for m, p in sorted(stale))
|
|
)
|
|
|
|
|
|
def test_dynamic_routes_also_covered():
|
|
"""All DYNAMIC_ROUTES must also be in ROUTE_PERMISSIONS."""
|
|
for entry in DYNAMIC_ROUTES:
|
|
assert entry in ROUTE_PERMISSIONS, (
|
|
f"{entry[0]} {entry[1]} is in DYNAMIC_ROUTES but missing from ROUTE_PERMISSIONS"
|
|
)
|
|
|
|
|
|
def test_all_roles_valid():
|
|
"""Allowed roles must be one of: owner, engineer, technician, anonymous, authenticated."""
|
|
valid_roles = {"owner", "engineer", "technician", "anonymous", "authenticated"}
|
|
for (method, path), allowed_roles in ROUTE_PERMISSIONS.items():
|
|
for role in allowed_roles:
|
|
assert role in valid_roles, (
|
|
f"{method} {path}: invalid role '{role}' (valid: {valid_roles})"
|
|
)
|