using ColaFlow.Modules.Mcp.Application.Services; using ColaFlow.Modules.Mcp.Contracts.Resources; using FluentAssertions; using Microsoft.Extensions.Logging; using NSubstitute; namespace ColaFlow.Modules.Mcp.Tests.Services; /// /// Unit tests for McpResourceRegistry /// public class McpResourceRegistryTests { private readonly ILogger _logger; private readonly McpResourceRegistry _sut; public McpResourceRegistryTests() { _logger = Substitute.For>(); _sut = new McpResourceRegistry(_logger); } [Fact] public void RegisterResource_AddsResourceToRegistry() { // Arrange var resource = CreateMockResource("colaflow://test", "Test Resource"); // Act _sut.RegisterResource(resource); // Assert var resources = _sut.GetAllResources(); resources.Should().ContainSingle(); resources[0].Uri.Should().Be("colaflow://test"); } [Fact] public void RegisterResource_WithDuplicateUri_Overwrites() { // Arrange var resource1 = CreateMockResource("colaflow://test", "Test Resource 1"); var resource2 = CreateMockResource("colaflow://test", "Test Resource 2"); // Act _sut.RegisterResource(resource1); _sut.RegisterResource(resource2); // Assert var resources = _sut.GetAllResources(); resources.Count.Should().Be(2); // Both are in the list var foundResource = _sut.GetResourceByUri("colaflow://test"); foundResource!.Name.Should().Be("Test Resource 2"); // But only the last one is returned } [Fact] public void GetResourceByUri_WithExactMatch_ReturnsResource() { // Arrange var resource = CreateMockResource("colaflow://projects.list", "Projects List"); _sut.RegisterResource(resource); // Act var result = _sut.GetResourceByUri("colaflow://projects.list"); // Assert result.Should().NotBeNull(); result!.Uri.Should().Be("colaflow://projects.list"); } [Fact] public void GetResourceByUri_WithTemplateMatch_ReturnsResource() { // Arrange var resource = CreateMockResource("colaflow://projects.get/{id}", "Project Details"); _sut.RegisterResource(resource); // Act var result = _sut.GetResourceByUri("colaflow://projects.get/123"); // Assert result.Should().NotBeNull(); result!.Uri.Should().Be("colaflow://projects.get/{id}"); } [Fact] public void GetResourceByUri_WithNonExistentUri_ReturnsNull() { // Act var result = _sut.GetResourceByUri("colaflow://nonexistent"); // Assert result.Should().BeNull(); } [Fact] public void GetResourceDescriptors_ReturnsAllDescriptors() { // Arrange var resource1 = CreateMockResource("colaflow://test1", "Test 1"); var resource2 = CreateMockResource("colaflow://test2", "Test 2"); _sut.RegisterResource(resource1); _sut.RegisterResource(resource2); // Act var descriptors = _sut.GetResourceDescriptors(); // Assert descriptors.Should().HaveCount(2); descriptors.Should().Contain(d => d.Uri == "colaflow://test1"); descriptors.Should().Contain(d => d.Uri == "colaflow://test2"); } [Fact] public void GetAllResources_ReturnsReadOnlyList() { // Arrange var resource = CreateMockResource("colaflow://test", "Test"); _sut.RegisterResource(resource); // Act var resources = _sut.GetAllResources(); // Assert resources.Should().BeAssignableTo>(); } private IMcpResource CreateMockResource(string uri, string name) { var resource = Substitute.For(); resource.Uri.Returns(uri); resource.Name.Returns(name); resource.Description.Returns($"Description for {name}"); resource.MimeType.Returns("application/json"); resource.GetContentAsync(Arg.Any(), Arg.Any()) .Returns(new McpResourceContent { Uri = uri, MimeType = "application/json", Text = "{}" }); return resource; } }