feat(cloud): surface cancellation over the internal Host<->Cloud protocol
- LeaseRenewalResponse gains cancel_requested (populated from the repository's renew_lease result) - TerminalResultRequest.status widened to accept "cancelled" - Add internal API tests for a renewal surfacing cancel_requested=True and a cancelled terminal report being accepted/idempotent
This commit is contained in:
@@ -473,10 +473,32 @@ def test_lease_renewal_extends_active_assignment(tmp_path) -> None:
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["status"] == "renewed"
|
||||
assert response.json()["cancel_requested"] is False
|
||||
renewed_expiry = datetime.fromisoformat(response.json()["lease_expires_at"])
|
||||
assert renewed_expiry > original_time + timedelta(seconds=30)
|
||||
|
||||
|
||||
def test_lease_renewal_surfaces_pending_cancellation(tmp_path) -> None:
|
||||
client, pool = _build_client(tmp_path)
|
||||
_seed_active_assignment(pool)
|
||||
pool.store.request_task_cancellation("active-task", requested_at=datetime.now(UTC))
|
||||
|
||||
response = client.post(
|
||||
"/internal/v1/hosts/host-a/assignments/active-task/renew",
|
||||
headers={"Authorization": "Bearer token-a"},
|
||||
json={
|
||||
"host_id": "host-a",
|
||||
"task_id": "active-task",
|
||||
"attempt": 1,
|
||||
"lease_id": "active-lease",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["status"] == "renewed"
|
||||
assert response.json()["cancel_requested"] is True
|
||||
|
||||
|
||||
def test_stale_renewal_returns_typed_conflict(tmp_path) -> None:
|
||||
client, pool = _build_client(tmp_path)
|
||||
_seed_active_assignment(pool)
|
||||
@@ -526,6 +548,37 @@ def test_terminal_result_is_idempotent_through_internal_api(tmp_path) -> None:
|
||||
assert pool.store.get_task("active-task").status == "done" # type: ignore[union-attr]
|
||||
|
||||
|
||||
def test_cancelled_terminal_result_is_accepted_and_idempotent(tmp_path) -> None:
|
||||
client, pool = _build_client(tmp_path)
|
||||
_seed_active_assignment(pool)
|
||||
pool.store.request_task_cancellation("active-task", requested_at=datetime.now(UTC))
|
||||
payload = {
|
||||
"host_id": "host-a",
|
||||
"task_id": "active-task",
|
||||
"attempt": 1,
|
||||
"lease_id": "active-lease",
|
||||
"status": "cancelled",
|
||||
"failure_reason": "cancellation requested by control plane",
|
||||
}
|
||||
|
||||
first = client.post(
|
||||
"/internal/v1/hosts/host-a/assignments/active-task/result",
|
||||
headers={"Authorization": "Bearer token-a"},
|
||||
json=payload,
|
||||
)
|
||||
repeated = client.post(
|
||||
"/internal/v1/hosts/host-a/assignments/active-task/result",
|
||||
headers={"Authorization": "Bearer token-a"},
|
||||
json=payload,
|
||||
)
|
||||
|
||||
assert first.status_code == 200
|
||||
assert first.json()["status"] == "recorded"
|
||||
assert repeated.status_code == 200
|
||||
assert repeated.json()["status"] == "already_recorded"
|
||||
assert pool.store.get_task("active-task").status == "cancelled" # type: ignore[union-attr]
|
||||
|
||||
|
||||
def test_conflicting_repeated_result_returns_stale_lease_conflict(tmp_path) -> None:
|
||||
client, pool = _build_client(tmp_path)
|
||||
_seed_active_assignment(pool)
|
||||
|
||||
Reference in New Issue
Block a user