"""Tests for the system-health dashboard view (#639). Covers the acceptance criteria directly: the page renders the health DTO fields (AC1), degraded dependencies are visible (AC2), stale runtime is warned prominently and never rendered as mutation-safe (AC3), healthy and degraded fixtures both render (AC4), and the shell carries a nav entry (AC5). """ import sys import unittest from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from starlette.testclient import TestClient from webui.app import create_app from webui.deployment_boundary import scan_text_for_client_secrets from webui.layout import render_page from webui.nav import iter_nav_items from webui.system_health import ( STATUS_DEGRADED, STATUS_DOWN, STATUS_OK, STATUS_SKIPPED, STATUS_UNPROVEN, DependencyProbe, StaleRuntime, SystemHealthSnapshot, VersionInfo, ) from webui.system_health_views import render_system_health_page DASHBOARD_PATH = "/system-health" def _version(*, known: bool = True) -> VersionInfo: return VersionInfo( git_sha="1c455b6ec0f9cb761fe6248de68c17e061fb5ecd" if known else None, git_describe="v0.4.1-12-g1c455b6" if known else None, control_plane_schema_version=4 if known else None, python_version="3.13.1", known=known, ) def _parity(*, stale: bool = False, determinable: bool = True) -> StaleRuntime: if stale: return StaleRuntime( daemon_head="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", checkout_head="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", remote_head="cccccccccccccccccccccccccccccccccccccccc", stale=True, determinable=True, mutation_safe=False, reasons=("runtime, checkout, and remote commits disagree",), ) if not determinable: return StaleRuntime( daemon_head=None, checkout_head=None, remote_head=None, stale=False, determinable=False, mutation_safe=False, reasons=("local checkout HEAD could not be read",), ) return StaleRuntime( daemon_head="1c455b6ec0f9cb761fe6248de68c17e061fb5ecd", checkout_head="1c455b6ec0f9cb761fe6248de68c17e061fb5ecd", remote_head="1c455b6ec0f9cb761fe6248de68c17e061fb5ecd", stale=False, determinable=True, mutation_safe=True, reasons=(), ) def _snapshot( *, status: str = STATUS_OK, ready: bool = True, readiness_complete: bool = True, readiness_reasons: tuple[str, ...] = (), dependencies: tuple[DependencyProbe, ...] | None = None, parity: StaleRuntime | None = None, namespaces: tuple[dict, ...] = (), probe_errors: tuple[str, ...] = (), version_known: bool = True, ) -> SystemHealthSnapshot: if dependencies is None: dependencies = ( DependencyProbe( name="control_plane_db", kind="sqlite", status=STATUS_OK, detail="schema version 4", required=True, latency_ms=1.25, metadata={"schema_version": 4}, ), ) return SystemHealthSnapshot( status=status, ready=ready, readiness_complete=readiness_complete, readiness_reasons=readiness_reasons, service="mcp-control-plane-webui", mode="read-only", version=_version(known=version_known), started_at="2026-07-23T19:50:47+00:00", uptime_seconds=3661.5, timestamp="2026-07-23T20:51:48+00:00", deep_probes_requested=False, dependencies=dependencies, mcp_namespaces=namespaces, stale_runtime=parity if parity is not None else _parity(), probe_errors=probe_errors, ) class TestHealthyRender(unittest.TestCase): """AC1 / AC4 — every health DTO field reaches the page.""" def setUp(self): self.html = render_system_health_page(_snapshot()) def test_readiness_fields_render(self): self.assertIn("System health", self.html) self.assertIn("Ready", self.html) self.assertIn("mcp-control-plane-webui", self.html) self.assertIn("read-only", self.html) self.assertIn("2026-07-23T20:51:48+00:00", self.html) def test_version_and_uptime_render(self): self.assertIn("1c455b6ec0f9cb761fe6248de68c17e061fb5ecd", self.html) self.assertIn("v0.4.1-12-g1c455b6", self.html) self.assertIn("3.13.1", self.html) self.assertIn("3661.500s", self.html) self.assertIn("1.02h", self.html) def test_dependency_row_renders_with_latency(self): self.assertIn("control_plane_db", self.html) self.assertIn("sqlite", self.html) self.assertIn("schema version 4", self.html) self.assertIn("1.2 ms", self.html) def test_healthy_page_shows_no_stale_warning(self): self.assertNotIn("Stale runtime:", self.html) self.assertNotIn("Staleness", self.html) def test_unknown_version_is_labelled_not_faked(self): html = render_system_health_page(_snapshot(version_known=False)) self.assertIn("unknown", html) self.assertIn("unresolved", html) class TestDegradedRender(unittest.TestCase): """AC2 — a degraded or unrun dependency is visible, not swallowed.""" def setUp(self): self.deps = ( DependencyProbe( name="control_plane_db", kind="sqlite", status=STATUS_OK, detail="schema version 4", required=True, latency_ms=0.9, ), DependencyProbe( name="repository", kind="git", status=STATUS_DOWN, detail="repository root is not a git checkout", required=True, latency_ms=4.0, ), DependencyProbe( name="gitea", kind="http", status=STATUS_SKIPPED, detail="deep probe not requested", required=False, ), ) self.html = render_system_health_page( _snapshot( status=STATUS_DEGRADED, ready=False, readiness_complete=False, readiness_reasons=("required dependency 'repository' is down",), dependencies=self.deps, ) ) def test_degraded_banner_names_the_dependency(self): self.assertIn("Degraded dependencies:", self.html) self.assertIn("repository", self.html) def test_not_run_probe_is_reported_separately(self): self.assertIn("Not probed:", self.html) self.assertIn("gitea", self.html) self.assertIn("not counted", self.html) def test_not_ready_headline_and_reason(self): self.assertIn("Not ready", self.html) self.assertIn("required dependency 'repository' is down", self.html) def test_degraded_status_badge_present(self): self.assertIn("badge-health-degraded", self.html) self.assertIn("badge-health-down", self.html) def test_ready_but_incomplete_is_not_shown_as_plain_ready(self): html = render_system_health_page( _snapshot(ready=True, readiness_complete=False) ) self.assertIn("Ready (incomplete evidence)", html) class TestStaleRuntimeWarning(unittest.TestCase): """AC3 — staleness is prominent and never claims mutation safety.""" def test_stale_runtime_warns_and_denies_mutation_safety(self): html = render_system_health_page(_snapshot(parity=_parity(stale=True))) self.assertIn("Stale runtime:", html) self.assertIn("do not treat this runtime as mutation-safe", html) self.assertIn("