"""Every section the public landing links to is reachable from the guest nav.
The landing wheel and the guest nav are built from two different tables --
`_LANDING_HUE_ORDER` in routes_views.py and `_GUEST_NAV_LINKS` in pages.py --
and nothing made them agree. They drifted: the landing offered `/graph`, the
guest nav did not, so a visitor who took that spoke landed on a page with no
way back to any of the other eight. CLAUDE.md had described GPH as being in the
guest nav for some time, which is how a doc can be the only place a thing is
true.
Both sides are read out of the RENDERED HTML, never from a hand-written list of
sections. A list here would be the same kind of denylist the admin-tier guard
exists to avoid: it covers what someone remembered on the day, and the tenth
section added next month is silently uncovered. Scraping the markup also tests
one step further than comparing the two constants would -- it catches a
divergence in the two builders, not just in the two tables.
Direction matters and only one direction is asserted: landing MUST be a subset
of the nav, the nav MAY hold more. The nav legitimately carries pages that are
deliberately not on the landing (`/zombo` is unlinked on purpose), and the
landing's own `admin` link is deliberately not a nav entry, so it is excluded
here by scoping the scrape to `.wheel-item`.
"""
import re
import pytest
# `` -- the landing's spokes, which excludes the
# `landing-admin` link to /login by construction.
_WHEEL = re.compile(r'...`; take its hrefs in isolation
# so a link elsewhere on the page cannot satisfy the assertion for it.
_NAV_BLOCK = re.compile(r'(.*?)
', re.S)
_HREF = re.compile(r'href="([^"]+)"')
def _landing_hrefs(client) -> set[str]:
r = client.get("/", headers={"Accept": "text/html"})
assert r.status_code == 200, f"landing: {r.status_code}"
hrefs = set(_WHEEL.findall(r.text))
assert hrefs, "no .wheel-item links found — the landing markup changed shape"
return hrefs
def _guest_nav_hrefs(client, guest_cookie) -> set[str]:
# /UI is guest-gated and renders the shared shell, so its nav IS the guest
# nav. Any other guest page would do; this one carries no live data.
r = client.get("/UI", headers=guest_cookie)
assert r.status_code == 200, f"/UI as guest: {r.status_code}"
block = _NAV_BLOCK.search(r.text)
assert block, "no .exec-nav block on /UI — the shell markup changed shape"
hrefs = set(_HREF.findall(block.group(1)))
assert hrefs, "guest nav rendered with no links"
return hrefs
def test_every_landing_link_is_in_the_guest_nav(client, guest_cookie):
landing = _landing_hrefs(client)
nav = _guest_nav_hrefs(client, guest_cookie)
missing = sorted(landing - nav)
assert not missing, (
f"landing links with no guest-nav entry: {missing}. A visitor who "
f"follows one of these has no way back to the other sections. Add it "
f"to _GUEST_NAV_LINKS in api/pages.py (and give it _NAV_LABELS / "
f"_NAV_ICONS entries), or drop it from _LANDING_HUE_ORDER."
)
def test_landing_sections_all_render_a_label_and_a_blurb(client):
"""A spoke with no title or no description is a blank slot on the wheel --
the landing tables are three parallel dicts keyed by section, so a section
added to one and forgotten in the others renders empty rather than failing."""
r = client.get("/", headers={"Accept": "text/html"})
assert r.status_code == 200
spokes = re.findall(
r'', r.text, re.S)
assert spokes, "no landing spokes rendered"
empty = [
s for s in spokes
if not re.search(r'\s*\S', s)
or not re.search(r'\s*\S', s)
]
assert not empty, (
f"{len(empty)} landing section(s) render an empty title or description "
f"— check _LANDING_BLURBS / _LANDING_DESCS in api/routes_views.py"
)
@pytest.mark.parametrize("tier", ["guest", "anon"])
def test_guest_nav_icons_resolve(client, guest_cookie, tier):
"""Every nav icon the guest shell references is actually served.
The nav moved from /x.png to /icons/x.svg; a typo in one name is a broken
image in the bar and nothing else would catch it."""
headers = guest_cookie if tier == "guest" else {"Accept": "text/html"}
path = "/UI" if tier == "guest" else "/"
r = client.get(path, headers=headers)
assert r.status_code == 200
srcs = sorted(set(re.findall(r'
{got.status_code}"
assert "svg" in got.headers.get("content-type", ""), (
f"{src} served as {got.headers.get('content-type')!r}, not SVG"
)