chore: import upstream snapshot with attribution
CI: cua-driver distro-compat matrix / debian:12 (glibc 2.36) (push) Has been cancelled
CI: SPDX Headers / Check SPDX headers (warn-only) (push) Has been cancelled
CD: Docs MCP Server / build (linux/amd64) (push) Has been cancelled
CD: Docs MCP Server / build (linux/arm64) (push) Has been cancelled
CD: Docs MCP Server / merge (push) Has been cancelled
CI: cua-driver distro-compat matrix / Resolve release version (push) Has been cancelled
CI: cua-driver distro-compat matrix / fedora:41 (glibc 2.40) (push) Has been cancelled
CI: cua-driver distro-compat matrix / rockylinux:9 (glibc 2.34) (push) Has been cancelled
CI: cua-driver distro-compat matrix / ubuntu:22.04 (glibc 2.35) (push) Has been cancelled
CI: cua-driver distro-compat matrix / ubuntu:24.04 (glibc 2.39) (push) Has been cancelled
CI: cua-driver distro-compat matrix / Distro compat summary (push) Has been cancelled
CI: Rust Linux unit / Rust Linux unit and compile (push) Has been cancelled
CI: Rust Windows unit / Rust Windows unit and compile (push) Has been cancelled
CI: Nix Linux Rust source / Nix / compositor build (push) Has been cancelled
CI: Nix Linux Rust source / Nix / driver package (push) Has been cancelled
CI: Nix Linux Rust source / Nix / Rust unit tests (push) Has been cancelled
CI: cua-driver distro-compat matrix / debian:12 (glibc 2.36) (push) Has been cancelled
CI: SPDX Headers / Check SPDX headers (warn-only) (push) Has been cancelled
CD: Docs MCP Server / build (linux/amd64) (push) Has been cancelled
CD: Docs MCP Server / build (linux/arm64) (push) Has been cancelled
CD: Docs MCP Server / merge (push) Has been cancelled
CI: cua-driver distro-compat matrix / Resolve release version (push) Has been cancelled
CI: cua-driver distro-compat matrix / fedora:41 (glibc 2.40) (push) Has been cancelled
CI: cua-driver distro-compat matrix / rockylinux:9 (glibc 2.34) (push) Has been cancelled
CI: cua-driver distro-compat matrix / ubuntu:22.04 (glibc 2.35) (push) Has been cancelled
CI: cua-driver distro-compat matrix / ubuntu:24.04 (glibc 2.39) (push) Has been cancelled
CI: cua-driver distro-compat matrix / Distro compat summary (push) Has been cancelled
CI: Rust Linux unit / Rust Linux unit and compile (push) Has been cancelled
CI: Rust Windows unit / Rust Windows unit and compile (push) Has been cancelled
CI: Nix Linux Rust source / Nix / compositor build (push) Has been cancelled
CI: Nix Linux Rust source / Nix / driver package (push) Has been cancelled
CI: Nix Linux Rust source / Nix / Rust unit tests (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
"""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"
|
||||
Reference in New Issue
Block a user