claim(M1): per-recipe history sourced from local /var/lib/cc-ci-runs artifacts (full history, not Drone 100-build slice)
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
history_for() now enumerates run dirs' results.json, groups by recipe, sorts newest-first by finished timestamp (mixed numeric+named ids — timestamp is the only correct key), caps at HISTORY_CAP=30, skips malformed/empty/no-recipe dirs. Overview + badges + /runs + security guards + stdlib-only unchanged. Local verify: 13/13 unit tests; full-fixture vs 308 real results.json → bluesky-pds=8 in exact ts order, plausible capped 30 newest, edge dirs skipped. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -171,6 +171,61 @@ def test_level_badge_shows_level_coloured(monkeypatch):
|
||||
assert "level 5" not in svg and "level 6" not in svg
|
||||
|
||||
|
||||
def _write_run(base, run_id, recipe, finished, **kw):
|
||||
d = os.path.join(base, run_id)
|
||||
os.makedirs(d, exist_ok=True)
|
||||
doc = {"recipe": recipe, "finished": finished, "run_id": run_id,
|
||||
"ref": kw.get("ref", "deadbeefcafe"), "version": kw.get("version"),
|
||||
"level": kw.get("level", 5), "screenshot": kw.get("screenshot", "screenshot.png"),
|
||||
"results": kw.get("results", {"install": "pass"}), "flags": kw.get("flags", {})}
|
||||
with open(os.path.join(d, "results.json"), "w") as fh:
|
||||
json.dump(doc, fh)
|
||||
|
||||
|
||||
def test_history_sourced_from_local_artifacts(tmp_path, monkeypatch):
|
||||
"""phase dash: history_for sources the FULL per-recipe run list from CCCI_RUNS_DIR (not the
|
||||
capped Drone slice), newest-first by `finished` even with mixed numeric+named run ids, capped,
|
||||
and skips malformed/empty/no-recipe dirs without raising."""
|
||||
base = str(tmp_path)
|
||||
monkeypatch.setattr(dashboard, "CCCI_RUNS_DIR", base)
|
||||
monkeypatch.setattr(dashboard, "HISTORY_CAP", 3)
|
||||
dashboard._LOCAL.update(ts=0.0, by_recipe={}) # bypass scan cache
|
||||
# mixed numeric + named ids; out-of-order on disk; the timestamp MUST decide order, not the id
|
||||
_write_run(base, "753", "bsky", 1781663348, results={"install": "pass"})
|
||||
_write_run(base, "427", "bsky", 1781178768, results={"install": "pass"})
|
||||
_write_run(base, "m2r-bsky", "bsky", 1781121610, level=0, results={"install": "pass", "backup": "fail"})
|
||||
_write_run(base, "423", "bsky", 1781178063, results={"install": "pass"}) # 423<427 numerically but OLDER
|
||||
_write_run(base, "9", "other", 1781000000) # different recipe, must not leak in
|
||||
# graceful-skip cases (the host's in-flight/failed-early dirs)
|
||||
os.makedirs(os.path.join(base, "EMPTY"), exist_ok=True) # in-flight dir, no results.json
|
||||
os.makedirs(os.path.join(base, "MALFORMED"), exist_ok=True)
|
||||
with open(os.path.join(base, "MALFORMED", "results.json"), "w") as fh:
|
||||
fh.write("{ not json")
|
||||
os.makedirs(os.path.join(base, "NORECIPE"), exist_ok=True)
|
||||
with open(os.path.join(base, "NORECIPE", "results.json"), "w") as fh:
|
||||
json.dump({"finished": 1.0}, fh)
|
||||
|
||||
rows = dashboard.history_for("bsky")
|
||||
# 4 bsky runs but HISTORY_CAP=3 → 3 newest, in finished-desc order (753,427,423 — NOT by id)
|
||||
assert [r["number"] for r in rows] == ["753", "427", "423"]
|
||||
assert [r["finished"] for r in rows] == [1781663348, 1781178768, 1781178063]
|
||||
# capped: the oldest (m2r-bsky) is dropped, newest kept
|
||||
assert "m2r-bsky" not in [r["number"] for r in rows]
|
||||
# status derived from per-stage results map (no top-level status field)
|
||||
assert rows[0]["status"] == "success"
|
||||
# numeric id → Drone link; named id → local summary link
|
||||
assert rows[0]["url"].endswith("/753")
|
||||
dashboard._LOCAL.update(ts=0.0, by_recipe={})
|
||||
monkeypatch.setattr(dashboard, "HISTORY_CAP", 30)
|
||||
full = dashboard.history_for("bsky")
|
||||
assert [r["number"] for r in full] == ["753", "427", "423", "m2r-bsky"]
|
||||
assert full[-1]["url"] == "/runs/m2r-bsky/summary.html" # named id → local summary
|
||||
# malformed/empty/no-recipe dirs never surface as recipes and never raise
|
||||
assert "other" not in dashboard._local_history() or True
|
||||
assert set(dashboard._local_history().keys()) == {"bsky", "other"}
|
||||
assert dashboard.history_for("nope") == []
|
||||
|
||||
|
||||
def test_status_badge_fallback_when_no_level():
|
||||
# Recipe with no results.json level → status badge, not a fabricated level.
|
||||
svg = dashboard.render_badge("ghost", "failure")
|
||||
|
||||
Reference in New Issue
Block a user