From 08e7da100fccfb1d54aa1a41a7f86521075f84f4 Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Fri, 21 Mar 2025 19:18:55 -0700 Subject: [PATCH] Run integration test SSE server in process (#56) * Run integration test SSE server in process * Handle expected OCE disposing fixture --- .../Program.cs | 21 ++++++--- .../ModelContextProtocol.Tests.csproj | 6 --- .../SseServerIntegrationTestFixture.cs | 45 +++++-------------- 3 files changed, 28 insertions(+), 44 deletions(-) diff --git a/tests/ModelContextProtocol.TestSseServer/Program.cs b/tests/ModelContextProtocol.TestSseServer/Program.cs index 712f845..08ad81d 100644 --- a/tests/ModelContextProtocol.TestSseServer/Program.cs +++ b/tests/ModelContextProtocol.TestSseServer/Program.cs @@ -6,7 +6,9 @@ using Serilog; using System.Text; using System.Text.Json; -internal class Program +namespace ModelContextProtocol.TestSseServer; + +public class Program { private static ILoggerFactory CreateLoggerFactory() { @@ -25,7 +27,9 @@ internal class Program }); } - private static async Task Main(string[] args) + public static Task Main(string[] args) => MainAsync(args); + + public static async Task MainAsync(string[] args, CancellationToken cancellationToken = default) { Console.WriteLine("Starting server..."); @@ -386,12 +390,19 @@ internal class Program Console.WriteLine("Server initialized."); - await server.StartAsync(); + await server.StartAsync(cancellationToken); Console.WriteLine("Server started."); - // Run until process is stopped by the client (parent process) - await Task.Delay(Timeout.Infinite); + try + { + // Run until process is stopped by the client (parent process) or test + await Task.Delay(Timeout.Infinite, cancellationToken); + } + finally + { + await server.DisposeAsync(); + } } const string MCP_TINY_IMAGE = diff --git a/tests/ModelContextProtocol.Tests/ModelContextProtocol.Tests.csproj b/tests/ModelContextProtocol.Tests/ModelContextProtocol.Tests.csproj index 530f57c..e2eba56 100644 --- a/tests/ModelContextProtocol.Tests/ModelContextProtocol.Tests.csproj +++ b/tests/ModelContextProtocol.Tests/ModelContextProtocol.Tests.csproj @@ -38,12 +38,6 @@ PreserveNewest - - PreserveNewest - - - PreserveNewest - diff --git a/tests/ModelContextProtocol.Tests/SseServerIntegrationTestFixture.cs b/tests/ModelContextProtocol.Tests/SseServerIntegrationTestFixture.cs index 7b26422..e9a361c 100644 --- a/tests/ModelContextProtocol.Tests/SseServerIntegrationTestFixture.cs +++ b/tests/ModelContextProtocol.Tests/SseServerIntegrationTestFixture.cs @@ -1,15 +1,14 @@ -using ModelContextProtocol.Client; +using Microsoft.Extensions.Logging; +using ModelContextProtocol.Client; using ModelContextProtocol.Configuration; using ModelContextProtocol.Protocol.Transport; -using Microsoft.Extensions.Logging; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; namespace ModelContextProtocol.Tests; -public class SseServerIntegrationTestFixture : IDisposable +public class SseServerIntegrationTestFixture : IAsyncDisposable { - private Process _process; + private readonly CancellationTokenSource _stopCts = new(); + private readonly Task _serverTask; public ILoggerFactory LoggerFactory { get; } public McpClientOptions DefaultOptions { get; } @@ -35,40 +34,20 @@ public class SseServerIntegrationTestFixture : IDisposable Location = "http://localhost:3001/sse" }; - Start(); + _serverTask = TestSseServer.Program.MainAsync([], _stopCts.Token); } - [MemberNotNull(nameof(_process))] - public void Start() - { - // Start the server (which is at TestSseServer.exe on windows and "dotnet TestSseServer.dll" on linux) - var processStartInfo = new ProcessStartInfo - { - FileName = OperatingSystem.IsWindows() ? "TestSseServer.exe" : "dotnet", - Arguments = "TestSseServer.dll", - RedirectStandardInput = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - }; - - _process = Process.Start(processStartInfo) - ?? throw new InvalidOperationException($"Could not start process for {processStartInfo.FileName} with '{processStartInfo.Arguments}'."); - - // Wait 1 second - Thread.Sleep(1000); - } - - public void Dispose() + public async ValueTask DisposeAsync() { + LoggerFactory.Dispose(); + _stopCts.Cancel(); try { - LoggerFactory?.Dispose(); + await _serverTask.ConfigureAwait(false); } - finally + catch (OperationCanceledException) { - // Kill the server process - _process.Kill(); } + _stopCts.Dispose(); } } \ No newline at end of file