Files
agentic-mobile-control/tests/test_device_pool.py
T
2026-07-06 23:44:18 +08:00

143 lines
4.5 KiB
Python

"""Unit tests for cloud.pool.DevicePool (task 3.4)."""
from __future__ import annotations
from datetime import UTC, datetime, timedelta
from cloud.config import CloudConfig
from cloud.pool import DevicePool
from cloud.store import CloudStore
from core.models import Device
def _config(**overrides) -> CloudConfig:
base = {
"sync_interval_seconds": 30,
"stale_after_seconds": 60,
"max_queue_depth": 100,
"default_assignment_strategy": "fifo_match",
"api_version_prefix": "/v1",
"db_path": "cloud/cloud.sqlite3",
}
base.update(overrides)
return CloudConfig(**base)
def _device(device_id: str, *, status: str = "idle", driver_type: str = "wda") -> Device:
return Device(id=device_id, status=status, driver_type=driver_type) # type: ignore[arg-type]
def test_new_host_sync_creates_registration_and_devices(tmp_path) -> None:
pool = DevicePool(
CloudStore(tmp_path / "cloud.sqlite3"),
_config(),
)
pool.sync_host_devices(
"host-a",
[_device("dev-1"), _device("dev-2", status="busy")],
address="10.0.0.1:8000",
)
hosts = pool.list_hosts()
assert [h.host_id for h in hosts] == ["host-a"]
assert hosts[0].address == "10.0.0.1:8000"
devices = pool.list_devices()
assert {d.device_id for d in devices} == {"dev-1", "dev-2"}
by_id = {d.device_id: d for d in devices}
assert by_id["dev-1"].status == "idle"
assert by_id["dev-2"].status == "busy"
assert all(d.host_id == "host-a" for d in devices)
def test_resync_updates_last_seen_and_replaces_devices(tmp_path) -> None:
pool = DevicePool(CloudStore(tmp_path / "cloud.sqlite3"), _config())
pool.sync_host_devices("host-a", [_device("dev-1"), _device("dev-2")])
first_hosts = pool.list_hosts()
first_seen = first_hosts[0].last_seen_at
# Force time forward by directly mutating the stored timestamp.
pool.store.upsert_host(
"host-a",
address=None,
last_seen_at=datetime.now(UTC) - timedelta(seconds=10),
)
pool.sync_host_devices("host-a", [_device("dev-3")])
hosts = pool.list_hosts()
devices = pool.list_devices()
assert [h.host_id for h in hosts] == ["host-a"]
assert {d.device_id for d in devices} == {"dev-3"}
assert hosts[0].last_seen_at > first_seen
def test_stale_host_devices_reported_unreachable(tmp_path) -> None:
pool = DevicePool(
CloudStore(tmp_path / "cloud.sqlite3"),
_config(stale_after_seconds=60),
)
pool.sync_host_devices("host-a", [_device("dev-1", status="idle")])
# Push the host's last_seen_at beyond the staleness threshold.
pool.store.upsert_host(
"host-a",
address=None,
last_seen_at=datetime.now(UTC) - timedelta(seconds=120),
)
devices = pool.list_devices()
assert len(devices) == 1
assert devices[0].status == "unreachable"
fetched = pool.get_device("dev-1")
assert fetched is not None
assert fetched.status == "unreachable"
def test_resync_after_stale_clears_unreachable(tmp_path) -> None:
pool = DevicePool(
CloudStore(tmp_path / "cloud.sqlite3"),
_config(stale_after_seconds=60),
)
pool.sync_host_devices("host-a", [_device("dev-1", status="idle")])
pool.store.upsert_host(
"host-a",
address=None,
last_seen_at=datetime.now(UTC) - timedelta(seconds=120),
)
# Stale right now.
assert pool.list_devices()[0].status == "unreachable"
# Host resyncs with a fresh snapshot.
pool.sync_host_devices("host-a", [_device("dev-1", status="idle")])
devices = pool.list_devices()
assert devices[0].status == "idle"
def test_unknown_device_returns_none(tmp_path) -> None:
pool = DevicePool(CloudStore(tmp_path / "cloud.sqlite3"), _config())
assert pool.get_device("does-not-exist") is None
def test_empty_pool_returns_empty_list(tmp_path) -> None:
pool = DevicePool(CloudStore(tmp_path / "cloud.sqlite3"), _config())
assert pool.list_devices() == []
assert pool.list_hosts() == []
def test_two_hosts_aggregate_into_one_listing(tmp_path) -> None:
pool = DevicePool(CloudStore(tmp_path / "cloud.sqlite3"), _config())
pool.sync_host_devices("host-a", [_device("a-dev-1")], address="a")
pool.sync_host_devices("host-b", [_device("b-dev-1"), _device("b-dev-2")], address="b")
devices = pool.list_devices()
assert {d.device_id: d.host_id for d in devices} == {
"a-dev-1": "host-a",
"b-dev-1": "host-b",
"b-dev-2": "host-b",
}