from __future__ import annotations import argparse import json from pathlib import Path 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 "make fix-consistency REPO=demo-service" 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) == []