Some checks failed
continuous-integration/drone/push Build is failing
Declare intentional skips + custom-html-tiny functional test; 4-rung level ladder
- recipe_meta.EXPECTED_NA = {rung: reason} lists intentionally-skipped rungs; any
essential rung skipped and not listed is unintentional. Skips still cap the level
(never inflate). results.json: skips:{intentional,unintentional} + level_cap_rung.
- Level ladder = the four essential rungs (install, upgrade, backup/restore,
functional; top = L4). integration & recipe-local are optional, not leveled
(SSO still enforced for the run verdict, unchanged).
- Card shows skipped rungs as INTENTIONAL SKIP (green, reason below) / UNINTENTIONAL
SKIP (amber); level badge gains an expected/gap? third segment.
- custom-html-tiny: functional serve test (exact-byte round-trip + 404); declares
backup_restore intentionally skipped (stateless static server).
Independently verified by the adversary: 138 unit tests pass cold; live full-stage
run on custom-html-tiny green (upgrade tier ran; level 2; correct skips/badge);
clean teardown.
120 lines
4.2 KiB
Python
120 lines
4.2 KiB
Python
"""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=3, cap="L4 functional (recipe-specific tests) 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
|
|
# plain cap (no intent) → two-box badge, no third segment
|
|
assert "expected" not in svg and "gap?" not in svg
|
|
|
|
|
|
def test_badge_svg_differentiates_intentional_vs_unintentional_skip():
|
|
# an intentional (declared) skip capped the climb → muted "expected" third segment
|
|
exp = C.level_badge_svg(2, "L3 backup/restore N/A", "intentional")
|
|
assert "level 2" in exp and "expected" in exp and C.EXPECT_COLOR in exp
|
|
assert "gap?" not in exp
|
|
# an unintentional skip (not declared) → amber "gap?" third segment
|
|
gap = C.level_badge_svg(2, "L3 backup/restore N/A", "unintentional")
|
|
assert "level 2" in gap and "gap?" in gap and C.GAP_COLOR in gap
|
|
assert "expected" not in gap
|
|
|
|
|
|
def test_skip_rows_intentional_and_unintentional():
|
|
html_out = C._skip_rows(
|
|
{"intentional": {"backup_restore": "no persistent data"}, "unintentional": ["functional"]}
|
|
)
|
|
# intentional skip: labelled row (muted green) + the reason on its own line
|
|
assert "intentional skip" in html_out and C.SKIP_GREEN in html_out
|
|
assert "backup/restore" in html_out and "no persistent data" in html_out
|
|
# unintentional skip: amber row + prompt to declare/add coverage
|
|
assert "unintentional skip" in html_out and C.GAP_COLOR in html_out
|
|
assert "functional" in html_out and "EXPECTED_NA" in html_out
|
|
|
|
|
|
def test_skip_rows_empty_when_no_skips():
|
|
assert C._skip_rows({"intentional": {}, "unintentional": []}) == ""
|
|
|
|
|
|
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
|