2026-06-22 13:52:13 +02:00
|
|
|
"""Tests for register_from_classification CLI (STATE-WP-0065 P3)."""
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import json
|
|
|
|
|
import subprocess
|
|
|
|
|
import sys
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
2026-08-23 11:30:36 +02:00
|
|
|
from api.classification import ClassificationData
|
|
|
|
|
|
2026-06-22 13:52:13 +02:00
|
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
|
|
|
SCRIPT = REPO_ROOT / "scripts" / "register_from_classification.py"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_cli_help():
|
|
|
|
|
result = subprocess.run(
|
|
|
|
|
[sys.executable, str(SCRIPT), "--help"],
|
|
|
|
|
capture_output=True,
|
|
|
|
|
text=True,
|
|
|
|
|
cwd=REPO_ROOT,
|
|
|
|
|
)
|
|
|
|
|
assert result.returncode == 0
|
|
|
|
|
assert "--repo-path" in result.stdout
|
|
|
|
|
assert "--bulk" in result.stdout
|
|
|
|
|
assert "--dry-run" in result.stdout
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_dry_run_repo_path_state_hub():
|
|
|
|
|
sys.path.insert(0, str(REPO_ROOT))
|
|
|
|
|
from scripts.register_from_classification import run_registration
|
|
|
|
|
import argparse
|
|
|
|
|
|
|
|
|
|
args = argparse.Namespace(
|
|
|
|
|
repo_path=str(REPO_ROOT),
|
|
|
|
|
slug=None,
|
|
|
|
|
bulk=False,
|
|
|
|
|
dry_run=True,
|
|
|
|
|
api=False,
|
|
|
|
|
db=False,
|
|
|
|
|
api_base="http://127.0.0.1:8000",
|
|
|
|
|
json=False,
|
|
|
|
|
)
|
|
|
|
|
report = await run_registration(args)
|
|
|
|
|
counts = report.counts()
|
|
|
|
|
assert counts["invalid"] == 0
|
|
|
|
|
assert counts["registered"] + counts["updated"] + counts["skipped"] >= 1
|
|
|
|
|
assert any(r.slug == "state-hub" for r in report.results)
|
|
|
|
|
# Valid classification file is always parsed even when DB domains are absent.
|
|
|
|
|
assert not any("repo_classification block" in r.detail for r in report.results)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_json_report_shape():
|
|
|
|
|
result = subprocess.run(
|
|
|
|
|
[
|
|
|
|
|
sys.executable,
|
|
|
|
|
str(SCRIPT),
|
|
|
|
|
"--repo-path",
|
|
|
|
|
str(REPO_ROOT),
|
|
|
|
|
"--dry-run",
|
|
|
|
|
"--json",
|
|
|
|
|
],
|
|
|
|
|
capture_output=True,
|
|
|
|
|
text=True,
|
|
|
|
|
cwd=REPO_ROOT,
|
|
|
|
|
)
|
|
|
|
|
payload = json.loads(result.stdout)
|
|
|
|
|
assert payload["summary"]["invalid"] == 0
|
|
|
|
|
assert "summary" in payload
|
|
|
|
|
assert "results" in payload
|
2026-08-23 11:30:36 +02:00
|
|
|
assert set(payload["summary"]) == {"registered", "updated", "skipped", "invalid"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_git_fingerprint_uses_one_root(monkeypatch, tmp_path):
|
|
|
|
|
from scripts import register_from_classification as registration
|
|
|
|
|
|
|
|
|
|
first = "a" * 40
|
|
|
|
|
second = "b" * 40
|
|
|
|
|
monkeypatch.setattr(
|
|
|
|
|
registration,
|
|
|
|
|
"_git_value",
|
|
|
|
|
lambda _path, _args: f"{first}\n{second}",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert registration._git_fingerprint(tmp_path) == first
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_api_update_separates_classification_from_identity(monkeypatch, tmp_path):
|
|
|
|
|
from scripts import register_from_classification as registration
|
|
|
|
|
|
|
|
|
|
calls = []
|
|
|
|
|
|
|
|
|
|
def fake_api_request(method, path, *, api_base, body=None):
|
|
|
|
|
calls.append((method, path, body))
|
|
|
|
|
if method == "GET":
|
|
|
|
|
return 200, {"slug": "example"}
|
|
|
|
|
return 200, {"slug": "example"}
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(registration, "_api_request", fake_api_request)
|
|
|
|
|
monkeypatch.setattr(registration, "_git_root", lambda path: path)
|
|
|
|
|
monkeypatch.setattr(registration, "_git_value", lambda _path, _args: "origin")
|
|
|
|
|
monkeypatch.setattr(registration, "_git_fingerprint", lambda _path: "a" * 40)
|
|
|
|
|
report = registration.RegistrationReport()
|
|
|
|
|
data = ClassificationData(
|
|
|
|
|
category="infrastructure",
|
|
|
|
|
domain="infotech",
|
|
|
|
|
capability_tags=["infrastructure.state"],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
await registration._upsert_via_api(
|
|
|
|
|
slug="example",
|
|
|
|
|
repo_path=tmp_path,
|
|
|
|
|
data=data,
|
|
|
|
|
dry_run=False,
|
|
|
|
|
api_base="http://state-hub.test",
|
|
|
|
|
report=report,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
patch = next(call for call in calls if call[:2] == ("PATCH", "/repos/example"))
|
|
|
|
|
assert patch[2]["domain_slug"] == "infotech"
|
|
|
|
|
assert "local_path" not in patch[2]
|
|
|
|
|
assert "remote_url" not in patch[2]
|
|
|
|
|
assert "git_fingerprint" not in patch[2]
|
|
|
|
|
assert ("POST", "/repos/example/paths") in [call[:2] for call in calls]
|