"""Test that all service portal routes return appropriate status codes. This helps catch 404 (missing routes), 500 (server errors), and auth issues. """ import pytest # Routes accessible without authentication (should return 200 or 302 redirect) PUBLIC_ROUTES = [ ("GET", "/service/login"), ("GET", "/health"), ] # All protected GET routes (require auth) PROTECTED_GET_ROUTES = [ "/service/dashboard", "/service/users", "/service/customers", "/service/objects", "/service/assignments", "/service/sla", "/service/ceo", "/service/coefficients", "/service/coefficients/test", "/service/formulas", "/service/tasks", "/service/reports", "/service/incidents", "/service/questionnaire", "/service/questionnaire-config", "/service/passports", "/service/checklist", "/service/blog", "/service/cases", "/service/ideas", "/service/charts", "/service/portal-settings", "/service/role-settings", "/service/bot-settings", "/service/bot-settings/responses", "/service/bot-settings/features", "/service/bot-settings/categories", "/service/bot-settings/kb", "/service/bot-settings/conversations", "/service/bot-settings/users", "/service/bot-settings/test-runner", "/service/bot-settings/analytics", "/service/quick-menu", "/service/documents/", ] API_GET_ROUTES = [ "/service/api/shs", "/service/api/tasks", "/service/api/changelog", "/service/api/quick-menu", "/service/api/role-permissions", "/service/api/charts/data", "/service/api/bot/settings", "/service/api/bot/templates", "/service/api/bot/features", "/service/api/bot/categories", "/service/api/bot/kb", "/service/api/bot/conversations", "/service/api/bot/users", "/service/api/bot/analytics", "/service/api/bot/broadcast/history", "/service/api/bot/broadcast/recipients", ] class TestPublicRoutes: def test_login_page(self, client): resp = client.get("/service/login") assert resp.status_code in (200, 302) def test_health(self, client): resp = client.get("/health") assert resp.status_code == 200 def test_root_redirect(self, client): resp = client.get("/", follow_redirects=False) assert resp.status_code == 302 class TestAuthRedirect: """Unauthenticated requests should redirect to login.""" @pytest.mark.parametrize("method,path", PUBLIC_ROUTES) def test_public(self, client, method, path): resp = getattr(client, method.lower())(path, follow_redirects=False) assert resp.status_code in (200, 302), f"{method} {path} returned {resp.status_code}" @pytest.mark.parametrize("path", PROTECTED_GET_ROUTES) def test_protected_redirect(self, client, path): resp = client.get(path, follow_redirects=False) assert resp.status_code == 302, f"GET {path} returned {resp.status_code} (expected 302)" @pytest.mark.parametrize("path", API_GET_ROUTES) def test_api_no_auth(self, client, path): """API routes bypass auth middleware, but may fail due to missing DB.""" resp = client.get(path) # Should not be 404 — either 200, 500 (DB error), or 403 assert resp.status_code != 404, f"GET {path} returned 404 (route missing)" class TestOwnerAccess: """Owner should have access to all pages.""" @pytest.mark.parametrize("path", PROTECTED_GET_ROUTES) def test_all_pages(self, owner_client, path): resp = owner_client.get(path, follow_redirects=False) # 200 = success, 302 = redirect (some pages redirect), 500 = server error assert resp.status_code in (200, 302, 500), f"GET {path} returned {resp.status_code}" if resp.status_code == 500: pytest.fail(f"GET {path} returned 500 Internal Server Error") @pytest.mark.parametrize("path", API_GET_ROUTES) def test_api_routes(self, owner_client, path): resp = owner_client.get(path, follow_redirects=False) assert resp.status_code != 404, f"GET {path} returned 404 (route missing)" class TestEngineerAccess: """Engineer should have access to permitted pages only.""" ENGINEER_ALLOWED = [ "/service/dashboard", "/service/customers", "/service/objects", "/service/sla", "/service/questionnaire", "/service/passports", "/service/tasks", "/service/reports", "/service/incidents", "/service/documents/", ] ENGINEER_FORBIDDEN = [ "/service/users", "/service/assignments", "/service/ceo", "/service/coefficients", "/service/coefficients/test", "/service/formulas", "/service/questionnaire-config", "/service/blog", "/service/cases", "/service/ideas", "/service/charts", "/service/portal-settings", "/service/role-settings", "/service/bot-settings", "/service/bot-settings/responses", "/service/bot-settings/features", "/service/bot-settings/categories", "/service/bot-settings/kb", "/service/bot-settings/users", "/service/bot-settings/test-runner", "/service/bot-settings/analytics", "/service/bot-settings/conversations", ] @pytest.mark.parametrize("path", ENGINEER_ALLOWED) def test_allowed(self, engineer_client, path): resp = engineer_client.get(path, follow_redirects=False) assert resp.status_code in (200, 302, 500), f"GET {path} returned {resp.status_code}" if resp.status_code == 500: pytest.fail(f"GET {path} returned 500 Internal Server Error") @pytest.mark.parametrize("path", ENGINEER_FORBIDDEN) def test_forbidden(self, engineer_client, path): resp = engineer_client.get(path, follow_redirects=False) assert resp.status_code in (302, 403), f"GET {path} returned {resp.status_code} (expected 302/403)" class TestTechnicianAccess: """Technician should have very limited access.""" TECHNICIAN_ALLOWED = [ "/service/dashboard", "/service/tasks", "/service/reports", "/service/incidents", "/service/checklist", "/service/documents/", ] TECHNICIAN_FORBIDDEN = [ "/service/users", "/service/customers", "/service/objects", "/service/assignments", "/service/sla", "/service/ceo", "/service/coefficients", "/service/formulas", "/service/questionnaire", "/service/questionnaire-config", "/service/passports", "/service/blog", "/service/cases", "/service/ideas", "/service/charts", "/service/portal-settings", "/service/role-settings", "/service/bot-settings", ] @pytest.mark.parametrize("path", TECHNICIAN_ALLOWED) def test_allowed(self, technician_client, path): resp = technician_client.get(path, follow_redirects=False) assert resp.status_code in (200, 302, 500), f"GET {path} returned {resp.status_code}" @pytest.mark.parametrize("path", TECHNICIAN_FORBIDDEN) def test_forbidden(self, technician_client, path): resp = technician_client.get(path, follow_redirects=False) assert resp.status_code in (302, 403), f"GET {path} returned {resp.status_code} (expected 302/403)" class TestSidebarRoutes: """Every sidebar link should resolve without 404.""" SIDEBAR_LINKS = [ "/service/dashboard", "/service/charts", "/service/ceo", "/service/customers", "/service/objects", "/service/sla", "/service/questionnaire", "/service/passports", "/service/users", "/service/assignments", "/service/documents/", "/service/tasks", "/service/reports", "/service/incidents", "/service/checklist", "/service/documents/tech-access", "/service/bot-settings/analytics", "/service/bot-settings/users", "/service/bot-settings/conversations", "/service/blog", "/service/cases", "/service/questionnaire-config", "/service/formulas", "/service/bot-settings", "/service/portal-settings", "/service/role-settings", "/service/ideas", ] @pytest.mark.parametrize("path", SIDEBAR_LINKS) def test_sidebar_link(self, owner_client, path): resp = owner_client.get(path, follow_redirects=False) assert resp.status_code != 404, f"Sidebar link {path} returned 404 (missing route)"