File: Agents\AgentTelemetryCatalogTests.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 Aspire.Cli.Agents.Hooks;
 
namespace Aspire.Cli.Tests.Agents;
 
public class AgentTelemetryCatalogTests
{
    private const string Script = """
        ASPIRE_SKILLS="new-skill another-skill"
        ASPIRE_MCP_TOOLS="new_tool another-tool"
        ASPIRE_REFERENCE_FILES="
        new-skill/references/new.md
        another-skill/references/guide.yml
        "
        """;
 
    [Theory]
    [InlineData("\n")]
    [InlineData("\r\n")]
    public void ReadsAllowlistsFromCanonicalScript(string newline)
    {
        var catalog = AgentTelemetryCatalog.Parse(Script.ReplaceLineEndings(newline));
        Assert.Equal(["another-skill", "new-skill"], catalog.Skills.Order());
        Assert.Equal(["another-tool", "new_tool"], catalog.Tools.Order());
        Assert.Equal(["another-skill/references/guide.yml", "new-skill/references/new.md"], catalog.References.Order());
        Assert.Contains("NEW-SKILL", catalog.Skills);
        Assert.False(catalog.Skills.Contains("third-party"));
    }
 
    [Theory]
    [InlineData("ASPIRE_SKILLS", "valid $(command)")]
    [InlineData("ASPIRE_SKILLS", "third/party")]
    [InlineData("ASPIRE_MCP_TOOLS", "valid name;command")]
    [InlineData("ASPIRE_MCP_TOOLS", "")]
    public void SkillAndToolIdentifiersMustBeValid(string variableName, string values)
    {
        Assert.Throws<InvalidDataException>(() => AgentTelemetryCatalog.Parse(ReplaceValues(variableName, values)));
    }
 
    [Theory]
    [InlineData("unknown-skill/references/new.md")]
    [InlineData("new-skill/private.md")]
    [InlineData("new-skill/references/../private.md")]
    [InlineData("new-skill\\references\\private.md")]
    public void ReferencesMustBelongToKnownSkillReferenceDirectories(string reference)
    {
        Assert.Throws<InvalidDataException>(() => AgentTelemetryCatalog.Parse(ReplaceValues("ASPIRE_REFERENCE_FILES", reference)));
    }
 
    [Theory]
    [InlineData("ASPIRE_SKILLS")]
    [InlineData("ASPIRE_MCP_TOOLS")]
    [InlineData("ASPIRE_REFERENCE_FILES")]
    public void MissingOrDuplicateDeclarationsAreRejected(string variableName)
    {
        var declaration = GetDeclaration(variableName);
        Assert.Throws<InvalidDataException>(() => AgentTelemetryCatalog.Parse(Script.Replace(declaration, "", StringComparison.Ordinal)));
        Assert.Throws<InvalidDataException>(() => AgentTelemetryCatalog.Parse($"{Script}\n{declaration}"));
    }
 
    [Theory]
    [InlineData("ASPIRE_SKILLS", "new-skill NEW-SKILL")]
    [InlineData("ASPIRE_MCP_TOOLS", "new_tool NEW_TOOL")]
    [InlineData("ASPIRE_REFERENCE_FILES", "new-skill/references/new.md NEW-SKILL/references/new.md")]
    public void DuplicateValuesAreRejectedCaseInsensitively(string variableName, string values)
    {
        Assert.Throws<InvalidDataException>(() => AgentTelemetryCatalog.Parse(ReplaceValues(variableName, values)));
    }
 
    private static string ReplaceValues(string variableName, string values)
    {
        var prefix = $"{variableName}=\"";
        var valueStart = Script.IndexOf(prefix, StringComparison.Ordinal) + prefix.Length;
        Assert.True(valueStart >= prefix.Length);
        var valueEnd = Script.IndexOf('"', valueStart);
        Assert.True(valueEnd >= valueStart);
        return Script[..valueStart] + values + Script[valueEnd..];
    }
 
    private static string GetDeclaration(string variableName)
    {
        var prefix = $"{variableName}=\"";
        var start = Script.IndexOf(prefix, StringComparison.Ordinal);
        Assert.True(start >= 0);
        var end = Script.IndexOf('"', start + prefix.Length);
        Assert.True(end >= 0);
        return Script[start..(end + 1)];
    }
}