|
|
@@ -0,0 +1,70 @@
|
|
|
+from __future__ import annotations
|
|
|
+
|
|
|
+from types import SimpleNamespace
|
|
|
+from typing import Any
|
|
|
+
|
|
|
+import pytest
|
|
|
+from agent.orchestration import TaskStatus
|
|
|
+
|
|
|
+from script_build_host.application.recovery import MissionRecoveryService
|
|
|
+from script_build_host.domain.records import (
|
|
|
+ BuildStatus,
|
|
|
+ Principal,
|
|
|
+ RecoveryClassification,
|
|
|
+)
|
|
|
+
|
|
|
+
|
|
|
+class _Repository:
|
|
|
+ def __init__(self, value: Any) -> None:
|
|
|
+ self.value = value
|
|
|
+
|
|
|
+ async def get_by_build(self, *_args: Any, **_kwargs: Any) -> Any:
|
|
|
+ return self.value
|
|
|
+
|
|
|
+ async def get_status(self, _script_build_id: int) -> BuildStatus:
|
|
|
+ return self.value
|
|
|
+
|
|
|
+
|
|
|
+class _Authorizer:
|
|
|
+ async def require_access(self, _principal: Principal, _script_build_id: int) -> None:
|
|
|
+ return None
|
|
|
+
|
|
|
+
|
|
|
+class _TaskStore:
|
|
|
+ async def load(self, _root_trace_id: str) -> Any:
|
|
|
+ root = SimpleNamespace(task_id="root", status=TaskStatus.RUNNING)
|
|
|
+ child = SimpleNamespace(task_id="legacy-child")
|
|
|
+ return SimpleNamespace(
|
|
|
+ root_task_id="root",
|
|
|
+ tasks={"root": root, "legacy-child": child},
|
|
|
+ operations={},
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+class _LegacyContractReader:
|
|
|
+ async def contract_for_task(self, _root_trace_id: str, _task: Any) -> Any:
|
|
|
+ error = RuntimeError("compiler manifest is missing")
|
|
|
+ error.code = "TASK_CONTRACT_INVALID" # type: ignore[attr-defined]
|
|
|
+ raise error
|
|
|
+
|
|
|
+
|
|
|
+@pytest.mark.asyncio
|
|
|
+async def test_unfinished_legacy_contract_requires_manual_reconciliation() -> None:
|
|
|
+ binding = SimpleNamespace(root_trace_id="root-trace")
|
|
|
+ coordinator = SimpleNamespace(task_store=_TaskStore())
|
|
|
+ service = MissionRecoveryService(
|
|
|
+ coordinator=coordinator,
|
|
|
+ bindings=_Repository(binding),
|
|
|
+ publications=_Repository(None),
|
|
|
+ legacy_state=_Repository(BuildStatus.RUNNING),
|
|
|
+ authorizer=_Authorizer(),
|
|
|
+ continuation=SimpleNamespace(),
|
|
|
+ finalization=SimpleNamespace(),
|
|
|
+ contract_reader=_LegacyContractReader(),
|
|
|
+ )
|
|
|
+
|
|
|
+ result = await service.resume(900049, Principal("owner"))
|
|
|
+
|
|
|
+ assert result.classification is RecoveryClassification.MANUAL_RECONCILIATION
|
|
|
+ assert result.status is BuildStatus.RUNNING
|
|
|
+ assert "compiler manifest" in result.detail
|