feat(host-agent): make execution history authoritative
Tests / Test failed: 2, passed: 830

This commit is contained in:
2026-07-15 11:46:27 +08:00
parent ccde30e378
commit 77d4813bb2
46 changed files with 890 additions and 3132 deletions
@@ -16,8 +16,6 @@ from host_agent.dependency_supervisor import (
_SupervisorKnobs,
appium_argv_factory,
probe_appium,
probe_runtime,
runtime_argv_factory,
)
from host_agent.config import HostAgentConfig
@@ -175,7 +173,6 @@ def test_probe_returns_no_listener_when_port_is_closed(monkeypatch) -> None:
_raise_connection_refused,
)
assert probe_appium("127.0.0.1", 4723) is ProbeResult.NO_LISTENER
assert probe_runtime("127.0.0.1", 8000) is ProbeResult.NO_LISTENER
def test_probe_returns_healthy_on_appium_status_endpoint(monkeypatch) -> None:
@@ -190,18 +187,6 @@ def test_probe_returns_healthy_on_appium_status_endpoint(monkeypatch) -> None:
assert probe_appium("127.0.0.1", 4723) is ProbeResult.HEALTHY
def test_probe_returns_healthy_on_runtime_devices_endpoint(monkeypatch) -> None:
monkeypatch.setattr(
"host_agent.dependency_supervisor.socket.create_connection",
_ok_connection,
)
monkeypatch.setattr(
"host_agent.dependency_supervisor.httpx.get",
lambda url, timeout=2.0: httpx.Response(200, json=[]),
)
assert probe_runtime("127.0.0.1", 8000) is ProbeResult.HEALTHY
def test_probe_returns_unhealthy_when_listener_returns_non_200(monkeypatch) -> None:
monkeypatch.setattr(
"host_agent.dependency_supervisor.socket.create_connection",
@@ -223,7 +208,7 @@ def test_probe_returns_unhealthy_when_listener_returns_non_json(monkeypatch) ->
"host_agent.dependency_supervisor.httpx.get",
lambda url, timeout=2.0: httpx.Response(200, text="not json"),
)
assert probe_runtime("127.0.0.1", 8000) is ProbeResult.UNHEALTHY_LISTENER
assert probe_appium("127.0.0.1", 4723) is ProbeResult.UNHEALTHY_LISTENER
def test_probe_returns_unhealthy_on_http_transport_error(monkeypatch) -> None:
@@ -335,38 +320,6 @@ def test_spawn_uses_appium_argv_factory() -> None:
asyncio.run(scenario())
def test_spawn_uses_runtime_argv_factory() -> None:
async def scenario() -> None:
captured: list[list[str]] = []
def recording_popen(argv, **kwargs):
captured.append(list(argv))
return _FakePopen(argv)
dep, _ = _dep(
name="runtime",
port=8000,
probe_responses=(ProbeResult.NO_LISTENER, ProbeResult.HEALTHY),
argv_factory=runtime_argv_factory,
)
sup, _ = _build_supervisor([dep], popen_factory=recording_popen)
await sup.start()
assert captured == [
[
"uvicorn",
"api.rest:create_app",
"--factory",
"--host",
"127.0.0.1",
"--port",
"8000",
]
]
asyncio.run(scenario())
def test_readiness_timeout_leaves_process_running_without_restart_loop() -> None:
async def scenario() -> None:
# NO_LISTENER for initial probe; probe never becomes HEALTHY → startup
@@ -529,7 +482,7 @@ def test_from_host_agent_config_builds_empty_supervisor_when_no_dep_selected() -
assert sup.dependencies == []
def test_from_host_agent_config_includes_appium_and_runtime_when_selected() -> None:
def test_from_host_agent_config_includes_appium_when_selected() -> None:
config = HostAgentConfig(
control_plane_url="https://control.example",
host_id="host-a",
@@ -538,17 +491,12 @@ def test_from_host_agent_config_includes_appium_and_runtime_when_selected() -> N
appium_supervised=True,
appium_host="10.0.0.5",
appium_port=4724,
runtime_supervised=True,
runtime_host="10.0.0.5",
runtime_port=8001,
dependency_restart_max_attempts=7,
)
sup = DependencySupervisor.from_host_agent_config(config)
names = [dep.name for dep in sup.dependencies]
assert names == ["appium", "runtime"]
assert names == ["appium"]
appium = sup.dependencies[0]
assert appium.host == "10.0.0.5"
assert appium.port == 4724
runtime = sup.dependencies[1]
assert runtime.port == 8001
assert sup._max_attempts == 7