Add reconciliation file write-through

This commit is contained in:
2026-05-23 17:41:30 +02:00
parent b78d73611c
commit 757c2c3345
5 changed files with 378 additions and 19 deletions

View File

@@ -28,10 +28,12 @@ async def _create_topic(client, domain_slug="testdomain", slug="testtopic", titl
return r.json()
async def _create_workstream(client, topic_id, slug="test-ws", title="Test WS", status="active"):
r = await client.post("/workstreams/", json={
async def _create_workstream(client, topic_id, slug="test-ws", title="Test WS", status="active", **extra):
payload = {
"topic_id": topic_id, "slug": slug, "title": title, "status": status,
})
}
payload.update(extra)
r = await client.post("/workstreams/", json=payload)
assert r.status_code == 201, r.text
return r.json()
@@ -44,6 +46,17 @@ async def _create_task(client, workstream_id, title="Test task"):
return r.json()
async def _create_repo(client, domain_slug="testdomain", slug="test-repo", local_path=None):
r = await client.post("/repos/", json={
"domain_slug": domain_slug,
"slug": slug,
"name": "Test Repo",
"local_path": str(local_path) if local_path else None,
})
assert r.status_code == 201, r.text
return r.json()
# ---------------------------------------------------------------------------
# Domain tests
# ---------------------------------------------------------------------------
@@ -464,3 +477,113 @@ class TestReconciliationEndpoints:
})
assert r.status_code == 404
async def test_apply_workstream_write_through_patches_file_then_db(self, client, tmp_path):
await _create_domain(client)
repo_root = tmp_path / "repo"
workplans = repo_root / "workplans"
workplans.mkdir(parents=True)
repo = await _create_repo(client, local_path=repo_root)
topic = await _create_topic(client)
ws = await _create_workstream(client, topic["id"], repo_id=repo["id"])
wp = workplans / "STATE-WP-9999-demo.md"
wp.write_text(
"---\n"
"id: STATE-WP-9999\n"
"type: workplan\n"
"title: Demo\n"
"domain: custodian\n"
"repo: state-hub\n"
"status: active\n"
f"state_hub_workstream_id: \"{ws['id']}\"\n"
"---\n",
encoding="utf-8",
)
r = await client.post("/reconciliation/state-change", json={
"target_type": "workstream",
"target_id": ws["id"],
"target_status": "backlog",
"actor": "dashboard",
"apply": True,
})
assert r.status_code == 200, r.text
body = r.json()
assert body["reconciliation_class"] == "write_through"
assert body["write_through_result"] == "applied"
assert body["workplan_path"] == "workplans/STATE-WP-9999-demo.md"
assert "status: backlog" in wp.read_text(encoding="utf-8")
r = await client.get(f"/workstreams/{ws['id']}")
assert r.json()["status"] == "backlog"
async def test_apply_workstream_without_file_does_not_mutate_db(self, client):
await _create_domain(client)
topic = await _create_topic(client)
ws = await _create_workstream(client, topic["id"])
r = await client.post("/reconciliation/state-change", json={
"target_type": "workstream",
"target_id": ws["id"],
"target_status": "backlog",
"file_backed": True,
"apply": True,
})
assert r.status_code == 200, r.text
body = r.json()
assert body["file_backed"] is False
assert body["reconciliation_class"] == "deferred"
assert body["write_through_result"] == "not_applicable"
r = await client.get(f"/workstreams/{ws['id']}")
assert r.json()["status"] == "active"
async def test_apply_task_write_through_patches_task_block_then_db(self, client, tmp_path):
await _create_domain(client)
repo_root = tmp_path / "repo"
workplans = repo_root / "workplans"
workplans.mkdir(parents=True)
repo = await _create_repo(client, local_path=repo_root)
topic = await _create_topic(client)
ws = await _create_workstream(client, topic["id"], repo_id=repo["id"])
task = await _create_task(client, ws["id"])
wp = workplans / "STATE-WP-9999-demo.md"
wp.write_text(
"---\n"
"id: STATE-WP-9999\n"
"type: workplan\n"
"title: Demo\n"
"domain: custodian\n"
"repo: state-hub\n"
"status: active\n"
f"state_hub_workstream_id: \"{ws['id']}\"\n"
"---\n\n"
"## Demo Task\n\n"
"```task\n"
"id: STATE-WP-9999-T01\n"
"status: todo\n"
"priority: high\n"
f"state_hub_task_id: \"{task['id']}\"\n"
"```\n",
encoding="utf-8",
)
r = await client.post("/reconciliation/state-change", json={
"target_type": "task",
"target_id": task["id"],
"target_status": "in_progress",
"actor": "dashboard",
"apply": True,
})
assert r.status_code == 200, r.text
body = r.json()
assert body["reconciliation_class"] == "write_through"
assert body["write_through_result"] == "applied"
assert body["workplan_path"] == "workplans/STATE-WP-9999-demo.md"
assert "status: in_progress" in wp.read_text(encoding="utf-8")
r = await client.get(f"/tasks/{task['id']}")
assert r.json()["status"] == "in_progress"