test(gateway): cover slow health clients

This commit is contained in:
Xubin Ren
2026-07-13 15:16:51 +08:00
parent 3824206ab2
commit a65a6f334c
3 changed files with 16 additions and 2 deletions
+3 -1
View File
@@ -56,7 +56,9 @@ Restart the deployed process after editing `config.json`. Long-running processes
> When the WebSocket `host` is `0.0.0.0`, the channel refuses to start unless `token` or `tokenIssueSecret` is also configured. See [`webui.md#lan-access`](./webui.md#lan-access) for details. > When the WebSocket `host` is `0.0.0.0`, the channel refuses to start unless `token` or `tokenIssueSecret` is also configured. See [`webui.md#lan-access`](./webui.md#lan-access) for details.
> The gateway health route itself is intentionally minimal and unauthenticated. When the > The gateway health route itself is intentionally minimal and unauthenticated. When the
> container binds it to `0.0.0.0`, publish port `18790` to host loopback only; place any > container binds it to `0.0.0.0`, publish port `18790` to host loopback only; place any
> remotely monitored health endpoint behind a firewall or reverse proxy. > remotely monitored health endpoint behind a firewall or reverse proxy. If another host
> must probe it directly, replace `127.0.0.1` in the port mapping with a trusted host
> interface and restrict inbound traffic to the monitoring system.
### Docker Compose ### Docker Compose
+2 -1
View File
@@ -1022,7 +1022,8 @@ def _print_gateway_health_endpoint(host: str, port: int) -> None:
return return
console.print( console.print(
"[yellow]Warning: the unauthenticated health endpoint is reachable beyond this device. " "[yellow]Warning: the unauthenticated health endpoint is listening beyond loopback "
"and may be reachable from other devices. "
f"Keep port {port} private or protect it with a firewall or reverse proxy.[/yellow]" f"Keep port {port} private or protect it with a firewall or reverse proxy.[/yellow]"
) )
+11
View File
@@ -2889,6 +2889,7 @@ def test_gateway_health_endpoint_binds_and_serves_expected_responses(
assert captured["port"] == 18791 assert captured["port"] == 18791
assert f"Health endpoint: {display_url}" in result.stdout assert f"Health endpoint: {display_url}" in result.stdout
assert ("unauthenticated health endpoint" in result.stdout) is warns_about_public_bind assert ("unauthenticated health endpoint" in result.stdout) is warns_about_public_bind
assert ("may be reachable from other devices" in result.stdout) is warns_about_public_bind
assert ("listening on 0.0.0.0" in result.stdout) is warns_about_public_bind assert ("listening on 0.0.0.0" in result.stdout) is warns_about_public_bind
health_handler = captured["handler"] health_handler = captured["handler"]
@@ -2954,6 +2955,16 @@ def test_gateway_health_endpoint_binds_and_serves_expected_responses(
asyncio.run(_exercise_connection_limit()) asyncio.run(_exercise_connection_limit())
class _NeverRespondingReader:
async def read(self, _size: int) -> bytes:
await asyncio.Event().wait()
monkeypatch.setattr(cli_commands, "_GATEWAY_HEALTH_READ_TIMEOUT_SECONDS", 0.01)
timed_out_writer = _FakeWriter()
asyncio.run(health_handler(_NeverRespondingReader(), timed_out_writer))
assert timed_out_writer.closed is True
assert timed_out_writer.output == b""
def test_gateway_shutdown_lets_agent_task_own_mcp_cleanup( def test_gateway_shutdown_lets_agent_task_own_mcp_cleanup(
monkeypatch, monkeypatch,