File: CredentialHandlingTests.cs
Web Access
Project: src\src\Microsoft.DotNet.SetupNugetSources.Tests\Microsoft.DotNet.SetupNugetSources.Tests.csproj (Microsoft.DotNet.SetupNugetSources.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;
using System.IO;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.DotNet.XUnitExtensions;
using Xunit;
 
namespace Microsoft.DotNet.SetupNugetSources.Tests
{
    public class CredentialHandlingTests : IClassFixture<SetupNugetSourcesFixture>, IDisposable
    {
        private readonly ScriptRunner _scriptRunner;
        private readonly string _testOutputDirectory;
 
        public CredentialHandlingTests(SetupNugetSourcesFixture fixture)
        {
            _testOutputDirectory = Path.Combine(Path.GetTempPath(), "SetupNugetSourcesTests", Guid.NewGuid().ToString());
            Directory.CreateDirectory(_testOutputDirectory);
            _scriptRunner = fixture.ScriptRunner;
        }
 
        public void Dispose()
        {
            try
            {
                if (Directory.Exists(_testOutputDirectory))
                {
                    Directory.Delete(_testOutputDirectory, true);
                }
            }
            catch { }
        }
 
        [Fact]
        public async Task ConfigWithCredentialProvided_AddsCredentialsForInternalFeeds()
        {
            // Arrange
            var originalConfig = @"<?xml version=""1.0"" encoding=""utf-8""?>
<configuration>
  <packageSources>
    <add key=""dotnet-public"" value=""https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json"" />
    <add key=""dotnet6"" value=""https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json"" />
  </packageSources>
</configuration>";
            var configPath = Path.Combine(_testOutputDirectory, "nuget.config");
            await Task.Run(() => File.WriteAllText(configPath, originalConfig));
            var testCredential = "Placeholder";
            // Act
            var result = await _scriptRunner.RunScript(configPath, testCredential);
 
            // Assert
            result.exitCode.Should().Be(0, "script should succeed, but got error: {result.error}");
            var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath));
 
            // Should add internal feeds
            modifiedConfig.ShouldContainPackageSource("dotnet6-internal");
            modifiedConfig.ShouldContainPackageSource("dotnet6-internal-transport");
 
            // Should add credentials for internal feeds
            modifiedConfig.ShouldContainCredentials("dotnet6-internal", "dn-bot", "should add credentials for internal feed");
            modifiedConfig.ShouldContainCredentials("dotnet6-internal-transport", "dn-bot", "should add credentials for transport feed");
 
            // Should use v2 endpoints when credentials are provided
            modifiedConfig.ShouldContainPackageSource("dotnet6-internal",
                "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal/nuget/v2",
                "should use v2 endpoint when credentials provided");
            modifiedConfig.ShouldContainPackageSource("dotnet6-internal-transport",
                "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal-transport/nuget/v2",
                "should use v2 endpoint when credentials provided");
        }
 
        [Fact]
        public async Task ConfigWithNoCredential_DoesNotAddCredentials()
        {
            // Arrange
            var originalConfig = @"<?xml version=""1.0"" encoding=""utf-8""?>
<configuration>
  <packageSources>
    <add key=""dotnet-public"" value=""https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json"" />
    <add key=""dotnet6"" value=""https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json"" />
  </packageSources>
</configuration>";
            var configPath = Path.Combine(_testOutputDirectory, "nuget.config");
            await Task.Run(() => File.WriteAllText(configPath, originalConfig));
 
            // Act - No credential provided
            var result = await _scriptRunner.RunScript(configPath);
 
            // Assert
            result.exitCode.Should().Be(0, "script should succeed, but got error: {result.error}");
            var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath));
 
            // Should add internal feeds
            modifiedConfig.ShouldContainPackageSource("dotnet6-internal");
            modifiedConfig.ShouldContainPackageSource("dotnet6-internal-transport");
 
            // Should NOT add credentials
            modifiedConfig.ShouldNotContainCredentials("dotnet6-internal", "should not add credentials without credential");
            modifiedConfig.ShouldNotContainCredentials("dotnet6-internal-transport", "should not add credentials without credential");
 
            // Should use v3 endpoints when no credentials are provided
            modifiedConfig.ShouldContainPackageSource("dotnet6-internal",
                "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal/nuget/v3/index.json",
                "should use v3 endpoint when no credentials provided");
        }
 
        [Fact]
        public async Task ConfigWithExistingCredentials_PreservesAndAddsNew()
        {
            // Arrange
            var originalConfig = @"<?xml version=""1.0"" encoding=""utf-8""?>
<configuration>
  <packageSources>
    <add key=""dotnet-public"" value=""https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json"" />
    <add key=""dotnet6"" value=""https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json"" />
    <add key=""existing-private"" value=""https://example.com/nuget"" />
  </packageSources>
  <packageSourceCredentials>
    <existing-private>
      <add key=""Username"" value=""existing-user"" />
      <add key=""ClearTextPassword"" value=""Placeholder"" />
    </existing-private>
  </packageSourceCredentials>
</configuration>";
            var configPath = Path.Combine(_testOutputDirectory, "nuget.config");
            await Task.Run(() => File.WriteAllText(configPath, originalConfig));
            var testCredential = "Placeholder";
            // Act
            var result = await _scriptRunner.RunScript(configPath, testCredential);
 
            // Assert
            result.exitCode.Should().Be(0, "script should succeed, but got error: {result.error}");
            var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath));
 
            // Should preserve existing credentials
            modifiedConfig.ShouldContainCredentials("existing-private", "existing-user", "should preserve existing credentials");
 
            // Should add new credentials for internal feeds
            modifiedConfig.ShouldContainCredentials("dotnet6-internal", "dn-bot", "should add credentials for new internal feed");
            modifiedConfig.ShouldContainCredentials("dotnet6-internal-transport", "dn-bot", "should add credentials for new transport feed");
        }
 
        [Fact]
        public async Task ConfigWithDarcIntFeeds_AddsCredentialsForEnabledFeeds()
        {
            // Arrange
            var originalConfig = @"<?xml version=""1.0"" encoding=""utf-8""?>
<configuration>
  <packageSources>
    <add key=""dotnet-public"" value=""https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json"" />
    <add key=""dotnet6"" value=""https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json"" />
  </packageSources>
  <disabledPackageSources>
    <add key=""darc-int-dotnet-roslyn-12345"" value=""true"" />
  </disabledPackageSources>
</configuration>";
            var configPath = Path.Combine(_testOutputDirectory, "nuget.config");
            await Task.Run(() => File.WriteAllText(configPath, originalConfig));
            var testCredential = "Placeholder";
            // Act
            var result = await _scriptRunner.RunScript(configPath, testCredential);
 
            // Assert
            result.exitCode.Should().Be(0, "script should succeed, but got error: {result.error}");
            var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath));
 
            // Should enable the darc-int feed
            modifiedConfig.ShouldNotBeDisabled("darc-int-dotnet-roslyn-12345", "darc-int feed should be enabled");
 
            // Should add credentials for enabled darc-int feed
            modifiedConfig.ShouldContainCredentials("darc-int-dotnet-roslyn-12345", "dn-bot", "should add credentials for enabled darc-int feed");
 
            // Should add credentials for new internal feeds
            modifiedConfig.ShouldContainCredentials("dotnet6-internal", "dn-bot", "should add credentials for internal feed");
            modifiedConfig.ShouldContainCredentials("dotnet6-internal-transport", "dn-bot", "should add credentials for transport feed");
        }
 
        [Fact]
        public async Task ConfigWithNoCredentialButExistingCredentials_DoesNotRemoveExistingCredentials()
        {
            // Arrange
            var originalConfig = @"<?xml version=""1.0"" encoding=""utf-8""?>
<configuration>
  <packageSources>
    <add key=""dotnet-public"" value=""https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json"" />
    <add key=""dotnet6"" value=""https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json"" />
  </packageSources>
  <packageSourceCredentials>
    <dotnet6-internal>
      <add key=""Username"" value=""dn-bot"" />
      <add key=""ClearTextPassword"" value=""Placeholder"" />
    </dotnet6-internal>
  </packageSourceCredentials>
</configuration>";
            var configPath = Path.Combine(_testOutputDirectory, "nuget.config");
            await Task.Run(() => File.WriteAllText(configPath, originalConfig));
 
            // Act - No credential provided
            var result = await _scriptRunner.RunScript(configPath);
 
            // Assert
            result.exitCode.Should().Be(0, "script should succeed, but got error: {result.error}");
            var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath));
 
            // Should preserve existing credentials
            modifiedConfig.ShouldContainCredentials("dotnet6-internal", "dn-bot", "should preserve existing credentials");
 
            // Should not add credentials for new feeds without credential
            modifiedConfig.ShouldNotContainCredentials("dotnet6-internal-transport", "should not add credentials without credential");
        }
    }
}