This commit is contained in:
@@ -10,12 +10,12 @@ import httpx
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from cloud.auth import BearerCredential
|
||||
from cloud.auth import digest_token
|
||||
from cloud.control_config import CloudControlConfig
|
||||
from cloud.sdk.client import CloudClient
|
||||
from cloud.scheduler import TaskConstraints
|
||||
from cloud_api.app import create_app
|
||||
from core.models import Scene
|
||||
from core.models import Scene, utc_now
|
||||
from device.manager import DeviceManager
|
||||
from driver.base import Driver
|
||||
from host_agent.assignment import AssignmentExecutionResult, AssignmentExecutor
|
||||
@@ -79,23 +79,6 @@ class FakeDriver(Driver):
|
||||
return None
|
||||
|
||||
|
||||
def _credential(host_id: str) -> BearerCredential:
|
||||
return BearerCredential(
|
||||
principal_id=f"agent-{host_id}",
|
||||
token=f"token-{host_id}",
|
||||
scopes=frozenset(),
|
||||
host_id=host_id,
|
||||
)
|
||||
|
||||
|
||||
def _public_credential() -> BearerCredential:
|
||||
return BearerCredential(
|
||||
principal_id="sdk",
|
||||
token="public-token",
|
||||
scopes=frozenset({"tasks:submit", "tasks:read"}),
|
||||
)
|
||||
|
||||
|
||||
def _config(host_id: str) -> HostAgentConfig:
|
||||
return HostAgentConfig(
|
||||
control_plane_url="http://control.test",
|
||||
@@ -120,10 +103,18 @@ async def _control_plane(
|
||||
scheduler_interval_seconds=60,
|
||||
lease_reaper_interval_seconds=60,
|
||||
lease_duration_seconds=lease_duration_seconds,
|
||||
credentials=tuple(_credential(host_id) for host_id in host_ids),
|
||||
)
|
||||
)
|
||||
async with app.router.lifespan_context(app):
|
||||
for host_id in host_ids:
|
||||
app.state.cloud_services.repository.enroll_host(
|
||||
host_id=host_id,
|
||||
agent_instance_id=f"agent-{host_id}",
|
||||
credential_digest=digest_token(f"token-{host_id}"),
|
||||
enrollment_token_digest=None,
|
||||
display_name=host_id,
|
||||
enrolled_at=utc_now(),
|
||||
)
|
||||
yield app
|
||||
|
||||
|
||||
@@ -162,12 +153,22 @@ class _RecordingTransport(httpx.AsyncBaseTransport):
|
||||
|
||||
|
||||
async def _sync_fake_device(
|
||||
app,
|
||||
client: HostAgentClient,
|
||||
host_id: str,
|
||||
device_id: str,
|
||||
*,
|
||||
driver_type: str = "wda",
|
||||
) -> FakeDriver:
|
||||
app.state.cloud_services.repository.enroll_device(
|
||||
device_id=device_id,
|
||||
host_id=host_id,
|
||||
local_device_id=device_id,
|
||||
driver_type=driver_type,
|
||||
name=device_id,
|
||||
capability_tags=[],
|
||||
enrolled_at=utc_now(),
|
||||
)
|
||||
driver = FakeDriver()
|
||||
manager = DeviceManager()
|
||||
manager.register_device(
|
||||
@@ -248,7 +249,7 @@ def test_one_host_executes_assignment_through_outbound_protocol(tmp_path) -> Non
|
||||
paths: list[str] = []
|
||||
async with _control_plane(tmp_path / "one-host.sqlite3", "host-a") as app:
|
||||
async with _host_client(app, "host-a", request_paths=paths) as client:
|
||||
await _sync_fake_device(client, "host-a", "device-a")
|
||||
await _sync_fake_device(app, client, "host-a", "device-a")
|
||||
task_id = app.state.cloud_services.scheduler.submit(
|
||||
goal="open settings"
|
||||
)
|
||||
@@ -275,7 +276,7 @@ def test_nat_style_host_requires_only_outbound_requests(tmp_path) -> None:
|
||||
paths: list[str] = []
|
||||
async with _control_plane(tmp_path / "outbound-only.sqlite3", "host-a") as app:
|
||||
async with _host_client(app, "host-a", request_paths=paths) as client:
|
||||
await _sync_fake_device(client, "host-a", "device-a")
|
||||
await _sync_fake_device(app, client, "host-a", "device-a")
|
||||
assert await client.claim() is None
|
||||
|
||||
assert paths == [
|
||||
@@ -297,8 +298,9 @@ def test_multiple_hosts_claim_only_their_matching_devices(tmp_path) -> None:
|
||||
_host_client(app, "host-a") as client_a,
|
||||
_host_client(app, "host-b") as client_b,
|
||||
):
|
||||
await _sync_fake_device(client_a, "host-a", "device-a")
|
||||
await _sync_fake_device(app, client_a, "host-a", "device-a")
|
||||
await _sync_fake_device(
|
||||
app,
|
||||
client_b,
|
||||
"host-b",
|
||||
"device-b",
|
||||
@@ -331,7 +333,7 @@ def test_control_plane_restart_preserves_dispatched_assignment(tmp_path) -> None
|
||||
assignment = None
|
||||
async with _control_plane(database_path, "host-a") as first_app:
|
||||
async with _host_client(first_app, "host-a") as client:
|
||||
await _sync_fake_device(client, "host-a", "device-a")
|
||||
await _sync_fake_device(first_app, client, "host-a", "device-a")
|
||||
task_id = first_app.state.cloud_services.scheduler.submit(goal="resume")
|
||||
first_app.state.cloud_services.scheduler.assign()
|
||||
assignment = await client.claim()
|
||||
@@ -351,7 +353,7 @@ def test_host_agent_restart_reuses_active_lease(tmp_path) -> None:
|
||||
async def scenario() -> None:
|
||||
async with _control_plane(tmp_path / "agent-restart.sqlite3", "host-a") as app:
|
||||
async with _host_client(app, "host-a") as first_client:
|
||||
await _sync_fake_device(first_client, "host-a", "device-a")
|
||||
await _sync_fake_device(app, first_client, "host-a", "device-a")
|
||||
task_id = app.state.cloud_services.scheduler.submit(goal="resume host")
|
||||
app.state.cloud_services.scheduler.assign()
|
||||
assignment = await first_client.claim()
|
||||
@@ -376,7 +378,7 @@ def test_lease_loss_rejects_stale_host_result(tmp_path) -> None:
|
||||
lease_duration_seconds=0.2,
|
||||
) as app:
|
||||
async with _host_client(app, "host-a") as client:
|
||||
await _sync_fake_device(client, "host-a", "device-a")
|
||||
await _sync_fake_device(app, client, "host-a", "device-a")
|
||||
task_id = app.state.cloud_services.scheduler.submit(goal="expire")
|
||||
app.state.cloud_services.scheduler.assign()
|
||||
assignment = await client.claim()
|
||||
@@ -405,7 +407,6 @@ def test_public_sdk_reports_fake_device_success_and_runtime_failure(tmp_path) ->
|
||||
scheduler_interval_seconds=60,
|
||||
lease_reaper_interval_seconds=60,
|
||||
lease_duration_seconds=30,
|
||||
credentials=(_public_credential(), _credential("host-a")),
|
||||
)
|
||||
)
|
||||
driver = FakeDriver()
|
||||
@@ -413,10 +414,38 @@ def test_public_sdk_reports_fake_device_success_and_runtime_failure(tmp_path) ->
|
||||
manager.register_device("device-a", lambda: driver, status="idle")
|
||||
|
||||
with TestClient(app) as http_client:
|
||||
app.state.cloud_services.user_auth_service.create_user(
|
||||
username="operator",
|
||||
display_name="Operator",
|
||||
role="admin",
|
||||
password="correct-horse-battery-staple",
|
||||
must_change_password=False,
|
||||
)
|
||||
login = http_client.post(
|
||||
"/v1/auth/login",
|
||||
json={"username": "operator", "password": "correct-horse-battery-staple"},
|
||||
)
|
||||
assert login.status_code == 200
|
||||
app.state.cloud_services.repository.enroll_host(
|
||||
host_id="host-a",
|
||||
agent_instance_id="agent-host-a",
|
||||
credential_digest=digest_token("token-host-a"),
|
||||
enrollment_token_digest=None,
|
||||
display_name="host-a",
|
||||
enrolled_at=utc_now(),
|
||||
)
|
||||
app.state.cloud_services.repository.enroll_device(
|
||||
device_id="device-a",
|
||||
host_id="host-a",
|
||||
local_device_id="device-a",
|
||||
driver_type="wda",
|
||||
name="device-a",
|
||||
capability_tags=[],
|
||||
enrolled_at=utc_now(),
|
||||
)
|
||||
cloud_client = CloudClient(
|
||||
"http://testserver",
|
||||
http_client=http_client,
|
||||
token="public-token",
|
||||
)
|
||||
|
||||
async def scenario() -> None:
|
||||
@@ -428,9 +457,7 @@ def test_public_sdk_reports_fake_device_success_and_runtime_failure(tmp_path) ->
|
||||
heartbeat.connect_devices()
|
||||
await heartbeat.sync_once()
|
||||
|
||||
successful_task_id = cloud_client.submit_task(goal="tap screen")[
|
||||
"task_id"
|
||||
]
|
||||
successful_task_id = cloud_client.submit_task(goal="tap screen")["task_id"]
|
||||
app.state.cloud_services.scheduler.assign()
|
||||
successful_assignment = await host_client.claim()
|
||||
assert successful_assignment is not None
|
||||
|
||||
Reference in New Issue
Block a user