Skip to content

Commit 78acc96

Browse files
authored
Adding more test coverage and fixing URL handling (#825)
* Adding more test coverage and fixing URL handling Fixes #822 * Whitespace fix
1 parent a7fa3d3 commit 78acc96

File tree

2 files changed

+84
-3
lines changed

2 files changed

+84
-3
lines changed

src/CommunityToolkit.Aspire.Hosting.McpInspector/McpInspectorResourceBuilderExtensions.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,9 @@ public static IResourceBuilder<McpInspectorResource> WithMcpServer<TResource>(
197197
ArgumentNullException.ThrowIfNull(builder);
198198

199199
builder.Resource.AddMcpServer(mcpServer.Resource, isDefault, transportType, path);
200+
201+
mcpServer.WithRelationship(builder.Resource, "InspectedBy");
202+
200203
return builder;
201204
}
202205

@@ -239,11 +242,12 @@ internal static Uri Combine(string baseUrl, params string[] segments)
239242
if (Uri.IsWellFormedUriString(segments[0], UriKind.Absolute))
240243
return new Uri(segments[0], UriKind.Absolute);
241244

242-
var escaped = segments
245+
var escapedSegments = segments
243246
.Where(s => !string.IsNullOrEmpty(s))
244-
.Select(s => Uri.EscapeDataString(s.Trim('/')));
247+
.SelectMany(s => s.Trim('/').Split('/', StringSplitOptions.RemoveEmptyEntries))
248+
.Select(Uri.EscapeDataString);
245249

246-
var relative = string.Join("/", escaped);
250+
var relative = string.Join("/", escapedSegments);
247251

248252
return new Uri(baseUri, relative);
249253
}

tests/CommunityToolkit.Aspire.Hosting.McpInspector.Tests/McpInspectorResourceBuilderExtensionsTests.cs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,4 +297,81 @@ public void AddMcpInspectorWithConfigurationDelegateCreatesResourceCorrectly()
297297
Assert.Equal(3333, clientEndpoint.Port);
298298
Assert.Equal(4444, serverEndpoint.Port);
299299
}
300+
301+
[Fact]
302+
public void WithMcpServerCreatesResourceRelationshipAnnotations()
303+
{
304+
// Arrange
305+
var appBuilder = DistributedApplication.CreateBuilder();
306+
307+
// Create a mock MCP server resource
308+
var mockServer = appBuilder.AddProject<Projects.CommunityToolkit_Aspire_Hosting_McpInspector_McpServer>("mcpServer");
309+
310+
// Act
311+
var inspector = appBuilder.AddMcpInspector("inspector")
312+
.WithMcpServer(mockServer, isDefault: true);
313+
314+
using var app = appBuilder.Build();
315+
316+
// Assert
317+
var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
318+
319+
var inspectorResource = Assert.Single(appModel.Resources.OfType<McpInspectorResource>());
320+
var serverResource = appModel.Resources.Single(r => r.Name == "mcpServer");
321+
322+
// The inspector and/or server should have a resource relationship annotation linking them.
323+
var serverHasRelationship = serverResource.TryGetAnnotationsOfType<ResourceRelationshipAnnotation>(out var serverRelationships);
324+
var inspectorHasRelationship = inspectorResource.TryGetAnnotationsOfType<ResourceRelationshipAnnotation>(out var inspectorRelationships);
325+
326+
Assert.True(serverHasRelationship || inspectorHasRelationship, "Expected a ResourceRelationshipAnnotation on either the server or inspector resource.");
327+
}
328+
329+
[Fact]
330+
public void WithMcpServerPreservesCustomPathSegments()
331+
{
332+
// Arrange
333+
var appBuilder = DistributedApplication.CreateBuilder();
334+
335+
// Create a mock MCP server resource
336+
var mockServer = appBuilder.AddProject<Projects.CommunityToolkit_Aspire_Hosting_McpInspector_McpServer>("mcpServer");
337+
338+
// Act
339+
var inspector = appBuilder.AddMcpInspector("inspector")
340+
.WithMcpServer(mockServer, isDefault: true, path: "/route/mcp");
341+
342+
using var app = appBuilder.Build();
343+
344+
// Assert
345+
var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
346+
347+
var inspectorResource = Assert.Single(appModel.Resources.OfType<McpInspectorResource>());
348+
349+
Assert.Single(inspectorResource.McpServers);
350+
var serverMeta = inspectorResource.McpServers.Single();
351+
352+
// Path should be preserved exactly as provided (not url-encoded)
353+
Assert.Equal("/route/mcp", serverMeta.Path);
354+
}
355+
356+
[Fact]
357+
public void CombineHandlesMultipleSegmentsAndDoesNotEncodeSlashes()
358+
{
359+
// Arrange
360+
var baseUrl = "http://localhost:1234";
361+
var segments = new[] { "/route/mcp", "nested/path" };
362+
363+
// Use reflection to call internal Combine
364+
var type = typeof(McpInspectorResourceBuilderExtensions);
365+
var method = type.GetMethod("Combine", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
366+
Assert.NotNull(method);
367+
368+
// Act
369+
var result = method!.Invoke(null, new object[] { baseUrl, segments }) as Uri;
370+
371+
// Assert
372+
Assert.NotNull(result);
373+
// Ensure that slashes from segments are preserved and not percent-encoded
374+
var expected = new Uri("http://localhost:1234/route/mcp/nested/path");
375+
Assert.Equal(expected, result);
376+
}
300377
}

0 commit comments

Comments
 (0)