Observability + warming guardrails (Codex)
* client_error details, not just a count: new client_errors table + POST /api/client-error (reason/path/user-agent/time) + GET /api/admin/client-errors. The boot-seatbelt beacon now sends the reason + path (once per page); the admin Overview lists the recent errors so we can tell chunk vs SW vs API vs JS — the truth meter for the next day as the new SW propagates. * Deploy warming now also hits the shell, routes (/play /account /admin), SW, version.json, word lists, and icons/logo/font — not just immutable chunks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -390,3 +390,15 @@ def test_word_pool_admin(tmp_path, monkeypatch):
|
||||
# remove the admin-added word
|
||||
tc.delete("/api/admin/word/pool/plumb")
|
||||
assert "plumb" not in tc.get("/api/admin/word/pool").json()["5"]["added"]
|
||||
|
||||
|
||||
def test_client_error_telemetry(tmp_path, monkeypatch):
|
||||
app, api = _make(tmp_path, monkeypatch, admin_email="boss@x.com")
|
||||
anon = TestClient(app)
|
||||
assert anon.post("/api/client-error", json={"reason": "boot-timeout", "path": "/play"}).json()["ok"] is True
|
||||
assert anon.get("/api/admin/client-errors").status_code == 401 # gated
|
||||
tc = _signin(app, api, "boss@x.com")
|
||||
rows = tc.get("/api/admin/client-errors").json()
|
||||
assert len(rows) == 1 and rows[0]["reason"] == "boot-timeout" and rows[0]["path"] == "/play"
|
||||
assert rows[0]["user_agent"] # captured from the request header
|
||||
assert tc.get("/api/admin/stats").json()["client_errors"]["today"] == 1
|
||||
|
||||
Reference in New Issue
Block a user