Files
ColaFlow/colaflow-api/tests/Modules/Mcp/ColaFlow.Modules.Mcp.Tests/Handlers/ResourceHealthCheckHandlerTests.cs
Yaojia Wang 3ab505e0f6 feat(backend): Implement Story 5.6 - Resource Registration & Discovery
Implemented pluggable resource registration and auto-discovery mechanism for MCP Resources.

Changes:
- Enhanced McpResourceDescriptor with metadata (Category, Version, Parameters, Examples, Tags, IsEnabled)
- Created ResourceDiscoveryService for Assembly scanning and auto-discovery
- Updated McpResourceRegistry with category support and grouping methods
- Enhanced ResourcesListMethodHandler to return categorized resources with full metadata
- Created ResourceHealthCheckHandler for resource availability verification
- Updated all existing Resources (Projects, Issues, Sprints, Users) with Categories and Versions
- Updated McpServiceExtensions to use auto-discovery at startup
- Added comprehensive unit tests for discovery and health check

Features:
 New Resources automatically discovered via Assembly scanning
 Resources organized by category (Projects, Issues, Sprints, Users)
 Rich metadata for documentation (parameters, examples, tags)
 Health check endpoint (resources/health) for monitoring
 Thread-safe registry operations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-09 16:07:50 +01:00

95 lines
3.2 KiB
C#

using ColaFlow.Modules.Mcp.Application.Handlers;
using ColaFlow.Modules.Mcp.Application.Services;
using ColaFlow.Modules.Mcp.Contracts.Resources;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
namespace ColaFlow.Modules.Mcp.Tests.Handlers;
public class ResourceHealthCheckHandlerTests
{
private readonly ILogger<ResourceHealthCheckHandler> _mockLogger;
private readonly IMcpResourceRegistry _mockRegistry;
private readonly ResourceHealthCheckHandler _handler;
public ResourceHealthCheckHandlerTests()
{
_mockLogger = Substitute.For<ILogger<ResourceHealthCheckHandler>>();
_mockRegistry = Substitute.For<IMcpResourceRegistry>();
_handler = new ResourceHealthCheckHandler(_mockLogger, _mockRegistry);
}
[Fact]
public void MethodName_ShouldBeResourcesHealth()
{
// Assert
_handler.MethodName.Should().Be("resources/health");
}
[Fact]
public async Task HandleAsync_WithHealthyResources_ShouldReturnHealthyStatus()
{
// Arrange
var mockResource = Substitute.For<IMcpResource>();
mockResource.Uri.Returns("test://resource");
mockResource.Name.Returns("Test Resource");
mockResource.Category.Returns("Testing");
mockResource.GetDescriptor().Returns(new McpResourceDescriptor
{
Uri = "test://resource",
Name = "Test Resource",
Category = "Testing",
IsEnabled = true,
Version = "1.0"
});
_mockRegistry.GetAllResources()
.Returns(new List<IMcpResource> { mockResource }.AsReadOnly());
// Act
var result = await _handler.HandleAsync(null, CancellationToken.None);
// Assert
result.Should().NotBeNull();
var response = result as dynamic;
((string)response.status).Should().Be("healthy");
((int)response.totalResources).Should().Be(1);
((int)response.healthyResources).Should().Be(1);
((int)response.unhealthyResources).Should().Be(0);
}
[Fact]
public async Task HandleAsync_WithUnhealthyResource_ShouldReturnDegradedStatus()
{
// Arrange
var mockResource = Substitute.For<IMcpResource>();
mockResource.Uri.Returns("test://resource");
mockResource.Name.Returns("Test Resource");
mockResource.Category.Returns("Testing");
mockResource.GetDescriptor().Returns(new McpResourceDescriptor
{
Uri = "", // Invalid - empty URI
Name = "Test Resource",
Category = "Testing",
IsEnabled = true,
Version = "1.0"
});
_mockRegistry.GetAllResources()
.Returns(new List<IMcpResource> { mockResource }.AsReadOnly());
// Act
var result = await _handler.HandleAsync(null, CancellationToken.None);
// Assert
result.Should().NotBeNull();
var response = result as dynamic;
((string)response.status).Should().Be("degraded");
((int)response.totalResources).Should().Be(1);
((int)response.healthyResources).Should().Be(0);
((int)response.unhealthyResources).Should().Be(1);
}
}