test: pin the sweep outcome per tool in a baseline, seed a GPG key, fail loudly when seeding finds no run
This commit is contained in:
1 parent
84b361962e
commit
9e51f72ec9
6 files changed
+761
-14
No files matched your search
@@ -12,6 +12,9 @@ that is data or pure logic, so it can be checked without Docker
|
||||
then deletes (leaf resources before their parents).
|
||||
* :func:`classify` decides whether a result is acceptable: a 2xx, a status the swagger
|
||||
documents for that operation, or an explicit entry of :data:`ALLOWANCES` with a reason.
|
||||
* :func:`compare_baseline` pins the outcome class of every tool (``2xx`` or the exact
|
||||
status of a documented non-2xx or an allowance) in ``sweep_baseline.json`` and reports
|
||||
every downgrade at once; ``AEGIS_IT_WRITE_BASELINE=1`` regenerates the file.
|
||||
* :func:`render_report` writes ``docs/integration-coverage.md``.
|
||||
"""
|
||||
|
||||
@@ -27,12 +30,15 @@ from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from aegis_gitea_mcp.catalog import Catalog, Operation
|
||||
from tests.integration.static_keys import GPG_PUBLIC_KEYS
|
||||
from tests.integration.sweep_seed import PNG_1X1, Ctx, b64, ssh_public_key
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
SPEC_PATH = REPO_ROOT / "spec" / "gitea-swagger.v1.json"
|
||||
REPORT_PATH = REPO_ROOT / "docs" / "integration-coverage.md"
|
||||
REPORT_ENV = "AEGIS_IT_WRITE_REPORT"
|
||||
BASELINE_PATH = Path(__file__).resolve().parent / "sweep_baseline.json"
|
||||
BASELINE_ENV = "AEGIS_IT_WRITE_BASELINE"
|
||||
RAW_TOOL = "gitea_api_request"
|
||||
|
||||
#: Tools the generic sweep never calls, with the test that covers them in isolation.
|
||||
@@ -172,6 +178,7 @@ ID_RULES: list[tuple[str, str]] = [
|
||||
(r"^/orgs/\{org\}/projects/\{id\}", "org_project"),
|
||||
(r"^/user/hooks/\{id\}", "user_hook"),
|
||||
(r"^/user/keys/\{id\}", "user_key"),
|
||||
(r"^/user/gpg_keys/\{id\}", "gpg_key"),
|
||||
(r"^/user/projects/\{id\}", "user_project"),
|
||||
(r"^/user/applications/oauth2/\{id\}", "oauth_app"),
|
||||
(r"^/admin/hooks/\{id\}", "admin_hook"),
|
||||
@@ -605,7 +612,9 @@ OVERRIDES: dict[str, Override] = {
|
||||
"gitea_user_current_post_key": lambda c: {
|
||||
"body": {"title": fresh(c, "key"), "key": ssh_public_key()}
|
||||
},
|
||||
"gitea_user_current_post_gpg_key": lambda c: {"body": {"armored_public_key": "not a key"}},
|
||||
"gitea_user_current_post_gpg_key": lambda c: {
|
||||
"body": {"armored_public_key": GPG_PUBLIC_KEYS[1]}
|
||||
},
|
||||
"gitea_user_delete_access_token": lambda c: {
|
||||
"username": c["admin"],
|
||||
"token": c["throwaway_token"],
|
||||
@@ -858,6 +867,88 @@ def write_report_if_enabled(catalog: Catalog, gitea_version: str) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Baseline: the pinned outcome class per tool
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
SUCCESS = "2xx"
|
||||
NO_STATUS = "no-status"
|
||||
Outcome = str | int
|
||||
|
||||
|
||||
def outcome_class(record: SweepRecord) -> Outcome:
|
||||
"""``"2xx"`` for a success, else the HTTP status Gitea answered (or ``"no-status"``)."""
|
||||
if record.status is not None and 200 <= record.status < 300 and not record.is_error:
|
||||
return SUCCESS
|
||||
return record.status if record.status is not None else NO_STATUS
|
||||
|
||||
|
||||
def baseline_snapshot(records: Iterable[SweepRecord], gitea_version: str) -> dict[str, Any]:
|
||||
"""The baseline document for ``records`` (what ``AEGIS_IT_WRITE_BASELINE=1`` writes)."""
|
||||
return {
|
||||
"gitea": gitea_version,
|
||||
"comment": (
|
||||
"Expected outcome class per tool in the sweep: 2xx, or the exact status of a "
|
||||
"documented non-2xx or allowance. Regenerate with AEGIS_IT_WRITE_BASELINE=1."
|
||||
),
|
||||
"tools": {record.tool: outcome_class(record) for record in records},
|
||||
}
|
||||
|
||||
|
||||
def load_baseline(path: Path = BASELINE_PATH) -> dict[str, Outcome]:
|
||||
"""Read the pinned outcome per tool."""
|
||||
data = json.loads(path.read_text(encoding="utf-8"))
|
||||
tools: dict[str, Outcome] = data["tools"]
|
||||
return tools
|
||||
|
||||
|
||||
def write_baseline(
|
||||
records: Iterable[SweepRecord], gitea_version: str, path: Path = BASELINE_PATH
|
||||
) -> None:
|
||||
"""Write the baseline file (tools sorted by name, so diffs show only real changes)."""
|
||||
document = baseline_snapshot(records, gitea_version)
|
||||
document["tools"] = dict(sorted(document["tools"].items()))
|
||||
path.write_text(json.dumps(document, indent=2) + "\n", encoding="utf-8")
|
||||
|
||||
|
||||
@dataclass
|
||||
class BaselineDiff:
|
||||
"""Result of comparing a sweep with the pinned baseline."""
|
||||
|
||||
regressions: list[str] = field(default_factory=list)
|
||||
upgrades: list[str] = field(default_factory=list)
|
||||
|
||||
|
||||
def compare_baseline(records: Iterable[SweepRecord], baseline: dict[str, Outcome]) -> BaselineDiff:
|
||||
"""Compare outcomes with the baseline.
|
||||
|
||||
A regression is a 2xx that is no longer a 2xx, a documented non-2xx that answers a
|
||||
different status, a tool without a baseline entry or a baseline entry without a tool.
|
||||
A non-2xx that became a 2xx is an upgrade: reported, but it does not fail the run.
|
||||
"""
|
||||
diff = BaselineDiff()
|
||||
seen: set[str] = set()
|
||||
for record in records:
|
||||
seen.add(record.tool)
|
||||
actual = outcome_class(record)
|
||||
if record.tool not in baseline:
|
||||
diff.regressions.append(f"{record.tool}: no baseline entry (got {actual})")
|
||||
continue
|
||||
expected = baseline[record.tool]
|
||||
if actual == expected:
|
||||
continue
|
||||
if actual == SUCCESS:
|
||||
diff.upgrades.append(f"{record.tool}: baseline {expected}, now 2xx")
|
||||
continue
|
||||
detail = f"{record.tool}: baseline {expected}, got {actual} ({record.method} {record.path}"
|
||||
if record.message:
|
||||
detail += f" :: {record.message}"
|
||||
diff.regressions.append(detail + ")")
|
||||
for tool in sorted(set(baseline) - seen):
|
||||
diff.regressions.append(f"{tool}: in the baseline but not called by the sweep")
|
||||
return diff
|
||||
|
||||
|
||||
def failing(records: Iterable[SweepRecord]) -> list[SweepRecord]:
|
||||
"""The records whose outcome is ``unexpected``."""
|
||||
return [record for record in records if record.outcome == "unexpected"]
|
||||
|
||||
Reference in new issue
Block a user