"""Render HTML and optional JUnit reports for the Pulumi HX suite."""
from __future__ import annotations
import html
import json
from pathlib import Path
from typing import Any
from xml.etree.ElementTree import Element, ElementTree, SubElement
def write_json(payload: dict[str, Any], path: Path) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
# Drop per-method lists from majors for a lighter default JSON? Keep full for debugging.
path.write_text(json.dumps(payload, indent=2, ensure_ascii=False), encoding="utf-8")
def write_junit(payload: dict[str, Any], path: Path) -> None:
surface = payload.get("surface") or []
scenarios = payload.get("scenarios") or []
cases: list[dict[str, Any]] = []
for major in surface:
cases.append(
{
"classname": "surface",
"name": f"PVE {major.get('version')} major={major.get('major')}",
"time": major.get("time") or 0,
"ok": bool(major.get("ok")),
"error": _surface_error(major),
}
)
for item in scenarios:
cases.append(
{
"classname": "lifecycle",
"name": item.get("id") or item.get("name") or "lifecycle",
"time": item.get("time") or 0,
"ok": bool(item.get("ok")),
"error": item.get("error") or "",
}
)
suite = Element(
"testsuite",
name="pulumi-hx",
tests=str(len(cases)),
failures=str(sum(1 for c in cases if not c["ok"])),
time=f"{sum(float(c['time']) for c in cases):.3f}",
)
for item in cases:
case = SubElement(
suite,
"testcase",
classname=str(item["classname"]),
name=str(item["name"]),
time=f"{float(item['time']):.3f}",
)
if not item["ok"]:
failure = SubElement(case, "failure", message=str(item["error"])[:500])
failure.text = str(item["error"])
path.parent.mkdir(parents=True, exist_ok=True)
ElementTree(suite).write(path, encoding="utf-8", xml_declaration=True)
def _surface_error(major: dict[str, Any]) -> str:
fails = major.get("failures") or []
if not fails:
return ""
parts = [
f"{f.get('verb')} {f.get('path')} -> {f.get('bucket')} {f.get('status', '')}"
for f in fails[:20]
]
return f"{len(fails)} critical: " + "; ".join(parts)
def _verb_histogram_rows(surface: list[dict[str, Any]]) -> list[str]:
rows: list[str] = []
for major in surface:
histogram = major.get("verb_histogram") or {}
if not histogram:
# Fall back to by_verb totals when slim payload lacks histogram.
by_verb = major.get("by_verb") or {}
histogram = {
verb: {"total": sum(buckets.values()), "buckets": buckets}
for verb, buckets in by_verb.items()
}
for verb, info in sorted(histogram.items()):
buckets = info.get("buckets") or {}
bucket_txt = ", ".join(
f"{name}={count}" for name, count in sorted(buckets.items()) if count
)
rows.append(
"
"
f"| {html.escape(str(major.get('version') or major.get('major')))} | "
f"{html.escape(str(verb))} | "
f"{html.escape(str(info.get('total') or 0))} | "
f"{html.escape(bucket_txt)} | "
"
"
)
return rows
def write_html(payload: dict[str, Any], path: Path) -> None:
surface = payload.get("surface") or []
scenarios = payload.get("scenarios") or []
coverage = payload.get("coverage") or {}
ok = bool(payload.get("ok"))
elapsed = float(payload.get("elapsed") or 0)
coverage_by_major = coverage.get("by_major") or []
coverage_rows = []
for item in coverage_by_major:
complete = bool(item.get("complete"))
coverage_rows.append(
""
f"| {html.escape(str(item.get('major')))} | "
f"{html.escape(str(item.get('version')))} | "
f"{html.escape(str(item.get('declared')))} | "
f"{html.escape(str(item.get('probed')))} | "
f"{html.escape(str(item.get('critical')))} | "
f""
f"{'yes' if complete else 'no'} | "
"
"
)
declared_total = int(coverage.get("declared_total") or 0)
probed_total = int(coverage.get("probed_total") or 0)
critical_total = int(coverage.get("critical_total") or 0)
coverage_ok = bool(coverage.get("ok")) if coverage_by_major else True
majs = coverage.get("majors") or []
if len(majs) >= 2:
majors_label = f"{majs[0]}–{majs[-1]}"
elif majs:
majors_label = str(majs[0])
else:
majors_label = "—"
surface_rows = []
failure_rows = []
for major in surface:
surface_rows.append(
""
f"| {html.escape(str(major.get('major')))} | "
f"{html.escape(str(major.get('version')))} | "
f"{html.escape(str(major.get('declared')))} | "
f"{html.escape(str(major.get('probed')))} | "
f"{html.escape(str(major.get('success_2xx')))} | "
f"{html.escape(str(major.get('client_4xx')))} | "
f""
f"{html.escape(str(major.get('failure_count')))} | "
f"{float(major.get('time') or 0):.1f}s | "
"
"
)
for fail in major.get("failures") or []:
failure_rows.append(
""
f"| {html.escape(str(major.get('version')))} | "
f"{html.escape(str(fail.get('verb')))} | "
f"{html.escape(str(fail.get('path')))} | "
f"{html.escape(str(fail.get('bucket')))} | "
f"{html.escape(str(fail.get('status', fail.get('error', ''))))} | "
f"{html.escape(str(fail.get('body', ''))[:200])} | "
"
"
)
scenario_rows = []
for item in scenarios:
scenario_rows.append(
""
f"| {html.escape(str(item.get('id') or item.get('name')))} | "
f""
f"{'PASS' if item.get('ok') else 'FAIL'} | "
f"{float(item.get('time') or 0):.2f}s | "
f"{html.escape(str(item.get('error') or ''))} | "
"
"
)
status = "PASS" if ok else "FAIL"
status_class = "ok" if ok else "fail"
coverage_status = "complete" if coverage_ok else "incomplete"
coverage_class = "ok" if coverage_ok else "fail"
doc = f"""
Pulumi HX suite report
Pulumi HX suite report
Contract surface probe (PVE majors 6–9) + lifecycle scenarios
{status}
Critical surface fails
{sum(int(m.get('failure_count') or 0) for m in surface)}
Scenarios
{sum(1 for s in scenarios if s.get('ok'))}/{len(scenarios)}
Full contract coverage
{probed_total}/{declared_total} methods across majors {html.escape(majors_label)}
(critical={critical_total}) —
{coverage_status}
| Major | Version | Declared | Probed |
Critical | declared==probed |
{''.join(coverage_rows) or '| No coverage data |
'}
| Total |
{declared_total} |
{probed_total} |
{critical_total} |
{'yes' if coverage_ok else 'no'} |
Surface by major
| Major | Version | Declared | Probed |
2xx | 4xx/auth | Critical | Time |
{''.join(surface_rows) or '| No surface results |
'}
Verb histogram (incl. synthetic HEAD)
Contract verbs GET/PUT/POST/DELETE count toward coverage.
HEAD is probed on every GET path for the matrix but is not part of declared/probed.
| Major | Verb | Total | Buckets |
{''.join(_verb_histogram_rows(surface)) or '| No histogram |
'}
Critical surface failures
| Version | Verb | Path | Bucket | Status | Body |
{''.join(failure_rows) or '| None |
'}
Lifecycle scenarios
| ID | Result | Time | Error |
{''.join(scenario_rows) or '| No scenarios |
'}
"""
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(doc, encoding="utf-8")