Files
Aether/tests/unit/test_gateway_tunnel_transport.py
fawney19 8f26e1a31f refactor: 移除独立 hub/proxy/executor/gateway crate,统一为 gateway tunnel 架构
- 删除 aether-hub、aether-proxy 独立项目及其 Dockerfile/配置
- 删除 crates/aether-executor 和 crates/aether-gateway 全部模块
- 新增 apps/ 目录作为应用入口
- 将 hub 概念重构为 gateway tunnel transport
- 将 executor 重构为 execution runtime
- 新增 tunnel.rs 合约定义和 testkit tunnel/execution_runtime 模块
- 更新 Python 服务层和测试适配新架构命名
2026-04-03 14:59:58 +08:00

132 lines
4.1 KiB
Python

from __future__ import annotations
import json
import struct
from typing import Any
import httpx
import pytest
from src.services.proxy_node.gateway_tunnel_transport import GatewayTunnelTransport
from src.services.proxy_node.tunnel_config import TunnelRelayConfig
class _FakeRelayClient:
def __init__(self, response: httpx.Response) -> None:
self.response = response
self.sent_request: httpx.Request | None = None
self.sent_body: bytes | None = None
def build_request(self, method: str, url: str, **kwargs: Any) -> httpx.Request:
return httpx.Request(method, url, **kwargs)
async def send(self, request: httpx.Request, *, stream: bool = False) -> httpx.Response:
_ = stream
self.sent_request = request
self.sent_body = await request.aread()
self.response.request = request
return self.response
async def aclose(self) -> None:
return None
def _relay_config() -> TunnelRelayConfig:
return TunnelRelayConfig(
enabled=True,
url="http://127.0.0.1:8084",
connect_timeout_seconds=1.0,
)
@pytest.mark.asyncio
async def test_transport_encodes_local_relay_envelope(monkeypatch: pytest.MonkeyPatch) -> None:
transport = GatewayTunnelTransport("node-1", timeout=12.0)
fake_client = _FakeRelayClient(httpx.Response(200, content=b"ok"))
monkeypatch.setattr(
"src.services.proxy_node.gateway_tunnel_transport.get_tunnel_relay_config",
_relay_config,
)
monkeypatch.setattr(transport, "_relay_client", fake_client)
request = httpx.Request(
"POST",
"https://example.com/v1/chat/completions",
headers={"content-type": "application/json", "connection": "keep-alive"},
content=b'{"hello":"world"}',
)
response = await transport.handle_async_request(request)
assert response.status_code == 200
await response.aclose()
assert fake_client.sent_request is not None
payload = fake_client.sent_body
assert payload is not None
meta_len = struct.unpack("!I", payload[:4])[0]
meta = json.loads(payload[4 : 4 + meta_len].decode("utf-8"))
assert meta == {
"method": "POST",
"url": "https://example.com/v1/chat/completions",
"headers": {"content-type": "application/json"},
"timeout": 12,
}
assert payload[4 + meta_len :] == b'{"hello":"world"}'
@pytest.mark.asyncio
async def test_transport_maps_relay_timeout_to_read_timeout(
monkeypatch: pytest.MonkeyPatch,
) -> None:
transport = GatewayTunnelTransport("node-1", timeout=12.0)
fake_client = _FakeRelayClient(
httpx.Response(
504,
headers={"x-aether-tunnel-error": "timeout"},
content=b"relay timed out",
)
)
monkeypatch.setattr(
"src.services.proxy_node.gateway_tunnel_transport.get_tunnel_relay_config",
_relay_config,
)
monkeypatch.setattr(transport, "_relay_client", fake_client)
request = httpx.Request("GET", "https://example.com")
with pytest.raises(httpx.ReadTimeout, match="relay timed out"):
await transport.handle_async_request(request)
@pytest.mark.asyncio
async def test_transport_streams_request_body_from_async_generator(
monkeypatch: pytest.MonkeyPatch,
) -> None:
transport = GatewayTunnelTransport("node-1", timeout=12.0)
fake_client = _FakeRelayClient(httpx.Response(200, content=b"ok"))
monkeypatch.setattr(
"src.services.proxy_node.gateway_tunnel_transport.get_tunnel_relay_config",
_relay_config,
)
monkeypatch.setattr(transport, "_relay_client", fake_client)
async def body() -> Any:
yield b'{"hello":'
yield b'"world"}'
request = httpx.Request(
"POST",
"https://example.com/v1/chat/completions",
headers={"content-type": "application/json"},
content=body(),
)
response = await transport.handle_async_request(request)
assert response.status_code == 200
await response.aclose()
payload = fake_client.sent_body
assert payload is not None
meta_len = struct.unpack("!I", payload[:4])[0]
assert payload[4 + meta_len :] == b'{"hello":"world"}'