test(observability): validate nav active page contract
Some checks failed
CD Pipeline / deploy (push) Has been cancelled
Some checks failed
CD Pipeline / deploy (push) Has been cancelled
This commit is contained in:
@@ -30,9 +30,23 @@ TEMPLATE_PATHS = [
|
||||
"templates/admin/ppt_audit_history.html",
|
||||
]
|
||||
|
||||
OBSERVABILITY_NAV_ITEMS = [
|
||||
("templates/admin/observability_overview.html", "obs_overview", "/observability/overview", "觀測台總覽"),
|
||||
("templates/admin/agent_orchestration.html", "obs_agent_orchestration", "/observability/agent_orchestration", "Agent 編排矩陣"),
|
||||
("templates/admin/business_intel.html", "obs_business_intel", "/observability/business_intel", "商業面 × AI"),
|
||||
("templates/admin/host_health.html", "obs_host_health", "/observability/host_health", "主機健康"),
|
||||
("templates/admin/ai_calls_dashboard.html", "obs_ai_calls", "/observability/ai_calls", "AI 呼叫"),
|
||||
("templates/admin/budget.html", "obs_budget", "/observability/budget", "預算控管"),
|
||||
("templates/admin/promotion_review.html", "obs_promotion_review", "/observability/promotion_review", "RAG 晉升審核"),
|
||||
("templates/admin/rag_queries.html", "obs_rag_queries", "/observability/rag_queries", "RAG 召回詳情"),
|
||||
("templates/admin/quality_trend.html", "obs_quality_trend", "/observability/quality_trend", "反饋趨勢"),
|
||||
("templates/admin/ppt_audit_history.html", "obs_ppt_audit", "/observability/ppt_audit_history", "PPT 視覺審核"),
|
||||
]
|
||||
|
||||
CSS_PATH = Path("static/css/observability-system.css")
|
||||
SHELL_PATH = Path("templates/components/_ewoooc_shell.html")
|
||||
BASE_PATH = Path("templates/ewoooc_base.html")
|
||||
ROUTE_PATH = Path("routes/admin_observability_routes.py")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -195,6 +209,55 @@ def scan_shell() -> list[str]:
|
||||
return findings
|
||||
|
||||
|
||||
def scan_nav_contract() -> list[str]:
|
||||
shell_path = ROOT / SHELL_PATH
|
||||
base_path = ROOT / BASE_PATH
|
||||
route_path = ROOT / ROUTE_PATH
|
||||
if not shell_path.exists() or not base_path.exists():
|
||||
return []
|
||||
|
||||
shell_text = shell_path.read_text(encoding="utf-8")
|
||||
base_text = base_path.read_text(encoding="utf-8")
|
||||
route_text = route_path.read_text(encoding="utf-8") if route_path.exists() else ""
|
||||
findings: list[str] = []
|
||||
|
||||
for template_path, active_page, url, label in OBSERVABILITY_NAV_ITEMS:
|
||||
template = ROOT / template_path
|
||||
if not template.exists():
|
||||
findings.append(f"{template_path}: missing observability template for `{label}`")
|
||||
continue
|
||||
|
||||
template_text = template.read_text(encoding="utf-8")
|
||||
active_pattern = re.compile(
|
||||
r"set\s+active_page\s*=\s*['\"]" + re.escape(active_page) + r"['\"]"
|
||||
)
|
||||
route_has_contract = template_path.replace("templates/", "") in route_text and active_page in route_text
|
||||
if not active_pattern.search(template_text) and not route_has_contract:
|
||||
findings.append(
|
||||
f"{template_path}: active_page must be `{active_page}` in template or route for `{label}`"
|
||||
)
|
||||
|
||||
if active_page not in shell_text:
|
||||
findings.append(f"{SHELL_PATH}: sidebar missing active_page `{active_page}`")
|
||||
if url not in shell_text:
|
||||
findings.append(f"{SHELL_PATH}: sidebar missing URL `{url}` for `{label}`")
|
||||
if label not in shell_text:
|
||||
findings.append(f"{SHELL_PATH}: sidebar missing label `{label}`")
|
||||
if active_page not in base_text:
|
||||
findings.append(f"{BASE_PATH}: momo-observability-mode list missing `{active_page}`")
|
||||
|
||||
sidebar_observability_links = set(re.findall(r'href="(/observability/[^"]+)"', shell_text))
|
||||
expected_links = {url for _, _, url, _ in OBSERVABILITY_NAV_ITEMS}
|
||||
extra_links = sorted(sidebar_observability_links - expected_links)
|
||||
missing_links = sorted(expected_links - sidebar_observability_links)
|
||||
for url in extra_links:
|
||||
findings.append(f"{SHELL_PATH}: unexpected observability sidebar URL `{url}`")
|
||||
for url in missing_links:
|
||||
findings.append(f"{SHELL_PATH}: missing observability sidebar URL `{url}`")
|
||||
|
||||
return findings
|
||||
|
||||
|
||||
def main() -> int:
|
||||
findings: list[str] = []
|
||||
for template_path in TEMPLATE_PATHS:
|
||||
@@ -202,6 +265,7 @@ def main() -> int:
|
||||
findings.extend(scan_css())
|
||||
findings.extend(scan_shell())
|
||||
findings.extend(scan_required_snippets(BASE_PATH, REQUIRED_BASE_SNIPPETS, "base/topbar"))
|
||||
findings.extend(scan_nav_contract())
|
||||
|
||||
if findings:
|
||||
print("Observability UI guard: FAIL")
|
||||
@@ -214,6 +278,7 @@ def main() -> int:
|
||||
print(f"- css guardrails checked: {len(REQUIRED_CSS_SNIPPETS)}")
|
||||
print(f"- sidebar/nav guardrails checked: {len(REQUIRED_SHELL_SNIPPETS)}")
|
||||
print(f"- base/topbar guardrails checked: {len(REQUIRED_BASE_SNIPPETS)}")
|
||||
print(f"- nav contract checked: {len(OBSERVABILITY_NAV_ITEMS)} pages")
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user