Check AMPG fixture manifests

This commit is contained in:
jwinterm
2026-07-05 21:16:29 -04:00
parent 3a51468d29
commit f930f606fd
6 changed files with 240 additions and 0 deletions
+35
View File
@@ -6,6 +6,7 @@ import sys
from .config import load_config
from .docsgen import generate_docs
from .fixtures import check_fixture_manifest
from .launch import prepare_open
from .plan import plan_url
from .platforms import PLATFORM_CHOICES
@@ -43,6 +44,11 @@ def main(argv: list[str] | None = None) -> int:
subcommands.add_parser("inspect", help="Inspect local transport readiness.")
fixture_parser = subcommands.add_parser("fixture", help="Check AMPG fixture manifests.")
fixture_subcommands = fixture_parser.add_subparsers(dest="fixture_command", required=True)
fixture_check = fixture_subcommands.add_parser("check", help="Validate route expectations.")
fixture_check.add_argument("manifest", type=Path)
docs_parser = subcommands.add_parser("docs", help="Generate or check generated docs.")
docs_subcommands = docs_parser.add_subparsers(dest="docs_command", required=True)
docs_generate = docs_subcommands.add_parser("generate", help="Generate docs from code.")
@@ -64,6 +70,8 @@ def main(argv: list[str] | None = None) -> int:
)
if args.command == "inspect":
return _cmd_inspect()
if args.command == "fixture":
return _cmd_fixture(args)
if args.command == "docs":
return _cmd_docs(args)
except Exception as exc: # noqa: BLE001
@@ -169,5 +177,32 @@ def _cmd_docs(args) -> int:
return 1
def _cmd_fixture(args) -> int:
if args.fixture_command == "check":
result = check_fixture_manifest(args.manifest)
for check in result.checks:
print(
"AMPBROWSER_FIXTURE "
f"site={check.site_id} "
f"protocol={check.protocol} "
f"url={check.url} "
f"expected_transport={check.expected_transport} "
f"actual_transport={check.actual_transport} "
f"expected_profile={check.expected_profile} "
f"actual_profile={check.actual_profile} "
f"status={check.status} "
f"message=\"{_safe(check.message)}\""
)
print(
"AMPBROWSER_FIXTURE_SUMMARY "
f"site={result.site_id} "
f"manifest={result.manifest_path} "
f"checks={len(result.checks)} "
f"status={'ok' if result.ok else 'fail'}"
)
return 0 if result.ok else 1
return 1
def _safe(value: str) -> str:
return value.replace('"', "'")
+105
View File
@@ -0,0 +1,105 @@
from __future__ import annotations
from dataclasses import dataclass
import json
from pathlib import Path
from typing import Any
from .routing import route_url
SUPPORTED_SCHEMAS = {"ampg.fixture-manifest.v1"}
@dataclass(frozen=True)
class FixtureCheck:
site_id: str
protocol: str
url: str
expected_transport: str
actual_transport: str
expected_profile: str
actual_profile: str
status: str
message: str
@dataclass(frozen=True)
class FixtureCheckResult:
manifest_path: Path
schema: str
site_id: str
checks: tuple[FixtureCheck, ...]
@property
def ok(self) -> bool:
return all(check.status == "ok" for check in self.checks)
def check_fixture_manifest(path: Path) -> FixtureCheckResult:
data = _load_manifest(path)
schema = _string(data, "schema")
if schema not in SUPPORTED_SCHEMAS:
raise ValueError(f"{path}: unsupported manifest schema {schema!r}")
site = data.get("site")
if not isinstance(site, dict):
raise ValueError(f"{path}: missing site table")
site_id = _string(site, "id")
fixtures = data.get("fixtures")
if not isinstance(fixtures, list):
raise ValueError(f"{path}: fixtures must be a list")
checks = tuple(_check_fixture(site_id, fixture, path) for fixture in fixtures)
return FixtureCheckResult(
manifest_path=path,
schema=schema,
site_id=site_id,
checks=checks,
)
def _check_fixture(site_id: str, fixture: Any, path: Path) -> FixtureCheck:
if not isinstance(fixture, dict):
raise ValueError(f"{path}: fixture entries must be tables")
protocol = _string(fixture, "protocol")
url = _string(fixture, "url")
checks = fixture.get("checks")
if not isinstance(checks, dict):
raise ValueError(f"{path}: {protocol}: missing checks table")
expected_transport = _string(checks, "transport")
expected_profile = _string(checks, "profile")
route = route_url(url)
failures: list[str] = []
if route.transport != expected_transport:
failures.append(f"transport expected {expected_transport} got {route.transport}")
if route.profile != expected_profile:
failures.append(f"profile expected {expected_profile} got {route.profile}")
return FixtureCheck(
site_id=site_id,
protocol=protocol,
url=route.normalized,
expected_transport=expected_transport,
actual_transport=route.transport,
expected_profile=expected_profile,
actual_profile=route.profile,
status="fail" if failures else "ok",
message="; ".join(failures) if failures else "route matches",
)
def _load_manifest(path: Path) -> dict[str, Any]:
data = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(data, dict):
raise ValueError(f"{path}: manifest must be a JSON object")
return data
def _string(data: dict[str, Any], key: str) -> str:
value = data.get(key)
if not isinstance(value, str) or not value:
raise ValueError(f"missing required string field {key!r}")
return value