File: Mcp\ListIntegrationsToolTests.cs
Web Access
Project: src\tests\Aspire.Cli.Tests\Aspire.Cli.Tests.csproj (Aspire.Cli.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Text.Json;
using Aspire.Cli.Mcp;
 
namespace Aspire.Cli.Tests.Mcp;
 
public class ListIntegrationsToolTests
{
    [Fact]
    public void ListIntegrationsTool_HasCorrectName()
    {
        var tool = new ListIntegrationsTool(new MockPackagingService(), TestExecutionContextFactory.CreateTestContext(), new MockAuxiliaryBackchannelMonitor());
 
        Assert.Equal("list_integrations", tool.Name);
    }
 
    [Fact]
    public void ListIntegrationsTool_HasCorrectDescription()
    {
        var tool = new ListIntegrationsTool(new MockPackagingService(), TestExecutionContextFactory.CreateTestContext(), new MockAuxiliaryBackchannelMonitor());
 
        Assert.Contains("List available Aspire hosting integrations", tool.Description);
        Assert.Contains("This tool does not require a running AppHost", tool.Description);
    }
 
    [Fact]
    public void ListIntegrationsTool_GetInputSchema_ReturnsValidSchema()
    {
        var tool = new ListIntegrationsTool(new MockPackagingService(), TestExecutionContextFactory.CreateTestContext(), new MockAuxiliaryBackchannelMonitor());
        var schema = tool.GetInputSchema();
 
        Assert.Equal(JsonValueKind.Object, schema.ValueKind);
        Assert.True(schema.TryGetProperty("type", out var typeElement));
        Assert.Equal("object", typeElement.GetString());
        Assert.True(schema.TryGetProperty("properties", out var propsElement));
        Assert.Equal(JsonValueKind.Object, propsElement.ValueKind);
        Assert.True(schema.TryGetProperty("description", out var descElement));
        Assert.Contains("This tool takes no input parameters", descElement.GetString());
        Assert.True(schema.TryGetProperty("additionalProperties", out var additionalPropsElement));
        Assert.False(additionalPropsElement.GetBoolean());
    }
 
    [Fact]
    public async Task ListIntegrationsTool_CallToolAsync_ReturnsEmptyJsonArray_WhenNoPackagesFound()
    {
        var mockPackagingService = new MockPackagingService();
        var tool = new ListIntegrationsTool(mockPackagingService, TestExecutionContextFactory.CreateTestContext(), new MockAuxiliaryBackchannelMonitor());
 
        var result = await tool.CallToolAsync(null!, null, CancellationToken.None);
 
        Assert.True(result.IsError is null or false);
        Assert.NotNull(result.Content);
        Assert.Single(result.Content);
        var textContent = result.Content[0] as ModelContextProtocol.Protocol.TextContentBlock;
        Assert.NotNull(textContent);
 
        // Verify it's valid JSON with empty integrations array
        using var json = JsonDocument.Parse(textContent.Text);
        Assert.True(json.RootElement.TryGetProperty("integrations", out var integrations));
        Assert.Equal(JsonValueKind.Array, integrations.ValueKind);
        Assert.Equal(0, integrations.GetArrayLength());
    }
 
    [Fact]
    public async Task ListIntegrationsTool_CallToolAsync_ReturnsJsonWithPackages_WhenPackagesFound()
    {
        var mockPackagingService = new MockPackagingService(new[]
        {
            new Aspire.Shared.NuGetPackageCli { Id = "Aspire.Hosting.Redis", Version = "9.0.0" },
            new Aspire.Shared.NuGetPackageCli { Id = "Aspire.Hosting.PostgreSQL", Version = "9.0.0" }
        });
        var tool = new ListIntegrationsTool(mockPackagingService, TestExecutionContextFactory.CreateTestContext(), new MockAuxiliaryBackchannelMonitor());
 
        var result = await tool.CallToolAsync(null!, null, CancellationToken.None);
 
        Assert.True(result.IsError is null or false);
        Assert.NotNull(result.Content);
        Assert.Single(result.Content);
        var textContent = result.Content[0] as ModelContextProtocol.Protocol.TextContentBlock;
        Assert.NotNull(textContent);
 
        // Verify it's valid JSON with proper structure
        using var json = JsonDocument.Parse(textContent.Text);
        Assert.True(json.RootElement.TryGetProperty("integrations", out var integrations));
        Assert.Equal(JsonValueKind.Array, integrations.ValueKind);
        Assert.Equal(2, integrations.GetArrayLength());
 
        // Verify the first integration has the expected properties
        var firstIntegration = integrations[0];
        Assert.True(firstIntegration.TryGetProperty("name", out _));
        Assert.True(firstIntegration.TryGetProperty("packageId", out _));
        Assert.True(firstIntegration.TryGetProperty("version", out _));
 
        // Check that the packages are included (order may vary)
        var packageIds = integrations.EnumerateArray()
            .Select(e => e.GetProperty("packageId").GetString())
            .ToList();
        Assert.Contains("Aspire.Hosting.Redis", packageIds);
        Assert.Contains("Aspire.Hosting.PostgreSQL", packageIds);
    }
 
    [Fact]
    public async Task ListIntegrationsTool_UsesDefaultChannelOnly()
    {
        // This test verifies that only the default (first) channel is used
        var mockPackagingService = new MockPackagingService(new[]
        {
            new Aspire.Shared.NuGetPackageCli { Id = "Aspire.Hosting.Redis", Version = "9.0.0" }
        });
        var tool = new ListIntegrationsTool(mockPackagingService, TestExecutionContextFactory.CreateTestContext(), new MockAuxiliaryBackchannelMonitor());
 
        var result = await tool.CallToolAsync(null!, null, CancellationToken.None);
 
        Assert.True(result.IsError is null or false);
        
        // Verify the result contains packages (confirming the default channel was used)
        using var json = JsonDocument.Parse(((ModelContextProtocol.Protocol.TextContentBlock)result.Content![0]).Text);
        Assert.True(json.RootElement.TryGetProperty("integrations", out var integrations));
        Assert.Equal(1, integrations.GetArrayLength());
    }
}