Skip to content

Commit 0154f98

Browse files
andreasrongeclaude
andcommitted
test: address codex review findings (MapSet order, ephemeral port)
Predicates: sort before comparing vec/1 on a MapSet — MapSet.to_list/1 enumeration order is not guaranteed across runtimes ([P3]). Http smoke: acquire a free OS port up front instead of passing http_port: 0. Config.resolve/1's read_int rejects non-positive ports and silently falls back to the default 7332, so the test bound a fixed port (CI collision risk) and never exercised a non-default bind ([P2]). Now binds a real free port via the production Config.resolve -> child_spec path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent d9f7e6f commit 0154f98

2 files changed

Lines changed: 29 additions & 12 deletions

File tree

mcp_server/test/ptc_runner_mcp/http/server_smoke_test.exs

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@ defmodule PtcRunnerMcp.Http.ServerSmokeTest do
22
@moduledoc """
33
Integration smoke test for the HTTP transport bootstrap.
44
5-
Boots `PtcRunnerMcp.Http.Server` for real on an OS-assigned ephemeral
6-
port (port 0), issues one real HTTP request over a TCP socket, asserts
7-
the server serves it, then lets `start_supervised!/1` tear it down
8-
cleanly. This exercises the production `Server.child_spec/1` Bandit glue
9-
and the `PlugWithConfig` -> `Router` dispatch path end to end (rather
10-
than calling `Router.call/2` in-process like the unit-style tests).
5+
Boots `PtcRunnerMcp.Http.Server` for real on a free OS-assigned port,
6+
issues one real HTTP request over a TCP socket, asserts the server
7+
serves it, then lets `start_supervised!/1` tear it down cleanly. This
8+
exercises the production `Server.child_spec/1` Bandit glue and the
9+
`PlugWithConfig` -> `Router` dispatch path end to end (rather than
10+
calling `Router.call/2` in-process like the unit-style tests).
11+
12+
The port is acquired from the OS up front via `free_port/0` rather than
13+
passing `0`: `Config.resolve/1` rejects a non-positive `http_port` and
14+
would silently fall back to the default fixed port, which could collide
15+
with a real listener in CI.
1116
"""
1217
use ExUnit.Case, async: false
1318

@@ -19,13 +24,13 @@ defmodule PtcRunnerMcp.Http.ServerSmokeTest do
1924
# required so `Config.resolve/1` succeeds and the bind host validates.
2025
@token String.duplicate("a", 32)
2126

22-
# Boot the real transport on an ephemeral port and return its bound
23-
# TCP port. The server is supervised so ExUnit tears it (and its
27+
# Boot the real transport on a free OS-assigned port and return its
28+
# bound TCP port. The server is supervised so ExUnit tears it (and its
2429
# listening socket) down at test end without leaking processes/ports.
2530
defp boot_server!(config_args) do
2631
{:ok, cfg} =
2732
HttpConfig.resolve(
28-
Map.merge(%{http: true, http_auth_token: @token, http_port: 0}, config_args)
33+
Map.merge(%{http: true, http_auth_token: @token, http_port: free_port()}, config_args)
2934
)
3035

3136
# Drive the production child-spec builder, not a hand-rolled Bandit
@@ -39,7 +44,17 @@ defmodule PtcRunnerMcp.Http.ServerSmokeTest do
3944
{cfg, port}
4045
end
4146

42-
describe "transport bootstrap on an ephemeral port" do
47+
# Ask the OS for an unused loopback TCP port, then release it so the
48+
# server can bind it. The brief window between close and re-bind is the
49+
# standard trade-off for test servers and is safe here (async: false).
50+
defp free_port do
51+
{:ok, socket} = :gen_tcp.listen(0, ip: {127, 0, 0, 1}, reuseaddr: true)
52+
{:ok, port} = :inet.port(socket)
53+
:ok = :gen_tcp.close(socket)
54+
port
55+
end
56+
57+
describe "transport bootstrap on a free OS-assigned port" do
4358
test "serves an unauthenticated GET /health over a real socket" do
4459
{_cfg, port} = boot_server!(%{})
4560
assert is_integer(port) and port > 0
@@ -56,7 +71,7 @@ defmodule PtcRunnerMcp.Http.ServerSmokeTest do
5671
# parse_ip/1 turned the string host into an inet tuple for Bandit.
5772
assert cfg.host == "127.0.0.1"
5873

59-
# The OS assigned a concrete port (config asked for 0).
74+
# The server bound the free port we acquired from the OS.
6075
assert port != 0
6176

6277
resp = Req.get!("http://127.0.0.1:#{port}/health", retry: false)

test/ptc_runner/lisp/runtime/predicates_test.exs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,9 @@ defmodule PtcRunner.Lisp.Runtime.PredicatesTest do
734734

735735
test "vec turns a set into a list of its elements" do
736736
assert Enum.sort(eval!("(vec (set [1 2 3]))")) == [1, 2, 3]
737-
assert Predicates.vec(MapSet.new([1, 2])) == [1, 2]
737+
# MapSet.to_list/1 enumeration order is not guaranteed across runtimes;
738+
# sort before comparing (the evaluated path above does the same).
739+
assert Enum.sort(Predicates.vec(MapSet.new([1, 2]))) == [1, 2]
738740
end
739741

740742
test "vec turns a string into a list of graphemes (utf8 aware)" do

0 commit comments

Comments
 (0)