test_recovery_workbench.py 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. from __future__ import annotations
  2. from types import SimpleNamespace
  3. from typing import Any
  4. import pytest
  5. from agent.orchestration import TaskStatus
  6. from script_build_host.application.recovery import MissionRecoveryService
  7. from script_build_host.domain.records import (
  8. BuildStatus,
  9. Principal,
  10. RecoveryClassification,
  11. )
  12. class _Repository:
  13. def __init__(self, value: Any) -> None:
  14. self.value = value
  15. async def get_by_build(self, *_args: Any, **_kwargs: Any) -> Any:
  16. return self.value
  17. async def get_status(self, _script_build_id: int) -> BuildStatus:
  18. return self.value
  19. class _Authorizer:
  20. async def require_access(self, _principal: Principal, _script_build_id: int) -> None:
  21. return None
  22. class _TaskStore:
  23. async def load(self, _root_trace_id: str) -> Any:
  24. root = SimpleNamespace(task_id="root", status=TaskStatus.RUNNING)
  25. child = SimpleNamespace(task_id="legacy-child")
  26. return SimpleNamespace(
  27. root_task_id="root",
  28. tasks={"root": root, "legacy-child": child},
  29. operations={},
  30. )
  31. class _LegacyContractReader:
  32. async def contract_for_task(self, _root_trace_id: str, _task: Any) -> Any:
  33. error = RuntimeError("compiler manifest is missing")
  34. error.code = "TASK_CONTRACT_INVALID" # type: ignore[attr-defined]
  35. raise error
  36. @pytest.mark.asyncio
  37. async def test_unfinished_legacy_contract_requires_manual_reconciliation() -> None:
  38. binding = SimpleNamespace(root_trace_id="root-trace")
  39. coordinator = SimpleNamespace(task_store=_TaskStore())
  40. service = MissionRecoveryService(
  41. coordinator=coordinator,
  42. bindings=_Repository(binding),
  43. publications=_Repository(None),
  44. legacy_state=_Repository(BuildStatus.RUNNING),
  45. authorizer=_Authorizer(),
  46. continuation=SimpleNamespace(),
  47. finalization=SimpleNamespace(),
  48. contract_reader=_LegacyContractReader(),
  49. )
  50. result = await service.resume(900049, Principal("owner"))
  51. assert result.classification is RecoveryClassification.MANUAL_RECONCILIATION
  52. assert result.status is BuildStatus.RUNNING
  53. assert "compiler manifest" in result.detail