Files
state-hub/tests/test_statehub_register_cli.py

245 lines
7.0 KiB
Python

from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
import pytest
import custodian_cli
from custodian_cli import cmd_fix_consistency
from statehub_register import (
RegisterInference,
_invoke_llm,
_normalise_inference,
_parse_json_object,
write_registration_files,
)
def test_parse_json_object_accepts_fenced_json():
parsed = _parse_json_object(
"""Here is the answer:
```json
{"repo_slug": "demo-repo", "in_scope": ["tests"]}
```
"""
)
assert parsed == {"repo_slug": "demo-repo", "in_scope": ["tests"]}
def test_normalise_inference_slugifies_and_normalises_prefix():
inference = _normalise_inference(
{
"project_description": "Provides demo automation.",
"domain_slug": "Custodian",
"repo_slug": "Demo Repo!",
"workplan_prefix": "demo",
"in_scope": ["one", "two"],
"out_of_scope": ["three"],
}
)
assert inference.domain_slug == "custodian"
assert inference.repo_slug == "demo-repo"
assert inference.workplan_prefix == "DEMO-WP"
assert inference.in_scope == ["one", "two"]
assert inference.out_of_scope == ["three"]
def test_invoke_llm_uses_mock_adapter(monkeypatch):
payload = {"project_description": "Mocked repo.", "repo_slug": "mocked-repo"}
monkeypatch.setenv("STATEHUB_REGISTER_MOCK_LLM_RESPONSE", json.dumps(payload))
args = argparse.Namespace(
llm_provider="mock",
llm_model=None,
llm_api_key=None,
llm_timeout=5,
)
assert json.loads(_invoke_llm("infer this repo", args)) == payload
def test_write_registration_files_primes_codex_repo(tmp_path: Path):
inference = RegisterInference(
in_scope=["Run the demo service."],
out_of_scope=["Operate unrelated services."],
current_state="Status: active; implementation is small.",
)
written = write_registration_files(
project_path=tmp_path,
project_name="demo-service",
project_description="Provides a demo service.",
domain="custodian",
topic_id="cee7bedf-2b48-46ef-8601-006474f2ad7a",
topic_slug="custodian",
repo_slug="demo-service",
wp_prefix="DEMO-WP",
intent_markdown="# INTENT\n\nDemo service intent.\n",
inference=inference,
)
assert {path.name for path in written} == {
"INTENT.md",
"SCOPE.md",
"AGENTS.md",
".custodian-brief.md",
"DEMO-WP-0001-statehub-bootstrap.md",
}
assert (tmp_path / "INTENT.md").read_text() == "# INTENT\n\nDemo service intent.\n"
assert "**Repo slug:** demo-service" in (tmp_path / "AGENTS.md").read_text()
assert "Run the demo service." in (tmp_path / "SCOPE.md").read_text()
workplan = (tmp_path / "workplans" / "DEMO-WP-0001-statehub-bootstrap.md").read_text()
assert "id: DEMO-WP-0001" in workplan
assert "id: DEMO-WP-0001-T01" in workplan
assert "statehub fix-consistency" in workplan
def test_write_registration_files_is_idempotent_without_force(tmp_path: Path):
inference = RegisterInference()
kwargs = {
"project_path": tmp_path,
"project_name": "demo",
"project_description": "Provides a demo.",
"domain": "custodian",
"topic_id": "topic",
"topic_slug": "custodian",
"repo_slug": "demo",
"wp_prefix": "DEMO-WP",
"intent_markdown": "# INTENT\n\nDemo.\n",
"inference": inference,
}
assert write_registration_files(**kwargs)
assert write_registration_files(**kwargs) == []
def _fix_args(**overrides):
values = {
"repo": None,
"all": False,
"path": None,
"repo_path": None,
"remote": False,
"max_seconds": None,
"no_writeback": False,
"archive_closed": False,
"archive_workplan": None,
"archive_date": None,
"api_base": "http://statehub.test",
"as_json": False,
"strict_warnings": False,
}
values.update(overrides)
return argparse.Namespace(**values)
def _install_fake_checker(monkeypatch, tmp_path: Path) -> Path:
checker = tmp_path / "scripts" / "consistency_check.py"
checker.parent.mkdir()
checker.write_text("#!/usr/bin/env python3\n", encoding="utf-8")
monkeypatch.setattr(custodian_cli, "STATE_HUB_DIR", tmp_path)
return checker
def test_fix_consistency_defaults_to_here_and_normalises_warning_exit(monkeypatch, tmp_path: Path):
checker = _install_fake_checker(monkeypatch, tmp_path)
repo = tmp_path / "repo"
repo.mkdir()
calls = []
def fake_run(cmd):
calls.append(cmd)
return argparse.Namespace(returncode=2)
monkeypatch.setattr(custodian_cli.subprocess, "run", fake_run)
with pytest.raises(SystemExit) as exc:
cmd_fix_consistency(_fix_args(path=str(repo)))
assert exc.value.code == 0
assert calls == [[
sys.executable,
str(checker),
"--here",
str(repo.resolve()),
"--fix",
"--api-base",
"http://statehub.test",
]]
def test_fix_consistency_strict_warnings_preserves_exit_two(monkeypatch, tmp_path: Path):
_install_fake_checker(monkeypatch, tmp_path)
repo = tmp_path / "repo"
repo.mkdir()
monkeypatch.setattr(
custodian_cli.subprocess,
"run",
lambda _cmd: argparse.Namespace(returncode=2),
)
with pytest.raises(SystemExit) as exc:
cmd_fix_consistency(_fix_args(path=str(repo), strict_warnings=True))
assert exc.value.code == 2
def test_fix_consistency_repo_remote_passes_pull_before_fix_options(monkeypatch, tmp_path: Path):
checker = _install_fake_checker(monkeypatch, tmp_path)
repo = tmp_path / "repo"
repo.mkdir()
calls = []
def fake_run(cmd):
calls.append(cmd)
return argparse.Namespace(returncode=0)
monkeypatch.setattr(custodian_cli.subprocess, "run", fake_run)
with pytest.raises(SystemExit) as exc:
cmd_fix_consistency(
_fix_args(
repo="demo-service",
repo_path=str(repo),
remote=True,
no_writeback=True,
as_json=True,
max_seconds=12,
)
)
assert exc.value.code == 0
assert calls == [[
sys.executable,
str(checker),
"--repo",
"demo-service",
"--repo-path",
str(repo.resolve()),
"--fix",
"--remote",
"--no-writeback",
"--api-base",
"http://statehub.test",
"--json",
"--max-seconds",
"12",
]]
def test_fix_consistency_remote_requires_explicit_repo_or_all(monkeypatch, tmp_path: Path):
_install_fake_checker(monkeypatch, tmp_path)
calls = []
monkeypatch.setattr(custodian_cli.subprocess, "run", lambda cmd: calls.append(cmd))
with pytest.raises(SystemExit) as exc:
cmd_fix_consistency(_fix_args(remote=True, path=str(tmp_path)))
assert exc.value.code == 1
assert calls == []