# ruff: noqa: S101 from __future__ import annotations from uuid import uuid4 import pytest from fastapi import HTTPException import app.services.openclaw.admin_service as admin_service import app.services.openclaw.gateway_compat as gateway_compat import app.services.openclaw.session_service as session_service from app.schemas.gateway_api import GatewayResolveQuery from app.services.openclaw.admin_service import GatewayAdminLifecycleService from app.services.openclaw.gateway_compat import GatewayVersionCheckResult from app.services.openclaw.gateway_rpc import GatewayConfig, OpenClawGatewayError from app.services.openclaw.session_service import GatewaySessionService def test_extract_gateway_version_prefers_primary_path() -> None: payload = { "gateway": {"version": "2026.2.1"}, "protocolVersion": 3, "meta": {"version": "2026.1.30"}, } assert gateway_compat.extract_gateway_version(payload) == "2026.2.1" def test_evaluate_gateway_version_detects_old_runtime() -> None: result = gateway_compat.evaluate_gateway_version( current_version="2025.12.1", minimum_version="2026.1.30", ) assert result.compatible is False assert result.minimum_version == "2026.1.30" assert "Minimum supported version is 2026.1.30" in (result.message or "") @pytest.mark.asyncio async def test_check_gateway_runtime_compatibility_prefers_schema_version( monkeypatch: pytest.MonkeyPatch, ) -> None: calls: list[str] = [] async def _fake_openclaw_call(method: str, params: object = None, *, config: object) -> object: _ = (params, config) calls.append(method) if method == "config.schema": return {"version": "2026.2.13"} raise AssertionError(f"unexpected method: {method}") monkeypatch.setattr(gateway_compat, "openclaw_call", _fake_openclaw_call) result = await gateway_compat.check_gateway_runtime_compatibility( GatewayConfig(url="ws://gateway.example/ws"), minimum_version="2026.1.30", ) assert calls == ["config.schema"] assert result.compatible is True assert result.current_version == "2026.2.13" @pytest.mark.asyncio async def test_check_gateway_runtime_compatibility_falls_back_to_health( monkeypatch: pytest.MonkeyPatch, ) -> None: calls: list[str] = [] async def _fake_openclaw_call(method: str, params: object = None, *, config: object) -> object: _ = (params, config) calls.append(method) if method == "config.schema": raise OpenClawGatewayError("unknown method") if method == "status": raise OpenClawGatewayError("unknown method") return {"version": "2026.2.0"} monkeypatch.setattr(gateway_compat, "openclaw_call", _fake_openclaw_call) result = await gateway_compat.check_gateway_runtime_compatibility( GatewayConfig(url="ws://gateway.example/ws"), minimum_version="2026.1.30", ) assert calls == ["config.schema", "status", "health"] assert result.compatible is True assert result.current_version == "2026.2.0" @pytest.mark.asyncio async def test_check_gateway_runtime_compatibility_uses_health_when_status_has_no_version( monkeypatch: pytest.MonkeyPatch, ) -> None: calls: list[str] = [] async def _fake_openclaw_call(method: str, params: object = None, *, config: object) -> object: _ = (params, config) calls.append(method) if method == "config.schema": return {"schema": {"title": "Gateway schema"}} if method == "status": return {"uptime": 1234} return {"version": "2026.2.0"} monkeypatch.setattr(gateway_compat, "openclaw_call", _fake_openclaw_call) result = await gateway_compat.check_gateway_runtime_compatibility( GatewayConfig(url="ws://gateway.example/ws"), minimum_version="2026.1.30", ) assert calls == ["config.schema", "status", "health"] assert result.compatible is True assert result.current_version == "2026.2.0" @pytest.mark.asyncio async def test_admin_service_rejects_incompatible_gateway( monkeypatch: pytest.MonkeyPatch, ) -> None: async def _fake_check(config: GatewayConfig, *, minimum_version: str | None = None) -> object: _ = (config, minimum_version) return GatewayVersionCheckResult( compatible=False, minimum_version="2026.1.30", current_version="2026.1.0", message="Gateway version 2026.1.0 is not supported.", ) monkeypatch.setattr(admin_service, "check_gateway_runtime_compatibility", _fake_check) service = GatewayAdminLifecycleService(session=object()) # type: ignore[arg-type] with pytest.raises(HTTPException) as exc_info: await service.assert_gateway_runtime_compatible(url="ws://gateway.example/ws", token=None) assert exc_info.value.status_code == 422 assert "not supported" in str(exc_info.value.detail) @pytest.mark.asyncio async def test_admin_service_maps_gateway_transport_errors( monkeypatch: pytest.MonkeyPatch, ) -> None: async def _fake_check(config: GatewayConfig, *, minimum_version: str | None = None) -> object: _ = (config, minimum_version) raise OpenClawGatewayError("connection refused") monkeypatch.setattr(admin_service, "check_gateway_runtime_compatibility", _fake_check) service = GatewayAdminLifecycleService(session=object()) # type: ignore[arg-type] with pytest.raises(HTTPException) as exc_info: await service.assert_gateway_runtime_compatible(url="ws://gateway.example/ws", token=None) assert exc_info.value.status_code == 502 assert "compatibility check failed" in str(exc_info.value.detail).lower() @pytest.mark.asyncio async def test_gateway_status_reports_incompatible_version( monkeypatch: pytest.MonkeyPatch, ) -> None: async def _fake_check(config: GatewayConfig, *, minimum_version: str | None = None) -> object: _ = (config, minimum_version) return GatewayVersionCheckResult( compatible=False, minimum_version="2026.1.30", current_version="2026.1.0", message="Gateway version 2026.1.0 is not supported.", ) monkeypatch.setattr(session_service, "check_gateway_runtime_compatibility", _fake_check) service = GatewaySessionService(session=object()) # type: ignore[arg-type] response = await service.get_status( params=GatewayResolveQuery(gateway_url="ws://gateway.example/ws"), organization_id=uuid4(), user=None, ) assert response.connected is False assert response.error == "Gateway version 2026.1.0 is not supported." @pytest.mark.asyncio async def test_gateway_status_returns_sessions_when_version_compatible( monkeypatch: pytest.MonkeyPatch, ) -> None: async def _fake_check(config: GatewayConfig, *, minimum_version: str | None = None) -> object: _ = (config, minimum_version) return GatewayVersionCheckResult( compatible=True, minimum_version="2026.1.30", current_version="2026.2.0", message=None, ) async def _fake_openclaw_call(method: str, params: object = None, *, config: object) -> object: _ = (params, config) assert method == "sessions.list" return {"sessions": [{"key": "agent:main"}]} monkeypatch.setattr(session_service, "check_gateway_runtime_compatibility", _fake_check) monkeypatch.setattr(session_service, "openclaw_call", _fake_openclaw_call) service = GatewaySessionService(session=object()) # type: ignore[arg-type] response = await service.get_status( params=GatewayResolveQuery(gateway_url="ws://gateway.example/ws"), organization_id=uuid4(), user=None, ) assert response.connected is True assert response.sessions_count == 1