"""HTML report for pulumi hybrid lab suite (pulumi-vsphere + REST + SOAP)."""
from __future__ import annotations
import html
from datetime import UTC, datetime
from pathlib import Path
from typing import Any
def _esc(value: Any) -> str:
return html.escape(str(value), quote=True)
def _section_rest(summary: dict[str, Any]) -> str:
rest = summary.get("rest") or {}
majors = rest.get("majors") or []
by_verb = rest.get("by_verb") or {}
failures = rest.get("failures") or []
coverage = rest.get("coverage_line") or f"{(rest.get('probed') or 0) - (rest.get('critical') or 0)}/{rest.get('declared') or rest.get('total') or 0}"
critical = rest.get("critical", rest.get("failed", 0))
major_rows = []
for m in majors:
buckets = m.get("buckets") or {}
deep = m.get("deep_vs_stub") or {}
major_rows.append(
"
"
f"| {_esc(m.get('major'))} | "
f"{_esc(m.get('version'))} | "
f"{_esc(m.get('declared'))} | "
f"{_esc(m.get('probed'))} | "
f"{_esc(m.get('coverage_line') or '—')} | "
f"{_esc(m.get('critical', m.get('failed')))} | "
f"{_esc(buckets.get('success_2xx', 0))} | "
f"{_esc(buckets.get('client_4xx', 0))} | "
f"{_esc(buckets.get('server_5xx', 0))} | "
f"{_esc(deep.get('deep', 0))}/{_esc(deep.get('stub', 0))} | "
"
"
)
verb_bits = " · ".join(f"{_esc(k)}={_esc(v)}" for k, v in sorted(by_verb.items()))
fail_rows = []
for f in failures[:40]:
fail_rows.append(
""
f"| {_esc(f.get('major'))} | "
f"{_esc(f.get('verb'))} | "
f"{_esc(f.get('path'))} | "
f"{_esc(f.get('kind'))} | "
f"{_esc(f.get('status'))} | "
f"{_esc((f.get('body') or f.get('expected') or '')[:180])} | "
"
"
)
return f"""
REST HTTP contract matrix (Layer A)
coverage {_esc(coverage)}
· critical={_esc(critical)}
· probed={_esc(rest.get("probed") or rest.get("total"))}
· declared={_esc(rest.get("declared"))}
· verbs: {verb_bits or "—"}
100% = HTTP matrix (IMPLEMENTED × majors + HEAD), not pulumi-vsphere resource count.
| Major | Version | Declared | Probed | Coverage |
Critical | 2xx | 4xx | 5xx | deep/stub |
{"".join(major_rows) or '| — |
'}
REST critical failures (sample)
| Major | Verb | Path | Kind | Status | Detail |
{"".join(fail_rows) or '| none |
'}
"""
def _section_crud(summary: dict[str, Any]) -> str:
crud = summary.get("crud") or {}
flows = crud.get("flows") or []
rows = []
for f in flows:
rows.append(
f""
f"| {_esc(f.get('flow'))} | "
f"{_esc(f.get('step'))} | "
f"{_esc(f.get('status'))} | "
f"{_esc(f.get('error') or '—')} | "
f"
"
)
return f"""
Deep REST CRUD
total={_esc(crud.get("total"))} · failed={_esc(crud.get("failed"))}
| Flow | Step | Status | Detail |
{"".join(rows) or '| — (smoke skips CRUD) |
'}
"""
def _section_soap(summary: dict[str, Any]) -> str:
soap = summary.get("soap") or {}
ops = soap.get("ops") or []
rows = []
for op in ops:
status = "passed" if op.get("ok") else "failed"
rows.append(
f""
f"{_esc(op.get('op'))} | "
f"{_esc(op.get('status'))} | "
f"{_esc(status)} | "
f"{_esc(op.get('error') or '—')} | "
f"
"
)
return f"""
SOAP WSDL ops
total={_esc(soap.get("total"))} · failed={_esc(soap.get("failed"))}
| Operation | HTTP | Status | Detail |
{"".join(rows) or '| — (smoke skips SOAP) |
'}
"""
def render_html(summary: dict[str, Any]) -> str:
generated = summary.get("generated_at") or datetime.now(UTC).isoformat()
cases = summary.get("cases") or []
total = len(cases)
passed = sum(1 for c in cases if c.get("status") == "passed")
failed = sum(1 for c in cases if c.get("status") == "failed")
skipped = sum(1 for c in cases if c.get("status") == "skipped")
rest = summary.get("rest") or {}
crud = summary.get("crud") or {}
soap = summary.get("soap") or {}
rows = []
for case in cases:
status = case.get("status") or ""
outs = case.get("outputs") or {}
out_html = "
".join(f"{_esc(k)}={_esc(v)}" for k, v in outs.items())
err = _esc(case.get("error") or "")
rows.append(
f""
f"| {_esc(case.get('id'))} | "
f"{_esc(case.get('title'))} | "
f"{_esc(status)} | "
f"{out_html or '—'} | "
f"{err or '—'} | "
f"
"
)
return f"""
pulumi hybrid lab report
Cases{total}
Passed{passed}
Failed{failed}
Skipped{skipped}
REST failed{_esc(rest.get("failed", 0))}
/ {_esc(rest.get("total", 0))} probes
CRUD failed{_esc(crud.get("failed", 0))}
/ {_esc(crud.get("total", 0))} flows
SOAP failed{_esc(soap.get("failed", 0))}
/ {_esc(soap.get("total", 0))} ops
Suite cases
| ID | Title | Status | Outputs | Error |
{"".join(rows)}
{_section_rest(summary)}
{_section_crud(summary)}
{_section_soap(summary)}
"""
def write_report(summary: dict[str, Any], path: Path) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(render_html(summary), encoding="utf-8")