generated from coulomb/repo-seed
Harden reconciliation conflict handling
This commit is contained in:
@@ -647,3 +647,110 @@ class TestReconciliationEndpoints:
|
||||
messages = r.json()
|
||||
assert len(messages) == 1
|
||||
assert "blocking reason" in messages[0]["body"]
|
||||
|
||||
async def test_apply_workstream_stale_expected_status_creates_conflict_message(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",
|
||||
)
|
||||
await client.patch(f"/workstreams/{ws['id']}", json={"status": "ready"})
|
||||
|
||||
r = await client.post("/reconciliation/state-change", json={
|
||||
"target_type": "workstream",
|
||||
"target_id": ws["id"],
|
||||
"target_status": "backlog",
|
||||
"expected_current_status": "active",
|
||||
"apply": True,
|
||||
})
|
||||
|
||||
assert r.status_code == 200, r.text
|
||||
body = r.json()
|
||||
assert body["conflict"] is True
|
||||
assert body["write_through_result"] == "not_applicable"
|
||||
assert body["current_status"] == "ready"
|
||||
assert "expected" in body["reason"]
|
||||
assert "status: active" in wp.read_text(encoding="utf-8")
|
||||
|
||||
r = await client.get(f"/workstreams/{ws['id']}")
|
||||
assert r.json()["status"] == "ready"
|
||||
|
||||
async def test_apply_workstream_file_status_drift_creates_conflict_message(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: ready\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",
|
||||
"expected_current_status": "active",
|
||||
"apply": True,
|
||||
})
|
||||
|
||||
assert r.status_code == 200, r.text
|
||||
body = r.json()
|
||||
assert body["conflict"] is True
|
||||
assert body["write_through_result"] == "not_applicable"
|
||||
assert "differs from cached DB status" in body["reason"]
|
||||
assert "status: ready" in wp.read_text(encoding="utf-8")
|
||||
|
||||
r = await client.get(f"/workstreams/{ws['id']}")
|
||||
assert r.json()["status"] == "active"
|
||||
|
||||
async def test_apply_task_unavailable_host_path_creates_conflict_message(self, client, tmp_path):
|
||||
await _create_domain(client)
|
||||
repo = await _create_repo(client, local_path=tmp_path / "missing-repo")
|
||||
topic = await _create_topic(client)
|
||||
ws = await _create_workstream(client, topic["id"], repo_id=repo["id"])
|
||||
task = await _create_task(client, ws["id"])
|
||||
|
||||
r = await client.post("/reconciliation/state-change", json={
|
||||
"target_type": "task",
|
||||
"target_id": task["id"],
|
||||
"target_status": "in_progress",
|
||||
"expected_current_status": "todo",
|
||||
"apply": True,
|
||||
})
|
||||
|
||||
assert r.status_code == 200, r.text
|
||||
body = r.json()
|
||||
assert body["conflict"] is True
|
||||
assert body["write_through_result"] == "not_applicable"
|
||||
assert "host path" in body["reason"]
|
||||
assert body["reconciliation_record_id"]
|
||||
|
||||
r = await client.get(f"/tasks/{task['id']}")
|
||||
assert r.json()["status"] == "todo"
|
||||
|
||||
Reference in New Issue
Block a user