feat(devkit): add resumable workspace automation and UI review tooling
Verified with the coordinated workspace changes by devkit full run 2026-09-08T225814-186389-0000-3e3ed7cd (all seven phases passed). This shared UI pass does not mark the individual module reviews complete.
This commit is contained in:
Executable
+158
@@ -0,0 +1,158 @@
|
||||
"""Public CLI contracts and an executable portable-project smoke fixture."""
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "tools/devkit"))
|
||||
from govoplan_devkit import cli, doctor, runner
|
||||
from govoplan_devkit.common import META_ROOT
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"arguments",
|
||||
[["--json", "commands"], ["commands", "--json"], ["commands", "--format", "json"]],
|
||||
)
|
||||
def test_global_output_flags_work_on_either_side_of_command(arguments, capsys):
|
||||
assert cli.main(arguments) == 0
|
||||
output = json.loads(capsys.readouterr().out)
|
||||
names = {item["command"] for item in output["commands"]}
|
||||
assert {"context", "check", "doctor", "docs", "issues", "release", "git"} <= names
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"command",
|
||||
[
|
||||
"context",
|
||||
"doctor",
|
||||
"check",
|
||||
"resume",
|
||||
"recover",
|
||||
"docs",
|
||||
"review",
|
||||
"issues",
|
||||
"release",
|
||||
"git",
|
||||
],
|
||||
)
|
||||
def test_command_help_does_not_require_live_services(command, capsys):
|
||||
with pytest.raises(SystemExit) as stopped:
|
||||
cli.main([command, "--help"])
|
||||
assert stopped.value.code == 0
|
||||
assert "usage:" in capsys.readouterr().out
|
||||
|
||||
|
||||
def test_portable_project_executes_registered_commands_and_reads_receipt(
|
||||
tmp_path, capsys, monkeypatch
|
||||
):
|
||||
monkeypatch.setenv("XDG_STATE_HOME", str(tmp_path / "state"))
|
||||
repo = tmp_path / "example"
|
||||
repo.mkdir()
|
||||
subprocess.run(["git", "init", "-q", str(repo)], check=True)
|
||||
subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"-C",
|
||||
str(repo),
|
||||
"-c",
|
||||
"user.name=Fixture",
|
||||
"-c",
|
||||
"user.email=fixture@example.invalid",
|
||||
"commit",
|
||||
"--allow-empty",
|
||||
"-qm",
|
||||
"fixture",
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
project = tmp_path / "project.json"
|
||||
project.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"schema_version": 1,
|
||||
"name": "Portable",
|
||||
"repositories": [{"name": "example", "path": "example"}],
|
||||
"checks": [
|
||||
{
|
||||
"id": "test",
|
||||
"argv": ["{python}", "-c", "print('portable ok')"],
|
||||
"cwd": "example",
|
||||
}
|
||||
],
|
||||
"profiles": {"quick": ["test"]},
|
||||
}
|
||||
)
|
||||
)
|
||||
common = ["--workspace-root", str(tmp_path), "--project", str(project), "--json"]
|
||||
with patch.object(runner, "environment_fingerprint", return_value="fixture"):
|
||||
assert cli.main(common + ["check", "--profile", "quick"]) == 0
|
||||
result = json.loads(capsys.readouterr().out)
|
||||
assert result["status"] == "passed"
|
||||
assert cli.main(["status", result["run_id"], *common]) == 0
|
||||
status = json.loads(capsys.readouterr().out)
|
||||
assert status["snapshot_verified"] is True
|
||||
assert cli.main(["logs", result["run_id"], "--stage", "test", *common]) == 0
|
||||
assert "portable ok" in capsys.readouterr().out
|
||||
|
||||
|
||||
def test_portable_example_matches_published_schema():
|
||||
import jsonschema
|
||||
|
||||
jsonschema.validate(
|
||||
json.loads((META_ROOT / "tools/devkit/examples/project.json").read_text()),
|
||||
json.loads((META_ROOT / "tools/devkit/project.schema.json").read_text()),
|
||||
)
|
||||
|
||||
|
||||
def test_malformed_project_is_a_controlled_json_error(tmp_path, capsys):
|
||||
project = tmp_path / "bad.json"
|
||||
project.write_text('{"schema_version":true,"repositories":[]}')
|
||||
assert (
|
||||
cli.main(
|
||||
[
|
||||
"context",
|
||||
"--workspace-root",
|
||||
str(tmp_path),
|
||||
"--project",
|
||||
str(project),
|
||||
"--json",
|
||||
]
|
||||
)
|
||||
== 2
|
||||
)
|
||||
assert json.loads(capsys.readouterr().out)["status"] == "error"
|
||||
|
||||
|
||||
def test_doctor_is_read_only_and_preserves_dependency_warnings(tmp_path, capsys):
|
||||
project = tmp_path / "project.json"
|
||||
project.write_text(
|
||||
json.dumps(
|
||||
{"schema_version": 1, "repositories": [{"name": "example", "path": "."}]}
|
||||
)
|
||||
)
|
||||
(tmp_path / "package.json").write_text("{}")
|
||||
before = set(tmp_path.iterdir())
|
||||
with (
|
||||
patch.object(doctor, "tool_version", return_value="fixture"),
|
||||
patch.object(doctor, "inspect_repository", return_value={"errors": []}),
|
||||
):
|
||||
assert (
|
||||
cli.main(
|
||||
[
|
||||
"doctor",
|
||||
"--workspace-root",
|
||||
str(tmp_path),
|
||||
"--project",
|
||||
str(project),
|
||||
"--json",
|
||||
]
|
||||
)
|
||||
== 0
|
||||
)
|
||||
output = json.loads(capsys.readouterr().out)
|
||||
assert any(item["status"] == "warning" for item in output["checks"])
|
||||
assert set(tmp_path.iterdir()) == before
|
||||
Reference in New Issue
Block a user