repository-rename verify: accept descendant heads and retired dispatch after source-synced
Finishing work during the operation retires it from active dispatch, and the source repository legitimately advances after source-synced. verify now treats retired dispatch ids as present and accepts a Forge head that contains the recorded source-synced head (Forgejo compare); unrelated drift still fails. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 101414@bnt-lap001 Assistant-Session: 60b5a221-d435-42b9-b778-983c90c7eed6
This commit is contained in:
parent
6924d2410f
commit
b451bd10e5
4 changed files with 114 additions and 14 deletions
|
|
@ -38,6 +38,7 @@ class FakeForge:
|
|||
self.rename_calls = 0
|
||||
self.extra: dict[str, int] = {}
|
||||
self.unreadable = False
|
||||
self.contains: dict[str, set[str]] = {}
|
||||
|
||||
def snapshot(self, name: str, repository_id: int | None = None) -> ForgeRepositorySnapshot:
|
||||
return ForgeRepositorySnapshot(
|
||||
|
|
@ -65,6 +66,9 @@ class FakeForge:
|
|||
return self.snapshot(name, self.extra[name])
|
||||
return None
|
||||
|
||||
async def descends_from(self, *, instance: str, owner: str, name: str, base: str, head: str):
|
||||
return base == head or base in self.contains.get(head, set())
|
||||
|
||||
async def rename(self, *, instance: str, owner: str, old_name: str, new_name: str):
|
||||
assert old_name == self.name
|
||||
assert new_name not in self.extra
|
||||
|
|
@ -769,21 +773,44 @@ async def test_source_sync_may_advance_forge_head_past_the_baseline(client, rena
|
|||
assert recorded["head_advanced"] is True
|
||||
assert recorded["baseline_commit"] == "a" * 40
|
||||
|
||||
# Later phases anchor on the recorded head, so drift beyond it still fails.
|
||||
verify = await client.get(
|
||||
f"/repos/{repo['id']}/rename/operations/{operation['id']}/verify"
|
||||
)
|
||||
assert verify.status_code == 200, verify.text
|
||||
assert all(
|
||||
check["ok"] for check in verify.json()["checks"] if check["name"] == "source_commit"
|
||||
)
|
||||
# Later commits that contain the recorded head are accepted; unrelated drift is not.
|
||||
verify_url = f"/repos/{repo['id']}/rename/operations/{operation['id']}/verify"
|
||||
|
||||
def source_commit_ok(response):
|
||||
assert response.status_code == 200, response.text
|
||||
return [c["ok"] for c in response.json()["checks"] if c["name"] == "source_commit"][0]
|
||||
|
||||
assert source_commit_ok(await client.get(verify_url)) is True
|
||||
forge.head = "d" * 40
|
||||
forge.contains[forge.head] = {advanced}
|
||||
assert source_commit_ok(await client.get(verify_url)) is True
|
||||
forge.head = "c" * 40
|
||||
drifted = await client.get(
|
||||
f"/repos/{repo['id']}/rename/operations/{operation['id']}/verify"
|
||||
assert source_commit_ok(await client.get(verify_url)) is False
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_task_completed_during_operation_is_retired_not_missing(client, rename_setup):
|
||||
repo, _forge = rename_setup
|
||||
workplan = await create_test_workplan(
|
||||
client, repo_id=repo["id"], slug="FLEX-WP-0043", status="active"
|
||||
)
|
||||
assert any(
|
||||
not check["ok"] for check in drifted.json()["checks"] if check["name"] == "source_commit"
|
||||
task = await client.post(
|
||||
"/tasks/",
|
||||
json={"workplan_id": workplan["id"], "title": "Finish during rename", "status": "todo", "priority": "high"},
|
||||
)
|
||||
assert task.status_code == 201, task.text
|
||||
preflight = await _preflight(client, repo["id"])
|
||||
operation, confirmation = await _operation(client, repo["id"], preflight)
|
||||
await _phase(client, repo["id"], operation["id"], "forge-renamed", "preflighted", confirmation)
|
||||
await _phase(client, repo["id"], operation["id"], "statehub-rebound", "forge-renamed", confirmation)
|
||||
done = await client.patch(f"/tasks/{task.json()['id']}", json={"status": "done"})
|
||||
assert done.status_code == 200, done.text
|
||||
verify = await client.get(f"/repos/{repo['id']}/rename/operations/{operation['id']}/verify")
|
||||
assert verify.status_code == 200, verify.text
|
||||
checks = {c["name"]: c for c in verify.json()["checks"]}
|
||||
assert checks["relationship_continuity"]["ok"] is True
|
||||
assert task.json()["id"] in checks["relationship_continuity"]["actual"]["retired_dispatch"]
|
||||
assert checks["record_counts_non_decreasing"]["ok"] is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue