Files
wehub-resource-sync 91e75e620b
CI: cua-driver distro-compat matrix / Resolve release version (push) Waiting to run
CI: cua-driver distro-compat matrix / debian:12 (glibc 2.36) (push) Blocked by required conditions
CI: cua-driver distro-compat matrix / fedora:41 (glibc 2.40) (push) Blocked by required conditions
CI: cua-driver distro-compat matrix / rockylinux:9 (glibc 2.34) (push) Blocked by required conditions
CI: cua-driver distro-compat matrix / ubuntu:22.04 (glibc 2.35) (push) Blocked by required conditions
CI: cua-driver distro-compat matrix / ubuntu:24.04 (glibc 2.39) (push) Blocked by required conditions
CI: cua-driver distro-compat matrix / Distro compat summary (push) Blocked by required conditions
CI: Nix Linux Rust source / Nix / compositor build (push) Waiting to run
CI: Nix Linux Rust source / Nix / driver package (push) Waiting to run
CI: Nix Linux Rust source / Nix / Rust unit tests (push) Waiting to run
CI: Rust Linux unit / Rust Linux unit and compile (push) Waiting to run
CI: Rust Windows unit / Rust Windows unit and compile (push) Waiting to run
CI: SPDX Headers / Check SPDX headers (warn-only) (push) Waiting to run
CD: Docs MCP Server / build (linux/amd64) (push) Waiting to run
CD: Docs MCP Server / build (linux/arm64) (push) Waiting to run
CD: Docs MCP Server / merge (push) Blocked by required conditions
chore: import upstream snapshot with attribution
2026-07-13 13:03:19 +08:00

125 lines
4.6 KiB
Python

"""Unit tests for direct-proxy support in the computer interface.
These tests verify that passing an explicit ``api_base_url`` (plus arbitrary
``api_headers``) makes the interface derive its REST/WebSocket/Playwright URLs
from that base URL -- preserving scheme, host, port and path prefix -- and sends
the extra headers. This is what allows connecting directly to a computer-server
behind an authenticated, path-prefixed reverse proxy (e.g. cyclops-cs) without a
localhost forwarder.
Following SRP: This file tests ONLY URL building and header propagation for the
interface layer.
"""
import pytest
from computer.interface.factory import InterfaceFactory
from computer.interface.windows import WindowsComputerInterface
PROXY_BASE = "https://run.cua.ai/api/svc/ns/sb-api"
class TestApiBaseUrlBuilding:
"""api_base_url should fully drive REST/WS/Playwright URLs."""
def test_rest_uri_uses_base_url(self):
iface = WindowsComputerInterface(
"ignored-ip", api_base_url=PROXY_BASE, api_headers={"Authorization": "Bearer t"}
)
assert iface.rest_uri == f"{PROXY_BASE}/cmd"
def test_ws_uri_swaps_scheme_and_keeps_prefix(self):
iface = WindowsComputerInterface("ignored-ip", api_base_url=PROXY_BASE)
assert iface.ws_uri == "wss://run.cua.ai/api/svc/ns/sb-api/ws"
def test_trailing_slash_is_stripped(self):
iface = WindowsComputerInterface("ignored-ip", api_base_url=PROXY_BASE + "/")
assert iface.rest_uri == f"{PROXY_BASE}/cmd"
assert iface.ws_uri == "wss://run.cua.ai/api/svc/ns/sb-api/ws"
def test_http_base_maps_to_ws(self):
iface = WindowsComputerInterface("ignored-ip", api_base_url="http://localhost:9999/prefix")
assert iface.rest_uri == "http://localhost:9999/prefix/cmd"
assert iface.ws_uri == "ws://localhost:9999/prefix/ws"
def test_api_headers_stored(self):
headers = {"Authorization": "Bearer secret"}
iface = WindowsComputerInterface("ignored-ip", api_base_url=PROXY_BASE, api_headers=headers)
assert iface._api_headers == headers
def test_factory_threads_params(self):
iface = InterfaceFactory.create_interface_for_os(
os="windows",
ip_address="ignored-ip",
api_base_url=PROXY_BASE,
api_headers={"Authorization": "Bearer t"},
)
assert iface.rest_uri == f"{PROXY_BASE}/cmd"
assert iface.ws_uri == "wss://run.cua.ai/api/svc/ns/sb-api/ws"
assert iface._api_headers == {"Authorization": "Bearer t"}
class TestBackwardCompatibility:
"""Without api_base_url, behavior is unchanged (ip_address + port)."""
def test_no_base_url_local(self):
iface = WindowsComputerInterface("192.168.1.5")
assert iface.rest_uri == "http://192.168.1.5:8000/cmd"
assert iface.ws_uri == "ws://192.168.1.5:8000/ws"
assert iface._api_headers == {}
def test_no_base_url_with_api_key(self):
iface = WindowsComputerInterface("192.168.1.5", api_key="abc", vm_name="vm")
assert iface.rest_uri == "https://192.168.1.5:8443/cmd"
assert iface.ws_uri == "wss://192.168.1.5:8443/ws"
def test_no_base_url_custom_port(self):
iface = WindowsComputerInterface("192.168.1.5", api_port=1234)
assert iface.rest_uri == "http://192.168.1.5:1234/cmd"
assert iface.ws_uri == "ws://192.168.1.5:1234/ws"
class TestRestHeaderMerging:
"""api_headers must be merged into REST requests."""
@pytest.mark.asyncio
async def test_send_command_rest_sends_headers(self, monkeypatch):
captured = {}
class FakeResp:
status = 200
async def __aenter__(self):
return self
async def __aexit__(self, *a):
return False
async def text(self):
return 'data: {"success": true}'
class FakeSession:
async def __aenter__(self):
return self
async def __aexit__(self, *a):
return False
def post(self, url, json=None, headers=None):
captured["url"] = url
captured["headers"] = headers
return FakeResp()
import aiohttp
monkeypatch.setattr(aiohttp, "ClientSession", lambda *a, **k: FakeSession())
iface = WindowsComputerInterface(
"ignored-ip",
api_base_url=PROXY_BASE,
api_headers={"Authorization": "Bearer secret"},
)
result = await iface._send_command_rest("version", {})
assert result.get("success") is True
assert captured["url"] == f"{PROXY_BASE}/cmd"
assert captured["headers"]["Authorization"] == "Bearer secret"