Files
6krrt/tests/test_admin_nav_is_complete.py
adlee-was-taken fd038125f7 feat(admin): the dashboard becomes a landing page, with Board and Live views
The dashboard was eight cards restating what the other seven pages own, so
it was the page you passed through rather than the one you started from. It
is now the landing page: no nav entry of its own, reached by the logo, with
two views over the one /api/snapshot payload so switching costs no request.

Board is one tile per destination, identical anatomy every time: what it is,
one number, one qualifier, a link. Under them, a full-width Activity band
plots routed decisions against billed requests on one axis, so the gap
between them -- local dispatch, cache hits, requests that never reached a
provider -- is readable, which it is in neither series alone. Its range
(24h/7d/30d) is separate from the tile sparklines, because it is the chart
you come to the page to read. Beside it, the eight busiest models ranked by
CALLS, not cost: cost coverage is partial, and an OpenRouter row with two
priced calls out of hundreds otherwise outranks the model doing the work.

Live loads the row itself -- category, model, provider, required context,
tier, classify time, exploratory flag, cost -- so the usual question does
not need the Decisions page, and groups rows by minute so a burst reads as
one. The dot is the classification source: green for a real classification,
amber for a degraded one, which still routes but never moves proficiency,
and until now was visible only in a /metrics warning. The rail beside it
answers what Board cannot: throughput against the last hour, classifier p50
and p95 (the latency floor every routed request pays before an upstream
token), degraded share, and the top five models by share of the live window
keyed on model AND provider, since the same model on two providers is two
different routing outcomes. Spend is not restated here; it belongs to Quota.

Warnings move behind a navbar bell. They are recomputed from live state on
every poll, so the only honest dismissal is "hide until the condition
changes": a dismissal is keyed on the warning's TEXT, so when the numbers
move the key stops matching and the warning returns on its own. Nothing has
to expire it. Per browser, in localStorage, wrapped in try/catch -- a
reading state, not a fact about the router.

Two tests changed shape rather than being deleted. The verdict-bar tests
pinned a card this page no longer has, but the lesson they recorded is the
expensive part and is preserved verbatim in the replacement's docstring:
`unverifiable` is not a finding, it is the default for prose and for any
turn ending in a tool call, and any bar scaled against it renders every
meaningful verdict at the 2px minimum. Exclude it from the scale, never from
the denominator. The Live rail states the same fact as one ratio, which
needs no scale. The nav test's rule is inverted for index.html rather than
skipped: the landing page must have no nav entry AND must claim no active
one, so neither can drift back in.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VRQXz5SYZYVWscxS1QqF6U
2026-09-12 00:43:55 -04:00

75 lines
2.9 KiB
Python

"""Every admin page links to every other admin page.
Two pages had drifted in opposite directions -- providers.html was missing
Proficiency and proficiency.html was missing Providers -- so you could not
navigate between them in either direction without editing the URL. Neither
gap was noticeable from the page you were on, which is why a test is worth
more here than care.
The page list is derived from the routes the router actually serves, so a
new admin page fails this until it is linked from the others, rather than
quietly launching unreachable.
"""
from __future__ import annotations
import re
from pathlib import Path
import pytest
FRONTEND = Path(__file__).resolve().parent.parent / "admin" / "frontend"
# href -> file that serves it. Kept explicit rather than globbed so that
# adding a page is a deliberate two-line edit here, not an accident.
PAGES = {
"/admin/": "index.html",
"/admin/models": "models.html",
"/admin/profiles": "profiles.html",
"/admin/proficiency": "proficiency.html",
"/admin/decisions": "decisions.html",
"/admin/quota": "quota.html",
"/admin/controls": "controls.html",
"/admin/providers": "providers.html",
}
NAV_RE = re.compile(r'<header class="navbar.*?</header>', re.DOTALL)
def _nav_hrefs(filename: str) -> set[str]:
html = (FRONTEND / filename).read_text(encoding="utf-8")
nav = NAV_RE.search(html)
assert nav, f"{filename}: no <header class=\"navbar\"> block found"
return set(re.findall(r'href="(/admin[^"]*)"', nav.group(0)))
@pytest.mark.parametrize("filename", sorted(set(PAGES.values())))
def test_every_page_links_to_every_page(filename):
missing = set(PAGES) - _nav_hrefs(filename)
assert not missing, (
f"{filename} nav is missing {sorted(missing)} -- a page you cannot "
f"reach from here"
)
@pytest.mark.parametrize("filename", sorted(set(PAGES.values())))
def test_no_nav_link_points_at_a_page_that_does_not_exist(filename):
"""The other direction: a typo'd href is a dead link, not a missing one."""
unknown = {h for h in _nav_hrefs(filename) if h not in PAGES}
assert not unknown, f"{filename} nav links to unknown {sorted(unknown)}"
@pytest.mark.parametrize("filename", sorted(set(PAGES.values())))
def test_each_page_marks_itself_active(filename):
"""A nav that never highlights leaves you unsure which page you are on."""
html = (FRONTEND / filename).read_text(encoding="utf-8")
nav = NAV_RE.search(html).group(0)
if filename == "index.html":
# The landing page is reached by the logo, not by a nav entry -- a
# second door to the same place, in the row where width is scarcest.
# So there is nothing for it to mark, and nothing may claim active.
assert 'nav-link" href="/admin/"' not in nav, "the landing page has no nav entry"
assert 'class="nav-item active"' not in nav
return
assert 'class="nav-item active"' in nav, f"{filename}: no active nav item"