Files
aiturk-hermes-ide/tests/gateway/test_api_server_runs_extraction.py
T

183 lines
6.4 KiB
Python

"""Compatibility seams for the extracted ``/v1/runs`` lifecycle."""
import sys
from types import SimpleNamespace
from unittest.mock import AsyncMock, MagicMock
import pytest
from gateway.platforms import api_server
from gateway.platforms import api_server_room_grants
from gateway.platforms import api_server_runs
_HTTP_HANDLER_DELEGATES = (
("_handle_get_run", "_handle_get_run"),
("_handle_run_events", "_handle_run_events"),
("_handle_run_approval", "_handle_run_approval"),
("_handle_steer_run", "_handle_steer_run"),
("_handle_stop_run", "_handle_stop_run"),
)
_RUN_METHODS = {
"_set_run_status",
"_make_run_event_callback",
"_run_idempotency_scope",
"_check_run_auth",
"_durable_run_status",
"_handle_runs",
"_request_owns_run",
*(adapter_name for adapter_name, _ in _HTTP_HANDLER_DELEGATES),
"_sweep_orphaned_runs",
"_sweep_orphaned_runs_once",
}
def test_api_server_keeps_run_methods_on_the_adapter_class():
assert _RUN_METHODS <= api_server.APIServerAdapter.__dict__.keys()
@pytest.mark.asyncio
@pytest.mark.parametrize(("adapter_name", "implementation_name"), _HTTP_HANDLER_DELEGATES)
async def test_run_http_handlers_delegate_without_changing_method_surface(
monkeypatch, adapter_name, implementation_name
):
adapter = api_server.APIServerAdapter.__new__(api_server.APIServerAdapter)
request = object()
expected = object()
implementation = AsyncMock(return_value=expected)
monkeypatch.setattr(api_server_runs, implementation_name, implementation)
assert await getattr(adapter, adapter_name)(request) is expected
implementation.assert_awaited_once_with(
adapter,
request,
_api_server=sys.modules[api_server.__name__],
)
@pytest.mark.asyncio
async def test_decorated_run_admission_delegates_and_releases_slot(monkeypatch):
adapter = api_server.APIServerAdapter.__new__(api_server.APIServerAdapter)
adapter._room_grant_token = MagicMock(return_value="room-grant")
adapter._check_run_auth = MagicMock(return_value=None)
adapter._draining_response = MagicMock(return_value=None)
adapter._pending_agent_requests = 0
request = SimpleNamespace(path="/v1/runs")
expected = object()
implementation = AsyncMock(return_value=expected)
monkeypatch.setattr(api_server_runs, "_handle_runs", implementation)
assert await adapter._handle_runs(request) is expected
implementation.assert_awaited_once_with(
adapter,
request,
_api_server=sys.modules[api_server.__name__],
)
adapter._check_run_auth.assert_called_once_with(request, permission="dispatch")
assert adapter._pending_agent_requests == 0
def test_run_status_method_delegates(monkeypatch):
adapter = api_server.APIServerAdapter.__new__(api_server.APIServerAdapter)
expected = {"run_id": "run-1", "status": "running"}
implementation = MagicMock(return_value=expected)
monkeypatch.setattr(api_server_runs, "_set_run_status", implementation)
assert adapter._set_run_status("run-1", "running", last_event="run.started") is expected
implementation.assert_called_once_with(
adapter,
"run-1",
"running",
last_event="run.started",
)
def test_room_scoped_run_policy_delegates_with_legacy_api_bindings(monkeypatch):
adapter = api_server.APIServerAdapter.__new__(api_server.APIServerAdapter)
request = object()
scope = MagicMock(return_value="scope-hash")
auth = MagicMock(return_value=None)
monkeypatch.setattr(api_server_runs, "_run_idempotency_scope", scope)
monkeypatch.setattr(api_server_runs, "_check_run_auth", auth)
assert adapter._run_idempotency_scope(request) == "scope-hash"
assert adapter._check_run_auth(request, permission="stop") is None
scope.assert_called_once_with(
adapter,
request,
_api_server=sys.modules[api_server.__name__],
)
auth.assert_called_once_with(
adapter,
request,
permission="stop",
_api_server=sys.modules[api_server.__name__],
)
def test_run_state_initialization_and_teardown_are_shard_owned():
adapter = api_server.APIServerAdapter.__new__(api_server.APIServerAdapter)
store = MagicMock()
store_factory = MagicMock(return_value=store)
api_server_runs._initialize_run_state(
adapter,
store_factory=store_factory,
)
store_factory.assert_called_once_with()
assert adapter._run_idempotency_store is store
assert adapter._run_idempotency_ids == set()
assert adapter._run_owners == {}
assert adapter._run_streams == {}
assert adapter._run_streams_created == {}
assert adapter._run_stream_subscribers == set()
assert adapter._active_run_agents == {}
assert adapter._active_run_tasks == {}
assert adapter._stopping_run_ids == set()
assert adapter._run_statuses == {}
assert adapter._run_approval_sessions == {}
api_server_runs._close_run_state(adapter)
store.close.assert_called_once_with()
def test_run_capability_metadata_is_shard_owned():
adapter = api_server.APIServerAdapter.__new__(api_server.APIServerAdapter)
adapter._run_idempotency_store = SimpleNamespace(durable=True)
store_type = SimpleNamespace(RETENTION_SECONDS=123)
assert api_server_runs._idempotency_capabilities(
adapter,
store_type=store_type,
) == {
"supported": True,
"durable": True,
"retention_seconds": 123,
}
def test_roomlink_and_run_route_tuples_are_shard_owned():
adapter = api_server.APIServerAdapter.__new__(api_server.APIServerAdapter)
room_routes = api_server_room_grants._http_routes(adapter)
run_routes = api_server_runs._http_routes(adapter)
assert [(method, path) for method, path, _ in room_routes] == [
("POST", "/v1/room-members/invitations"),
("GET", "/v1/room-members/capabilities"),
("POST", "/v1/room-members/grants/refresh"),
("POST", "/v1/room-members/grants/revoke"),
]
assert [(method, path) for method, path, _ in run_routes] == [
("POST", "/v1/runs"),
("GET", "/v1/runs/{run_id}"),
("GET", "/v1/runs/{run_id}/events"),
("POST", "/v1/runs/{run_id}/approval"),
("POST", "/v1/runs/{run_id}/steer"),
("POST", "/v1/runs/{run_id}/stop"),
]
assert all(handler.__self__ is adapter for _, _, handler in room_routes)
assert all(handler.__self__ is adapter for _, _, handler in run_routes)