feat(host-agent): report assignment outcomes

This commit is contained in:
2026-07-12 19:09:07 +08:00
parent ec1e8c20d8
commit 91f08509a7
4 changed files with 166 additions and 1 deletions
@@ -0,0 +1,87 @@
from __future__ import annotations
import asyncio
from datetime import UTC, datetime
from cloud.internal_api.models import AssignmentModel, TerminalResultResponse
from host_agent.assignment import AssignmentExecutionResult
from host_agent.processor import AssignmentProcessor
def _assignment() -> AssignmentModel:
return AssignmentModel(
task_id="task-a",
attempt=1,
lease_id="lease-a",
lease_expires_at=datetime(2026, 7, 12, tzinfo=UTC),
host_id="host-a",
device_id="device-a",
goal="open settings",
)
def test_processor_reports_success_with_execution_metadata() -> None:
async def scenario() -> None:
reports: list[dict[str, object]] = []
class SuccessfulExecutor:
async def run(self, assignment):
return AssignmentExecutionResult(
status="done",
failure_reason="ignored success detail",
metadata={"runtime_task_id": "runtime-a"},
)
class RecordingClient:
async def report_result(self, assignment, **kwargs):
reports.append(kwargs)
return TerminalResultResponse(status="recorded")
result = await AssignmentProcessor(
RecordingClient(), # type: ignore[arg-type]
SuccessfulExecutor(),
).process(_assignment())
assert result.report_status == "recorded"
assert reports == [
{
"status": "done",
"failure_reason": None,
"result": {"runtime_task_id": "runtime-a"},
}
]
asyncio.run(scenario())
def test_processor_preserves_runtime_failure_reason() -> None:
async def scenario() -> None:
reports: list[dict[str, object]] = []
class FailedExecutor:
async def run(self, assignment):
return AssignmentExecutionResult(
status="failed",
failure_reason="planner unavailable",
metadata={"runtime_status": "failed"},
)
class RecordingClient:
async def report_result(self, assignment, **kwargs):
reports.append(kwargs)
return TerminalResultResponse(status="already_recorded")
result = await AssignmentProcessor(
RecordingClient(), # type: ignore[arg-type]
FailedExecutor(),
).process(_assignment())
assert result.report_status == "already_recorded"
assert result.execution.failure_reason == "planner unavailable"
assert reports[0] == {
"status": "failed",
"failure_reason": "planner unavailable",
"result": {"runtime_status": "failed"},
}
asyncio.run(scenario())