File: CommandTests\New\DotnetAddPostActionTests.cs
Web Access
Project: ..\..\..\test\dotnet.Tests\dotnet.Tests.csproj (dotnet.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.DotNet.Cli.Commands.New.PostActions;
using Microsoft.TemplateEngine.Abstractions;
using Microsoft.TemplateEngine.Mocks;
using Microsoft.TemplateEngine.TestHelper;
 
namespace Microsoft.DotNet.Cli.New.Tests
{
    public class DotnetAddPostActionTests : IClassFixture<EnvironmentSettingsHelper>
    {
        private readonly IEngineEnvironmentSettings _engineEnvironmentSettings;
 
        public DotnetAddPostActionTests(EnvironmentSettingsHelper environmentSettingsHelper)
        {
            _engineEnvironmentSettings = environmentSettingsHelper.CreateEnvironment(hostIdentifier: GetType().Name, virtualize: true);
        }
 
        private static string TestCsprojFile
        {
            get
            {
                return @"
<Project Sdk=""Microsoft.NET.Sdk"">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp1.1</TargetFramework>
  </PropertyGroup>
</Project>";
            }
        }
 
        [Fact(DisplayName = nameof(AddRefFindsOneDefaultProjFileInOutputDirectory))]
        public void AddRefFindsOneDefaultProjFileInOutputDirectory()
        {
            string targetBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
            string projFileFullPath = Path.Combine(targetBasePath, "MyApp.proj");
            _engineEnvironmentSettings.Host.FileSystem.WriteAllText(projFileFullPath, TestCsprojFile);
 
            DotnetAddPostActionProcessor actionProcessor = new();
            string outputBasePath = targetBasePath;
 
            IReadOnlyList<string> projFilesFound = DotnetAddPostActionProcessor.FindProjFileAtOrAbovePath(_engineEnvironmentSettings.Host.FileSystem, outputBasePath, new HashSet<string>());
            Assert.Single(projFilesFound);
        }
 
        [Fact(DisplayName = nameof(AddRefFindsOneNameConfiguredProjFileInOutputDirectory))]
        public void AddRefFindsOneNameConfiguredProjFileInOutputDirectory()
        {
            string targetBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
            string fooprojFileFullPath = Path.Combine(targetBasePath, "MyApp.fooproj");
            _engineEnvironmentSettings.Host.FileSystem.WriteAllText(fooprojFileFullPath, TestCsprojFile);
 
            DotnetAddPostActionProcessor actionProcessor = new();
            string outputBasePath = targetBasePath;
 
            HashSet<string> projectFileExtensions = new() { ".fooproj" };
            IReadOnlyList<string> projFilesFound = DotnetAddPostActionProcessor.FindProjFileAtOrAbovePath(_engineEnvironmentSettings.Host.FileSystem, outputBasePath, projectFileExtensions);
            Assert.Single(projFilesFound);
        }
 
        [Fact(DisplayName = nameof(AddRefFindsOneNameConfiguredProjFileWhenMultipleExtensionsAreAllowed))]
        public void AddRefFindsOneNameConfiguredProjFileWhenMultipleExtensionsAreAllowed()
        {
            string targetBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
            string fooprojFileFullPath = Path.Combine(targetBasePath, "MyApp.fooproj");
            _engineEnvironmentSettings.Host.FileSystem.WriteAllText(fooprojFileFullPath, TestCsprojFile);
 
            DotnetAddPostActionProcessor actionProcessor = new();
            string outputBasePath = targetBasePath;
 
            HashSet<string> projectFileExtensions = new() { ".fooproj", ".barproj" };
            IReadOnlyList<string> projFilesFound = DotnetAddPostActionProcessor.FindProjFileAtOrAbovePath(_engineEnvironmentSettings.Host.FileSystem, outputBasePath, projectFileExtensions);
            Assert.Single(projFilesFound);
        }
 
        [Fact(DisplayName = nameof(AddRefIgnoresOtherProjectTypesWhenMultipleTypesAreAllowed))]
        public void AddRefIgnoresOtherProjectTypesWhenMultipleTypesAreAllowed()
        {
            string targetBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
            string fooprojFileFullPath = Path.Combine(targetBasePath, "MyApp.fooproj");
            _engineEnvironmentSettings.Host.FileSystem.WriteAllText(fooprojFileFullPath, TestCsprojFile);
 
            string barprojFileFullPath = Path.Combine(targetBasePath, "MyApp.barproj");
            _engineEnvironmentSettings.Host.FileSystem.WriteAllText(barprojFileFullPath, TestCsprojFile);
 
            string csprojFileFullPath = Path.Combine(targetBasePath, "MyApp.csproj");
            _engineEnvironmentSettings.Host.FileSystem.WriteAllText(csprojFileFullPath, TestCsprojFile);
 
            string fsprojFileFullPath = Path.Combine(targetBasePath, "MyApp.fsproj");
            _engineEnvironmentSettings.Host.FileSystem.WriteAllText(fsprojFileFullPath, TestCsprojFile);
 
            DotnetAddPostActionProcessor actionProcessor = new();
            string outputBasePath = targetBasePath;
 
            HashSet<string> projectFileExtensions = new() { ".bazproj", ".fsproj" };
            IReadOnlyList<string> projFilesFound = DotnetAddPostActionProcessor.FindProjFileAtOrAbovePath(_engineEnvironmentSettings.Host.FileSystem, outputBasePath, projectFileExtensions);
            Assert.Single(projFilesFound);
        }
 
        [Fact(DisplayName = nameof(AddRefFindsOneDefaultProjFileInAncestorOfOutputDirectory))]
        public void AddRefFindsOneDefaultProjFileInAncestorOfOutputDirectory()
        {
            string targetBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
            string projFileFullPath = Path.Combine(targetBasePath, "MyApp.xproj");
            _engineEnvironmentSettings.Host.FileSystem.WriteAllText(projFileFullPath, TestCsprojFile);
 
            string outputBasePath = Path.Combine(targetBasePath, "ChildDir", "GrandchildDir");
            _engineEnvironmentSettings.Host.FileSystem.CreateDirectory(outputBasePath);
 
            DotnetAddPostActionProcessor actionProcessor = new();
            IReadOnlyList<string> projFilesFound = DotnetAddPostActionProcessor.FindProjFileAtOrAbovePath(_engineEnvironmentSettings.Host.FileSystem, outputBasePath, new HashSet<string>());
            Assert.Single(projFilesFound);
        }
 
        [Fact(DisplayName = nameof(AddRefFindsMultipleDefaultProjFilesInOutputDirectory))]
        public void AddRefFindsMultipleDefaultProjFilesInOutputDirectory()
        {
            string projFilesOriginalContent = TestCsprojFile;
            string targetBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
            string projFileFullPathOne = Path.Combine(targetBasePath, "MyApp.anysproj");
            _engineEnvironmentSettings.Host.FileSystem.WriteAllText(projFileFullPathOne, projFilesOriginalContent);
 
            string projFileFullPathTwo = Path.Combine(targetBasePath, "MyApp2.someproj");
            _engineEnvironmentSettings.Host.FileSystem.WriteAllText(projFileFullPathTwo, projFilesOriginalContent);
 
            DotnetAddPostActionProcessor actionProcessor = new();
            string outputBasePath = targetBasePath;
            IReadOnlyList<string> projFilesFound = DotnetAddPostActionProcessor.FindProjFileAtOrAbovePath(_engineEnvironmentSettings.Host.FileSystem, outputBasePath, new HashSet<string>());
            Assert.Equal(2, projFilesFound.Count);
        }
 
        [Fact(DisplayName = nameof(AddRefFindsMultipleDefaultProjFilesInAncestorOfOutputDirectory))]
        public void AddRefFindsMultipleDefaultProjFilesInAncestorOfOutputDirectory()
        {
            string projFilesOriginalContent = TestCsprojFile;
            string targetBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
            string projFileFullPathOne = Path.Combine(targetBasePath, "MyApp.fooproj");
            _engineEnvironmentSettings.Host.FileSystem.WriteAllText(projFileFullPathOne, projFilesOriginalContent);
 
            string projFileFullPathTwo = Path.Combine(targetBasePath, "MyApp2.barproj");
            _engineEnvironmentSettings.Host.FileSystem.WriteAllText(projFileFullPathTwo, projFilesOriginalContent);
 
            string outputBasePath = Path.Combine(targetBasePath, "ChildDir", "GrandchildDir");
 
            DotnetAddPostActionProcessor actionProcessor = new();
            IReadOnlyList<string> projFilesFound = DotnetAddPostActionProcessor.FindProjFileAtOrAbovePath(_engineEnvironmentSettings.Host.FileSystem, outputBasePath, new HashSet<string>());
            Assert.Equal(2, projFilesFound.Count);
        }
 
        [Fact(DisplayName = nameof(AddRefCanHandleProjectFileRenames))]
        public void AddRefCanHandleProjectFileRenames()
        {
            var callback = new MockAddProjectReferenceCallback();
            DotnetAddPostActionProcessor actionProcessor = new(callback.AddPackageReference, callback.AddProjectReference);
 
            string targetBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
            string projFileFullPath = Path.Combine(targetBasePath, "MyApp.csproj");
            string referencedProjFileFullPath = Path.Combine(targetBasePath, "NewName.csproj");
 
            var args = new Dictionary<string, string>() { { "targetFiles", "[\"MyApp.csproj\"]" }, { "referenceType", "project" }, { "reference", "./OldName.csproj" } };
            var postAction = new MockPostAction(default, default, default, default, default!) { ActionId = DotnetAddPostActionProcessor.ActionProcessorId, Args = args };
 
            MockCreationEffects creationEffects = new MockCreationEffects()
                .WithFileChange(new MockFileChange("./OldName.csproj", "./NewName.csproj", ChangeKind.Create))
                .WithFileChange(new MockFileChange("./MyApp.csproj", "./MyApp.csproj", ChangeKind.Create));
 
            actionProcessor.Process(
                _engineEnvironmentSettings,
                postAction,
                creationEffects,
                new MockCreationResult(),
                targetBasePath);
 
            Assert.Equal(projFileFullPath, callback.Target);
            Assert.Equal(referencedProjFileFullPath, callback.Reference);
        }
 
        [Fact(DisplayName = nameof(AddRefCanHandleProjectFilesWithoutRenames))]
        public void AddRefCanHandleProjectFilesWithoutRenames()
        {
            var callback = new MockAddProjectReferenceCallback();
            DotnetAddPostActionProcessor actionProcessor = new(callback.AddPackageReference, callback.AddProjectReference);
 
            string targetBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
            string projFileFullPath = Path.Combine(targetBasePath, "MyApp.csproj");
            string referencedProjFileFullPath = Path.Combine(targetBasePath, "Reference.csproj");
 
            var args = new Dictionary<string, string>() { { "targetFiles", "[\"MyApp.csproj\"]" }, { "referenceType", "project" }, { "reference", "./Reference.csproj" } };
            var postAction = new MockPostAction(default, default, default, default, default!) { ActionId = DotnetAddPostActionProcessor.ActionProcessorId, Args = args };
 
            MockCreationEffects creationEffects = new MockCreationEffects()
                .WithFileChange(new MockFileChange("./MyApp.csproj", "./MyApp.csproj", ChangeKind.Create));
 
            actionProcessor.Process(
                _engineEnvironmentSettings,
                postAction,
                creationEffects,
                new MockCreationResult(),
                targetBasePath);
 
            Assert.Equal(projFileFullPath, callback.Target);
            Assert.Equal(referencedProjFileFullPath, callback.Reference);
        }
 
        [Fact(DisplayName = nameof(AddRefCanHandleExistingProjectFiles))]
        public void AddRefCanHandleExistingProjectFiles()
        {
            var callback = new MockAddProjectReferenceCallback();
            DotnetAddPostActionProcessor actionProcessor = new(callback.AddPackageReference, callback.AddProjectReference);
             string targetBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath(); //Commented code throws exception
            _engineEnvironmentSettings.Host.VirtualizeDirectory(targetBasePath);
 
            const string existingProjectFolder = "ExistingProjectFolder";
            string existingProjectPath = Path.Combine(targetBasePath, existingProjectFolder);
 
            const string existingProjectFile = "ExistingProject.csproj";
            string existingProjectFileFullPath = Path.Combine(existingProjectPath, existingProjectFile);
            _engineEnvironmentSettings.Host.FileSystem.WriteAllText(existingProjectFileFullPath, TestCsprojFile);
 
            string referencedProjectFileFullPath = Path.Combine(targetBasePath, "Reference.csproj");
 
            //TODO: Add test for both target files passed as array and string
            var args = new Dictionary<string, string>()
            {
                { "targetFiles", $"[\"{existingProjectFolder}/{existingProjectFile}\"]" },
                { "referenceType", "project" },
                { "reference", "Reference.csproj" }
            };
            var postAction =
                new MockPostAction(default, default, default, default, default!)
                {
                    ActionId = DotnetAddPostActionProcessor.ActionProcessorId, Args = args
                };
 
            MockCreationEffects creationEffects = new MockCreationEffects();
 
            actionProcessor.Process(
                _engineEnvironmentSettings,
                postAction,
                creationEffects,
                new MockCreationResult(),
                targetBasePath);
 
            Assert.Equal(existingProjectFileFullPath, callback.Target);
            Assert.Equal(referencedProjectFileFullPath, callback.Reference);
        }
 
        [Fact(DisplayName = nameof(AddRefCanTargetASingleProjectWithAJsonArray))]
        public void AddRefCanTargetASingleProjectWithAJsonArray()
        {
            var callback = new MockAddProjectReferenceCallback();
            DotnetAddPostActionProcessor actionProcessor = new(callback.AddPackageReference, callback.AddProjectReference);
 
            string targetBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
            string projFileFullPath = Path.Combine(targetBasePath, "MyApp.csproj");
 
            var args = new Dictionary<string, string>() { { "targetFiles", "[\"MyApp.csproj\"]" }, { "referenceType", "package" }, { "reference", "System.Net.Json" } };
            var postAction = new MockPostAction(default, default, default, default, default!) { ActionId = DotnetAddPostActionProcessor.ActionProcessorId, Args = args };
 
            MockCreationEffects creationEffects = new MockCreationEffects()
                .WithFileChange(new MockFileChange("./MyApp.csproj", "./MyApp.csproj", ChangeKind.Create));
 
            actionProcessor.Process(
                _engineEnvironmentSettings,
                postAction,
                creationEffects,
                new MockCreationResult(),
                targetBasePath);
 
            Assert.Equal(projFileFullPath, callback.Target);
            Assert.Equal("System.Net.Json", callback.Reference);
        }
 
        [Fact(DisplayName = nameof(AddRefCanTargetASingleProjectWithTheProjectName))]
        public void AddRefCanTargetASingleProjectWithTheProjectName()
        {
            var callback = new MockAddProjectReferenceCallback();
            DotnetAddPostActionProcessor actionProcessor = new(callback.AddPackageReference, callback.AddProjectReference);
 
            string targetBasePath = _engineEnvironmentSettings.GetTempVirtualizedPath();
            string projFileFullPath = Path.Combine(targetBasePath, "MyApp.csproj");
 
            var args = new Dictionary<string, string>() { { "targetFiles", "MyApp.csproj" }, { "referenceType", "package" }, { "reference", "System.Net.Json" } };
            var postAction = new MockPostAction(default, default, default, default, default!) { ActionId = DotnetAddPostActionProcessor.ActionProcessorId, Args = args };
 
            MockCreationEffects creationEffects = new MockCreationEffects()
                .WithFileChange(new MockFileChange("./MyApp.csproj", "./MyApp.csproj", ChangeKind.Create));
 
 
            actionProcessor.Process(
                _engineEnvironmentSettings,
                postAction,
                creationEffects,
                new MockCreationResult(),
                targetBasePath);
 
            Assert.Equal(projFileFullPath, callback.Target);
            Assert.Equal("System.Net.Json", callback.Reference);
        }
 
        private class MockAddProjectReferenceCallback
        {
            public string? Target { get; private set; }
 
            public string? Reference { get; private set; }
 
            public bool AddProjectReference(string target, string reference)
            {
                if (Target != null)
                {
                    throw new Exception($"{nameof(AddProjectReference)} is called more than once.");
                }
 
                Target = target;
                Reference = reference;
 
                return true;
            }
 
            public bool AddPackageReference(string target, string reference, string? version)
            {
                if (Target != null)
                {
                    throw new Exception($"{nameof(AddPackageReference)} is called more than once.");
                }
 
                Target = target;
                Reference = reference;
 
                return true;
            }
        }
    }
}