status: consolidate the pill to one dot + one word; upgrade the panel

- pill: two host dots -> ONE consolidated state (down > slow > up) —
  the at-a-glance question is 'can I watch?', which is one state;
  per-host detail belongs in the tap panel
- panel: dashed slow-threshold line in the CDN sparkline (drawn at the
  server's real threshold, not a client copy) + origin up/down step
  sparkline so 'how long has the origin been down' is visible
- server: origin probe keeps a 20-sample hist like the CDN probe;
  DEGRADED threshold is now ANIWEB_CDN_KBPS (default 40) and is
  returned in /api/cdn so the UI draws the line at the live value
- tests: pill assertions switched to consolidated states; fixtures
  carry origin hist + threshold; assert both sparklines + threshold line
- docs: README + SPEC synced; preview on :8322 restarted (pid 44382)
This commit is contained in:
jozamudi committed 2026-09-21 00:10:10 +00:00
1 parent 778bf2bfc1
commit 5f7aa010f1
8 files changed
+105 -56

No files matched your search

+23 -11
View File
@@ -4,6 +4,9 @@
runs against LIVE upstream, so its real data must never leak into these
assertions. Everything else (login, home) hits the throwaway server, which
never touches the real data. Skips if playwright is absent.
Pill states are CONSOLIDATED (one dot + one word): down (origin dead)
> slow (CDN degraded) > up. Per-host detail lives in the tap panel.
"""
import asyncio
import json
@@ -18,10 +21,14 @@ POLL = 100
def _cdn(state, kbps, origin="OK"):
# origin hist: all OK except the last 3 when origin is DOWN
ohist = [{"t": 1750000000 - 180 * (10 - i),
"state": "DOWN" if (origin == "DOWN" and i >= 8) else "OK"}
for i in range(11)]
return {
"state": state, "kbps": kbps, "ts": 1750000000,
"state": state, "kbps": kbps, "ts": 1750000000, "threshold": 40,
"hist": [{"t": 1750000000 - 180 * (10 - i), "kbps": 20 + i * 10} for i in range(11)],
"origin": {"state": origin, "msg": "http 200", "ts": 1750000000},
"origin": {"state": origin, "msg": "http 200", "ts": 1750000000, "hist": ohist},
}
@@ -57,13 +64,15 @@ async def _pill_ok():
browser, page = await _open(pw, _cdn("OK", 120))
try:
await page.wait_for_function(
"document.querySelector('#pill .c').className.includes('ok')",
"document.querySelector('#pill .d').className.includes('ok')",
timeout=5000, polling=POLL)
assert await page.text_content("#pill span") == "up"
# tap opens the panel: detail rows + sparkline with one point
# per hist sample
# tap opens the panel: two sparklines (CDN + origin), the CDN
# one carries the dashed threshold line and one point per sample
await _js_click(page, "#pill")
await page.wait_for_selector("#status-panel .sp-svg polyline")
assert await page.locator("#status-panel polyline").count() == 2
assert await page.locator("#status-panel .sp-thr").count() == 1
pts = await page.get_attribute("#status-panel .sp-svg polyline", "points")
assert len(pts.split()) == 11
assert "CDN: OK — 120 KB/s" in await page.text_content("#status-panel")
@@ -87,9 +96,9 @@ async def _pill_degraded():
browser, page = await _open(pw, _cdn("DEGRADED", 23))
try:
await page.wait_for_function(
"document.querySelector('#pill .c').className.includes('degraded')",
"document.querySelector('#pill .d').className.includes('slow')",
timeout=5000, polling=POLL)
assert await page.text_content("#pill span") == "cdn 23 KB/s"
assert await page.text_content("#pill span") == "slow · 23 KB/s"
await _js_click(page, "#pill")
await page.wait_for_selector("#status-panel")
assert "CDN: DEGRADED — 23 KB/s" in await page.text_content("#status-panel")
@@ -105,11 +114,14 @@ async def _pill_origin_down():
async with async_playwright() as pw:
browser, page = await _open(pw, _cdn("OK", 120, origin="DOWN"))
try:
# down beats a healthy CDN: one consolidated state
await page.wait_for_function(
"document.querySelector('#pill .o').className.includes('down')",
"document.querySelector('#pill .d').className.includes('down')",
timeout=5000, polling=POLL)
assert await page.text_content("#pill span") == "upstream down"
cls = await page.get_attribute("#pill", "class")
assert "odown" in cls
assert await page.text_content("#pill span") == "down"
assert "down" in await page.get_attribute("#pill", "class")
await _js_click(page, "#pill")
await page.wait_for_selector("#status-panel .sp-svg.bad")
assert "upstream: DOWN" in await page.text_content("#status-panel")
finally:
await browser.close()