Skip to content

Add XunitLoggerProvider #81

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions tests/ModelContextProtocol.TestSseServer/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ namespace ModelContextProtocol.TestSseServer;

public class Program
{
private static ILoggerFactory CreateLoggerFactory()
private static ILoggerFactory CreateLoggerFactory() => LoggerFactory.Create(ConfigureSerilog);

public static void ConfigureSerilog(ILoggingBuilder loggingBuilder)
{
// Use serilog
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Verbose() // Capture all log levels
.WriteTo.File(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs", "TestServer_.log"),
Expand All @@ -21,15 +22,12 @@ private static ILoggerFactory CreateLoggerFactory()
.CreateLogger();

var logsPath = Path.Combine(AppContext.BaseDirectory, "testserver.log");
return LoggerFactory.Create(builder =>
{
builder.AddSerilog();
});
loggingBuilder.AddSerilog();
}

public static Task Main(string[] args) => MainAsync(args);

public static async Task MainAsync(string[] args, CancellationToken cancellationToken = default)
public static async Task MainAsync(string[] args, ILoggerFactory? loggerFactory = null, CancellationToken cancellationToken = default)
{
Console.WriteLine("Starting server...");

Expand Down Expand Up @@ -385,7 +383,7 @@ static CreateMessageRequestParams CreateRequestSamplingParams(string context, st
},
};

using var loggerFactory = CreateLoggerFactory();
loggerFactory ??= CreateLoggerFactory();
server = McpServerFactory.Create(new HttpListenerSseServerTransport("TestServer", 3001, loggerFactory), options, loggerFactory);

Console.WriteLine("Server initialized.");
Expand Down
22 changes: 9 additions & 13 deletions tests/ModelContextProtocol.Tests/ClientIntegrationTestFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@

namespace ModelContextProtocol.Tests;

public class ClientIntegrationTestFixture : IDisposable
public class ClientIntegrationTestFixture
{
public ILoggerFactory LoggerFactory { get; }
private ILoggerFactory? _loggerFactory;

public McpClientOptions DefaultOptions { get; }
public McpServerConfig EverythingServerConfig { get; }
public McpServerConfig TestServerConfig { get; }
Expand All @@ -16,10 +17,6 @@ public class ClientIntegrationTestFixture : IDisposable

public ClientIntegrationTestFixture()
{
LoggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create(builder =>
builder.AddConsole()
.SetMinimumLevel(LogLevel.Debug));

DefaultOptions = new()
{
ClientInfo = new() { Name = "IntegrationTestClient", Version = "1.0.0" },
Expand Down Expand Up @@ -56,17 +53,16 @@ public ClientIntegrationTestFixture()
}
}

public void Initialize(ILoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory;
}

public Task<IMcpClient> CreateClientAsync(string clientId, McpClientOptions? clientOptions = null) =>
McpClientFactory.CreateAsync(clientId switch
{
"everything" => EverythingServerConfig,
"test_server" => TestServerConfig,
_ => throw new ArgumentException($"Unknown client ID: {clientId}")
}, clientOptions ?? DefaultOptions, loggerFactory: LoggerFactory);

public void Dispose()
{
LoggerFactory?.Dispose();
GC.SuppressFinalize(this);
}
}, clientOptions ?? DefaultOptions, loggerFactory: _loggerFactory);
}
9 changes: 6 additions & 3 deletions tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,25 @@
using System.Text.Json;
using ModelContextProtocol.Configuration;
using ModelContextProtocol.Protocol.Transport;
using ModelContextProtocol.Tests.Utils;
using Xunit.Sdk;
using System.Text.Encodings.Web;
using System.Text.Json.Serialization.Metadata;
using System.Text.Json.Serialization;

namespace ModelContextProtocol.Tests;

public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture>
public class ClientIntegrationTests : LoggedTest, IClassFixture<ClientIntegrationTestFixture>
{
private static readonly string? s_openAIKey = Environment.GetEnvironmentVariable("AI:OpenAI:ApiKey")!;

private readonly ClientIntegrationTestFixture _fixture;

public ClientIntegrationTests(ClientIntegrationTestFixture fixture)
public ClientIntegrationTests(ClientIntegrationTestFixture fixture, ITestOutputHelper testOutputHelper)
: base(testOutputHelper)
{
_fixture = fixture;
_fixture.Initialize(LoggerFactory);
}

public static IEnumerable<object[]> GetClients() =>
Expand Down Expand Up @@ -474,7 +477,7 @@ public async Task CallTool_Stdio_MemoryServer()
await using var client = await McpClientFactory.CreateAsync(
serverConfig,
clientOptions,
loggerFactory: _fixture.LoggerFactory,
loggerFactory: LoggerFactory,
cancellationToken: TestContext.Current.CancellationToken);

// act
Expand Down
15 changes: 7 additions & 8 deletions tests/ModelContextProtocol.Tests/Server/McpServerFactoryTests.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
using ModelContextProtocol.Protocol.Transport;
using ModelContextProtocol.Protocol.Types;
using ModelContextProtocol.Server;
using Microsoft.Extensions.Logging.Abstractions;
using ModelContextProtocol.Tests.Utils;
using Moq;

namespace ModelContextProtocol.Tests.Server;

public class McpServerFactoryTests
public class McpServerFactoryTests : LoggedTest
{
private readonly Mock<IServerTransport> _serverTransport;
private readonly McpServerOptions _options;
private readonly IServiceProvider _serviceProvider;

public McpServerFactoryTests()
public McpServerFactoryTests(ITestOutputHelper testOutputHelper)
: base(testOutputHelper)
{
_serverTransport = new Mock<IServerTransport>();
_options = new McpServerOptions
Expand All @@ -21,14 +21,13 @@ public McpServerFactoryTests()
ProtocolVersion = "1.0",
InitializationTimeout = TimeSpan.FromSeconds(30)
};
_serviceProvider = new Mock<IServiceProvider>().Object;
}

[Fact]
public async Task Create_Should_Initialize_With_Valid_Parameters()
{
// Arrange & Act
await using IMcpServer server = McpServerFactory.Create(_serverTransport.Object, _options, NullLoggerFactory.Instance);
await using IMcpServer server = McpServerFactory.Create(_serverTransport.Object, _options, LoggerFactory);

// Assert
Assert.NotNull(server);
Expand All @@ -38,13 +37,13 @@ public async Task Create_Should_Initialize_With_Valid_Parameters()
public void Constructor_Throws_For_Null_ServerTransport()
{
// Arrange, Act & Assert
Assert.Throws<ArgumentNullException>("serverTransport", () => McpServerFactory.Create(null!, _options, NullLoggerFactory.Instance));
Assert.Throws<ArgumentNullException>("serverTransport", () => McpServerFactory.Create(null!, _options, LoggerFactory));
}

[Fact]
public void Constructor_Throws_For_Null_Options()
{
// Arrange, Act & Assert
Assert.Throws<ArgumentNullException>("serverOptions", () => McpServerFactory.Create(_serverTransport.Object, null!, NullLoggerFactory.Instance));
Assert.Throws<ArgumentNullException>("serverOptions", () => McpServerFactory.Create(_serverTransport.Object, null!, LoggerFactory));
}
}
38 changes: 18 additions & 20 deletions tests/ModelContextProtocol.Tests/Server/McpServerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,18 @@

namespace ModelContextProtocol.Tests.Server;

public class McpServerTests
public class McpServerTests : LoggedTest
{
private readonly Mock<IServerTransport> _serverTransport;
private readonly Mock<ILoggerFactory> _loggerFactory;
private readonly Mock<ILogger> _logger;
private readonly McpServerOptions _options;
private readonly IServiceProvider _serviceProvider;

public McpServerTests()
public McpServerTests(ITestOutputHelper testOutputHelper)
: base(testOutputHelper)
{
_serverTransport = new Mock<IServerTransport>();
_loggerFactory = new Mock<ILoggerFactory>();
_logger = new Mock<ILogger>();
_loggerFactory.Setup(f => f.CreateLogger(It.IsAny<string>())).Returns(_logger.Object);
_options = CreateOptions();
_serviceProvider = new Mock<IServiceProvider>().Object;
}
Expand All @@ -44,7 +42,7 @@ private static McpServerOptions CreateOptions(ServerCapabilities? capabilities =
public async Task Constructor_Should_Initialize_With_Valid_Parameters()
{
// Arrange & Act
await using var server = McpServerFactory.Create(_serverTransport.Object, _options, _loggerFactory.Object, _serviceProvider);
await using var server = McpServerFactory.Create(_serverTransport.Object, _options, LoggerFactory, _serviceProvider);

// Assert
Assert.NotNull(server);
Expand All @@ -54,14 +52,14 @@ public async Task Constructor_Should_Initialize_With_Valid_Parameters()
public void Constructor_Throws_For_Null_Transport()
{
// Arrange, Act & Assert
Assert.Throws<ArgumentNullException>(() => McpServerFactory.Create(null!, _options, _loggerFactory.Object, _serviceProvider));
Assert.Throws<ArgumentNullException>(() => McpServerFactory.Create(null!, _options, LoggerFactory, _serviceProvider));
}

[Fact]
public void Constructor_Throws_For_Null_Options()
{
// Arrange, Act & Assert
Assert.Throws<ArgumentNullException>(() => McpServerFactory.Create(_serverTransport.Object, null!, _loggerFactory.Object, _serviceProvider));
Assert.Throws<ArgumentNullException>(() => McpServerFactory.Create(_serverTransport.Object, null!, LoggerFactory, _serviceProvider));
}

[Fact]
Expand All @@ -78,7 +76,7 @@ public async Task Constructor_Does_Not_Throw_For_Null_Logger()
public async Task Constructor_Does_Not_Throw_For_Null_ServiceProvider()
{
// Arrange & Act
await using var server = McpServerFactory.Create(_serverTransport.Object, _options, _loggerFactory.Object, null);
await using var server = McpServerFactory.Create(_serverTransport.Object, _options, LoggerFactory, null);

// Assert
Assert.NotNull(server);
Expand All @@ -88,7 +86,7 @@ public async Task Constructor_Does_Not_Throw_For_Null_ServiceProvider()
public async Task StartAsync_Should_Throw_InvalidOperationException_If_Already_Initializing()
{
// Arrange
await using var server = McpServerFactory.Create(_serverTransport.Object, _options, _loggerFactory.Object, _serviceProvider);
await using var server = McpServerFactory.Create(_serverTransport.Object, _options, LoggerFactory, _serviceProvider);
var task = server.StartAsync(TestContext.Current.CancellationToken);

// Act & Assert
Expand All @@ -101,7 +99,7 @@ public async Task StartAsync_Should_Throw_InvalidOperationException_If_Already_I
public async Task StartAsync_Should_Do_Nothing_If_Already_Initialized()
{
// Arrange
await using var server = McpServerFactory.Create(_serverTransport.Object, _options, _loggerFactory.Object, _serviceProvider);
await using var server = McpServerFactory.Create(_serverTransport.Object, _options, LoggerFactory, _serviceProvider);
SetInitialized(server, true);

await server.StartAsync(TestContext.Current.CancellationToken);
Expand All @@ -114,7 +112,7 @@ public async Task StartAsync_Should_Do_Nothing_If_Already_Initialized()
public async Task StartAsync_ShouldStartListening()
{
// Arrange
await using var server = McpServerFactory.Create(_serverTransport.Object, _options, _loggerFactory.Object, _serviceProvider);
await using var server = McpServerFactory.Create(_serverTransport.Object, _options, LoggerFactory, _serviceProvider);

// Act
await server.StartAsync(TestContext.Current.CancellationToken);
Expand All @@ -127,7 +125,7 @@ public async Task StartAsync_ShouldStartListening()
public async Task StartAsync_Sets_Initialized_After_Transport_Responses_Initialized_Notification()
{
await using var transport = new TestServerTransport();
await using var server = McpServerFactory.Create(transport, _options, _loggerFactory.Object, _serviceProvider);
await using var server = McpServerFactory.Create(transport, _options, LoggerFactory, _serviceProvider);

await server.StartAsync(TestContext.Current.CancellationToken);

Expand All @@ -147,7 +145,7 @@ await transport.SendMessageAsync(new JsonRpcNotification
public async Task RequestSamplingAsync_Should_Throw_McpServerException_If_Client_Does_Not_Support_Sampling()
{
// Arrange
await using var server = McpServerFactory.Create(_serverTransport.Object, _options, _loggerFactory.Object, _serviceProvider);
await using var server = McpServerFactory.Create(_serverTransport.Object, _options, LoggerFactory, _serviceProvider);
SetClientCapabilities(server, new ClientCapabilities());

var action = () => server.RequestSamplingAsync(new CreateMessageRequestParams { Messages = [] }, CancellationToken.None);
Expand All @@ -161,7 +159,7 @@ public async Task RequestSamplingAsync_Should_SendRequest()
{
// Arrange
await using var transport = new TestServerTransport();
await using var server = McpServerFactory.Create(transport, _options, _loggerFactory.Object, _serviceProvider);
await using var server = McpServerFactory.Create(transport, _options, LoggerFactory, _serviceProvider);
SetClientCapabilities(server, new ClientCapabilities { Sampling = new SamplingCapability() });

await server.StartAsync(TestContext.Current.CancellationToken);
Expand All @@ -179,7 +177,7 @@ public async Task RequestSamplingAsync_Should_SendRequest()
public async Task RequestRootsAsync_Should_Throw_McpServerException_If_Client_Does_Not_Support_Roots()
{
// Arrange
await using var server = McpServerFactory.Create(_serverTransport.Object, _options, _loggerFactory.Object, _serviceProvider);
await using var server = McpServerFactory.Create(_serverTransport.Object, _options, LoggerFactory, _serviceProvider);
SetClientCapabilities(server, new ClientCapabilities());

// Act & Assert
Expand All @@ -191,7 +189,7 @@ public async Task RequestRootsAsync_Should_SendRequest()
{
// Arrange
await using var transport = new TestServerTransport();
await using var server = McpServerFactory.Create(transport, _options, _loggerFactory.Object, _serviceProvider);
await using var server = McpServerFactory.Create(transport, _options, LoggerFactory, _serviceProvider);
SetClientCapabilities(server, new ClientCapabilities { Roots = new RootsCapability() });
await server.StartAsync(TestContext.Current.CancellationToken);

Expand All @@ -208,7 +206,7 @@ public async Task RequestRootsAsync_Should_SendRequest()
[Fact]
public async Task Throws_Exception_If_Not_Connected()
{
await using var server = McpServerFactory.Create(_serverTransport.Object, _options, _loggerFactory.Object, _serviceProvider);
await using var server = McpServerFactory.Create(_serverTransport.Object, _options, LoggerFactory, _serviceProvider);
SetClientCapabilities(server, new ClientCapabilities { Roots = new RootsCapability() });
_serverTransport.SetupGet(t => t.IsConnected).Returns(false);

Expand Down Expand Up @@ -555,7 +553,7 @@ private async Task Can_Handle_Requests(ServerCapabilities? serverCapabilities, s
var options = CreateOptions(serverCapabilities);
configureOptions?.Invoke(options);

await using var server = McpServerFactory.Create(transport, options, _loggerFactory.Object, _serviceProvider);
await using var server = McpServerFactory.Create(transport, options, LoggerFactory, _serviceProvider);

await server.StartAsync();

Expand Down Expand Up @@ -587,7 +585,7 @@ private async Task Throws_Exception_If_No_Handler_Assigned(ServerCapabilities se
await using var transport = new TestServerTransport();
var options = CreateOptions(serverCapabilities);

Assert.Throws<McpServerException>(() => McpServerFactory.Create(transport, options, _loggerFactory.Object, _serviceProvider));
Assert.Throws<McpServerException>(() => McpServerFactory.Create(transport, options, LoggerFactory, _serviceProvider));
}

[Fact]
Expand Down
Loading