repo-manager/tests/test_dual_run.py
tegwick d103955217 feat: finish register receiving and authority routing
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a023c0-a0a3-7c03-b395-5a0d2757214d
2026-08-21 23:15:48 +02:00

150 lines
4.4 KiB
Python

"""Dual-run flags, config file, meter, and hardened task status command."""
from __future__ import annotations
import subprocess
from pathlib import Path
import pytest
from repo_manager import dual_run
from repo_manager.commands.task_status import update_task_status
def _git(repo: Path, *args: str) -> None:
subprocess.run(["git", *args], cwd=repo, check=True, capture_output=True)
def _fixture(tmp_path: Path) -> Path:
repo = tmp_path / "pilot"
repo.mkdir()
_git(repo, "init")
_git(repo, "config", "user.email", "t@example.com")
_git(repo, "config", "user.name", "T")
(repo / "workplans").mkdir()
(repo / "workplans" / "P-WP-0001.md").write_text(
"""---
id: P-WP-0001
type: workplan
title: Pilot
status: active
---
## T
```task
id: P-WP-0001-T01
status: todo
state_hub_task_id: "11111111-1111-4111-8111-111111111111"
```
x
""",
encoding="utf-8",
)
_git(repo, "add", ".")
_git(repo, "commit", "-m", "seed")
return repo
@pytest.fixture(autouse=True)
def _clear_dual_run_cache():
dual_run.reload_config()
yield
dual_run.reload_config()
def test_flags_and_pilot(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
monkeypatch.delenv("RM_WRITEBACK", raising=False)
monkeypatch.delenv("RM_RECONCILE", raising=False)
monkeypatch.delenv("RM_PILOT_REPOS", raising=False)
monkeypatch.setenv("RM_DUAL_RUN_CONFIG", str(tmp_path / "missing-dual-run.yaml"))
dual_run.reload_config()
assert dual_run.writeback_enabled() is False
monkeypatch.setenv("RM_WRITEBACK", "1")
monkeypatch.setenv("RM_PILOT_REPOS", "repo-manager,other")
assert dual_run.writeback_for_repo("repo-manager") is True
assert dual_run.writeback_for_repo("nope") is False
monkeypatch.delenv("RM_PILOT_REPOS", raising=False)
assert dual_run.writeback_for_repo("any") is True
def test_config_file(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
cfg = tmp_path / "dual-run.yaml"
cfg.write_text(
"writeback: true\nreconcile: true\nwriteback_push: true\n"
"pilot_repos:\n - repo-manager\n",
encoding="utf-8",
)
monkeypatch.delenv("RM_WRITEBACK", raising=False)
monkeypatch.delenv("RM_RECONCILE", raising=False)
monkeypatch.delenv("RM_WRITEBACK_PUSH", raising=False)
monkeypatch.delenv("RM_PILOT_REPOS", raising=False)
monkeypatch.setenv("RM_DUAL_RUN_CONFIG", str(cfg))
dual_run.reload_config()
assert dual_run.writeback_enabled() is True
assert dual_run.reconcile_enabled() is True
assert dual_run.writeback_push_enabled() is True
assert dual_run.writeback_for_repo("repo-manager") is True
assert dual_run.writeback_for_repo("other") is False
# env overrides file
monkeypatch.setenv("RM_WRITEBACK", "0")
dual_run.reload_config()
assert dual_run.writeback_enabled() is False
def test_meter(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
meter = tmp_path / "meter.jsonl"
monkeypatch.setenv("RM_METER_PATH", str(meter))
dual_run.record_mutation(
source="repo-manager",
kind="test",
repo_slug="x",
detail={"a": 1},
)
dual_run.record_mutation(source="state-hub", kind="test", repo_slug="x")
summary = dual_run.meter_summary(meter)
assert summary["repo-manager"] == 1
assert summary["state-hub"] == 1
assert summary["total"] == 2
def test_update_by_uuid_and_idempotency(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
repo = _fixture(tmp_path)
monkeypatch.setenv("RM_METER_PATH", str(tmp_path / "m.jsonl"))
monkeypatch.setenv("RM_IDEMPOTENCY_PATH", str(tmp_path / "idemp.json"))
r1 = update_task_status(
repo,
"11111111-1111-4111-8111-111111111111",
"progress",
idempotency_key="k1",
repo_slug="pilot",
)
assert r1.status == "applied"
assert r1.evidence.get("git_sha")
text = (repo / "workplans" / "P-WP-0001.md").read_text(encoding="utf-8")
assert "status: progress" in text
r2 = update_task_status(
repo,
"11111111-1111-4111-8111-111111111111",
"progress",
idempotency_key="k1",
repo_slug="pilot",
)
assert r2.status == "applied"
assert r2.evidence.get("git_sha") == r1.evidence.get("git_sha")
r3 = update_task_status(
repo,
"11111111-1111-4111-8111-111111111111",
"done",
idempotency_key="k1",
repo_slug="pilot",
)
assert r3.status == "rejected"
assert r3.error and r3.error.get("code") == "conflict"