Skip to content

Commit ddca848

Browse files
committed
Avoid docker port conflicts from test suites running concurrently
1 parent 9650ce8 commit ddca848

File tree

4 files changed

+34
-12
lines changed

4 files changed

+34
-12
lines changed

tests/ModelContextProtocol.TestSseServer/Program.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,10 +377,12 @@ public static async Task MainAsync(string[] args, ILoggerProvider? loggerProvide
377377
{
378378
Console.WriteLine("Starting server...");
379379

380+
int port = args.Length > 0 && uint.TryParse(args[0], out var parsedPort) ? (int)parsedPort : 3001;
381+
380382
var builder = WebApplication.CreateSlimBuilder(args);
381383
builder.WebHost.ConfigureKestrel(options =>
382384
{
383-
options.ListenLocalhost(3001);
385+
options.ListenLocalhost(port);
384386
});
385387

386388
ConfigureSerilog(builder.Logging);

tests/ModelContextProtocol.Tests/SseIntegrationTests.cs

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,25 @@ namespace ModelContextProtocol.Tests;
88

99
public class SseIntegrationTests(ITestOutputHelper outputHelper) : LoggedTest(outputHelper)
1010
{
11+
/// <summary>Port number to be grabbed by the next test.</summary>
12+
private static int s_nextPort = 3000;
13+
14+
// If the tests run concurrently against different versions of the runtime, tests can conflict with
15+
// each other in the ports set up for interacting with containers. Ensure that such suites running
16+
// against different TFMs use different port numbers.
17+
private static readonly int s_portOffset = 1000 * (Environment.Version.Major switch
18+
{
19+
int v when v >= 8 => Environment.Version.Major - 7,
20+
_ => 0,
21+
});
22+
23+
private static int CreatePortNumber() => Interlocked.Increment(ref s_nextPort) + s_portOffset;
24+
1125
[Fact]
1226
public async Task ConnectAndReceiveMessage_InMemoryServer()
1327
{
1428
// Arrange
15-
await using InMemoryTestSseServer server = new(logger: LoggerFactory.CreateLogger<InMemoryTestSseServer>());
29+
await using InMemoryTestSseServer server = new(CreatePortNumber(), LoggerFactory.CreateLogger<InMemoryTestSseServer>());
1630
await server.StartAsync();
1731

1832
var defaultOptions = new McpClientOptions
@@ -26,7 +40,7 @@ public async Task ConnectAndReceiveMessage_InMemoryServer()
2640
Name = "In-memory Test Server",
2741
TransportType = TransportTypes.Sse,
2842
TransportOptions = [],
29-
Location = "http://localhost:5000/sse"
43+
Location = $"http://localhost:{server.Port}/sse"
3044
};
3145

3246
// Act
@@ -52,7 +66,7 @@ public async Task ConnectAndReceiveMessage_EverythingServerWithSse()
5266
{
5367
Assert.SkipWhen(!EverythingSseServerFixture.IsDockerAvailable, "docker is not available");
5468

55-
int port = 3001;
69+
int port = CreatePortNumber();
5670

5771
await using var fixture = new EverythingSseServerFixture(port);
5872
await fixture.StartAsync();
@@ -89,7 +103,7 @@ public async Task Sampling_Sse_EverythingServer()
89103
{
90104
Assert.SkipWhen(!EverythingSseServerFixture.IsDockerAvailable, "docker is not available");
91105

92-
int port = 3002;
106+
int port = CreatePortNumber();
93107

94108
await using var fixture = new EverythingSseServerFixture(port);
95109
await fixture.StartAsync();
@@ -157,11 +171,10 @@ public async Task Sampling_Sse_EverythingServer()
157171
public async Task ConnectAndReceiveMessage_InMemoryServer_WithFullEndpointEventUri()
158172
{
159173
// Arrange
160-
await using InMemoryTestSseServer server = new(logger: LoggerFactory.CreateLogger<InMemoryTestSseServer>());
174+
await using InMemoryTestSseServer server = new(CreatePortNumber(), LoggerFactory.CreateLogger<InMemoryTestSseServer>());
161175
server.UseFullUrlForEndpointEvent = true;
162176
await server.StartAsync();
163177

164-
165178
var defaultOptions = new McpClientOptions
166179
{
167180
ClientInfo = new() { Name = "IntegrationTestClient", Version = "1.0.0" }
@@ -173,7 +186,7 @@ public async Task ConnectAndReceiveMessage_InMemoryServer_WithFullEndpointEventU
173186
Name = "In-memory Test Server",
174187
TransportType = TransportTypes.Sse,
175188
TransportOptions = [],
176-
Location = "http://localhost:5000/sse"
189+
Location = $"http://localhost:{server.Port}/sse"
177190
};
178191

179192
// Act
@@ -197,7 +210,7 @@ public async Task ConnectAndReceiveMessage_InMemoryServer_WithFullEndpointEventU
197210
public async Task ConnectAndReceiveNotification_InMemoryServer()
198211
{
199212
// Arrange
200-
await using InMemoryTestSseServer server = new(logger: LoggerFactory.CreateLogger<InMemoryTestSseServer>());
213+
await using InMemoryTestSseServer server = new(CreatePortNumber(), LoggerFactory.CreateLogger<InMemoryTestSseServer>());
201214
await server.StartAsync();
202215

203216

@@ -212,7 +225,7 @@ public async Task ConnectAndReceiveNotification_InMemoryServer()
212225
Name = "In-memory Test Server",
213226
TransportType = TransportTypes.Sse,
214227
TransportOptions = [],
215-
Location = "http://localhost:5000/sse"
228+
Location = $"http://localhost:{server.Port}/sse"
216229
};
217230

218231
// Act

tests/ModelContextProtocol.Tests/SseServerIntegrationTestFixture.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,19 @@ public class SseServerIntegrationTestFixture : IAsyncDisposable
1717

1818
public SseServerIntegrationTestFixture()
1919
{
20+
// Ensure that test suites running against different TFMs and possibly concurrently use different port numbers.
21+
int port = 3001 + Environment.Version.Major;
22+
2023
DefaultConfig = new McpServerConfig
2124
{
2225
Id = "test_server",
2326
Name = "TestServer",
2427
TransportType = TransportTypes.Sse,
2528
TransportOptions = [],
26-
Location = "http://localhost:3001/sse"
29+
Location = $"http://localhost:{port}/sse"
2730
};
2831

29-
_serverTask = Program.MainAsync([], new XunitLoggerProvider(_delegatingTestOutputHelper), _stopCts.Token);
32+
_serverTask = Program.MainAsync([port.ToString()], new XunitLoggerProvider(_delegatingTestOutputHelper), _stopCts.Token);
3033
}
3134

3235
public static McpClientOptions CreateDefaultClientOptions() => new()

tests/ModelContextProtocol.Tests/Utils/InMemoryTestSseServer.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public sealed class InMemoryTestSseServer : IAsyncDisposable
2626

2727
public InMemoryTestSseServer(int port = 5000, ILogger<InMemoryTestSseServer>? logger = null)
2828
{
29+
Port = port;
30+
2931
_listener = new HttpListener();
3032
_listener.Prefixes.Add($"http://localhost:{port}/");
3133
_cts = new CancellationTokenSource();
@@ -35,6 +37,8 @@ public InMemoryTestSseServer(int port = 5000, ILogger<InMemoryTestSseServer>? lo
3537
_messagePath = "/message";
3638
}
3739

40+
public int Port { get; }
41+
3842
/// <summary>
3943
/// This is to be able to use the full URL for the endpoint event.
4044
/// </summary>

0 commit comments

Comments
 (0)