File: PublishToSymbolServerTest.cs
Web Access
Project: src\src\Microsoft.DotNet.Build.Tasks.Feed.Tests\Microsoft.DotNet.Build.Tasks.Feed.Tests.csproj (Microsoft.DotNet.Build.Tasks.Feed.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.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Arcade.Common;
using Microsoft.Arcade.Test.Common;
using Microsoft.DotNet.Arcade.Test.Common;
using Microsoft.DotNet.Build.Tasks.Feed.Model;
using Microsoft.DotNet.Maestro.Client.Models;
using Xunit;
 
namespace Microsoft.DotNet.Build.Tasks.Feed.Tests
{
    public class PublishToSymbolServerTest
    {
        private const string MsdlToken = "msdlToken";
        private const string SymWebToken = "SymWebToken";
        private const string TargetUrl = "TargetUrl";
        private const string Msdl = "https://microsoftpublicsymbols.artifacts.visualstudio.com/DefaultCollection";
        private const string SymWeb = "https://microsoft.artifacts.visualstudio.com/DefaultCollection";
 
        [Theory]
        [InlineData(SymbolTargetType.Msdl, Msdl)]
        [InlineData(SymbolTargetType.SymWeb, SymWeb)]
        public void PublishToSymbolServersTest(SymbolTargetType symbolTargetType , string symbolServer)
        {
            var publishTask = new PublishArtifactsInManifestV3();
            var feedConfigsForSymbols = new HashSet<TargetFeedConfig>();
            feedConfigsForSymbols.Add(new TargetFeedConfig(
                TargetFeedContentType.Symbols,
                "TargetUrl",
                FeedType.AzDoNugetFeed,
                MsdlToken,
                new List<string>(),
                AssetSelection.All,
                isolated: true,
                @internal: false,
                allowOverwrite: true,
                symbolTargetType));
            Dictionary<string, string> test =
                publishTask.GetTargetSymbolServers(feedConfigsForSymbols, MsdlToken, SymWebToken);
            Assert.True(
                test.ContainsKey(symbolServer));
            Assert.True(test.Count == 1);
        }
 
        [Fact]
        public void PublishToBothSymbolServerTest()
        {
            var publishTask = new PublishArtifactsInManifestV3();
            var feedConfigsForSymbols = new HashSet<TargetFeedConfig>();
            feedConfigsForSymbols.Add(new TargetFeedConfig(
                TargetFeedContentType.Symbols,
                "testUrl",
                FeedType.AzDoNugetFeed,
                SymWebToken,
                new List<string>(),
                AssetSelection.All,
                isolated: true,
                @internal: false,
                allowOverwrite: true,
                SymbolTargetType.SymWeb));
            feedConfigsForSymbols.Add(new TargetFeedConfig(
                TargetFeedContentType.Symbols,
                TargetUrl,
                FeedType.AzDoNugetFeed,
                MsdlToken,
                new List<string>(),
                AssetSelection.All,
                isolated: true,
                @internal: false,
                allowOverwrite: true,
                SymbolTargetType.Msdl));
            Dictionary<string, string> test =
                publishTask.GetTargetSymbolServers(feedConfigsForSymbols, MsdlToken, SymWebToken);
            Assert.True(
                test.ContainsKey(Msdl));
            Assert.True(test.ContainsKey(SymWeb));
            Assert.True(test.Count == 2);
        }
 
        [Fact]
        public async Task TemporarySymbolDirectoryDoesNotExists()
        {
            var buildEngine = new MockBuildEngine();
            var task = new PublishArtifactsInManifestV3()
            {
                BuildEngine = buildEngine,
            };
            var path = TestInputs.GetFullPath("Symbol");
            var buildAsset = new Dictionary<string, Asset>().AsReadOnly();
            await task.HandleSymbolPublishingAsync(path, MsdlToken, SymWebToken, "", false, buildAsset, null, path);
            Assert.True(task.Log.HasLoggedErrors);
        }
 
        [Fact]
        public void TemporarySymbolsDirectoryTest()
        {
            var buildEngine = new MockBuildEngine();
            var publishTask = new PublishArtifactsInManifestV3()
            {
                BuildEngine = buildEngine,
            };
            var path = TestInputs.GetFullPath("Test");
            publishTask.EnsureTemporaryDirectoryExists(path);
            Assert.True(Directory.Exists(path));
            publishTask.DeleteTemporaryDirectory(path);
            Assert.False(Directory.Exists(path));
        }
 
        [Fact]
        public void PublishSymbolApiIsCalledTest()
        {
            var path = TestInputs.GetFullPath("Symbols");
            string[] fileEntries = Directory.GetFiles(path);
            var feedConfigsForSymbols = new HashSet<TargetFeedConfig>();
            feedConfigsForSymbols.Add(new TargetFeedConfig(
                TargetFeedContentType.Symbols,
                TargetUrl,
                FeedType.AzDoNugetFeed,
                SymWebToken,
                new List<string>(),
                AssetSelection.All,
                isolated: true,
                @internal: false,
                allowOverwrite: true,
                SymbolTargetType.SymWeb));
            Assert.True(PublishSymbolsHelper.PublishAsync(
                log: null,
                symbolServerPath: path,
                personalAccessToken: SymWebToken,
                inputPackages: fileEntries,
                inputFiles: fileEntries,
                packageExcludeFiles: null,
                expirationInDays: 365,
                convertPortablePdbsToWindowsPdbs: false,
                publishSpecialClrFiles: false,
                pdbConversionTreatAsWarning: null,
                treatPdbConversionIssuesAsInfo: false,
                dryRun: false,
                timer: false,
                verboseLogging: false).IsCompleted);
        }
 
        [Fact]
        public void DownloadFileAsyncSucceedsForValidUrl()
        {
            var buildEngine = new MockBuildEngine();
            var publishTask = new PublishArtifactsInManifestV3
            {
                BuildEngine = buildEngine,
            };
 
            var testFile = Path.Combine("Symbols", "test.txt");
            var responseContent = TestInputs.ReadAllBytes(testFile);
            var response = new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new ByteArrayContent(responseContent)
            };
 
            using HttpClient client = FakeHttpClient.WithResponses(response);
            var path = TestInputs.GetFullPath(Guid.NewGuid().ToString());
 
            var test = publishTask.DownloadFileAsync(
                client,
                PublishArtifactsInManifestBase.ArtifactName.BlobArtifacts,
                "1234",
                "test.txt",
                path);
 
            Assert.True(File.Exists(path));
            publishTask.DeleteTemporaryFiles(path);
            publishTask.DeleteTemporaryDirectory(path);
        }
 
        [Theory]
        [InlineData(HttpStatusCode.BadRequest)]
        [InlineData(HttpStatusCode.NotFound)]
        [InlineData(HttpStatusCode.GatewayTimeout)]
        public async Task DownloadFileAsyncFailsForInValidUrlTest(HttpStatusCode httpStatus)
        {
            var buildEngine = new MockBuildEngine();
            var publishTask = new PublishArtifactsInManifestV3
            {
                BuildEngine = buildEngine,
            };
            var testFile = Path.Combine("Symbols", "test.txt");
            var responseContent = TestInputs.ReadAllBytes(testFile);
            publishTask.RetryHandler = new ExponentialRetry() { MaxAttempts = 3, DelayBase = 1 };
 
            var responses = new[]
            {
                new HttpResponseMessage(httpStatus)
                {
                    Content = new ByteArrayContent(responseContent)
                },
                new HttpResponseMessage(httpStatus)
                {
                    Content = new ByteArrayContent(responseContent)
                },
                new HttpResponseMessage(httpStatus)
                {
                    Content = new ByteArrayContent(responseContent)
                }
            };
            using HttpClient client = FakeHttpClient.WithResponses(responses);
            var path = TestInputs.GetFullPath(Guid.NewGuid().ToString());
 
            var actualError = await Assert.ThrowsAsync<Exception>(() =>
                publishTask.DownloadFileAsync(
                    client,
                    PublishArtifactsInManifestBase.ArtifactName.BlobArtifacts,
                    "1234",
                    "test.txt",
                    path));
            Assert.Contains($"Failed to download local file '{path}' after {publishTask.RetryHandler.MaxAttempts} attempts.  See inner exception for details.", actualError.Message);
        }
 
        [Theory]
        [InlineData(HttpStatusCode.BadRequest)]
        [InlineData(HttpStatusCode.NotFound)]
        [InlineData(HttpStatusCode.GatewayTimeout)]
        public async Task DownloadFailureWhenStatusCodeIsInvalid(HttpStatusCode httpStatus)
        {
            var buildEngine = new MockBuildEngine();
            var publishTask = new PublishArtifactsInManifestV3
            {
                BuildEngine = buildEngine,
            };
            var testFile = Path.Combine("Symbols", "test.txt");
            var responseContent = TestInputs.ReadAllBytes(testFile);
            publishTask.RetryHandler = new ExponentialRetry() { MaxAttempts = 3, DelayBase = 1 };
 
            var responses = new[]
            {
                new HttpResponseMessage(httpStatus)
                {
                    Content = new ByteArrayContent(responseContent)
                },
                new HttpResponseMessage(httpStatus)
                {
                    Content = new ByteArrayContent(responseContent)
                },
                new HttpResponseMessage(httpStatus)
                {
                    Content = new ByteArrayContent(responseContent)
                }
            };
            using HttpClient client = FakeHttpClient.WithResponses(responses);
            var path = TestInputs.GetFullPath(Guid.NewGuid().ToString());
 
            var actualError = await Assert.ThrowsAsync<Exception>(() =>
                publishTask.DownloadFileAsync(
                    client,
                    PublishArtifactsInManifestBase.ArtifactName.BlobArtifacts,
                    "1234",
                    "test.txt",
                    path));
            Assert.Contains($"Failed to download local file '{path}' after {publishTask.RetryHandler.MaxAttempts} attempts.  See inner exception for details.", actualError.Message);
        }
 
        [Theory]
        [InlineData(HttpStatusCode.BadRequest)]
        [InlineData(HttpStatusCode.NotFound)]
        [InlineData(HttpStatusCode.GatewayTimeout)]
        public async Task DownloadFileSuccessfulAfterRetryTest(HttpStatusCode httpStatus)
        {
            var buildEngine = new MockBuildEngine();
            var publishTask = new PublishArtifactsInManifestV3
            {
                BuildEngine = buildEngine,
            };
            var testFile = Path.Combine("Symbols", "test.txt");
            var responseContent = TestInputs.ReadAllBytes(testFile);
            publishTask.RetryHandler = new ExponentialRetry() { MaxAttempts = 2, DelayBase = 1 };
 
            var responses = new[]
            {
                new HttpResponseMessage(httpStatus)
                {
                    Content = new ByteArrayContent(responseContent)
                },
                new HttpResponseMessage(HttpStatusCode.OK)
                {
                    Content = new ByteArrayContent(responseContent)
                }
            };
            using HttpClient client = FakeHttpClient.WithResponses(responses);
            var path = TestInputs.GetFullPath(Guid.NewGuid().ToString());
 
            await publishTask.DownloadFileAsync(
                client,
                PublishArtifactsInManifestBase.ArtifactName.BlobArtifacts,
                "1234",
                "test.txt",
                path);
            Assert.True(File.Exists(path));
            publishTask.DeleteTemporaryFiles(path);
            publishTask.DeleteTemporaryDirectory(path);
        }
 
        [Theory]
        [InlineData(PublishArtifactsInManifestBase.ArtifactName.BlobArtifacts, "1")]
        [InlineData(PublishArtifactsInManifestBase.ArtifactName.PackageArtifacts, "1234")]
        public async Task GetContainerIdToDownloadArtifactAsync(PublishArtifactsInManifestBase.ArtifactName artifactName, string containerId)
        {
            var buildEngine = new MockBuildEngine();
            var publishTask = new PublishArtifactsInManifestV3
            {
                BuildEngine = buildEngine,
            };
            publishTask.BuildId = "1243456";
            var testPackageName = Path.Combine("Symbols", "test.txt");
            var responseContent = TestInputs.ReadAllBytes(testPackageName);
            var responses = new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new ByteArrayContent(responseContent)
            };
 
            using HttpClient client = FakeHttpClient.WithResponses(responses);
            var test = await publishTask.GetContainerIdAsync(
                client,
                artifactName);
            Assert.Equal(containerId, test);
        }
 
        [Theory]
        [InlineData(HttpStatusCode.BadRequest)]
        [InlineData(HttpStatusCode.NotFound)]
        public async Task ErrorAfterMaxRetriesToGetContainerId(HttpStatusCode httpStatus)
        {
            var buildEngine = new MockBuildEngine();
            var publishTask = new PublishArtifactsInManifestV3
            {
                BuildEngine = buildEngine,
            };
            publishTask.BuildId = "1243456";
            publishTask.RetryHandler = new ExponentialRetry() {MaxAttempts = 3, DelayBase = 1};
 
            var testPackageName = Path.Combine("Symbols", "test.txt");
            var responseContent = TestInputs.ReadAllBytes(testPackageName);
            var responses = new[]
            {
                new HttpResponseMessage(httpStatus)
                {
                    Content = new ByteArrayContent(responseContent)
                },
                new HttpResponseMessage(httpStatus)
                {
                    Content = new ByteArrayContent(responseContent)
                },
                new HttpResponseMessage(httpStatus)
                {
                    Content = new ByteArrayContent(responseContent)
                }
            };
 
            using HttpClient client = FakeHttpClient.WithResponses(responses);
 
            var actualError = await Assert.ThrowsAsync<Exception>(() =>
                publishTask.GetContainerIdAsync(
                    client,
                    PublishArtifactsInManifestBase.ArtifactName.BlobArtifacts));
            Assert.Contains($"Failed to get container id after {publishTask.RetryHandler.MaxAttempts} attempts.  See inner exception for details,", actualError.Message);
        }
    }
}