File: Templating\DotNetTemplateFactoryTests.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.NuGet;
using Aspire.Cli.Packaging;
using Aspire.Cli.Tests.Utils;
 
namespace Aspire.Cli.Tests.Templating;
 
public class DotNetTemplateFactoryTests
{
    private readonly ITestOutputHelper _outputHelper;
 
    public DotNetTemplateFactoryTests(ITestOutputHelper outputHelper)
    {
        _outputHelper = outputHelper;
    }
 
    private sealed class FakeNuGetPackageCache : INuGetPackageCache
    {
        public Task<IEnumerable<Aspire.Shared.NuGetPackageCli>> GetTemplatePackagesAsync(DirectoryInfo workingDirectory, bool prerelease, FileInfo? nugetConfigFile, CancellationToken cancellationToken)
        {
            _ = workingDirectory; _ = prerelease; _ = nugetConfigFile; _ = cancellationToken; return Task.FromResult<IEnumerable<Aspire.Shared.NuGetPackageCli>>([]);
        }
        public Task<IEnumerable<Aspire.Shared.NuGetPackageCli>> GetIntegrationPackagesAsync(DirectoryInfo workingDirectory, bool prerelease, FileInfo? nugetConfigFile, CancellationToken cancellationToken)
        {
            _ = workingDirectory; _ = prerelease; _ = nugetConfigFile; _ = cancellationToken; return Task.FromResult<IEnumerable<Aspire.Shared.NuGetPackageCli>>([]);
        }
        public Task<IEnumerable<Aspire.Shared.NuGetPackageCli>> GetCliPackagesAsync(DirectoryInfo workingDirectory, bool prerelease, FileInfo? nugetConfigFile, CancellationToken cancellationToken)
        {
            _ = workingDirectory; _ = prerelease; _ = nugetConfigFile; _ = cancellationToken; return Task.FromResult<IEnumerable<Aspire.Shared.NuGetPackageCli>>([]);
        }
        public Task<IEnumerable<Aspire.Shared.NuGetPackageCli>> GetPackagesAsync(DirectoryInfo workingDirectory, string packageId, Func<string, bool>? filter, bool prerelease, FileInfo? nugetConfigFile, CancellationToken cancellationToken)
        {
            _ = workingDirectory; _ = packageId; _ = filter; _ = prerelease; _ = nugetConfigFile; _ = cancellationToken; return Task.FromResult<IEnumerable<Aspire.Shared.NuGetPackageCli>>([]);
        }
    }
 
    private static PackageChannel CreateExplicitChannel(PackageMapping[] mappings) => 
        PackageChannel.CreateExplicitChannel("test", PackageChannelQuality.Both, mappings, new FakeNuGetPackageCache());
 
    private static async Task WriteNuGetConfigAsync(DirectoryInfo dir, string content)
    {
        var path = Path.Combine(dir.FullName, "NuGet.config");
        await File.WriteAllTextAsync(path, content);
    }
 
    /// <summary>
    /// Test that simulates the path comparison logic by testing NuGetConfigMerger behavior
    /// directly, which is what PromptToCreateOrUpdateNuGetConfigAsync will ultimately call.
    /// </summary>
    [Fact]
    public async Task NuGetConfigMerger_InPlaceCreation_WithoutExistingConfig_CreatesInWorkingDirectory()
    {
        // Arrange
        using var workspace = TemporaryWorkspace.Create(_outputHelper);
        var workingDir = workspace.WorkspaceRoot;
        
        var mappings = new[]
        {
            new PackageMapping("Aspire.*", "https://test.feed.example.com")
        };
        var channel = CreateExplicitChannel(mappings);
 
        // Act - Simulate in-place creation: output directory same as working directory
        await NuGetConfigMerger.CreateOrUpdateAsync(workingDir, channel);
 
        // Assert
        var nugetConfigPath = Path.Combine(workingDir.FullName, "NuGet.config");
        Assert.True(File.Exists(nugetConfigPath), "NuGet.config should be created in working directory for in-place creation");
    }
 
    [Fact]
    public async Task NuGetConfigMerger_InPlaceCreation_WithExistingConfig_UpdatesWorkingDirectoryConfig()
    {
        // Arrange
        using var workspace = TemporaryWorkspace.Create(_outputHelper);
        var workingDir = workspace.WorkspaceRoot;
        
        // Create existing NuGet.config in working directory without the required source
        await WriteNuGetConfigAsync(workingDir, 
            """
            <?xml version="1.0"?>
            <configuration>
                <packageSources>
                    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
                </packageSources>
            </configuration>
            """);
 
        var mappings = new[]
        {
            new PackageMapping("Aspire.*", "https://test.feed.example.com")
        };
        var channel = CreateExplicitChannel(mappings);
 
        // Act - Simulate in-place creation: output directory same as working directory
        await NuGetConfigMerger.CreateOrUpdateAsync(workingDir, channel);
 
        // Assert
        var nugetConfigPath = Path.Combine(workingDir.FullName, "NuGet.config");
        Assert.True(File.Exists(nugetConfigPath), "NuGet.config should exist in working directory");
        
        var content = await File.ReadAllTextAsync(nugetConfigPath);
        Assert.Contains("https://test.feed.example.com", content);
    }
 
    [Fact]
    public async Task NuGetConfigMerger_SubdirectoryCreation_WithParentConfig_IgnoresParentAndCreatesInOutputDirectory()
    {
        // Arrange
        using var workspace = TemporaryWorkspace.Create(_outputHelper);
        var workingDir = workspace.WorkspaceRoot;
        var outputDir = Directory.CreateDirectory(Path.Combine(workingDir.FullName, "MyProject"));
 
        // Create existing NuGet.config in working directory (parent)
        var parentConfigContent = 
            """
            <?xml version="1.0"?>
            <configuration>
                <packageSources>
                    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
                </packageSources>
            </configuration>
            """;
        await WriteNuGetConfigAsync(workingDir, parentConfigContent);
 
        var mappings = new[]
        {
            new PackageMapping("Aspire.*", "https://test.feed.example.com")
        };
        var channel = CreateExplicitChannel(mappings);
 
        // Act - Simulate subdirectory creation: output directory different from working directory
        await NuGetConfigMerger.CreateOrUpdateAsync(outputDir, channel);
 
        // Assert
        // Parent NuGet.config should remain unchanged
        var parentConfigPath = Path.Combine(workingDir.FullName, "NuGet.config");
        var parentContent = await File.ReadAllTextAsync(parentConfigPath);
        Assert.Equal(parentConfigContent.ReplaceLineEndings(), parentContent.ReplaceLineEndings());
        Assert.DoesNotContain("https://test.feed.example.com", parentContent);
 
        // New NuGet.config should be created in output directory
        var outputConfigPath = Path.Combine(outputDir.FullName, "NuGet.config");
        Assert.True(File.Exists(outputConfigPath), "NuGet.config should be created in output directory");
        
        var outputContent = await File.ReadAllTextAsync(outputConfigPath);
        Assert.Contains("https://test.feed.example.com", outputContent);
    }
 
    [Fact]
    public async Task NuGetConfigMerger_SubdirectoryCreation_WithExistingConfigInOutputDirectory_MergesInOutputDirectory()
    {
        // Arrange
        using var workspace = TemporaryWorkspace.Create(_outputHelper);
        var workingDir = workspace.WorkspaceRoot;
        var outputDir = Directory.CreateDirectory(Path.Combine(workingDir.FullName, "MyProject"));
 
        // Create existing NuGet.config in output directory
        await WriteNuGetConfigAsync(outputDir, 
            """
            <?xml version="1.0"?>
            <configuration>
                <packageSources>
                    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
                </packageSources>
            </configuration>
            """);
 
        var mappings = new[]
        {
            new PackageMapping("Aspire.*", "https://test.feed.example.com")
        };
        var channel = CreateExplicitChannel(mappings);
 
        // Act - Simulate subdirectory creation: merge into existing config in output directory
        await NuGetConfigMerger.CreateOrUpdateAsync(outputDir, channel);
 
        // Assert
        var outputConfigPath = Path.Combine(outputDir.FullName, "NuGet.config");
        Assert.True(File.Exists(outputConfigPath), "NuGet.config should exist in output directory");
        
        var content = await File.ReadAllTextAsync(outputConfigPath);
        Assert.Contains("https://test.feed.example.com", content);
        Assert.Contains("https://api.nuget.org/v3/index.json", content);
    }
 
    [Fact]
    public async Task NuGetConfigMerger_SubdirectoryCreation_WithoutAnyConfig_CreatesInOutputDirectory()
    {
        // Arrange
        using var workspace = TemporaryWorkspace.Create(_outputHelper);
        var workingDir = workspace.WorkspaceRoot;
        var outputDir = Directory.CreateDirectory(Path.Combine(workingDir.FullName, "MyProject"));
 
        var mappings = new[]
        {
            new PackageMapping("Aspire.*", "https://test.feed.example.com")
        };
        var channel = CreateExplicitChannel(mappings);
 
        // Act - Simulate subdirectory creation: create new config in output directory
        await NuGetConfigMerger.CreateOrUpdateAsync(outputDir, channel);
 
        // Assert
        // No NuGet.config should exist in working directory
        var workingConfigPath = Path.Combine(workingDir.FullName, "NuGet.config");
        Assert.False(File.Exists(workingConfigPath), "No NuGet.config should be created in working directory");
 
        // New NuGet.config should be created in output directory
        var outputConfigPath = Path.Combine(outputDir.FullName, "NuGet.config");
        Assert.True(File.Exists(outputConfigPath), "NuGet.config should be created in output directory");
        
        var content = await File.ReadAllTextAsync(outputConfigPath);
        Assert.Contains("https://test.feed.example.com", content);
    }
 
    [Fact]
    public async Task NuGetConfigMerger_ImplicitChannel_DoesNothing()
    {
        // Arrange
        using var workspace = TemporaryWorkspace.Create(_outputHelper);
        var workingDir = workspace.WorkspaceRoot;
        var outputDir = Directory.CreateDirectory(Path.Combine(workingDir.FullName, "MyProject"));
 
        var channel = PackageChannel.CreateImplicitChannel(new FakeNuGetPackageCache());
 
        // Act
        await NuGetConfigMerger.CreateOrUpdateAsync(outputDir, channel);
 
        // Assert
        // No NuGet.config should be created anywhere
        var workingConfigPath = Path.Combine(workingDir.FullName, "NuGet.config");
        var outputConfigPath = Path.Combine(outputDir.FullName, "NuGet.config");
        Assert.False(File.Exists(workingConfigPath), "No NuGet.config should be created for implicit channel");
        Assert.False(File.Exists(outputConfigPath), "No NuGet.config should be created for implicit channel");
    }
 
    [Fact]
    public async Task NuGetConfigMerger_ExplicitChannelWithoutMappings_DoesNothing()
    {
        // Arrange
        using var workspace = TemporaryWorkspace.Create(_outputHelper);
        var workingDir = workspace.WorkspaceRoot;
        var outputDir = Directory.CreateDirectory(Path.Combine(workingDir.FullName, "MyProject"));
 
        var channel = CreateExplicitChannel([]); // No mappings
 
        // Act
        await NuGetConfigMerger.CreateOrUpdateAsync(outputDir, channel);
 
        // Assert
        // No NuGet.config should be created anywhere
        var workingConfigPath = Path.Combine(workingDir.FullName, "NuGet.config");
        var outputConfigPath = Path.Combine(outputDir.FullName, "NuGet.config");
        Assert.False(File.Exists(workingConfigPath), "No NuGet.config should be created when no mappings exist");
        Assert.False(File.Exists(outputConfigPath), "No NuGet.config should be created when no mappings exist");
    }
}