feat(3 U2-scaffold): summary card + level/status SVG badge renderers (offline; pure)
harness/card.py: render_badge_svg/level_badge_svg (shields-style SVG, colour-by-level, R6) + render_card_html (recipe+version, level badge, per-stage/per-test ✔/✘ table, embedded screenshot, invariant flags — REPORTS results.json verbatim, never recomputes; cardinal no-inflation guardrail) + render_card_png (best-effort Playwright HTML->PNG, R7). 8 pure unit tests. Orchestrator wiring + stable-URL serving + live PNG demo come after U0 PASSes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
90
tests/unit/test_card.py
Normal file
90
tests/unit/test_card.py
Normal file
@ -0,0 +1,90 @@
|
||||
"""Unit tests for the pure card/badge renderers (harness.card), Phase 3 U2 (R3/R6).
|
||||
|
||||
Covers the deterministic HTML + SVG string builders (the PNG step needs Playwright + is exercised in
|
||||
the U2 live demo). The cardinal check: the card REPORTS the data verbatim — level/marks come straight
|
||||
from the dict, never recomputed. Run cold: cc-ci-run -m pytest tests/unit/test_card.py -q
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "runner"))
|
||||
from harness import card as C # noqa: E402
|
||||
|
||||
|
||||
def _data(level=4, cap="L5 integration (SSO/OIDC + cross-app) N/A"):
|
||||
return {
|
||||
"recipe": "uptime-kuma",
|
||||
"version": "1.23.0",
|
||||
"level": level,
|
||||
"level_cap_reason": cap,
|
||||
"flags": {"clean_teardown": True, "no_secret_leak": True},
|
||||
"screenshot": "screenshot.png",
|
||||
"stages": [
|
||||
{
|
||||
"name": "install",
|
||||
"status": "pass",
|
||||
"tests": [{"name": "test_serving", "status": "pass", "ms": 168}],
|
||||
},
|
||||
{
|
||||
"name": "custom",
|
||||
"status": "fail",
|
||||
"tests": [
|
||||
{"name": "test_health", "status": "pass", "ms": 17},
|
||||
{"name": "test_broken", "status": "fail", "ms": 5},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def test_level_color_ramp():
|
||||
assert C.level_color(0) != C.level_color(6)
|
||||
assert C.level_color(6) == "#3fb950"
|
||||
assert C.level_color(99) == "#8b949e" # unknown → grey
|
||||
|
||||
|
||||
def test_badge_svg_wellformed():
|
||||
svg = C.level_badge_svg(4)
|
||||
assert svg.startswith("<svg") and svg.endswith("</svg>")
|
||||
assert "level 4" in svg
|
||||
assert C.level_color(4) in svg
|
||||
|
||||
|
||||
def test_card_html_reports_level_verbatim():
|
||||
html = C.render_card_html(_data(level=2, cap="L3 backup/restore (data integrity) N/A"))
|
||||
assert "uptime-kuma" in html
|
||||
assert "1.23.0" in html
|
||||
# the level shown is exactly what was passed (no recompute)
|
||||
assert ">2<" in html
|
||||
assert "L3 backup/restore" in html
|
||||
assert C.level_color(2) in html
|
||||
|
||||
|
||||
def test_card_html_shows_stage_and_test_marks():
|
||||
html = C.render_card_html(_data())
|
||||
assert "install" in html and "custom" in html
|
||||
assert "test_serving" in html and "test_broken" in html
|
||||
assert C.STATUS_MARK["pass"] in html and C.STATUS_MARK["fail"] in html
|
||||
|
||||
|
||||
def test_card_html_flags_rendered():
|
||||
html = C.render_card_html(_data())
|
||||
assert "clean teardown" in html and "no secret leak" in html
|
||||
|
||||
|
||||
def test_card_html_no_screenshot_placeholder():
|
||||
d = _data()
|
||||
d["screenshot"] = None
|
||||
html = C.render_card_html(d)
|
||||
assert "no screenshot" in html
|
||||
|
||||
|
||||
def test_card_html_escapes_recipe_name():
|
||||
d = _data()
|
||||
d["recipe"] = "<script>x</script>"
|
||||
html = C.render_card_html(d)
|
||||
assert "<script>x" not in html
|
||||
assert "<script>" in html
|
||||
Reference in New Issue
Block a user