File: ToolManifestTests\ToolManifestEditorTests.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;
using Microsoft.DotNet.Cli.ToolManifest;
using Microsoft.DotNet.Cli.ToolPackage;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.Extensions.DependencyModel.Tests;
using Microsoft.Extensions.EnvironmentAbstractions;
using NuGet.Versioning;
 
namespace Microsoft.DotNet.Tests.Commands.Tool
{
    public class ToolManifestEditorTests
    {
        private readonly IFileSystem _fileSystem;
        private readonly string _testDirectoryRoot;
        private const string _manifestFilename = "dotnet-tools.json";
 
        public ToolManifestEditorTests()
        {
            _fileSystem = new FileSystemMockBuilder().UseCurrentSystemTemporaryDirectory().Build();
            _testDirectoryRoot = _fileSystem.Directory.CreateTemporaryDirectory().DirectoryPath;
        }
 
        [Fact]
        public void GivenManifestFileItCanAddEntryToIt()
        {
            string manifestFile = Path.Combine(_testDirectoryRoot, _manifestFilename);
            _fileSystem.File.WriteAllText(manifestFile, _jsonContent);
 
            var toolManifestFileEditor = new ToolManifestEditor(_fileSystem, new FakeDangerousFileDetector());
 
            toolManifestFileEditor.Add(new FilePath(manifestFile),
                new PackageId("new-tool"),
                NuGetVersion.Parse("3.0.0"),
                new[] { new ToolCommandName("newtool") });
 
            _fileSystem.File.ReadAllText(manifestFile).Should().Be(
                @"{
  ""version"": 1,
  ""isRoot"": true,
  ""tools"": {
    ""t-rex"": {
      ""version"": ""1.0.53"",
      ""commands"": [
        ""t-rex""
      ],
      ""rollForward"": false
    },
    ""dotnetsay"": {
      ""version"": ""2.1.4"",
      ""commands"": [
        ""dotnetsay""
      ],
      ""rollForward"": false
    },
    ""new-tool"": {
      ""version"": ""3.0.0"",
      ""commands"": [
        ""newtool""
      ],
      ""rollForward"": false
    }
  }
}");
        }
 
        [Fact]
        public void GivenManifestFileWithoutToolsEntryItCanAddEntryToIt()
        {
            string manifestFile = Path.Combine(_testDirectoryRoot, _manifestFilename);
            _fileSystem.File.WriteAllText(manifestFile, _jsonContentWithoutToolsEntry);
 
            var toolManifestFileEditor = new ToolManifestEditor(_fileSystem, new FakeDangerousFileDetector());
 
            toolManifestFileEditor.Add(new FilePath(manifestFile),
                new PackageId("new-tool"),
                NuGetVersion.Parse("3.0.0"),
                new[] { new ToolCommandName("newtool") });
 
            _fileSystem.File.ReadAllText(manifestFile).Should().Be(
                @"{
  ""isRoot"": true,
  ""tools"": {
    ""new-tool"": {
      ""version"": ""3.0.0"",
      ""commands"": [
        ""newtool""
      ],
      ""rollForward"": false
    }
  }
}");
        }
 
        [Fact]
        public void GivenManifestFileWhenAddingTheSamePackageIdToolItThrows()
        {
            string manifestFile = Path.Combine(_testDirectoryRoot, _manifestFilename);
            _fileSystem.File.WriteAllText(manifestFile, _jsonContent);
 
            var toolManifestFileEditor = new ToolManifestEditor(_fileSystem, new FakeDangerousFileDetector());
 
            PackageId packageId = new("dotnetsay");
            NuGetVersion nuGetVersion = NuGetVersion.Parse("3.0.0");
            Action a = () => toolManifestFileEditor.Add(new FilePath(manifestFile),
                packageId,
                nuGetVersion,
                new[] { new ToolCommandName("dotnetsay") });
 
            var expectedString = string.Format(
                CliStrings.ManifestPackageIdCollision,
                "2.1.4",
                packageId.ToString(),
                manifestFile,
                nuGetVersion.ToNormalizedString());
 
            a.Should().Throw<ToolManifestException>()
                .And.Message.Should().Contain(expectedString);
 
            _fileSystem.File.ReadAllText(manifestFile).Should().Be(_jsonContent);
        }
 
        [Fact]
        public void GivenManifestFileWhenAddingTheSamePackageIdSameVersionSameCommandsItDoesNothing()
        {
            string manifestFile = Path.Combine(_testDirectoryRoot, _manifestFilename);
            _fileSystem.File.WriteAllText(manifestFile, _jsonContent);
 
            var toolManifestFileEditor = new ToolManifestEditor(_fileSystem, new FakeDangerousFileDetector());
 
            PackageId packageId = new("dotnetsay");
            NuGetVersion nuGetVersion = NuGetVersion.Parse("2.1.4");
            Action a = () => toolManifestFileEditor.Add(new FilePath(manifestFile),
                packageId,
                nuGetVersion,
                new[] { new ToolCommandName("dotnetsay") });
 
            a.Should().NotThrow();
 
            _fileSystem.File.ReadAllText(manifestFile).Should().Be(_jsonContent);
        }
 
        [Fact]
        public void GivenAnInvalidManifestFileWhenAddItThrows()
        {
            string manifestFile = Path.Combine(_testDirectoryRoot, _manifestFilename);
            _fileSystem.File.WriteAllText(manifestFile, _jsonWithInvalidField);
 
            var toolManifestFileEditor = new ToolManifestEditor(_fileSystem, new FakeDangerousFileDetector());
 
            PackageId packageId = new("dotnetsay");
            NuGetVersion nuGetVersion = NuGetVersion.Parse("3.0.0");
            Action a = () => toolManifestFileEditor.Add(new FilePath(manifestFile),
                packageId,
                nuGetVersion,
                new[] { new ToolCommandName("dotnetsay") });
 
            a.Should().Throw<ToolManifestException>()
                .And.Message.Should().Contain(
                    string.Format(CliStrings.InvalidManifestFilePrefix,
                        manifestFile,
                        string.Empty));
 
            _fileSystem.File.ReadAllText(manifestFile).Should().Be(_jsonWithInvalidField);
        }
 
        [Fact]
        public void GivenAnMissingManifestFileVersionItShouldNotThrow()
        {
            string manifestFile = Path.Combine(_testDirectoryRoot, _manifestFilename);
            _fileSystem.File.WriteAllText(manifestFile, _jsonContentMissingVersion);
 
            var toolManifestFileEditor = new ToolManifestEditor(_fileSystem, new FakeDangerousFileDetector());
 
            Action a = () =>
                toolManifestFileEditor.Read(new FilePath(manifestFile), new DirectoryPath(_testDirectoryRoot));
 
            a.Should().NotThrow<ToolManifestException>();
        }
 
        [Fact]
        public void GivenManifestFileItCanRemoveEntryFromIt()
        {
            string manifestFile = Path.Combine(_testDirectoryRoot, _manifestFilename);
            _fileSystem.File.WriteAllText(manifestFile, _jsonContent);
 
            var toolManifestFileEditor = new ToolManifestEditor(_fileSystem, new FakeDangerousFileDetector());
 
            toolManifestFileEditor.Remove(new FilePath(manifestFile),
                new PackageId("dotnetsay"));
 
            _fileSystem.File.ReadAllText(manifestFile).Should().Be(
                @"{
  ""version"": 1,
  ""isRoot"": true,
  ""tools"": {
    ""t-rex"": {
      ""version"": ""1.0.53"",
      ""commands"": [
        ""t-rex""
      ],
      ""rollForward"": false
    }
  }
}");
        }
 
        [Fact]
        public void GivenManifestFileWhenRemoveNonExistPackageIdToolItThrows()
        {
            string manifestFile = Path.Combine(_testDirectoryRoot, _manifestFilename);
            _fileSystem.File.WriteAllText(manifestFile, _jsonContent);
 
            var toolManifestFileEditor = new ToolManifestEditor(_fileSystem, new FakeDangerousFileDetector());
 
            Action a = () => toolManifestFileEditor.Remove(
                new FilePath(manifestFile),
                new PackageId("non-exist"));
 
            a.Should().Throw<ToolManifestException>()
                .And.Message.Should().Contain(string.Format(
                    CliStrings.CannotFindPackageIdInManifest, "non-exist"));
 
            _fileSystem.File.ReadAllText(manifestFile).Should().Be(_jsonContent);
        }
 
        [Fact]
        public void GivenAnInvalidManifestFileWhenRemoveItThrows()
        {
            string manifestFile = Path.Combine(_testDirectoryRoot, _manifestFilename);
            _fileSystem.File.WriteAllText(manifestFile, _jsonWithInvalidField);
 
            var toolManifestFileEditor = new ToolManifestEditor(_fileSystem, new FakeDangerousFileDetector());
 
            Action a = () => toolManifestFileEditor.Remove(
                new FilePath(manifestFile),
                new PackageId("dotnetsay"));
 
            a.Should().Throw<ToolManifestException>()
                .And.Message.Should().Contain(
                    string.Format(CliStrings.InvalidManifestFilePrefix,
                        manifestFile,
                        string.Empty));
 
            _fileSystem.File.ReadAllText(manifestFile).Should().Be(_jsonWithInvalidField);
        }
 
        [Fact]
        public void GivenManifestFileWhenEditNonExistPackageIdItThrows()
        {
            string manifestFile = Path.Combine(_testDirectoryRoot, _manifestFilename);
            _fileSystem.File.WriteAllText(manifestFile, _jsonContent);
 
            var toolManifestFileEditor = new ToolManifestEditor(_fileSystem, new FakeDangerousFileDetector());
 
            Action a = () => toolManifestFileEditor.Edit(new FilePath(manifestFile),
                new PackageId("non-exist"),
                NuGetVersion.Parse("3.0.0"),
                new[] { new ToolCommandName("t-rex3") });
 
            a.Should().Throw<ArgumentException>().And.Message.Should()
                .Contain($"Manifest {manifestFile} does not contain package id 'non-exist'.");
        }
 
 
        [Fact]
        public void GivenManifestFileItCanEditEntry()
        {
            string manifestFile = Path.Combine(_testDirectoryRoot, _manifestFilename);
            _fileSystem.File.WriteAllText(manifestFile, _jsonContent);
 
            var toolManifestFileEditor = new ToolManifestEditor(_fileSystem, new FakeDangerousFileDetector());
 
            toolManifestFileEditor.Edit(new FilePath(manifestFile),
                new PackageId("t-rex"),
                NuGetVersion.Parse("3.0.0"),
                new[] { new ToolCommandName("t-rex3") });
 
            _fileSystem.File.ReadAllText(manifestFile).Should().Be(
                @"{
  ""version"": 1,
  ""isRoot"": true,
  ""tools"": {
    ""t-rex"": {
      ""version"": ""3.0.0"",
      ""commands"": [
        ""t-rex3""
      ],
      ""rollForward"": false
    },
    ""dotnetsay"": {
      ""version"": ""2.1.4"",
      ""commands"": [
        ""dotnetsay""
      ],
      ""rollForward"": false
    }
  }
}", "And original tools entry order is preserved.");
        }
 
        private string _jsonContent =
            @"{
  ""version"": 1,
  ""isRoot"": true,
  ""tools"": {
    ""t-rex"": {
      ""version"": ""1.0.53"",
      ""commands"": [
        ""t-rex""
      ],
      ""rollForward"": false
    },
    ""dotnetsay"": {
      ""version"": ""2.1.4"",
      ""commands"": [
        ""dotnetsay""
      ],
      ""rollForward"": false
    }
  }
}";
 
        private string _jsonContentWithoutToolsEntry =
            @"{
   ""isRoot"":true
}";
 
        private string _jsonWithInvalidField =
            @"{
   ""version"":1,
   ""isRoot"":true,
   ""tools"":{
      ""t-rex"":{
         ""version"":""1.*"",
         ""commands"":[
            ""t-rex""
         ]
      }
   }
}";
 
        private string _jsonContentMissingVersion =
            @"{
   ""isRoot"":true,
   ""tools"":{
      ""t-rex"":{
         ""version"":""1.0.53"",
         ""commands"":[
            ""t-rex""
         ]
      }
   }
}";
    }
}