Files
DashBoard/tests/test_portal_shell_routes.py

314 lines
11 KiB
Python

# -*- coding: utf-8 -*-
"""Tests for portal shell routes and navigation API."""
from __future__ import annotations
import json
from unittest.mock import patch
from mes_dashboard.app import create_app
def _login_as_admin(client) -> None:
with client.session_transaction() as sess:
sess["admin"] = {"displayName": "Admin", "employeeNo": "A001"}
def test_portal_shell_fallback_html_served_when_dist_missing(monkeypatch):
app = create_app("testing")
app.config["TESTING"] = True
# Force fallback path by simulating missing dist file.
monkeypatch.setattr("os.path.exists", lambda *_args, **_kwargs: False)
client = app.test_client()
response = client.get("/portal-shell")
assert response.status_code == 200
html = response.data.decode("utf-8")
assert "/static/dist/portal-shell.js" in html
assert "/static/dist/portal-shell.css" in html
assert "/static/dist/tailwind.css" in html
response_with_trailing_slash = client.get("/portal-shell/")
assert response_with_trailing_slash.status_code == 200
def test_portal_shell_uses_nested_dist_html_when_top_level_missing(monkeypatch):
app = create_app("testing")
app.config["TESTING"] = True
def fake_exists(path: str) -> bool:
if path.endswith("/dist/portal-shell.html"):
return False
return path.endswith("/dist/src/portal-shell/index.html")
monkeypatch.setattr("os.path.exists", fake_exists)
client = app.test_client()
response = client.get("/portal-shell")
assert response.status_code == 200
html = response.data.decode("utf-8")
assert "/static/dist/portal-shell.js" in html
assert "/static/dist/portal-shell.css" in html
assert "/static/dist/tailwind.css" in html
def test_portal_navigation_non_admin_visibility_matches_release_only():
app = create_app("testing")
app.config["TESTING"] = True
client = app.test_client()
response = client.get("/api/portal/navigation")
assert response.status_code == 200
payload = json.loads(response.data.decode("utf-8"))
assert payload["is_admin"] is False
assert payload["admin_user"] is None
all_routes = {
page["route"]
for drawer in payload["drawers"]
for page in drawer["pages"]
}
# Non-admin baseline from current config.
assert "/wip-overview" in all_routes
assert "/resource" in all_routes
assert "/qc-gate" in all_routes
assert "/resource-history" in all_routes
assert "/job-query" in all_routes
assert "/admin/pages" not in all_routes
assert "/excel-query" not in all_routes
def test_portal_navigation_admin_includes_admin_drawer_routes():
app = create_app("testing")
app.config["TESTING"] = True
client = app.test_client()
_login_as_admin(client)
response = client.get("/api/portal/navigation")
assert response.status_code == 200
payload = json.loads(response.data.decode("utf-8"))
assert payload["is_admin"] is True
assert payload["admin_user"]["displayName"] == "Admin"
all_routes = {
page["route"]
for drawer in payload["drawers"]
for page in drawer["pages"]
}
assert "/admin/pages" in all_routes
assert "/admin/performance" in all_routes
assert "/excel-query" in all_routes
def test_wrapper_telemetry_endpoint_removed_after_wrapper_decommission():
app = create_app("testing")
app.config["TESTING"] = True
client = app.test_client()
post_response = client.post(
"/api/portal/wrapper-telemetry",
json={
"route": "/job-query",
"event_type": "wrapper_load_success",
},
)
assert post_response.status_code == 404
get_response = client.get("/api/portal/wrapper-telemetry")
assert get_response.status_code == 404
def test_navigation_drawer_and_page_order_deterministic_non_admin():
app = create_app("testing")
app.config["TESTING"] = True
client = app.test_client()
response = client.get("/api/portal/navigation")
assert response.status_code == 200
payload = json.loads(response.data.decode("utf-8"))
drawer_ids = [drawer["id"] for drawer in payload["drawers"]]
assert drawer_ids == ["reports", "drawer-2", "drawer"]
reports_routes = [page["route"] for page in payload["drawers"][0]["pages"]]
assert reports_routes == ["/wip-overview", "/resource", "/qc-gate"]
def test_navigation_contract_page_metadata_fields_present_and_typed():
app = create_app("testing")
app.config["TESTING"] = True
client = app.test_client()
payload = json.loads(client.get("/api/portal/navigation").data.decode("utf-8"))
assert isinstance(payload["drawers"], list)
for drawer in payload["drawers"]:
assert isinstance(drawer["id"], str) and drawer["id"]
assert isinstance(drawer["name"], str) and drawer["name"]
assert isinstance(drawer["order"], int)
assert isinstance(drawer["admin_only"], bool)
assert isinstance(drawer["pages"], list)
for page in drawer["pages"]:
assert isinstance(page["route"], str) and page["route"].startswith("/")
assert isinstance(page["name"], str) and page["name"]
assert page["status"] in {"released", "dev"}
assert isinstance(page["order"], int)
def test_navigation_duplicate_order_values_still_resolve_deterministically():
app = create_app("testing")
app.config["TESTING"] = True
client = app.test_client()
config = [
{
"id": "reports",
"name": "Reports",
"order": 1,
"admin_only": False,
"pages": [
{"route": "/qc-gate", "name": "QC", "status": "released", "order": 1},
{"route": "/resource", "name": "Resource", "status": "released", "order": 1},
{"route": "/wip-overview", "name": "WIP", "status": "released", "order": 1},
],
},
{
"id": "tools",
"name": "Tools",
"order": 1,
"admin_only": False,
"pages": [
{"route": "/job-query", "name": "Job", "status": "released", "order": 1},
],
},
]
with patch("mes_dashboard.app.get_navigation_config", return_value=config):
payload = json.loads(client.get("/api/portal/navigation").data.decode("utf-8"))
# Drawer tie breaks by name and page tie breaks by name.
assert [drawer["id"] for drawer in payload["drawers"]] == ["reports", "tools"]
assert [page["route"] for page in payload["drawers"][0]["pages"]] == ["/qc-gate", "/resource", "/wip-overview"]
def test_navigation_mixed_release_dev_visibility_admin_vs_non_admin():
app = create_app("testing")
app.config["TESTING"] = True
non_admin_client = app.test_client()
non_admin_resp = non_admin_client.get("/api/portal/navigation")
assert non_admin_resp.status_code == 200
non_admin_payload = json.loads(non_admin_resp.data.decode("utf-8"))
non_admin_routes = {
page["route"]
for drawer in non_admin_payload["drawers"]
for page in drawer["pages"]
}
assert "/hold-overview" not in non_admin_routes
assert "/hold-history" not in non_admin_routes
admin_client = app.test_client()
_login_as_admin(admin_client)
admin_resp = admin_client.get("/api/portal/navigation")
assert admin_resp.status_code == 200
admin_payload = json.loads(admin_resp.data.decode("utf-8"))
admin_routes = {
page["route"]
for drawer in admin_payload["drawers"]
for page in drawer["pages"]
}
assert "/hold-overview" in admin_routes
assert "/hold-history" in admin_routes
def test_portal_navigation_includes_admin_links_by_auth_state():
app = create_app("testing")
app.config["TESTING"] = True
non_admin_client = app.test_client()
non_admin_payload = json.loads(non_admin_client.get("/api/portal/navigation").data.decode("utf-8"))
assert non_admin_payload["admin_links"]["login"].startswith("/admin/login?next=")
assert non_admin_payload["admin_links"]["pages"] is None
assert non_admin_payload["admin_links"]["logout"] is None
admin_client = app.test_client()
_login_as_admin(admin_client)
admin_payload = json.loads(admin_client.get("/api/portal/navigation").data.decode("utf-8"))
assert admin_payload["admin_links"]["pages"] == "/admin/pages"
assert admin_payload["admin_links"]["logout"] == "/admin/logout"
def test_portal_navigation_emits_diagnostics_for_invalid_navigation_payload():
app = create_app("testing")
app.config["TESTING"] = True
client = app.test_client()
malformed = [
{"id": "", "name": "bad-drawer", "pages": []},
{
"id": "reports",
"name": "Reports",
"order": 1,
"admin_only": False,
"pages": [
{"route": "", "name": "invalid-route"},
{"route": "missing-leading-slash", "name": "invalid-route-2"},
"not-a-dict",
],
},
]
with patch("mes_dashboard.app.get_navigation_config", return_value=malformed):
response = client.get("/api/portal/navigation")
assert response.status_code == 200
payload = json.loads(response.data.decode("utf-8"))
diagnostics = payload["diagnostics"]
assert diagnostics["invalid_drawers"] >= 1
assert diagnostics["invalid_pages"] >= 2
assert payload["drawers"] == []
def test_portal_navigation_logs_contract_mismatch_route():
app = create_app("testing")
app.config["TESTING"] = True
client = app.test_client()
with (
patch("mes_dashboard.app._load_shell_route_contract_routes", return_value={"/wip-overview"}),
patch(
"mes_dashboard.app.get_navigation_config",
return_value=[
{
"id": "reports",
"name": "Reports",
"order": 1,
"admin_only": False,
"pages": [
{"route": "/wip-overview", "name": "WIP", "status": "released", "order": 1},
{"route": "/resource", "name": "Resource", "status": "released", "order": 2},
],
}
],
),
):
response = client.get("/api/portal/navigation")
assert response.status_code == 200
payload = json.loads(response.data.decode("utf-8"))
assert payload["diagnostics"]["contract_mismatch_routes"] == ["/resource"]
def test_wave_b_native_routes_are_reachable():
app = create_app("testing")
app.config["TESTING"] = True
client = app.test_client()
_login_as_admin(client)
for route in ["/job-query", "/excel-query", "/query-tool", "/tmtt-defect"]:
response = client.get(route)
assert response.status_code == 200, f"{route} should be reachable"