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
143 lines
4.7 KiB
Python
143 lines
4.7 KiB
Python
"""Test role switching lifecycle — owner→engineer→reset, etc.
|
|
|
|
These tests verify that the session remains consistent after role switches.
|
|
The critical check: after a reset, the original role's access is fully restored.
|
|
"""
|
|
|
|
import httpx
|
|
from tests.conftest import SERVER_URL, PASSWORD
|
|
|
|
|
|
class TestRoleSwitch:
|
|
|
|
def _login(self, role: str) -> httpx.Client:
|
|
"""Create a fresh authenticated client."""
|
|
c = httpx.Client(base_url=SERVER_URL, follow_redirects=False, timeout=10)
|
|
resp = c.post("/service/login", data={"login": role, "password": PASSWORD})
|
|
assert resp.status_code == 302
|
|
return c
|
|
|
|
def _switch_role(self, client: httpx.Client, target: str):
|
|
resp = client.post("/service/api/role/switch", json={"role": target})
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert data["ok"] is True
|
|
return data
|
|
|
|
def _reset_role(self, client: httpx.Client):
|
|
resp = client.post("/service/api/role/reset")
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert data["ok"] is True
|
|
return data
|
|
|
|
def test_owner_to_engineer_and_back(self):
|
|
"""Owner switches to engineer, then resets — full access restored."""
|
|
client = self._login("owner")
|
|
|
|
# Owner can access owner pages
|
|
r = client.get("/service/users")
|
|
assert r.status_code == 200
|
|
|
|
# Switch to engineer
|
|
data = self._switch_role(client, "engineer")
|
|
assert data["role"] == "engineer"
|
|
assert data["original_role"] == "owner"
|
|
|
|
# Engineer cannot access owner pages
|
|
r = client.get("/service/users")
|
|
assert r.status_code == 403, "Engineer should be denied owner pages"
|
|
|
|
# Engineer CAN access shared pages
|
|
r = client.get("/service/customers")
|
|
assert r.status_code == 200
|
|
|
|
# Reset to owner
|
|
data = self._reset_role(client)
|
|
assert data["role"] == "owner"
|
|
|
|
# CRITICAL: Owner access restored
|
|
r = client.get("/service/users")
|
|
assert r.status_code == 200, "Owner access not restored after reset!"
|
|
|
|
# Dashboard still works
|
|
r = client.get("/service/dashboard")
|
|
assert r.status_code == 200
|
|
|
|
def test_owner_to_technician_and_back(self):
|
|
"""Owner switches directly to technician."""
|
|
client = self._login("owner")
|
|
|
|
r = client.get("/service/users")
|
|
assert r.status_code == 200
|
|
|
|
self._switch_role(client, "technician")
|
|
|
|
r = client.get("/service/users")
|
|
assert r.status_code == 403
|
|
|
|
r = client.get("/service/tasks")
|
|
assert r.status_code == 200
|
|
|
|
self._reset_role(client)
|
|
|
|
r = client.get("/service/users")
|
|
assert r.status_code == 200
|
|
|
|
def test_engineer_to_technician_and_back(self):
|
|
"""Engineer switches to technician, then resets."""
|
|
client = self._login("engineer")
|
|
|
|
# Engineer can access engineer pages
|
|
r = client.get("/service/customers")
|
|
assert r.status_code == 200
|
|
|
|
self._switch_role(client, "technician")
|
|
|
|
# Technician cannot access engineer pages
|
|
r = client.get("/service/customers")
|
|
assert r.status_code == 403
|
|
|
|
r = client.get("/service/checklist")
|
|
assert r.status_code == 200
|
|
|
|
self._reset_role(client)
|
|
|
|
# Engineer access restored
|
|
r = client.get("/service/customers")
|
|
assert r.status_code == 200
|
|
|
|
def test_double_switch_owner_engineer_technician_then_reset(self):
|
|
"""Multi-step: owner→engineer→technician→reset→owner."""
|
|
client = self._login("owner")
|
|
|
|
self._switch_role(client, "engineer")
|
|
r = client.get("/service/users")
|
|
assert r.status_code == 403
|
|
|
|
# Switch deeper while already impersonating
|
|
self._switch_role(client, "technician")
|
|
r = client.get("/service/users")
|
|
assert r.status_code == 403
|
|
r = client.get("/service/tasks")
|
|
assert r.status_code == 200
|
|
|
|
# Single reset should restore owner
|
|
self._reset_role(client)
|
|
r = client.get("/service/users")
|
|
assert r.status_code == 200
|
|
|
|
def test_forbidden_switch_technician_cannot_switch(self):
|
|
"""Technician cannot use role switch (not in allowed_roles)."""
|
|
client = self._login("technician")
|
|
resp = client.post("/service/api/role/switch", json={"role": "owner"})
|
|
assert resp.status_code == 403
|
|
|
|
def test_reset_without_switch_does_not_error(self):
|
|
"""Reset without an active override should still work."""
|
|
client = self._login("owner")
|
|
resp = client.post("/service/api/role/reset")
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert data["role"] == "owner"
|