phase: 30_gateway_connection_map
**Phase 30 final verification pass — all 6 tasks already in `complete/`, so this was the phase-completion pass.** - **Verified (re-run, not trusted):** `/gateway` serves exactly two panels (nginx→haproxy, `GATEWAY_CARD_KEYS`); haproxy rows `.solo` straight across, nginx `:443` branched with one tick per leaf (18 live, fixture 17); every pipe computes the theme `--ok` in dark+light, animates only at count ≥1, fully static under `prefers-reduced-motion`; switch = `role="switch"`+`aria-checked`+`aria-disabled`, no listener, footer+title name the missing write path; zero non-GET requests anywhere. - **Live read-only re-run** (`data/logs/30-final-live-verify.out`): 17 PASS — `warnings: []`, 6 haproxy frontends→own `server`, `:443` 18 map leaves incl. `default→127.0.0.1:4443`, `:4443` 6 / `:8443` 3, `:80` bare listener, no `unmatched`/`outbound`/`by_port`/`partial`, no `9120`; inbound reconciliation **page 16 = kernel 16**; `gateway` block = the 12 declared keys; no `gateway:config` alert on a clean config. `git diff --stat agent/ deploy/` empty; agents 1.2.0 (gateway) / 1.1.0 ×2, all `tls`, `bundle_rev 2`. - **Tests/lint (exact):** `uv run pytest` → **1791 passed, 16 skipped**, exit 0 · `bash .agents/validate.sh` → **exit 0**, `--cov` **98.94%** (floor 94), `ruff check src tests agent` clean, `pyright` 0 errors, `node --check` all page scripts, hygiene clean · `uv run pytest -m live --live` → **12 passed** · `uv pip list --outdated` empty · `./scripts/dev-server.sh status` → **10 sources ok**, `gateway` included · browser pass `30-connection-map.out` 63 PASS/0 FAIL (5 screenshots at 1440/360) · `30-final-mobile-audit.out` 371 PASS/0 FAIL/0 FINDING, exit 0, nine pages. - **Docs fixed:** README `alerts.py` bullet omitted phase 30's keyword-only `gateway_payload` and its one `warn` config-integrity row (+suppression) — added; overview ingress line labelled a dated reading. AGENTS gap 13 corrected 391 px → **361 px** (task 05's pill took 30 px), "task 06 measured" → "phase 29 measured", coverage figure un-pinned to 98.9 %, live-suite count noted (12 today), "all five pages" severity sentence clarified for `/gateway`'s existence-only palette. Everything else reviewed in full and **verified consistent**. - **Defect fixed:** duplicated footer-assertion block at the end of `tests/test_api.py::test_the_switch_is_built_and_explicitly_unwired` (one copy removed). - **Notable:** `NOTES.md` is empty in the working tree — both items in it (non-mTLS agents, disappearing admin links) landed in phases 24 and 20; left cleared. Fixture/live `map`-count drift (17 vs 18) is recorded in AGENTS.md, not silently re-captured. - **Next pending phase:** none — `30_gateway_connection_map` was the only entry in `.agents/phases/todo/`.
This commit is contained in:
+23
-5
@@ -15,7 +15,7 @@ import asyncio
|
||||
import logging
|
||||
import sqlite3
|
||||
import time
|
||||
from collections.abc import Iterator
|
||||
from collections.abc import Callable, Iterator
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
@@ -208,6 +208,24 @@ async def test_states_can_be_filtered_to_one_dashboard(store) -> None:
|
||||
assert [s["key"] for s in hub.states("ups")] == ["nut"]
|
||||
|
||||
|
||||
async def _wait_for(predicate: Callable[[], bool], deadline: float = 10.0) -> bool:
|
||||
"""Poll `predicate` on the loop until it holds or the deadline passes.
|
||||
|
||||
The two maintenance tests below cross a thread boundary (`anyio.to_thread.run_sync`, because a
|
||||
SQLite write must not stall the event loop), so *when* the first pass lands is a property of the
|
||||
machine and of whether coverage is tracing every line — a fixed `asyncio.sleep()` fitted to a
|
||||
quiet laptop is a flake on a loaded one. Waiting for the thing itself is the assertion; the
|
||||
deadline only stops a broken loop hanging the suite.
|
||||
"""
|
||||
loop = asyncio.get_running_loop()
|
||||
end = loop.time() + deadline
|
||||
while loop.time() < end:
|
||||
if predicate():
|
||||
return True
|
||||
await asyncio.sleep(0.02)
|
||||
return predicate()
|
||||
|
||||
|
||||
async def test_maintenance_failure_is_logged_not_swallowed(store, monkeypatch,
|
||||
caplog: pytest.LogCaptureFixture) -> None:
|
||||
"""A broken rollup means every chart past the raw window is empty. Retrying quietly
|
||||
@@ -220,12 +238,12 @@ async def test_maintenance_failure_is_logged_not_swallowed(store, monkeypatch,
|
||||
task = asyncio.create_task(hub._maintain_loop(interval=0.02))
|
||||
try:
|
||||
with caplog.at_level(logging.ERROR, logger="dashboards.poller"):
|
||||
await asyncio.sleep(0.15)
|
||||
logged = await _wait_for(lambda: "store maintenance failed" in caplog.text)
|
||||
finally:
|
||||
task.cancel()
|
||||
with pytest.raises(asyncio.CancelledError):
|
||||
await task
|
||||
assert "store maintenance failed" in caplog.text
|
||||
assert logged, "a failed rollup must be logged, never retried in silence"
|
||||
assert "no such table" in caplog.text, "the log line must carry the real reason"
|
||||
|
||||
|
||||
@@ -236,12 +254,12 @@ async def test_maintenance_runs_from_the_hub_and_records_its_pass(store) -> None
|
||||
"pruned": 0}
|
||||
task = asyncio.create_task(hub._maintain_loop(interval=0.02))
|
||||
try:
|
||||
await asyncio.sleep(0.1)
|
||||
ran = await _wait_for(lambda: bool(calls))
|
||||
finally:
|
||||
task.cancel()
|
||||
with pytest.raises(asyncio.CancelledError):
|
||||
await task
|
||||
assert calls, "the maintenance loop must actually call maintain"
|
||||
assert ran, "the maintenance loop must actually call maintain"
|
||||
|
||||
|
||||
async def test_events_written_by_a_job_land_in_the_store(store) -> None:
|
||||
|
||||
Reference in New Issue
Block a user