From e14d062686bb4e7d29f79a46fcbbf56df6c01ac7 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Wed, 24 Jul 2019 17:00:14 -0700 Subject: [PATCH 01/48] Add VersionSpecificationType --- .../VersionSpecificationType.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/DependencyManagement/VersionSpecificationType.cs diff --git a/src/DependencyManagement/VersionSpecificationType.cs b/src/DependencyManagement/VersionSpecificationType.cs new file mode 100644 index 00000000..2ccd3ef1 --- /dev/null +++ b/src/DependencyManagement/VersionSpecificationType.cs @@ -0,0 +1,15 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +#pragma warning disable 1591 + +namespace Microsoft.Azure.Functions.PowerShellWorker.DependencyManagement +{ + public enum VersionSpecificationType + { + ExactVersion, + MajorVersion + } +} From 5fc4f27255a5b635fa561096e56a9e6a6d6c5c17 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Wed, 24 Jul 2019 17:00:32 -0700 Subject: [PATCH 02/48] Add DependencyManifestEntry.VersionSpecificationType property --- .../DependencyManifestEntry.cs | 3 ++ .../DependencyManifestTests.cs | 30 ++++++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/DependencyManagement/DependencyManifestEntry.cs b/src/DependencyManagement/DependencyManifestEntry.cs index b0f838b2..e9493e0f 100644 --- a/src/DependencyManagement/DependencyManifestEntry.cs +++ b/src/DependencyManagement/DependencyManifestEntry.cs @@ -9,11 +9,14 @@ internal class DependencyManifestEntry { public string Name { get; } + public VersionSpecificationType VersionSpecificationType { get; } + public string MajorVersion { get; } public DependencyManifestEntry(string name, string majorVersion) { Name = name; + VersionSpecificationType = VersionSpecificationType.MajorVersion; MajorVersion = majorVersion; } } diff --git a/test/Unit/DependencyManagement/DependencyManifestTests.cs b/test/Unit/DependencyManagement/DependencyManifestTests.cs index 1438ea54..a9f53174 100644 --- a/test/Unit/DependencyManagement/DependencyManifestTests.cs +++ b/test/Unit/DependencyManagement/DependencyManifestTests.cs @@ -64,11 +64,15 @@ public void GetEntriesParsesRequirementsFileWithNoEntries() } [Theory] - [InlineData("@{ MyModule = '0.*' }", "MyModule", "0")] - [InlineData("@{ MyModule = '1.*' }", "MyModule", "1")] - [InlineData("@{ MyModule = '23.*' }", "MyModule", "23")] - [InlineData("@{ MyModule = '456.*' }", "MyModule", "456")] - public void GetEntriesParsesRequirementsFileWithSingleEntry(string content, string moduleName, string majorVersion) + [InlineData("@{ MyModule = '0.*' }", "MyModule", "0", VersionSpecificationType.MajorVersion)] + [InlineData("@{ MyModule = '1.*' }", "MyModule", "1", VersionSpecificationType.MajorVersion)] + [InlineData("@{ MyModule = '23.*' }", "MyModule", "23", VersionSpecificationType.MajorVersion)] + [InlineData("@{ MyModule = '456.*' }", "MyModule", "456", VersionSpecificationType.MajorVersion)] + public void GetEntriesParsesRequirementsFileWithSingleEntry( + string content, + string moduleName, + string majorVersion, + VersionSpecificationType versionSpecificationType) { CreateRequirementsFile(content); @@ -77,6 +81,7 @@ public void GetEntriesParsesRequirementsFileWithSingleEntry(string content, stri Assert.Single(entries); Assert.Equal(moduleName, entries.Single().Name); + Assert.Equal(versionSpecificationType, entries.Single().VersionSpecificationType); Assert.Equal(majorVersion, entries.Single().MajorVersion); } @@ -89,9 +94,18 @@ public void GetEntriesParsesRequirementsFileWithMultipleEntries() var entries = manifest.GetEntries().ToList(); Assert.Equal(3, entries.Count); - Assert.Equal("3", entries.Single(entry => entry.Name == "A").MajorVersion); - Assert.Equal("7", entries.Single(entry => entry.Name == "B").MajorVersion); - Assert.Equal("0", entries.Single(entry => entry.Name == "C").MajorVersion); + + var entryA = entries.Single(entry => entry.Name == "A"); + Assert.Equal(VersionSpecificationType.MajorVersion, entryA.VersionSpecificationType); + Assert.Equal("3", entryA.MajorVersion); + + var entryB = entries.Single(entry => entry.Name == "B"); + Assert.Equal(VersionSpecificationType.MajorVersion, entryB.VersionSpecificationType); + Assert.Equal("7", entryB.MajorVersion); + + var entryC = entries.Single(entry => entry.Name == "C"); + Assert.Equal(VersionSpecificationType.MajorVersion, entryC.VersionSpecificationType); + Assert.Equal("0", entryC.MajorVersion); } [Theory] From 70788ebe94f07184ecba48c1b9551ed3ff011764 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Wed, 24 Jul 2019 17:13:10 -0700 Subject: [PATCH 03/48] Add IDependencyManagerStorage.IsModuleVersionInstalled --- src/DependencyManagement/DependencyManagerStorage.cs | 7 +++++++ src/DependencyManagement/IDependencyManagerStorage.cs | 2 ++ 2 files changed, 9 insertions(+) diff --git a/src/DependencyManagement/DependencyManagerStorage.cs b/src/DependencyManagement/DependencyManagerStorage.cs index 2994d078..6c0c360d 100644 --- a/src/DependencyManagement/DependencyManagerStorage.cs +++ b/src/DependencyManagement/DependencyManagerStorage.cs @@ -55,6 +55,7 @@ public IEnumerable GetInstalledAndInstallingSnapshots() return Directory.EnumerateDirectories(_managedDependenciesRootPath); } + // TODO: replace with IsMajorModuleVersionInstalled? public IEnumerable GetInstalledModuleVersions(string snapshotPath, string moduleName, string majorVersion) { var modulePath = Path.Join(snapshotPath, moduleName); @@ -66,6 +67,12 @@ public IEnumerable GetInstalledModuleVersions(string snapshotPath, strin return Directory.EnumerateDirectories(modulePath, $"{majorVersion}.*"); } + public bool IsModuleVersionInstalled(string snapshotPath, string moduleName, string version) + { + var moduleVersionPath = Path.Join(snapshotPath, moduleName, version); + return Directory.Exists(moduleVersionPath); + } + public string CreateNewSnapshotPath() { return Path.Join( diff --git a/src/DependencyManagement/IDependencyManagerStorage.cs b/src/DependencyManagement/IDependencyManagerStorage.cs index d22ff595..5825d48e 100644 --- a/src/DependencyManagement/IDependencyManagerStorage.cs +++ b/src/DependencyManagement/IDependencyManagerStorage.cs @@ -20,6 +20,8 @@ internal interface IDependencyManagerStorage IEnumerable GetInstalledModuleVersions(string snapshotPath, string moduleName, string majorVersion); + bool IsModuleVersionInstalled(string snapshotPath, string moduleName, string version); + string CreateNewSnapshotPath(); string CreateInstallingSnapshot(string path); From 70fcaae3ed2980c81ce95642996ac11fc76415f2 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Wed, 24 Jul 2019 17:16:04 -0700 Subject: [PATCH 04/48] Fix test name --- .../DependencyManagement/InstalledDependenciesLocatorTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/Unit/DependencyManagement/InstalledDependenciesLocatorTests.cs b/test/Unit/DependencyManagement/InstalledDependenciesLocatorTests.cs index 73da8dff..7fce0d3f 100644 --- a/test/Unit/DependencyManagement/InstalledDependenciesLocatorTests.cs +++ b/test/Unit/DependencyManagement/InstalledDependenciesLocatorTests.cs @@ -53,7 +53,7 @@ public void ReturnsNull_WhenAnyDependencyDoesNotHaveAcceptableVersionInstalled() } [Fact] - public void ReturnsLatestSnapshotPath_WhenAnyDependencyDoesNotHaveAcceptableVersionInstalled() + public void ReturnsLatestSnapshotPath_WhenAllDependenciesHaveAcceptableVersionInstalled() { // Even though multiple snapshots can be currently installed, only the latest one will be considered // (determined by name). @@ -61,7 +61,6 @@ public void ReturnsLatestSnapshotPath_WhenAnyDependencyDoesNotHaveAcceptableVers _mockStorage.Setup(_ => _.GetDependencies()).Returns(_dependencyManifestEntries); - // No 11.* version for module B detected! _mockStorage.Setup(_ => _.GetInstalledModuleVersions("s3", "A", "3")).Returns(new[] { "3.1", "3.3" }); _mockStorage.Setup(_ => _.GetInstalledModuleVersions("s3", "B", "11")).Returns(new [] { "11.8.0.2" }); _mockStorage.Setup(_ => _.GetInstalledModuleVersions("s3", "C", "7")).Returns(new[] { "7.0" }); From f60b8d4212226c0571e2f5bbccc1946ac326cd4c Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Wed, 24 Jul 2019 17:21:37 -0700 Subject: [PATCH 05/48] Rename DependencyManifestEntry.MajorVersion to VersionSpecification --- src/DependencyManagement/DependencyManifestEntry.cs | 6 +++--- src/DependencyManagement/DependencySnapshotInstaller.cs | 2 +- src/DependencyManagement/InstalledDependenciesLocator.cs | 2 +- test/Unit/DependencyManagement/DependencyManifestTests.cs | 8 ++++---- .../DependencySnapshotInstallerTests.cs | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/DependencyManagement/DependencyManifestEntry.cs b/src/DependencyManagement/DependencyManifestEntry.cs index e9493e0f..a9eaf626 100644 --- a/src/DependencyManagement/DependencyManifestEntry.cs +++ b/src/DependencyManagement/DependencyManifestEntry.cs @@ -11,13 +11,13 @@ internal class DependencyManifestEntry public VersionSpecificationType VersionSpecificationType { get; } - public string MajorVersion { get; } + public string VersionSpecification { get; } - public DependencyManifestEntry(string name, string majorVersion) + public DependencyManifestEntry(string name, string versionSpecification) { Name = name; VersionSpecificationType = VersionSpecificationType.MajorVersion; - MajorVersion = majorVersion; + VersionSpecification = versionSpecification; } } } diff --git a/src/DependencyManagement/DependencySnapshotInstaller.cs b/src/DependencyManagement/DependencySnapshotInstaller.cs index 64d96454..ae713a30 100644 --- a/src/DependencyManagement/DependencySnapshotInstaller.cs +++ b/src/DependencyManagement/DependencySnapshotInstaller.cs @@ -134,7 +134,7 @@ private List GetLatestPublishedVersionsOfDependencies( foreach (var entry in dependencies) { - var latestVersion = GetModuleLatestPublishedVersion(entry.Name, entry.MajorVersion); + var latestVersion = GetModuleLatestPublishedVersion(entry.Name, entry.VersionSpecification); var dependencyInfo = new DependencyInfo(entry.Name, latestVersion); result.Add(dependencyInfo); diff --git a/src/DependencyManagement/InstalledDependenciesLocator.cs b/src/DependencyManagement/InstalledDependenciesLocator.cs index 1a8f9dad..8235c809 100644 --- a/src/DependencyManagement/InstalledDependenciesLocator.cs +++ b/src/DependencyManagement/InstalledDependenciesLocator.cs @@ -36,7 +36,7 @@ private bool IsMajorVersionInstalled(string snapshotPath, DependencyManifestEntr { var installedVersions = _storage.GetInstalledModuleVersions( - snapshotPath, dependency.Name, dependency.MajorVersion); + snapshotPath, dependency.Name, dependency.VersionSpecification); return installedVersions.Any(); } diff --git a/test/Unit/DependencyManagement/DependencyManifestTests.cs b/test/Unit/DependencyManagement/DependencyManifestTests.cs index a9f53174..070ae39b 100644 --- a/test/Unit/DependencyManagement/DependencyManifestTests.cs +++ b/test/Unit/DependencyManagement/DependencyManifestTests.cs @@ -82,7 +82,7 @@ public void GetEntriesParsesRequirementsFileWithSingleEntry( Assert.Single(entries); Assert.Equal(moduleName, entries.Single().Name); Assert.Equal(versionSpecificationType, entries.Single().VersionSpecificationType); - Assert.Equal(majorVersion, entries.Single().MajorVersion); + Assert.Equal(majorVersion, entries.Single().VersionSpecification); } [Fact] @@ -97,15 +97,15 @@ public void GetEntriesParsesRequirementsFileWithMultipleEntries() var entryA = entries.Single(entry => entry.Name == "A"); Assert.Equal(VersionSpecificationType.MajorVersion, entryA.VersionSpecificationType); - Assert.Equal("3", entryA.MajorVersion); + Assert.Equal("3", entryA.VersionSpecification); var entryB = entries.Single(entry => entry.Name == "B"); Assert.Equal(VersionSpecificationType.MajorVersion, entryB.VersionSpecificationType); - Assert.Equal("7", entryB.MajorVersion); + Assert.Equal("7", entryB.VersionSpecification); var entryC = entries.Single(entry => entry.Name == "C"); Assert.Equal(VersionSpecificationType.MajorVersion, entryC.VersionSpecificationType); - Assert.Equal("0", entryC.MajorVersion); + Assert.Equal("0", entryC.VersionSpecification); } [Theory] diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index 72afe0ce..a5731852 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -66,7 +66,7 @@ public void InstallsDependencySnapshots() foreach (var entry in _testDependencyManifestEntries) { _mockModuleProvider.Setup( - _ => _.GetLatestPublishedModuleVersion(entry.Name, entry.MajorVersion)) + _ => _.GetLatestPublishedModuleVersion(entry.Name, entry.VersionSpecification)) .Returns(_testLatestPublishedModuleVersions[entry.Name]); _mockModuleProvider.Setup( @@ -170,7 +170,7 @@ public void DoesNotPromoteDependenciesSnapshotIfSaveModuleKeepsThrowing() foreach (var entry in _testDependencyManifestEntries) { _mockModuleProvider.Setup( - _ => _.GetLatestPublishedModuleVersion(entry.Name, entry.MajorVersion)) + _ => _.GetLatestPublishedModuleVersion(entry.Name, entry.VersionSpecification)) .Returns(_testLatestPublishedModuleVersions[entry.Name]); } From fb4c0cd91ab43a05d303c386ac0b768e51f92eaa Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Wed, 24 Jul 2019 17:25:11 -0700 Subject: [PATCH 06/48] Pass VersionSpecificationType to DependencyManifestEntry ctor --- src/DependencyManagement/DependencyManifest.cs | 5 ++++- src/DependencyManagement/DependencyManifestEntry.cs | 7 +++++-- .../DependencyManagement/DependencyManagerTests.cs | 10 +++++----- .../DependencySnapshotInstallerTests.cs | 6 +++--- .../InstalledDependenciesLocatorTests.cs | 6 +++--- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/DependencyManagement/DependencyManifest.cs b/src/DependencyManagement/DependencyManifest.cs index 9f7d4908..9a6966e8 100644 --- a/src/DependencyManagement/DependencyManifest.cs +++ b/src/DependencyManagement/DependencyManifest.cs @@ -44,7 +44,10 @@ public IEnumerable GetEntries() ValidateModuleName(name); - yield return new DependencyManifestEntry(name, GetMajorVersion(version)); + yield return new DependencyManifestEntry( + name, + VersionSpecificationType.MajorVersion, + GetMajorVersion(version)); } } diff --git a/src/DependencyManagement/DependencyManifestEntry.cs b/src/DependencyManagement/DependencyManifestEntry.cs index a9eaf626..9d17c4cb 100644 --- a/src/DependencyManagement/DependencyManifestEntry.cs +++ b/src/DependencyManagement/DependencyManifestEntry.cs @@ -13,10 +13,13 @@ internal class DependencyManifestEntry public string VersionSpecification { get; } - public DependencyManifestEntry(string name, string versionSpecification) + public DependencyManifestEntry( + string name, + VersionSpecificationType versionSpecificationType, + string versionSpecification) { Name = name; - VersionSpecificationType = VersionSpecificationType.MajorVersion; + VersionSpecificationType = versionSpecificationType; VersionSpecification = versionSpecification; } } diff --git a/test/Unit/DependencyManagement/DependencyManagerTests.cs b/test/Unit/DependencyManagement/DependencyManagerTests.cs index b2673ccd..f46c9da5 100644 --- a/test/Unit/DependencyManagement/DependencyManagerTests.cs +++ b/test/Unit/DependencyManagement/DependencyManagerTests.cs @@ -35,7 +35,7 @@ public void DoesNothingOnConstruction() public void Initialize_ReturnsNewSnapshotPath_WhenNoAcceptableDependencyVersionsInstalled() { _mockStorage.Setup(_ => _.GetDependencies()).Returns( - new[] { new DependencyManifestEntry("ModuleName", "1") }); + new[] { new DependencyManifestEntry("ModuleName", VersionSpecificationType.MajorVersion, "1") }); _mockInstalledDependenciesLocator.Setup(_ => _.GetPathWithAcceptableDependencyVersionsInstalled()) .Returns(default(string)); _mockStorage.Setup(_ => _.CreateNewSnapshotPath()).Returns("NewSnapshot"); @@ -100,9 +100,9 @@ public void StartDependencyInstallationIfNeeded_InstallsSnapshotWithLatestPublis var dependencyManifestEntries = new[] { - new DependencyManifestEntry("A", "3"), - new DependencyManifestEntry("C", "7"), - new DependencyManifestEntry("B", "11") + new DependencyManifestEntry("A", VersionSpecificationType.MajorVersion, "3"), + new DependencyManifestEntry("C", VersionSpecificationType.MajorVersion, "7"), + new DependencyManifestEntry("B", VersionSpecificationType.MajorVersion, "11") }; _mockStorage.Setup(_ => _.GetDependencies()).Returns(dependencyManifestEntries); @@ -317,7 +317,7 @@ public void StartDependencyInstallationIfNeeded_HandlesExceptionThrownBy_Install private static DependencyManifestEntry[] GetAnyNonEmptyDependencyManifestEntries() { - return new[] { new DependencyManifestEntry("ModuleName", "1") }; + return new[] { new DependencyManifestEntry("ModuleName", VersionSpecificationType.MajorVersion, "1") }; } private void VerifyExactlyOneSnapshotInstalled() diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index a5731852..5f804945 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -26,9 +26,9 @@ public class DependencySnapshotInstallerTests private readonly IEnumerable _testDependencyManifestEntries = new[] { - new DependencyManifestEntry("A", "3"), - new DependencyManifestEntry("C", "7"), - new DependencyManifestEntry("B", "11") + new DependencyManifestEntry("A", VersionSpecificationType.MajorVersion, "3"), + new DependencyManifestEntry("C", VersionSpecificationType.MajorVersion, "7"), + new DependencyManifestEntry("B", VersionSpecificationType.MajorVersion, "11") }; private readonly Dictionary _testLatestPublishedModuleVersions = diff --git a/test/Unit/DependencyManagement/InstalledDependenciesLocatorTests.cs b/test/Unit/DependencyManagement/InstalledDependenciesLocatorTests.cs index 7fce0d3f..5dcc1065 100644 --- a/test/Unit/DependencyManagement/InstalledDependenciesLocatorTests.cs +++ b/test/Unit/DependencyManagement/InstalledDependenciesLocatorTests.cs @@ -16,9 +16,9 @@ public class InstalledDependenciesLocatorTests private readonly DependencyManifestEntry[] _dependencyManifestEntries = { - new DependencyManifestEntry("A", "3"), - new DependencyManifestEntry("C", "7"), - new DependencyManifestEntry("B", "11") + new DependencyManifestEntry("A", VersionSpecificationType.MajorVersion, "3"), + new DependencyManifestEntry("C", VersionSpecificationType.MajorVersion, "7"), + new DependencyManifestEntry("B", VersionSpecificationType.MajorVersion, "11") }; [Fact] From 23f26006de1030b372524418228d4f1bfd41138a Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Wed, 24 Jul 2019 17:51:51 -0700 Subject: [PATCH 07/48] Handle VersionSpecificationType.ExactVersion in InstalledDependenciesLocator --- .../InstalledDependenciesLocator.cs | 25 ++++++++++++---- .../InstalledDependenciesLocatorTests.cs | 30 ++++++++++++++----- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/DependencyManagement/InstalledDependenciesLocator.cs b/src/DependencyManagement/InstalledDependenciesLocator.cs index 8235c809..897c7194 100644 --- a/src/DependencyManagement/InstalledDependenciesLocator.cs +++ b/src/DependencyManagement/InstalledDependenciesLocator.cs @@ -5,6 +5,7 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.DependencyManagement { + using System; using System.Linq; internal class InstalledDependenciesLocator : IInstalledDependenciesLocator @@ -23,7 +24,7 @@ public string GetPathWithAcceptableDependencyVersionsInstalled() if (lastSnapshotPath != null) { var dependencies = _storage.GetDependencies(); - if (dependencies.All(entry => IsMajorVersionInstalled(lastSnapshotPath, entry))) + if (dependencies.All(entry => IsAcceptableVersionInstalled(lastSnapshotPath, entry))) { return lastSnapshotPath; } @@ -32,12 +33,26 @@ public string GetPathWithAcceptableDependencyVersionsInstalled() return null; } - private bool IsMajorVersionInstalled(string snapshotPath, DependencyManifestEntry dependency) + private bool IsAcceptableVersionInstalled(string snapshotPath, DependencyManifestEntry dependency) { - var installedVersions = - _storage.GetInstalledModuleVersions( - snapshotPath, dependency.Name, dependency.VersionSpecification); + switch (dependency.VersionSpecificationType) + { + case VersionSpecificationType.ExactVersion: + return _storage.IsModuleVersionInstalled( + snapshotPath, dependency.Name, dependency.VersionSpecification); + + case VersionSpecificationType.MajorVersion: + return IsMajorVersionInstalled( + snapshotPath, dependency.Name, dependency.VersionSpecification); + + default: + throw new ArgumentException($"Unknown version specification type: {dependency.VersionSpecificationType}"); + } + } + private bool IsMajorVersionInstalled(string snapshotPath, string name, string majorVersion) + { + var installedVersions = _storage.GetInstalledModuleVersions(snapshotPath, name, majorVersion); return installedVersions.Any(); } } diff --git a/test/Unit/DependencyManagement/InstalledDependenciesLocatorTests.cs b/test/Unit/DependencyManagement/InstalledDependenciesLocatorTests.cs index 5dcc1065..05141a8a 100644 --- a/test/Unit/DependencyManagement/InstalledDependenciesLocatorTests.cs +++ b/test/Unit/DependencyManagement/InstalledDependenciesLocatorTests.cs @@ -16,8 +16,7 @@ public class InstalledDependenciesLocatorTests private readonly DependencyManifestEntry[] _dependencyManifestEntries = { - new DependencyManifestEntry("A", VersionSpecificationType.MajorVersion, "3"), - new DependencyManifestEntry("C", VersionSpecificationType.MajorVersion, "7"), + new DependencyManifestEntry("A", VersionSpecificationType.ExactVersion, "3"), new DependencyManifestEntry("B", VersionSpecificationType.MajorVersion, "11") }; @@ -33,7 +32,7 @@ public void ReturnsNull_WhenNoInstalledDependencySnapshotsFound() } [Fact] - public void ReturnsNull_WhenAnyDependencyDoesNotHaveAcceptableVersionInstalled() + public void ReturnsNull_WhenNoMajorVersionInstalled() { // Even though multiple snapshots can be currently installed, only the latest one will be considered // (determined by name). @@ -42,9 +41,27 @@ public void ReturnsNull_WhenAnyDependencyDoesNotHaveAcceptableVersionInstalled() _mockStorage.Setup(_ => _.GetDependencies()).Returns(_dependencyManifestEntries); // No 11.* version for module B detected! - _mockStorage.Setup(_ => _.GetInstalledModuleVersions("s3", "A", "3")).Returns(new[] { "3.1", "3.3" }); + _mockStorage.Setup(_ => _.IsModuleVersionInstalled("s3", "A", "3")).Returns(true); _mockStorage.Setup(_ => _.GetInstalledModuleVersions("s3", "B", "11")).Returns(new string[0]); - _mockStorage.Setup(_ => _.GetInstalledModuleVersions("s3", "C", "7")).Returns(new[] { "7.0" }); + + var installedDependenciesLocator = new InstalledDependenciesLocator(_mockStorage.Object); + var result = installedDependenciesLocator.GetPathWithAcceptableDependencyVersionsInstalled(); + + Assert.Null(result); + } + + [Fact] + public void ReturnsNull_WhenExactModuleVersionIsNotInstalled() + { + // Even though multiple snapshots can be currently installed, only the latest one will be considered + // (determined by name). + _mockStorage.Setup(_ => _.GetInstalledSnapshots()).Returns(new[] { "s1", "s3", "s2" }); + + _mockStorage.Setup(_ => _.GetDependencies()).Returns(_dependencyManifestEntries); + + // The specified module A version is not installed + _mockStorage.Setup(_ => _.IsModuleVersionInstalled("s3", "A", "3")).Returns(false); + _mockStorage.Setup(_ => _.GetInstalledModuleVersions("s3", "B", "11")).Returns(new [] { "11.8.0.2" }); var installedDependenciesLocator = new InstalledDependenciesLocator(_mockStorage.Object); var result = installedDependenciesLocator.GetPathWithAcceptableDependencyVersionsInstalled(); @@ -61,9 +78,8 @@ public void ReturnsLatestSnapshotPath_WhenAllDependenciesHaveAcceptableVersionIn _mockStorage.Setup(_ => _.GetDependencies()).Returns(_dependencyManifestEntries); - _mockStorage.Setup(_ => _.GetInstalledModuleVersions("s3", "A", "3")).Returns(new[] { "3.1", "3.3" }); + _mockStorage.Setup(_ => _.IsModuleVersionInstalled("s3", "A", "3")).Returns(true); _mockStorage.Setup(_ => _.GetInstalledModuleVersions("s3", "B", "11")).Returns(new [] { "11.8.0.2" }); - _mockStorage.Setup(_ => _.GetInstalledModuleVersions("s3", "C", "7")).Returns(new[] { "7.0" }); var installedDependenciesLocator = new InstalledDependenciesLocator(_mockStorage.Object); var result = installedDependenciesLocator.GetPathWithAcceptableDependencyVersionsInstalled(); From aefae608cd6f477daee1c21edde3194512a85d3b Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Wed, 24 Jul 2019 17:56:41 -0700 Subject: [PATCH 08/48] Better fake version values --- .../InstalledDependenciesLocatorTests.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/Unit/DependencyManagement/InstalledDependenciesLocatorTests.cs b/test/Unit/DependencyManagement/InstalledDependenciesLocatorTests.cs index 05141a8a..ceaaf695 100644 --- a/test/Unit/DependencyManagement/InstalledDependenciesLocatorTests.cs +++ b/test/Unit/DependencyManagement/InstalledDependenciesLocatorTests.cs @@ -16,8 +16,8 @@ public class InstalledDependenciesLocatorTests private readonly DependencyManifestEntry[] _dependencyManifestEntries = { - new DependencyManifestEntry("A", VersionSpecificationType.ExactVersion, "3"), - new DependencyManifestEntry("B", VersionSpecificationType.MajorVersion, "11") + new DependencyManifestEntry("A", VersionSpecificationType.ExactVersion, "exact version of A"), + new DependencyManifestEntry("B", VersionSpecificationType.MajorVersion, "major version of B") }; [Fact] @@ -40,9 +40,9 @@ public void ReturnsNull_WhenNoMajorVersionInstalled() _mockStorage.Setup(_ => _.GetDependencies()).Returns(_dependencyManifestEntries); - // No 11.* version for module B detected! - _mockStorage.Setup(_ => _.IsModuleVersionInstalled("s3", "A", "3")).Returns(true); - _mockStorage.Setup(_ => _.GetInstalledModuleVersions("s3", "B", "11")).Returns(new string[0]); + // No version for module B detected! + _mockStorage.Setup(_ => _.IsModuleVersionInstalled("s3", "A", "exact version of A")).Returns(true); + _mockStorage.Setup(_ => _.GetInstalledModuleVersions("s3", "B", "major version of B")).Returns(new string[0]); var installedDependenciesLocator = new InstalledDependenciesLocator(_mockStorage.Object); var result = installedDependenciesLocator.GetPathWithAcceptableDependencyVersionsInstalled(); @@ -60,8 +60,8 @@ public void ReturnsNull_WhenExactModuleVersionIsNotInstalled() _mockStorage.Setup(_ => _.GetDependencies()).Returns(_dependencyManifestEntries); // The specified module A version is not installed - _mockStorage.Setup(_ => _.IsModuleVersionInstalled("s3", "A", "3")).Returns(false); - _mockStorage.Setup(_ => _.GetInstalledModuleVersions("s3", "B", "11")).Returns(new [] { "11.8.0.2" }); + _mockStorage.Setup(_ => _.IsModuleVersionInstalled("s3", "A", "exact version of A")).Returns(false); + _mockStorage.Setup(_ => _.GetInstalledModuleVersions("s3", "B", "major version of B")).Returns(new [] { "exact version of B" }); var installedDependenciesLocator = new InstalledDependenciesLocator(_mockStorage.Object); var result = installedDependenciesLocator.GetPathWithAcceptableDependencyVersionsInstalled(); @@ -78,8 +78,8 @@ public void ReturnsLatestSnapshotPath_WhenAllDependenciesHaveAcceptableVersionIn _mockStorage.Setup(_ => _.GetDependencies()).Returns(_dependencyManifestEntries); - _mockStorage.Setup(_ => _.IsModuleVersionInstalled("s3", "A", "3")).Returns(true); - _mockStorage.Setup(_ => _.GetInstalledModuleVersions("s3", "B", "11")).Returns(new [] { "11.8.0.2" }); + _mockStorage.Setup(_ => _.IsModuleVersionInstalled("s3", "A", "exact version of A")).Returns(true); + _mockStorage.Setup(_ => _.GetInstalledModuleVersions("s3", "B", "major version of B")).Returns(new [] { "exact version of B" }); var installedDependenciesLocator = new InstalledDependenciesLocator(_mockStorage.Object); var result = installedDependenciesLocator.GetPathWithAcceptableDependencyVersionsInstalled(); From 9f527015c8afaa4c16232aa599bbe7b74e8a058a Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Wed, 24 Jul 2019 17:57:30 -0700 Subject: [PATCH 09/48] Reorder methods --- .../InstalledDependenciesLocatorTests.cs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/test/Unit/DependencyManagement/InstalledDependenciesLocatorTests.cs b/test/Unit/DependencyManagement/InstalledDependenciesLocatorTests.cs index ceaaf695..6b2b7bb6 100644 --- a/test/Unit/DependencyManagement/InstalledDependenciesLocatorTests.cs +++ b/test/Unit/DependencyManagement/InstalledDependenciesLocatorTests.cs @@ -20,6 +20,24 @@ public class InstalledDependenciesLocatorTests new DependencyManifestEntry("B", VersionSpecificationType.MajorVersion, "major version of B") }; + [Fact] + public void ReturnsLatestSnapshotPath_WhenAllDependenciesHaveAcceptableVersionInstalled() + { + // Even though multiple snapshots can be currently installed, only the latest one will be considered + // (determined by name). + _mockStorage.Setup(_ => _.GetInstalledSnapshots()).Returns(new[] { "s1", "s3", "s2" }); + + _mockStorage.Setup(_ => _.GetDependencies()).Returns(_dependencyManifestEntries); + + _mockStorage.Setup(_ => _.IsModuleVersionInstalled("s3", "A", "exact version of A")).Returns(true); + _mockStorage.Setup(_ => _.GetInstalledModuleVersions("s3", "B", "major version of B")).Returns(new [] { "exact version of B" }); + + var installedDependenciesLocator = new InstalledDependenciesLocator(_mockStorage.Object); + var result = installedDependenciesLocator.GetPathWithAcceptableDependencyVersionsInstalled(); + + Assert.Equal("s3", result); + } + [Fact] public void ReturnsNull_WhenNoInstalledDependencySnapshotsFound() { @@ -68,23 +86,5 @@ public void ReturnsNull_WhenExactModuleVersionIsNotInstalled() Assert.Null(result); } - - [Fact] - public void ReturnsLatestSnapshotPath_WhenAllDependenciesHaveAcceptableVersionInstalled() - { - // Even though multiple snapshots can be currently installed, only the latest one will be considered - // (determined by name). - _mockStorage.Setup(_ => _.GetInstalledSnapshots()).Returns(new[] { "s1", "s3", "s2" }); - - _mockStorage.Setup(_ => _.GetDependencies()).Returns(_dependencyManifestEntries); - - _mockStorage.Setup(_ => _.IsModuleVersionInstalled("s3", "A", "exact version of A")).Returns(true); - _mockStorage.Setup(_ => _.GetInstalledModuleVersions("s3", "B", "major version of B")).Returns(new [] { "exact version of B" }); - - var installedDependenciesLocator = new InstalledDependenciesLocator(_mockStorage.Object); - var result = installedDependenciesLocator.GetPathWithAcceptableDependencyVersionsInstalled(); - - Assert.Equal("s3", result); - } } } From 9748145b972ebc421df5e2ddf10a82e49ddbbdf6 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Wed, 24 Jul 2019 18:17:04 -0700 Subject: [PATCH 10/48] Rename DependencyInfo.LatestVersion to ExactVersion --- src/DependencyManagement/DependencyInfo.cs | 6 +++--- .../DependencySnapshotInstaller.cs | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/DependencyManagement/DependencyInfo.cs b/src/DependencyManagement/DependencyInfo.cs index b18cd511..bbe20c24 100644 --- a/src/DependencyManagement/DependencyInfo.cs +++ b/src/DependencyManagement/DependencyInfo.cs @@ -8,12 +8,12 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.DependencyManagement internal class DependencyInfo { internal readonly string Name; - internal readonly string LatestVersion; + internal readonly string ExactVersion; - internal DependencyInfo(string name, string latestVersion) + internal DependencyInfo(string name, string exactVersion) { Name = name; - LatestVersion = latestVersion; + ExactVersion = exactVersion; } } } diff --git a/src/DependencyManagement/DependencySnapshotInstaller.cs b/src/DependencyManagement/DependencySnapshotInstaller.cs index ae713a30..8981ed8b 100644 --- a/src/DependencyManagement/DependencySnapshotInstaller.cs +++ b/src/DependencyManagement/DependencySnapshotInstaller.cs @@ -46,9 +46,9 @@ public void InstallSnapshot( foreach (DependencyInfo module in GetLatestPublishedVersionsOfDependencies(dependencies)) { string moduleName = module.Name; - string latestVersion = module.LatestVersion; + string exactVersion = module.ExactVersion; - logger.Log(isUserOnlyLog: false, LogLevel.Trace, string.Format(PowerShellWorkerStrings.StartedInstallingModule, moduleName, latestVersion)); + logger.Log(isUserOnlyLog: false, LogLevel.Trace, string.Format(PowerShellWorkerStrings.StartedInstallingModule, moduleName, exactVersion)); int tries = 1; @@ -56,9 +56,9 @@ public void InstallSnapshot( { try { - _moduleProvider.SaveModule(pwsh, moduleName, latestVersion, installingPath); + _moduleProvider.SaveModule(pwsh, moduleName, exactVersion, installingPath); - var message = string.Format(PowerShellWorkerStrings.ModuleHasBeenInstalled, moduleName, latestVersion); + var message = string.Format(PowerShellWorkerStrings.ModuleHasBeenInstalled, moduleName, exactVersion); logger.Log(isUserOnlyLog: false, LogLevel.Trace, message); break; @@ -66,7 +66,7 @@ public void InstallSnapshot( catch (Exception e) { string currentAttempt = GetCurrentAttemptMessage(tries); - var errorMsg = string.Format(PowerShellWorkerStrings.FailToInstallModule, moduleName, latestVersion, currentAttempt, e.Message); + var errorMsg = string.Format(PowerShellWorkerStrings.FailToInstallModule, moduleName, exactVersion, currentAttempt, e.Message); logger.Log(isUserOnlyLog: false, LogLevel.Error, errorMsg); if (tries >= MaxNumberOfTries) From e17c63116981f5fb3ff332568e70f074d8fbefe3 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Wed, 24 Jul 2019 18:25:28 -0700 Subject: [PATCH 11/48] Don't try to determine latest published version if exact version is specified --- .../DependencySnapshotInstaller.cs | 23 ++++++++++--- .../DependencySnapshotInstallerTests.cs | 34 ++++++++++++++++++- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/DependencyManagement/DependencySnapshotInstaller.cs b/src/DependencyManagement/DependencySnapshotInstaller.cs index 8981ed8b..0eb95b1b 100644 --- a/src/DependencyManagement/DependencySnapshotInstaller.cs +++ b/src/DependencyManagement/DependencySnapshotInstaller.cs @@ -43,7 +43,7 @@ public void InstallSnapshot( try { - foreach (DependencyInfo module in GetLatestPublishedVersionsOfDependencies(dependencies)) + foreach (DependencyInfo module in GetExactVersionsOfDependencies(dependencies)) { string moduleName = module.Name; string exactVersion = module.ExactVersion; @@ -127,22 +127,35 @@ internal static string GetCurrentAttemptMessage(int attempt) } } - private List GetLatestPublishedVersionsOfDependencies( + private List GetExactVersionsOfDependencies( IEnumerable dependencies) { var result = new List(); foreach (var entry in dependencies) { - var latestVersion = GetModuleLatestPublishedVersion(entry.Name, entry.VersionSpecification); - - var dependencyInfo = new DependencyInfo(entry.Name, latestVersion); + var dependencyInfo = new DependencyInfo(entry.Name, GetExactVersion(entry)); result.Add(dependencyInfo); } return result; } + private string GetExactVersion(DependencyManifestEntry entry) + { + switch (entry.VersionSpecificationType) + { + case VersionSpecificationType.ExactVersion: + return entry.VersionSpecification; + + case VersionSpecificationType.MajorVersion: + return GetModuleLatestPublishedVersion(entry.Name, entry.VersionSpecification); + + default: + throw new ArgumentException($"Unknown version specification type: {entry.VersionSpecificationType}"); + } + } + /// /// Gets the latest published module version for the given module name and major version. /// diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index 5f804945..99f26f44 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -27,7 +27,7 @@ public class DependencySnapshotInstallerTests new[] { new DependencyManifestEntry("A", VersionSpecificationType.MajorVersion, "3"), - new DependencyManifestEntry("C", VersionSpecificationType.MajorVersion, "7"), + new DependencyManifestEntry("C", VersionSpecificationType.ExactVersion, "7.0.1.3"), new DependencyManifestEntry("B", VersionSpecificationType.MajorVersion, "11") }; @@ -197,6 +197,38 @@ public void DoesNotPromoteDependenciesSnapshotIfSaveModuleKeepsThrowing() _mockModuleProvider.Verify(_ => _.Cleanup(dummyPowerShell), Times.Once); } + [Fact] + public void DoesNotTryToDetermineLatestPublishedModuleVersionIfExactVersionIsSpecified() + { + // Arrange + + var testDependencyManifestEntries = + new[] + { + new DependencyManifestEntry("A", VersionSpecificationType.ExactVersion, "Exact version of A") + }; + + _mockStorage.Setup( + _ => _.CreateInstallingSnapshot(It.IsAny())).Returns(_targetPathInstalling); + + _mockModuleProvider.Setup( + _ => _.SaveModule(It.IsAny(), "A", "Exact version of A", _targetPathInstalling)); + + _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(It.IsAny())); + _mockModuleProvider.Setup(_ => _.Cleanup(It.IsAny())); + + // Act + + var installer = CreateDependenciesSnapshotInstallerWithMocks(); + installer.InstallSnapshot(testDependencyManifestEntries, _targetPathInstalled, PowerShell.Create(), _mockLogger.Object); + + // Assert + + _mockModuleProvider.Verify( + _ => _.GetLatestPublishedModuleVersion(It.IsAny(), It.IsAny()), + Times.Never); + } + private DependencySnapshotInstaller CreateDependenciesSnapshotInstallerWithMocks() { return new DependencySnapshotInstaller(_mockModuleProvider.Object, _mockStorage.Object); From a52d35c88e6d77df66269d8c29c34634ea1a1156 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Wed, 24 Jul 2019 18:29:34 -0700 Subject: [PATCH 12/48] Move local variable declaration --- .../DependencySnapshotInstallerTests.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index 99f26f44..7b2fe2e6 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -202,12 +202,6 @@ public void DoesNotTryToDetermineLatestPublishedModuleVersionIfExactVersionIsSpe { // Arrange - var testDependencyManifestEntries = - new[] - { - new DependencyManifestEntry("A", VersionSpecificationType.ExactVersion, "Exact version of A") - }; - _mockStorage.Setup( _ => _.CreateInstallingSnapshot(It.IsAny())).Returns(_targetPathInstalling); @@ -220,7 +214,14 @@ public void DoesNotTryToDetermineLatestPublishedModuleVersionIfExactVersionIsSpe // Act var installer = CreateDependenciesSnapshotInstallerWithMocks(); - installer.InstallSnapshot(testDependencyManifestEntries, _targetPathInstalled, PowerShell.Create(), _mockLogger.Object); + + var manifestEntries = + new[] + { + new DependencyManifestEntry("A", VersionSpecificationType.ExactVersion, "Exact version of A") + }; + + installer.InstallSnapshot(manifestEntries, _targetPathInstalled, PowerShell.Create(), _mockLogger.Object); // Assert From 00b7deff438837f1823a2ba2a5e5d960456f6320 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 10:23:36 -0700 Subject: [PATCH 13/48] Update InvalidVersionFormat message --- src/DependencyManagement/DependencyManifest.cs | 4 ++-- src/resources/PowerShellWorkerStrings.resx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DependencyManagement/DependencyManifest.cs b/src/DependencyManagement/DependencyManifest.cs index 9a6966e8..f196e0a9 100644 --- a/src/DependencyManagement/DependencyManifest.cs +++ b/src/DependencyManagement/DependencyManifest.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // @@ -129,7 +129,7 @@ private static void ValidateVersionFormat(string version) if (!IsValidVersionFormat(version)) { - var errorMessage = string.Format(PowerShellWorkerStrings.InvalidVersionFormat, "MajorVersion.*"); + var errorMessage = string.Format(PowerShellWorkerStrings.InvalidVersionFormat, version, "MajorVersion.*"); throw new ArgumentException(errorMessage); } } diff --git a/src/resources/PowerShellWorkerStrings.resx b/src/resources/PowerShellWorkerStrings.resx index 03940110..aea97078 100644 --- a/src/resources/PowerShellWorkerStrings.resx +++ b/src/resources/PowerShellWorkerStrings.resx @@ -188,7 +188,7 @@ The PowerShell data file '{0}' is invalid since it cannot be evaluated into a Hashtable object. - Version is not in the correct format. Please use the following notation: '{0}' + Version specification '{0}' is not in the correct format. Please specify the exact version or use the following notation: '{1}' Fail to install FunctionApp dependencies. Error: '{0}' From 2d0a7dc4d09698eb9745013782a4e6743b8f287e Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 11:10:01 -0700 Subject: [PATCH 14/48] Allow exact versions in the manifest --- .../DependencyManifest.cs | 70 ++++++++----------- .../DependencyManagementTests.cs | 6 +- .../DependencyManifestTests.cs | 17 +++-- 3 files changed, 46 insertions(+), 47 deletions(-) diff --git a/src/DependencyManagement/DependencyManifest.cs b/src/DependencyManagement/DependencyManifest.cs index f196e0a9..eb535f25 100644 --- a/src/DependencyManagement/DependencyManifest.cs +++ b/src/DependencyManagement/DependencyManifest.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // @@ -38,16 +38,41 @@ public IEnumerable GetEntries() foreach (DictionaryEntry entry in hashtable) { - // A valid entry is of the form: 'ModuleName'='MajorVersion.*" + // A valid entry is of the form: + // 'ModuleName'='MajorVersion.*' + // or + // 'ModuleName'='ExactVersion' var name = (string)entry.Key; var version = (string)entry.Value; ValidateModuleName(name); - yield return new DependencyManifestEntry( - name, - VersionSpecificationType.MajorVersion, - GetMajorVersion(version)); + // Look for the 'MajorVersion.*' pattern + var match = Regex.Match(version, @"^(\d+)\.\*$"); + if (match.Success) + { + yield return new DependencyManifestEntry( + name, + VersionSpecificationType.MajorVersion, + match.Groups[1].Value); + } + else + { + // This is a very basic sanity check of the format that allows + // us detect some obviously wrong cases: make sure it starts with digits, + // does not contain * anywhere, and ends with a word character. + // Not even trying to match the actual version format rules. + if (!Regex.IsMatch(version, @"^\d+([^\*]*?\w)?$")) + { + var errorMessage = string.Format(PowerShellWorkerStrings.InvalidVersionFormat, version, "MajorVersion.*"); + throw new ArgumentException(errorMessage); + } + + yield return new DependencyManifestEntry( + name, + VersionSpecificationType.ExactVersion, + version); + } } } @@ -109,38 +134,5 @@ private static void ValidateModuleName(string name) throw new ArgumentException(PowerShellWorkerStrings.DependencyNameIsNullOrEmpty); } } - - /// - /// Parses the given string version and extracts the major version. - /// Please note that the only version we currently support is of the form '1.*'. - /// - private static string GetMajorVersion(string version) - { - ValidateVersionFormat(version); - return version.Split(".")[0]; - } - - private static void ValidateVersionFormat(string version) - { - if (version == null) - { - throw new ArgumentNullException(version); - } - - if (!IsValidVersionFormat(version)) - { - var errorMessage = string.Format(PowerShellWorkerStrings.InvalidVersionFormat, version, "MajorVersion.*"); - throw new ArgumentException(errorMessage); - } - } - - /// - /// Validates the given version format. Currently, we only support 'Number.*'. - /// - private static bool IsValidVersionFormat(string version) - { - var pattern = @"^(\d)+(\.)(\*)"; - return Regex.IsMatch(version, pattern); - } } } diff --git a/test/Unit/DependencyManagement/DependencyManagementTests.cs b/test/Unit/DependencyManagement/DependencyManagementTests.cs index 0f04b26d..704b1ab3 100644 --- a/test/Unit/DependencyManagement/DependencyManagementTests.cs +++ b/test/Unit/DependencyManagement/DependencyManagementTests.cs @@ -179,9 +179,9 @@ public void TestManagedDependencyInvalidRequirementsFormatShouldThrow() var exception = Assert.Throws( () => { dependencyManager.Initialize(_testLogger); }); - Assert.Contains("Version is not in the correct format.", exception.Message); - Assert.Contains("Please use the following notation:", exception.Message); - Assert.Contains("MajorVersion.*", exception.Message); + Assert.Contains("not in the correct format.", exception.Message); + Assert.Contains("1.0.*", exception.Message); + Assert.Contains("Please specify the exact version or use the following notation: 'MajorVersion.*'", exception.Message); } } diff --git a/test/Unit/DependencyManagement/DependencyManifestTests.cs b/test/Unit/DependencyManagement/DependencyManifestTests.cs index 070ae39b..73057a96 100644 --- a/test/Unit/DependencyManagement/DependencyManifestTests.cs +++ b/test/Unit/DependencyManagement/DependencyManifestTests.cs @@ -68,6 +68,12 @@ public void GetEntriesParsesRequirementsFileWithNoEntries() [InlineData("@{ MyModule = '1.*' }", "MyModule", "1", VersionSpecificationType.MajorVersion)] [InlineData("@{ MyModule = '23.*' }", "MyModule", "23", VersionSpecificationType.MajorVersion)] [InlineData("@{ MyModule = '456.*' }", "MyModule", "456", VersionSpecificationType.MajorVersion)] + [InlineData("@{ MyModule = '0' }", "MyModule", "0", VersionSpecificationType.ExactVersion)] + [InlineData("@{ MyModule = '1' }", "MyModule", "1", VersionSpecificationType.ExactVersion)] + [InlineData("@{ MyModule = '1.0' }", "MyModule", "1.0", VersionSpecificationType.ExactVersion)] + [InlineData("@{ MyModule = '3.4.5' }", "MyModule", "3.4.5", VersionSpecificationType.ExactVersion)] + [InlineData("@{ MyModule = '123.45.67.89' }", "MyModule", "123.45.67.89", VersionSpecificationType.ExactVersion)] + [InlineData("@{ MyModule = '123.45.67.89-alpha4' }", "MyModule", "123.45.67.89-alpha4", VersionSpecificationType.ExactVersion)] public void GetEntriesParsesRequirementsFileWithSingleEntry( string content, string moduleName, @@ -110,13 +116,14 @@ public void GetEntriesParsesRequirementsFileWithMultipleEntries() [Theory] [InlineData("@{ MyModule = '' }")] + [InlineData("@{ MyModule = ' ' }")] [InlineData("@{ MyModule = 'a' }")] [InlineData("@{ MyModule = '.' }")] - [InlineData("@{ MyModule = '1' }")] [InlineData("@{ MyModule = '1.' }")] - [InlineData("@{ MyModule = '1.0' }")] - [InlineData("@{ MyModule = '1.2' }")] - [InlineData("@{ MyModule = '2.3.4' }")] + [InlineData("@{ MyModule = '*' }")] + [InlineData("@{ MyModule = '*.1' }")] + [InlineData("@{ MyModule = '1.*.2' }")] + [InlineData("@{ MyModule = '1.0.*' }")] public void GetEntriesThrowsOnInvalidVersionSpecification(string content) { CreateRequirementsFile(content); @@ -124,7 +131,7 @@ public void GetEntriesThrowsOnInvalidVersionSpecification(string content) var manifest = new DependencyManifest(_appRootPath); var exception = Assert.Throws(() => manifest.GetEntries().ToList()); - Assert.Contains("Version is not in the correct format", exception.Message); + Assert.Contains("not in the correct format", exception.Message); } [Theory] From 308c025729583aa0d57e3556f8e9ecf18160b48a Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 11:20:21 -0700 Subject: [PATCH 15/48] Extract CreateDependencyManifestEntry method --- .../DependencyManifest.cs | 57 ++++++++++--------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/src/DependencyManagement/DependencyManifest.cs b/src/DependencyManagement/DependencyManifest.cs index eb535f25..5975d285 100644 --- a/src/DependencyManagement/DependencyManifest.cs +++ b/src/DependencyManagement/DependencyManifest.cs @@ -45,35 +45,38 @@ public IEnumerable GetEntries() var name = (string)entry.Key; var version = (string)entry.Value; - ValidateModuleName(name); + yield return CreateDependencyManifestEntry(name, version); + } + } - // Look for the 'MajorVersion.*' pattern - var match = Regex.Match(version, @"^(\d+)\.\*$"); - if (match.Success) - { - yield return new DependencyManifestEntry( - name, - VersionSpecificationType.MajorVersion, - match.Groups[1].Value); - } - else - { - // This is a very basic sanity check of the format that allows - // us detect some obviously wrong cases: make sure it starts with digits, - // does not contain * anywhere, and ends with a word character. - // Not even trying to match the actual version format rules. - if (!Regex.IsMatch(version, @"^\d+([^\*]*?\w)?$")) - { - var errorMessage = string.Format(PowerShellWorkerStrings.InvalidVersionFormat, version, "MajorVersion.*"); - throw new ArgumentException(errorMessage); - } - - yield return new DependencyManifestEntry( - name, - VersionSpecificationType.ExactVersion, - version); - } + private static DependencyManifestEntry CreateDependencyManifestEntry(string name, string version) + { + ValidateModuleName(name); + + // Look for the 'MajorVersion.*' pattern first + var match = Regex.Match(version, @"^(\d+)\.\*$"); + if (match.Success) + { + return new DependencyManifestEntry( + name, + VersionSpecificationType.MajorVersion, + match.Groups[1].Value); } + + // This is a very basic sanity check of the format that allows + // us detect some obviously wrong cases: make sure it starts with digits, + // does not contain * anywhere, and ends with a word character. + // Not even trying to match the actual version format rules. + if (!Regex.IsMatch(version, @"^\d+([^\*]*?\w)?$")) + { + var errorMessage = string.Format(PowerShellWorkerStrings.InvalidVersionFormat, version, "MajorVersion.*"); + throw new ArgumentException(errorMessage); + } + + return new DependencyManifestEntry( + name, + VersionSpecificationType.ExactVersion, + version); } /// From d88660c79f514f8a8b4dfc91efbaf74fb5fd8b9d Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 12:14:34 -0700 Subject: [PATCH 16/48] Add an invalid version specification case --- test/Unit/DependencyManagement/DependencyManifestTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Unit/DependencyManagement/DependencyManifestTests.cs b/test/Unit/DependencyManagement/DependencyManifestTests.cs index 73057a96..48c9c81a 100644 --- a/test/Unit/DependencyManagement/DependencyManifestTests.cs +++ b/test/Unit/DependencyManagement/DependencyManifestTests.cs @@ -118,6 +118,7 @@ public void GetEntriesParsesRequirementsFileWithMultipleEntries() [InlineData("@{ MyModule = '' }")] [InlineData("@{ MyModule = ' ' }")] [InlineData("@{ MyModule = 'a' }")] + [InlineData("@{ MyModule = '1a' }")] [InlineData("@{ MyModule = '.' }")] [InlineData("@{ MyModule = '1.' }")] [InlineData("@{ MyModule = '*' }")] From c52fe5ccac6f4be6e4871b9460254794b0cceaa5 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 12:16:31 -0700 Subject: [PATCH 17/48] Avoid parsing the version specification twice --- .../DependencyManifest.cs | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/DependencyManagement/DependencyManifest.cs b/src/DependencyManagement/DependencyManifest.cs index 5975d285..fca9366b 100644 --- a/src/DependencyManagement/DependencyManifest.cs +++ b/src/DependencyManagement/DependencyManifest.cs @@ -53,30 +53,34 @@ private static DependencyManifestEntry CreateDependencyManifestEntry(string name { ValidateModuleName(name); - // Look for the 'MajorVersion.*' pattern first - var match = Regex.Match(version, @"^(\d+)\.\*$"); + var match = Regex.Match(version, @"^(\d+)(.*)"); if (match.Success) { - return new DependencyManifestEntry( - name, - VersionSpecificationType.MajorVersion, - match.Groups[1].Value); - } + // Look for the 'MajorVersion.*' pattern first. + var afterMajorVersion = match.Groups[2].Value; + if (afterMajorVersion == ".*") + { + return new DependencyManifestEntry( + name, + VersionSpecificationType.MajorVersion, + match.Groups[1].Value); + } - // This is a very basic sanity check of the format that allows - // us detect some obviously wrong cases: make sure it starts with digits, - // does not contain * anywhere, and ends with a word character. - // Not even trying to match the actual version format rules. - if (!Regex.IsMatch(version, @"^\d+([^\*]*?\w)?$")) - { - var errorMessage = string.Format(PowerShellWorkerStrings.InvalidVersionFormat, version, "MajorVersion.*"); - throw new ArgumentException(errorMessage); + // This is a very basic sanity check of the format that allows us detect some + // obviously wrong cases: make sure afterMajorVersion starts with a dot, + // does not contain * anywhere, and ends with a word character. + // Not even trying to match the actual version format rules, though. + if (Regex.IsMatch(afterMajorVersion, @"^(\.[^\*]*?\w)?$")) + { + return new DependencyManifestEntry( + name, + VersionSpecificationType.ExactVersion, + version); + } } - return new DependencyManifestEntry( - name, - VersionSpecificationType.ExactVersion, - version); + var errorMessage = string.Format(PowerShellWorkerStrings.InvalidVersionFormat, version, "MajorVersion.*"); + throw new ArgumentException(errorMessage); } /// From a290ee165a9f4c986adce4be0ec4b91503a766fc Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 12:49:25 -0700 Subject: [PATCH 18/48] Remove TODO --- src/DependencyManagement/DependencyManagerStorage.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/DependencyManagement/DependencyManagerStorage.cs b/src/DependencyManagement/DependencyManagerStorage.cs index 6c0c360d..dfd5b37d 100644 --- a/src/DependencyManagement/DependencyManagerStorage.cs +++ b/src/DependencyManagement/DependencyManagerStorage.cs @@ -55,7 +55,6 @@ public IEnumerable GetInstalledAndInstallingSnapshots() return Directory.EnumerateDirectories(_managedDependenciesRootPath); } - // TODO: replace with IsMajorModuleVersionInstalled? public IEnumerable GetInstalledModuleVersions(string snapshotPath, string moduleName, string majorVersion) { var modulePath = Path.Join(snapshotPath, moduleName); From 52e874fa8668903bf360d8a0eb2a6700e3fcead9 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 12:49:36 -0700 Subject: [PATCH 19/48] Add a comment --- src/DependencyManagement/VersionSpecificationType.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DependencyManagement/VersionSpecificationType.cs b/src/DependencyManagement/VersionSpecificationType.cs index 2ccd3ef1..890e8cda 100644 --- a/src/DependencyManagement/VersionSpecificationType.cs +++ b/src/DependencyManagement/VersionSpecificationType.cs @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -#pragma warning disable 1591 +#pragma warning disable 1591 // Missing XML comment for publicly visible type or member 'member' namespace Microsoft.Azure.Functions.PowerShellWorker.DependencyManagement { From 0a7cae51a07d0db748e2e8a6e23ff82d38f4ea20 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 12:52:20 -0700 Subject: [PATCH 20/48] Inline unnecessary local variables --- .../DependencySnapshotInstaller.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/DependencyManagement/DependencySnapshotInstaller.cs b/src/DependencyManagement/DependencySnapshotInstaller.cs index 0eb95b1b..5f1244c5 100644 --- a/src/DependencyManagement/DependencySnapshotInstaller.cs +++ b/src/DependencyManagement/DependencySnapshotInstaller.cs @@ -45,10 +45,7 @@ public void InstallSnapshot( { foreach (DependencyInfo module in GetExactVersionsOfDependencies(dependencies)) { - string moduleName = module.Name; - string exactVersion = module.ExactVersion; - - logger.Log(isUserOnlyLog: false, LogLevel.Trace, string.Format(PowerShellWorkerStrings.StartedInstallingModule, moduleName, exactVersion)); + logger.Log(isUserOnlyLog: false, LogLevel.Trace, string.Format(PowerShellWorkerStrings.StartedInstallingModule, module.Name, module.ExactVersion)); int tries = 1; @@ -56,9 +53,9 @@ public void InstallSnapshot( { try { - _moduleProvider.SaveModule(pwsh, moduleName, exactVersion, installingPath); + _moduleProvider.SaveModule(pwsh, module.Name, module.ExactVersion, installingPath); - var message = string.Format(PowerShellWorkerStrings.ModuleHasBeenInstalled, moduleName, exactVersion); + var message = string.Format(PowerShellWorkerStrings.ModuleHasBeenInstalled, module.Name, module.ExactVersion); logger.Log(isUserOnlyLog: false, LogLevel.Trace, message); break; @@ -66,7 +63,7 @@ public void InstallSnapshot( catch (Exception e) { string currentAttempt = GetCurrentAttemptMessage(tries); - var errorMsg = string.Format(PowerShellWorkerStrings.FailToInstallModule, moduleName, exactVersion, currentAttempt, e.Message); + var errorMsg = string.Format(PowerShellWorkerStrings.FailToInstallModule, module.Name, module.ExactVersion, currentAttempt, e.Message); logger.Log(isUserOnlyLog: false, LogLevel.Error, errorMsg); if (tries >= MaxNumberOfTries) From 800107a9137b0e3f0d450eb8faaf175e22bed242 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 12:54:47 -0700 Subject: [PATCH 21/48] Simplify test data --- test/Unit/DependencyManagement/DependencyManagerTests.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/test/Unit/DependencyManagement/DependencyManagerTests.cs b/test/Unit/DependencyManagement/DependencyManagerTests.cs index f46c9da5..5df2e026 100644 --- a/test/Unit/DependencyManagement/DependencyManagerTests.cs +++ b/test/Unit/DependencyManagement/DependencyManagerTests.cs @@ -98,12 +98,7 @@ public void StartDependencyInstallationIfNeeded_InstallsSnapshotWithLatestPublis _mockInstalledDependenciesLocator.Setup(_ => _.GetPathWithAcceptableDependencyVersionsInstalled()) .Returns(default(string)); - var dependencyManifestEntries = new[] - { - new DependencyManifestEntry("A", VersionSpecificationType.MajorVersion, "3"), - new DependencyManifestEntry("C", VersionSpecificationType.MajorVersion, "7"), - new DependencyManifestEntry("B", VersionSpecificationType.MajorVersion, "11") - }; + var dependencyManifestEntries = GetAnyNonEmptyDependencyManifestEntries(); _mockStorage.Setup(_ => _.GetDependencies()).Returns(dependencyManifestEntries); _mockStorage.Setup(_ => _.CreateNewSnapshotPath()).Returns("NewSnapshot"); From b8b5cce6ef75b6c61bc2a42515d7cb7176950be3 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 12:58:21 -0700 Subject: [PATCH 22/48] Remove outdated test: StartDependencyInstallationIfNeeded_InstallsSnapshotWithLatestPublishedModuleVersions --- .../DependencyManagerTests.cs | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/test/Unit/DependencyManagement/DependencyManagerTests.cs b/test/Unit/DependencyManagement/DependencyManagerTests.cs index 5df2e026..a1623755 100644 --- a/test/Unit/DependencyManagement/DependencyManagerTests.cs +++ b/test/Unit/DependencyManagement/DependencyManagerTests.cs @@ -92,40 +92,6 @@ public void StartDependencyInstallationIfNeeded_InstallsNothing_WhenNoDependenci _mockInstaller.VerifyNoOtherCalls(); } - [Fact] - public void StartDependencyInstallationIfNeeded_InstallsSnapshotWithLatestPublishedModuleVersions() - { - _mockInstalledDependenciesLocator.Setup(_ => _.GetPathWithAcceptableDependencyVersionsInstalled()) - .Returns(default(string)); - - var dependencyManifestEntries = GetAnyNonEmptyDependencyManifestEntries(); - - _mockStorage.Setup(_ => _.GetDependencies()).Returns(dependencyManifestEntries); - _mockStorage.Setup(_ => _.CreateNewSnapshotPath()).Returns("NewSnapshot"); - _mockPurger.Setup(_ => _.SetCurrentlyUsedSnapshot(It.IsAny(), _mockLogger.Object)); - - _mockInstaller.Setup( - _ => _.InstallSnapshot( - dependencyManifestEntries, - "NewSnapshot", - It.IsAny(), - _mockLogger.Object)); - - _mockStorage.Setup(_ => _.SnapshotExists(It.IsAny())).Returns(false); - - var dependencyManager = CreateDependencyManagerWithMocks(); - dependencyManager.Initialize(_mockLogger.Object); - dependencyManager.StartDependencyInstallationIfNeeded(PowerShell.Create(), PowerShell.Create, _mockLogger.Object); - dependencyManager.WaitForDependenciesAvailability(() => _mockLogger.Object); - - _mockInstaller.Verify( - _ => _.InstallSnapshot( - It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny()), - Times.Once()); - - _mockInstaller.VerifyNoOtherCalls(); - } - [Fact] public void StartDependencyInstallationIfNeeded_InstallsSnapshotInForeground_WhenNoAcceptableDependenciesInstalled() { From d9876adc02bfef72ed90bff9bb7ff5a0cabc2d5b Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 13:03:14 -0700 Subject: [PATCH 23/48] Simplify test data --- test/Unit/DependencyManagement/DependencyManagerTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/Unit/DependencyManagement/DependencyManagerTests.cs b/test/Unit/DependencyManagement/DependencyManagerTests.cs index a1623755..7806e9a9 100644 --- a/test/Unit/DependencyManagement/DependencyManagerTests.cs +++ b/test/Unit/DependencyManagement/DependencyManagerTests.cs @@ -34,8 +34,7 @@ public void DoesNothingOnConstruction() [Fact] public void Initialize_ReturnsNewSnapshotPath_WhenNoAcceptableDependencyVersionsInstalled() { - _mockStorage.Setup(_ => _.GetDependencies()).Returns( - new[] { new DependencyManifestEntry("ModuleName", VersionSpecificationType.MajorVersion, "1") }); + _mockStorage.Setup(_ => _.GetDependencies()).Returns(GetAnyNonEmptyDependencyManifestEntries()); _mockInstalledDependenciesLocator.Setup(_ => _.GetPathWithAcceptableDependencyVersionsInstalled()) .Returns(default(string)); _mockStorage.Setup(_ => _.CreateNewSnapshotPath()).Returns("NewSnapshot"); From ca5e0786f95d19401c13993c79a65b52e8e49d07 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 13:32:54 -0700 Subject: [PATCH 24/48] Add InstallsSpecifiedVersion_WhenExactVersionIsSpecified test --- .../DependencySnapshotInstallerTests.cs | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index 7b2fe2e6..302ff030 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -198,15 +198,25 @@ public void DoesNotPromoteDependenciesSnapshotIfSaveModuleKeepsThrowing() } [Fact] - public void DoesNotTryToDetermineLatestPublishedModuleVersionIfExactVersionIsSpecified() + public void InstallsLatestPublishedVersion_WhenMajorVersionIsSpecified() { // Arrange + var manifestEntries = + new[] + { + new DependencyManifestEntry("Module", VersionSpecificationType.MajorVersion, "Major version") + }; + _mockStorage.Setup( _ => _.CreateInstallingSnapshot(It.IsAny())).Returns(_targetPathInstalling); _mockModuleProvider.Setup( - _ => _.SaveModule(It.IsAny(), "A", "Exact version of A", _targetPathInstalling)); + _ => _.GetLatestPublishedModuleVersion("Module", "Major version")) + .Returns("Latest version"); + + _mockModuleProvider.Setup( + _ => _.SaveModule(It.IsAny(), "Module", "Latest version", _targetPathInstalling)); _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(It.IsAny())); _mockModuleProvider.Setup(_ => _.Cleanup(It.IsAny())); @@ -215,12 +225,39 @@ public void DoesNotTryToDetermineLatestPublishedModuleVersionIfExactVersionIsSpe var installer = CreateDependenciesSnapshotInstallerWithMocks(); + installer.InstallSnapshot(manifestEntries, _targetPathInstalled, PowerShell.Create(), _mockLogger.Object); + + // Assert + + _mockModuleProvider.Verify( + _ => _.GetLatestPublishedModuleVersion(It.IsAny(), It.IsAny()), + Times.Once); + } + + [Fact] + public void InstallsSpecifiedVersion_WhenExactVersionIsSpecified() + { var manifestEntries = new[] { - new DependencyManifestEntry("A", VersionSpecificationType.ExactVersion, "Exact version of A") + new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Exact version") }; + // Arrange + + _mockStorage.Setup( + _ => _.CreateInstallingSnapshot(It.IsAny())).Returns(_targetPathInstalling); + + _mockModuleProvider.Setup( + _ => _.SaveModule(It.IsAny(), "Module", "Exact version", _targetPathInstalling)); + + _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(It.IsAny())); + _mockModuleProvider.Setup(_ => _.Cleanup(It.IsAny())); + + // Act + + var installer = CreateDependenciesSnapshotInstallerWithMocks(); + installer.InstallSnapshot(manifestEntries, _targetPathInstalled, PowerShell.Create(), _mockLogger.Object); // Assert From 9ca28c54636cdf1b03ea756a840425ecd74ed69e Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 13:45:38 -0700 Subject: [PATCH 25/48] Simplify tests --- .../DependencySnapshotInstallerTests.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index 302ff030..606b2515 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -19,7 +19,7 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.Test.DependencyManagement public class DependencySnapshotInstallerTests { - private readonly Mock _mockModuleProvider = new Mock(MockBehavior.Strict); + private readonly Mock _mockModuleProvider = new Mock(); private readonly Mock _mockStorage = new Mock(MockBehavior.Strict); private readonly Mock _mockLogger = new Mock(); @@ -180,8 +180,6 @@ public void DoesNotPromoteDependenciesSnapshotIfSaveModuleKeepsThrowing() _mockStorage.Setup(_ => _.RemoveSnapshot(_targetPathInstalling)); - _mockModuleProvider.Setup(_ => _.Cleanup(dummyPowerShell)); - // Act var installer = CreateDependenciesSnapshotInstallerWithMocks(); @@ -219,18 +217,16 @@ public void InstallsLatestPublishedVersion_WhenMajorVersionIsSpecified() _ => _.SaveModule(It.IsAny(), "Module", "Latest version", _targetPathInstalling)); _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(It.IsAny())); - _mockModuleProvider.Setup(_ => _.Cleanup(It.IsAny())); // Act var installer = CreateDependenciesSnapshotInstallerWithMocks(); - installer.InstallSnapshot(manifestEntries, _targetPathInstalled, PowerShell.Create(), _mockLogger.Object); // Assert _mockModuleProvider.Verify( - _ => _.GetLatestPublishedModuleVersion(It.IsAny(), It.IsAny()), + _ => _.SaveModule(It.IsAny(), "Module", "Latest version", _targetPathInstalling), Times.Once); } @@ -252,16 +248,18 @@ public void InstallsSpecifiedVersion_WhenExactVersionIsSpecified() _ => _.SaveModule(It.IsAny(), "Module", "Exact version", _targetPathInstalling)); _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(It.IsAny())); - _mockModuleProvider.Setup(_ => _.Cleanup(It.IsAny())); // Act var installer = CreateDependenciesSnapshotInstallerWithMocks(); - installer.InstallSnapshot(manifestEntries, _targetPathInstalled, PowerShell.Create(), _mockLogger.Object); // Assert + _mockModuleProvider.Verify( + _ => _.SaveModule(It.IsAny(), "Module", "Exact version", _targetPathInstalling), + Times.Once); + _mockModuleProvider.Verify( _ => _.GetLatestPublishedModuleVersion(It.IsAny(), It.IsAny()), Times.Never); From f9be71299d89e71ad0069dcca3db611762ede023 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 14:06:20 -0700 Subject: [PATCH 26/48] Add LogsInstallationStartAndFinish test --- .../DependencySnapshotInstallerTests.cs | 66 ++++++++++++------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index 606b2515..b32e490f 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -7,6 +7,7 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.Test.DependencyManagement { using System; using System.Collections.Generic; + using System.Linq; using System.Management.Automation; using Moq; @@ -87,31 +88,9 @@ public void InstallsDependencySnapshots() foreach (var entry in _testDependencyManifestEntries) { - _mockLogger.Verify( - _ => _.Log( - false, - LogLevel.Trace, - It.Is( - message => message.Contains("Started installing") - && message.Contains(entry.Name) - && message.Contains(_testLatestPublishedModuleVersions[entry.Name])), - null), - Times.Once); - _mockModuleProvider.Verify( _ => _.SaveModule(dummyPowerShell, entry.Name, _testLatestPublishedModuleVersions[entry.Name], _targetPathInstalling), Times.Once); - - _mockLogger.Verify( - _ => _.Log( - false, - LogLevel.Trace, - It.Is( - message => message.Contains("has been installed") - && message.Contains(entry.Name) - && message.Contains(_testLatestPublishedModuleVersions[entry.Name])), - null), - Times.Once); } _mockStorage.Verify(_ => _.PromoteInstallingSnapshotToInstalledAtomically(_targetPathInstalled), Times.Once); @@ -265,9 +244,52 @@ public void InstallsSpecifiedVersion_WhenExactVersionIsSpecified() Times.Never); } + [Fact] + public void LogsInstallationStartAndFinish() + { + // Arrange + + var manifestEntries = + new[] + { + new DependencyManifestEntry("A", VersionSpecificationType.ExactVersion, "Exact A version"), + new DependencyManifestEntry("B", VersionSpecificationType.MajorVersion, "Major B version") + }; + + _mockModuleProvider.Setup( + _ => _.GetLatestPublishedModuleVersion(It.IsAny(), It.IsAny())) + .Returns("Exact B version"); + + _mockStorage.Setup(_ => _.CreateInstallingSnapshot(It.IsAny())).Returns(_targetPathInstalling); + _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(_targetPathInstalled)); + + // Act + + var installer = CreateDependenciesSnapshotInstallerWithMocks(); + installer.InstallSnapshot(manifestEntries, _targetPathInstalled, PowerShell.Create(), _mockLogger.Object); + + // Assert + VerifyLoggedOnce(new[] { "Started installing", "A", "Exact A version" }); + VerifyLoggedOnce(new[] { "has been installed", "A", "Exact A version" }); + VerifyLoggedOnce(new[] { "Started installing", "B", "Exact B version" }); + VerifyLoggedOnce(new[] { "has been installed", "B", "Exact B version" }); + } + private DependencySnapshotInstaller CreateDependenciesSnapshotInstallerWithMocks() { return new DependencySnapshotInstaller(_mockModuleProvider.Object, _mockStorage.Object); } + + private void VerifyLoggedOnce(IEnumerable messageParts) + { + _mockLogger.Verify( + _ => _.Log( + false, + LogLevel.Trace, + It.Is( + message => messageParts.All(part => message.Contains(part))), + null), + Times.Once); + } } } From 215058343ad91889d9f3537a335d6dad0817181f Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 14:17:43 -0700 Subject: [PATCH 27/48] Add PromotesInstallingSnapshotToInstalledAfterSuccessfullySavingModule test --- .../DependencySnapshotInstallerTests.cs | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index b32e490f..54e1cbd1 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -84,17 +84,12 @@ public void InstallsDependencySnapshots() // Assert - _mockStorage.Verify(_ => _.CreateInstallingSnapshot(_targetPathInstalled), Times.Once); - foreach (var entry in _testDependencyManifestEntries) { _mockModuleProvider.Verify( _ => _.SaveModule(dummyPowerShell, entry.Name, _testLatestPublishedModuleVersions[entry.Name], _targetPathInstalling), Times.Once); } - - _mockStorage.Verify(_ => _.PromoteInstallingSnapshotToInstalledAtomically(_targetPathInstalled), Times.Once); - _mockModuleProvider.Verify(_ => _.Cleanup(dummyPowerShell), Times.Once); } [Fact] @@ -244,6 +239,37 @@ public void InstallsSpecifiedVersion_WhenExactVersionIsSpecified() Times.Never); } + [Fact] + public void PromotesInstallingSnapshotToInstalledAfterSuccessfullySavingModule() + { + // Arrange + + var manifestEntries = + new[] + { + new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Version") + }; + + var dummyPowerShell = PowerShell.Create(); + _mockStorage.Setup(_ => _.CreateInstallingSnapshot(_targetPathInstalled)).Returns(_targetPathInstalling); + + _mockModuleProvider.Setup( + _ => _.SaveModule(dummyPowerShell, "Module", "Version", _targetPathInstalling)); + + _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(_targetPathInstalled)); + + // Act + + var installer = CreateDependenciesSnapshotInstallerWithMocks(); + installer.InstallSnapshot(manifestEntries, _targetPathInstalled, dummyPowerShell, _mockLogger.Object); + + // Assert + + _mockStorage.Verify(_ => _.CreateInstallingSnapshot(_targetPathInstalled), Times.Once); + _mockStorage.Verify(_ => _.PromoteInstallingSnapshotToInstalledAtomically(_targetPathInstalled), Times.Once); + _mockModuleProvider.Verify(_ => _.Cleanup(dummyPowerShell), Times.Once); + } + [Fact] public void LogsInstallationStartAndFinish() { From 951b082638a6911403c2c35befecf6c85eeb633f Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 14:18:55 -0700 Subject: [PATCH 28/48] Remove InstallsDependencySnapshots test --- .../DependencySnapshotInstallerTests.cs | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index 54e1cbd1..b4ab67c7 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -55,43 +55,6 @@ public void DoesNothingOnConstruction() CreateDependenciesSnapshotInstallerWithMocks(); } - [Fact] - public void InstallsDependencySnapshots() - { - // Arrange - - var dummyPowerShell = PowerShell.Create(); - _mockStorage.Setup(_ => _.CreateInstallingSnapshot(_targetPathInstalled)) - .Returns(_targetPathInstalling); - - foreach (var entry in _testDependencyManifestEntries) - { - _mockModuleProvider.Setup( - _ => _.GetLatestPublishedModuleVersion(entry.Name, entry.VersionSpecification)) - .Returns(_testLatestPublishedModuleVersions[entry.Name]); - - _mockModuleProvider.Setup( - _ => _.SaveModule(dummyPowerShell, entry.Name, _testLatestPublishedModuleVersions[entry.Name], _targetPathInstalling)); - } - - _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(_targetPathInstalled)); - _mockModuleProvider.Setup(_ => _.Cleanup(dummyPowerShell)); - - // Act - - var installer = CreateDependenciesSnapshotInstallerWithMocks(); - installer.InstallSnapshot(_testDependencyManifestEntries, _targetPathInstalled, dummyPowerShell, _mockLogger.Object); - - // Assert - - foreach (var entry in _testDependencyManifestEntries) - { - _mockModuleProvider.Verify( - _ => _.SaveModule(dummyPowerShell, entry.Name, _testLatestPublishedModuleVersions[entry.Name], _targetPathInstalling), - Times.Once); - } - } - [Fact] public void DoesNotSaveModuleIfGetLatestPublishedModuleVersionThrows() { From f2b9c23b65f704be7edc5f052fba4c644f888adf Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 14:19:34 -0700 Subject: [PATCH 29/48] Rename tests --- .../DependencyManagement/DependencySnapshotInstallerTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index b4ab67c7..f9706d11 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -133,7 +133,7 @@ public void DoesNotPromoteDependenciesSnapshotIfSaveModuleKeepsThrowing() } [Fact] - public void InstallsLatestPublishedVersion_WhenMajorVersionIsSpecified() + public void SavesLatestPublishedVersion_WhenMajorVersionIsSpecified() { // Arrange @@ -168,7 +168,7 @@ public void InstallsLatestPublishedVersion_WhenMajorVersionIsSpecified() } [Fact] - public void InstallsSpecifiedVersion_WhenExactVersionIsSpecified() + public void SavesSpecifiedVersion_WhenExactVersionIsSpecified() { var manifestEntries = new[] From 4b83c08ea1a67b1d5083eb8b50e9587d42970198 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 14:21:23 -0700 Subject: [PATCH 30/48] Reorder tests --- .../DependencySnapshotInstallerTests.cs | 186 +++++++++--------- 1 file changed, 93 insertions(+), 93 deletions(-) diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index f9706d11..85b73fb0 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -56,80 +56,38 @@ public void DoesNothingOnConstruction() } [Fact] - public void DoesNotSaveModuleIfGetLatestPublishedModuleVersionThrows() + public void SavesSpecifiedVersion_WhenExactVersionIsSpecified() { - // Arrange + var manifestEntries = + new[] + { + new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Exact version") + }; - var dummyPowerShell = PowerShell.Create(); - _mockStorage.Setup(_ => _.CreateInstallingSnapshot(_targetPathInstalled)) - .Returns(_targetPathInstalling); + // Arrange - var injectedException = new InvalidOperationException("Couldn't get latest published module version"); + _mockStorage.Setup( + _ => _.CreateInstallingSnapshot(It.IsAny())).Returns(_targetPathInstalling); _mockModuleProvider.Setup( - _ => _.GetLatestPublishedModuleVersion(It.IsAny(), It.IsAny())) - .Throws(injectedException); - - _mockStorage.Setup(_ => _.RemoveSnapshot(_targetPathInstalling)); + _ => _.SaveModule(It.IsAny(), "Module", "Exact version", _targetPathInstalling)); - _mockModuleProvider.Setup(_ => _.Cleanup(dummyPowerShell)); + _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(It.IsAny())); // Act var installer = CreateDependenciesSnapshotInstallerWithMocks(); - var caughtException = Assert.Throws( - () => installer.InstallSnapshot(_testDependencyManifestEntries, _targetPathInstalled, dummyPowerShell, _mockLogger.Object)); + installer.InstallSnapshot(manifestEntries, _targetPathInstalled, PowerShell.Create(), _mockLogger.Object); // Assert - Assert.Contains(injectedException.Message, caughtException.Message); + _mockModuleProvider.Verify( + _ => _.SaveModule(It.IsAny(), "Module", "Exact version", _targetPathInstalling), + Times.Once); _mockModuleProvider.Verify( - _ => _.SaveModule(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), + _ => _.GetLatestPublishedModuleVersion(It.IsAny(), It.IsAny()), Times.Never); - - _mockStorage.Verify(_ => _.PromoteInstallingSnapshotToInstalledAtomically(It.IsAny()), Times.Never); - _mockStorage.Verify(_ => _.RemoveSnapshot(_targetPathInstalling)); - _mockModuleProvider.Verify(_ => _.Cleanup(dummyPowerShell), Times.Once); - } - - [Fact] - public void DoesNotPromoteDependenciesSnapshotIfSaveModuleKeepsThrowing() - { - // Arrange - - var dummyPowerShell = PowerShell.Create(); - _mockStorage.Setup(_ => _.CreateInstallingSnapshot(_targetPathInstalled)) - .Returns(_targetPathInstalling); - - var injectedException = new Exception("Couldn't save module"); - - foreach (var entry in _testDependencyManifestEntries) - { - _mockModuleProvider.Setup( - _ => _.GetLatestPublishedModuleVersion(entry.Name, entry.VersionSpecification)) - .Returns(_testLatestPublishedModuleVersions[entry.Name]); - } - - _mockModuleProvider.Setup( - _ => _.SaveModule(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Throws(injectedException); - - _mockStorage.Setup(_ => _.RemoveSnapshot(_targetPathInstalling)); - - // Act - - var installer = CreateDependenciesSnapshotInstallerWithMocks(); - var thrownException = Assert.Throws( - () => installer.InstallSnapshot(_testDependencyManifestEntries, _targetPathInstalled, dummyPowerShell, _mockLogger.Object)); - - // Assert - - Assert.Contains(injectedException.Message, thrownException.Message); - - _mockStorage.Verify(_ => _.PromoteInstallingSnapshotToInstalledAtomically(It.IsAny()), Times.Never); - _mockStorage.Verify(_ => _.RemoveSnapshot(_targetPathInstalling)); - _mockModuleProvider.Verify(_ => _.Cleanup(dummyPowerShell), Times.Once); } [Fact] @@ -167,41 +125,6 @@ public void SavesLatestPublishedVersion_WhenMajorVersionIsSpecified() Times.Once); } - [Fact] - public void SavesSpecifiedVersion_WhenExactVersionIsSpecified() - { - var manifestEntries = - new[] - { - new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Exact version") - }; - - // Arrange - - _mockStorage.Setup( - _ => _.CreateInstallingSnapshot(It.IsAny())).Returns(_targetPathInstalling); - - _mockModuleProvider.Setup( - _ => _.SaveModule(It.IsAny(), "Module", "Exact version", _targetPathInstalling)); - - _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(It.IsAny())); - - // Act - - var installer = CreateDependenciesSnapshotInstallerWithMocks(); - installer.InstallSnapshot(manifestEntries, _targetPathInstalled, PowerShell.Create(), _mockLogger.Object); - - // Assert - - _mockModuleProvider.Verify( - _ => _.SaveModule(It.IsAny(), "Module", "Exact version", _targetPathInstalling), - Times.Once); - - _mockModuleProvider.Verify( - _ => _.GetLatestPublishedModuleVersion(It.IsAny(), It.IsAny()), - Times.Never); - } - [Fact] public void PromotesInstallingSnapshotToInstalledAfterSuccessfullySavingModule() { @@ -264,6 +187,83 @@ public void LogsInstallationStartAndFinish() VerifyLoggedOnce(new[] { "has been installed", "B", "Exact B version" }); } + [Fact] + public void DoesNotSaveModuleIfGetLatestPublishedModuleVersionThrows() + { + // Arrange + + var dummyPowerShell = PowerShell.Create(); + _mockStorage.Setup(_ => _.CreateInstallingSnapshot(_targetPathInstalled)) + .Returns(_targetPathInstalling); + + var injectedException = new InvalidOperationException("Couldn't get latest published module version"); + + _mockModuleProvider.Setup( + _ => _.GetLatestPublishedModuleVersion(It.IsAny(), It.IsAny())) + .Throws(injectedException); + + _mockStorage.Setup(_ => _.RemoveSnapshot(_targetPathInstalling)); + + _mockModuleProvider.Setup(_ => _.Cleanup(dummyPowerShell)); + + // Act + + var installer = CreateDependenciesSnapshotInstallerWithMocks(); + var caughtException = Assert.Throws( + () => installer.InstallSnapshot(_testDependencyManifestEntries, _targetPathInstalled, dummyPowerShell, _mockLogger.Object)); + + // Assert + + Assert.Contains(injectedException.Message, caughtException.Message); + + _mockModuleProvider.Verify( + _ => _.SaveModule(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), + Times.Never); + + _mockStorage.Verify(_ => _.PromoteInstallingSnapshotToInstalledAtomically(It.IsAny()), Times.Never); + _mockStorage.Verify(_ => _.RemoveSnapshot(_targetPathInstalling)); + _mockModuleProvider.Verify(_ => _.Cleanup(dummyPowerShell), Times.Once); + } + + [Fact] + public void DoesNotPromoteDependenciesSnapshotIfSaveModuleKeepsThrowing() + { + // Arrange + + var dummyPowerShell = PowerShell.Create(); + _mockStorage.Setup(_ => _.CreateInstallingSnapshot(_targetPathInstalled)) + .Returns(_targetPathInstalling); + + var injectedException = new Exception("Couldn't save module"); + + foreach (var entry in _testDependencyManifestEntries) + { + _mockModuleProvider.Setup( + _ => _.GetLatestPublishedModuleVersion(entry.Name, entry.VersionSpecification)) + .Returns(_testLatestPublishedModuleVersions[entry.Name]); + } + + _mockModuleProvider.Setup( + _ => _.SaveModule(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Throws(injectedException); + + _mockStorage.Setup(_ => _.RemoveSnapshot(_targetPathInstalling)); + + // Act + + var installer = CreateDependenciesSnapshotInstallerWithMocks(); + var thrownException = Assert.Throws( + () => installer.InstallSnapshot(_testDependencyManifestEntries, _targetPathInstalled, dummyPowerShell, _mockLogger.Object)); + + // Assert + + Assert.Contains(injectedException.Message, thrownException.Message); + + _mockStorage.Verify(_ => _.PromoteInstallingSnapshotToInstalledAtomically(It.IsAny()), Times.Never); + _mockStorage.Verify(_ => _.RemoveSnapshot(_targetPathInstalling)); + _mockModuleProvider.Verify(_ => _.Cleanup(dummyPowerShell), Times.Once); + } + private DependencySnapshotInstaller CreateDependenciesSnapshotInstallerWithMocks() { return new DependencySnapshotInstaller(_mockModuleProvider.Object, _mockStorage.Object); From 66f1099a5ec91052250744ff70562aaffe54c938 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 14:28:46 -0700 Subject: [PATCH 31/48] Get rid of _testDependencyManifestEntries and _testLatestPublishedModuleVersions --- .../DependencySnapshotInstallerTests.cs | 39 +++++++------------ 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index 85b73fb0..ae37c0ee 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -24,22 +24,6 @@ public class DependencySnapshotInstallerTests private readonly Mock _mockStorage = new Mock(MockBehavior.Strict); private readonly Mock _mockLogger = new Mock(); - private readonly IEnumerable _testDependencyManifestEntries = - new[] - { - new DependencyManifestEntry("A", VersionSpecificationType.MajorVersion, "3"), - new DependencyManifestEntry("C", VersionSpecificationType.ExactVersion, "7.0.1.3"), - new DependencyManifestEntry("B", VersionSpecificationType.MajorVersion, "11") - }; - - private readonly Dictionary _testLatestPublishedModuleVersions = - new Dictionary - { - { "A", "3.8.2" }, - { "B", "11.0" }, - { "C", "7.0.1.3" } - }; - private readonly string _targetPathInstalled; private readonly string _targetPathInstalling; @@ -192,6 +176,12 @@ public void DoesNotSaveModuleIfGetLatestPublishedModuleVersionThrows() { // Arrange + var manifestEntries = + new[] + { + new DependencyManifestEntry("Module", VersionSpecificationType.MajorVersion, "Version") + }; + var dummyPowerShell = PowerShell.Create(); _mockStorage.Setup(_ => _.CreateInstallingSnapshot(_targetPathInstalled)) .Returns(_targetPathInstalling); @@ -210,7 +200,7 @@ public void DoesNotSaveModuleIfGetLatestPublishedModuleVersionThrows() var installer = CreateDependenciesSnapshotInstallerWithMocks(); var caughtException = Assert.Throws( - () => installer.InstallSnapshot(_testDependencyManifestEntries, _targetPathInstalled, dummyPowerShell, _mockLogger.Object)); + () => installer.InstallSnapshot(manifestEntries, _targetPathInstalled, dummyPowerShell, _mockLogger.Object)); // Assert @@ -230,19 +220,18 @@ public void DoesNotPromoteDependenciesSnapshotIfSaveModuleKeepsThrowing() { // Arrange + var manifestEntries = + new[] + { + new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Version") + }; + var dummyPowerShell = PowerShell.Create(); _mockStorage.Setup(_ => _.CreateInstallingSnapshot(_targetPathInstalled)) .Returns(_targetPathInstalling); var injectedException = new Exception("Couldn't save module"); - foreach (var entry in _testDependencyManifestEntries) - { - _mockModuleProvider.Setup( - _ => _.GetLatestPublishedModuleVersion(entry.Name, entry.VersionSpecification)) - .Returns(_testLatestPublishedModuleVersions[entry.Name]); - } - _mockModuleProvider.Setup( _ => _.SaveModule(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Throws(injectedException); @@ -253,7 +242,7 @@ public void DoesNotPromoteDependenciesSnapshotIfSaveModuleKeepsThrowing() var installer = CreateDependenciesSnapshotInstallerWithMocks(); var thrownException = Assert.Throws( - () => installer.InstallSnapshot(_testDependencyManifestEntries, _targetPathInstalled, dummyPowerShell, _mockLogger.Object)); + () => installer.InstallSnapshot(manifestEntries, _targetPathInstalled, dummyPowerShell, _mockLogger.Object)); // Assert From 81b2ad334539169420777fa41215bfd918f98096 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 14:33:09 -0700 Subject: [PATCH 32/48] Add CleansUpPowerShellRunspaceAfterSuccessfullySavingModule test --- .../DependencySnapshotInstallerTests.cs | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index ae37c0ee..31b61b33 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -114,6 +114,35 @@ public void PromotesInstallingSnapshotToInstalledAfterSuccessfullySavingModule() { // Arrange + var manifestEntries = + new[] + { + new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Version") + }; + + _mockStorage.Setup(_ => _.CreateInstallingSnapshot(_targetPathInstalled)).Returns(_targetPathInstalling); + + _mockModuleProvider.Setup( + _ => _.SaveModule(It.IsAny(), "Module", "Version", _targetPathInstalling)); + + _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(_targetPathInstalled)); + + // Act + + var installer = CreateDependenciesSnapshotInstallerWithMocks(); + installer.InstallSnapshot(manifestEntries, _targetPathInstalled, PowerShell.Create(), _mockLogger.Object); + + // Assert + + _mockStorage.Verify(_ => _.CreateInstallingSnapshot(_targetPathInstalled), Times.Once); + _mockStorage.Verify(_ => _.PromoteInstallingSnapshotToInstalledAtomically(_targetPathInstalled), Times.Once); + } + + [Fact] + public void CleansUpPowerShellRunspaceAfterSuccessfullySavingModule() + { + // Arrange + var manifestEntries = new[] { @@ -135,8 +164,6 @@ public void PromotesInstallingSnapshotToInstalledAfterSuccessfullySavingModule() // Assert - _mockStorage.Verify(_ => _.CreateInstallingSnapshot(_targetPathInstalled), Times.Once); - _mockStorage.Verify(_ => _.PromoteInstallingSnapshotToInstalledAtomically(_targetPathInstalled), Times.Once); _mockModuleProvider.Verify(_ => _.Cleanup(dummyPowerShell), Times.Once); } From b93b8e3443557d1982fe3f0640ccfe3549d9801d Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 14:34:55 -0700 Subject: [PATCH 33/48] Verify no other calls in DoesNothingOnConstruction test --- .../DependencyManagement/DependencySnapshotInstallerTests.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index 31b61b33..07368a96 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -37,6 +37,10 @@ public DependencySnapshotInstallerTests() public void DoesNothingOnConstruction() { CreateDependenciesSnapshotInstallerWithMocks(); + + _mockModuleProvider.VerifyNoOtherCalls(); + _mockStorage.VerifyNoOtherCalls(); + _mockLogger.VerifyNoOtherCalls(); } [Fact] From 0586d2279e3b022e57c9eac2a63c06ccf31c781f Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 14:38:15 -0700 Subject: [PATCH 34/48] Remove unnecessary SaveModule setups --- .../DependencySnapshotInstallerTests.cs | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index 07368a96..aeb1e6ce 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -54,12 +54,7 @@ public void SavesSpecifiedVersion_WhenExactVersionIsSpecified() // Arrange - _mockStorage.Setup( - _ => _.CreateInstallingSnapshot(It.IsAny())).Returns(_targetPathInstalling); - - _mockModuleProvider.Setup( - _ => _.SaveModule(It.IsAny(), "Module", "Exact version", _targetPathInstalling)); - + _mockStorage.Setup(_ => _.CreateInstallingSnapshot(It.IsAny())).Returns(_targetPathInstalling); _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(It.IsAny())); // Act @@ -96,9 +91,6 @@ public void SavesLatestPublishedVersion_WhenMajorVersionIsSpecified() _ => _.GetLatestPublishedModuleVersion("Module", "Major version")) .Returns("Latest version"); - _mockModuleProvider.Setup( - _ => _.SaveModule(It.IsAny(), "Module", "Latest version", _targetPathInstalling)); - _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(It.IsAny())); // Act @@ -125,10 +117,6 @@ public void PromotesInstallingSnapshotToInstalledAfterSuccessfullySavingModule() }; _mockStorage.Setup(_ => _.CreateInstallingSnapshot(_targetPathInstalled)).Returns(_targetPathInstalling); - - _mockModuleProvider.Setup( - _ => _.SaveModule(It.IsAny(), "Module", "Version", _targetPathInstalling)); - _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(_targetPathInstalled)); // Act @@ -155,10 +143,6 @@ public void CleansUpPowerShellRunspaceAfterSuccessfullySavingModule() var dummyPowerShell = PowerShell.Create(); _mockStorage.Setup(_ => _.CreateInstallingSnapshot(_targetPathInstalled)).Returns(_targetPathInstalling); - - _mockModuleProvider.Setup( - _ => _.SaveModule(dummyPowerShell, "Module", "Version", _targetPathInstalling)); - _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(_targetPathInstalled)); // Act From 66e7c312967173103482bff4136854756899b1fe Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 14:46:11 -0700 Subject: [PATCH 35/48] Extract ExpectSnapshotCreationAndPromotion method --- .../DependencySnapshotInstallerTests.cs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index aeb1e6ce..101a1327 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -46,16 +46,15 @@ public void DoesNothingOnConstruction() [Fact] public void SavesSpecifiedVersion_WhenExactVersionIsSpecified() { + // Arrange + var manifestEntries = new[] { new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Exact version") }; - // Arrange - - _mockStorage.Setup(_ => _.CreateInstallingSnapshot(It.IsAny())).Returns(_targetPathInstalling); - _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(It.IsAny())); + ExpectSnapshotCreationAndPromotion(); // Act @@ -84,15 +83,12 @@ public void SavesLatestPublishedVersion_WhenMajorVersionIsSpecified() new DependencyManifestEntry("Module", VersionSpecificationType.MajorVersion, "Major version") }; - _mockStorage.Setup( - _ => _.CreateInstallingSnapshot(It.IsAny())).Returns(_targetPathInstalling); + ExpectSnapshotCreationAndPromotion(); _mockModuleProvider.Setup( _ => _.GetLatestPublishedModuleVersion("Module", "Major version")) .Returns("Latest version"); - _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(It.IsAny())); - // Act var installer = CreateDependenciesSnapshotInstallerWithMocks(); @@ -116,8 +112,7 @@ public void PromotesInstallingSnapshotToInstalledAfterSuccessfullySavingModule() new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Version") }; - _mockStorage.Setup(_ => _.CreateInstallingSnapshot(_targetPathInstalled)).Returns(_targetPathInstalling); - _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(_targetPathInstalled)); + ExpectSnapshotCreationAndPromotion(); // Act @@ -141,9 +136,9 @@ public void CleansUpPowerShellRunspaceAfterSuccessfullySavingModule() new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Version") }; + ExpectSnapshotCreationAndPromotion(); + var dummyPowerShell = PowerShell.Create(); - _mockStorage.Setup(_ => _.CreateInstallingSnapshot(_targetPathInstalled)).Returns(_targetPathInstalling); - _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(_targetPathInstalled)); // Act @@ -171,8 +166,7 @@ public void LogsInstallationStartAndFinish() _ => _.GetLatestPublishedModuleVersion(It.IsAny(), It.IsAny())) .Returns("Exact B version"); - _mockStorage.Setup(_ => _.CreateInstallingSnapshot(It.IsAny())).Returns(_targetPathInstalling); - _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(_targetPathInstalled)); + ExpectSnapshotCreationAndPromotion(); // Act @@ -197,9 +191,9 @@ public void DoesNotSaveModuleIfGetLatestPublishedModuleVersionThrows() new DependencyManifestEntry("Module", VersionSpecificationType.MajorVersion, "Version") }; + ExpectSnapshotCreationAndPromotion(); + var dummyPowerShell = PowerShell.Create(); - _mockStorage.Setup(_ => _.CreateInstallingSnapshot(_targetPathInstalled)) - .Returns(_targetPathInstalling); var injectedException = new InvalidOperationException("Couldn't get latest published module version"); @@ -241,9 +235,9 @@ public void DoesNotPromoteDependenciesSnapshotIfSaveModuleKeepsThrowing() new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Version") }; + ExpectSnapshotCreationAndPromotion(); + var dummyPowerShell = PowerShell.Create(); - _mockStorage.Setup(_ => _.CreateInstallingSnapshot(_targetPathInstalled)) - .Returns(_targetPathInstalling); var injectedException = new Exception("Couldn't save module"); @@ -273,6 +267,12 @@ private DependencySnapshotInstaller CreateDependenciesSnapshotInstallerWithMocks return new DependencySnapshotInstaller(_mockModuleProvider.Object, _mockStorage.Object); } + private void ExpectSnapshotCreationAndPromotion() + { + _mockStorage.Setup(_ => _.CreateInstallingSnapshot(_targetPathInstalled)).Returns(_targetPathInstalling); + _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(_targetPathInstalled)); + } + private void VerifyLoggedOnce(IEnumerable messageParts) { _mockLogger.Verify( From 60964d49937b853bf8524725e4998a1bb0cc3305 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 14:47:54 -0700 Subject: [PATCH 36/48] Compact formatting --- .../DependencySnapshotInstallerTests.cs | 30 ++++--------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index 101a1327..ad3f304b 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -49,10 +49,7 @@ public void SavesSpecifiedVersion_WhenExactVersionIsSpecified() // Arrange var manifestEntries = - new[] - { - new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Exact version") - }; + new[] { new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Exact version") }; ExpectSnapshotCreationAndPromotion(); @@ -78,10 +75,7 @@ public void SavesLatestPublishedVersion_WhenMajorVersionIsSpecified() // Arrange var manifestEntries = - new[] - { - new DependencyManifestEntry("Module", VersionSpecificationType.MajorVersion, "Major version") - }; + new[] { new DependencyManifestEntry("Module", VersionSpecificationType.MajorVersion, "Major version") }; ExpectSnapshotCreationAndPromotion(); @@ -107,10 +101,7 @@ public void PromotesInstallingSnapshotToInstalledAfterSuccessfullySavingModule() // Arrange var manifestEntries = - new[] - { - new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Version") - }; + new[] { new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Version") }; ExpectSnapshotCreationAndPromotion(); @@ -131,10 +122,7 @@ public void CleansUpPowerShellRunspaceAfterSuccessfullySavingModule() // Arrange var manifestEntries = - new[] - { - new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Version") - }; + new[] { new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Version") }; ExpectSnapshotCreationAndPromotion(); @@ -186,10 +174,7 @@ public void DoesNotSaveModuleIfGetLatestPublishedModuleVersionThrows() // Arrange var manifestEntries = - new[] - { - new DependencyManifestEntry("Module", VersionSpecificationType.MajorVersion, "Version") - }; + new[] { new DependencyManifestEntry("Module", VersionSpecificationType.MajorVersion, "Version") }; ExpectSnapshotCreationAndPromotion(); @@ -230,10 +215,7 @@ public void DoesNotPromoteDependenciesSnapshotIfSaveModuleKeepsThrowing() // Arrange var manifestEntries = - new[] - { - new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Version") - }; + new[] { new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Version") }; ExpectSnapshotCreationAndPromotion(); From f559f9eef699beb6ad3ad22ce0485723e591f8b3 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 14:50:53 -0700 Subject: [PATCH 37/48] Add comments --- .../DependencySnapshotInstallerTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index ad3f304b..37b1097d 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -259,11 +259,11 @@ private void VerifyLoggedOnce(IEnumerable messageParts) { _mockLogger.Verify( _ => _.Log( - false, + false, // isUserOnlyLog LogLevel.Trace, - It.Is( + It.Is( // the message should contain every item of messageParts message => messageParts.All(part => message.Contains(part))), - null), + null), // exception Times.Once); } } From a113269d5f4e01bc3650a10ce2c27046d26b31bb Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 14:53:10 -0700 Subject: [PATCH 38/48] Remove AAA comments --- .../DependencySnapshotInstallerTests.cs | 41 ------------------- 1 file changed, 41 deletions(-) diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index 37b1097d..e24d1c34 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -46,20 +46,14 @@ public void DoesNothingOnConstruction() [Fact] public void SavesSpecifiedVersion_WhenExactVersionIsSpecified() { - // Arrange - var manifestEntries = new[] { new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Exact version") }; ExpectSnapshotCreationAndPromotion(); - // Act - var installer = CreateDependenciesSnapshotInstallerWithMocks(); installer.InstallSnapshot(manifestEntries, _targetPathInstalled, PowerShell.Create(), _mockLogger.Object); - // Assert - _mockModuleProvider.Verify( _ => _.SaveModule(It.IsAny(), "Module", "Exact version", _targetPathInstalling), Times.Once); @@ -72,8 +66,6 @@ public void SavesSpecifiedVersion_WhenExactVersionIsSpecified() [Fact] public void SavesLatestPublishedVersion_WhenMajorVersionIsSpecified() { - // Arrange - var manifestEntries = new[] { new DependencyManifestEntry("Module", VersionSpecificationType.MajorVersion, "Major version") }; @@ -83,13 +75,9 @@ public void SavesLatestPublishedVersion_WhenMajorVersionIsSpecified() _ => _.GetLatestPublishedModuleVersion("Module", "Major version")) .Returns("Latest version"); - // Act - var installer = CreateDependenciesSnapshotInstallerWithMocks(); installer.InstallSnapshot(manifestEntries, _targetPathInstalled, PowerShell.Create(), _mockLogger.Object); - // Assert - _mockModuleProvider.Verify( _ => _.SaveModule(It.IsAny(), "Module", "Latest version", _targetPathInstalling), Times.Once); @@ -98,20 +86,14 @@ public void SavesLatestPublishedVersion_WhenMajorVersionIsSpecified() [Fact] public void PromotesInstallingSnapshotToInstalledAfterSuccessfullySavingModule() { - // Arrange - var manifestEntries = new[] { new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Version") }; ExpectSnapshotCreationAndPromotion(); - // Act - var installer = CreateDependenciesSnapshotInstallerWithMocks(); installer.InstallSnapshot(manifestEntries, _targetPathInstalled, PowerShell.Create(), _mockLogger.Object); - // Assert - _mockStorage.Verify(_ => _.CreateInstallingSnapshot(_targetPathInstalled), Times.Once); _mockStorage.Verify(_ => _.PromoteInstallingSnapshotToInstalledAtomically(_targetPathInstalled), Times.Once); } @@ -119,8 +101,6 @@ public void PromotesInstallingSnapshotToInstalledAfterSuccessfullySavingModule() [Fact] public void CleansUpPowerShellRunspaceAfterSuccessfullySavingModule() { - // Arrange - var manifestEntries = new[] { new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Version") }; @@ -128,21 +108,15 @@ public void CleansUpPowerShellRunspaceAfterSuccessfullySavingModule() var dummyPowerShell = PowerShell.Create(); - // Act - var installer = CreateDependenciesSnapshotInstallerWithMocks(); installer.InstallSnapshot(manifestEntries, _targetPathInstalled, dummyPowerShell, _mockLogger.Object); - // Assert - _mockModuleProvider.Verify(_ => _.Cleanup(dummyPowerShell), Times.Once); } [Fact] public void LogsInstallationStartAndFinish() { - // Arrange - var manifestEntries = new[] { @@ -156,12 +130,9 @@ public void LogsInstallationStartAndFinish() ExpectSnapshotCreationAndPromotion(); - // Act - var installer = CreateDependenciesSnapshotInstallerWithMocks(); installer.InstallSnapshot(manifestEntries, _targetPathInstalled, PowerShell.Create(), _mockLogger.Object); - // Assert VerifyLoggedOnce(new[] { "Started installing", "A", "Exact A version" }); VerifyLoggedOnce(new[] { "has been installed", "A", "Exact A version" }); VerifyLoggedOnce(new[] { "Started installing", "B", "Exact B version" }); @@ -171,8 +142,6 @@ public void LogsInstallationStartAndFinish() [Fact] public void DoesNotSaveModuleIfGetLatestPublishedModuleVersionThrows() { - // Arrange - var manifestEntries = new[] { new DependencyManifestEntry("Module", VersionSpecificationType.MajorVersion, "Version") }; @@ -190,14 +159,10 @@ public void DoesNotSaveModuleIfGetLatestPublishedModuleVersionThrows() _mockModuleProvider.Setup(_ => _.Cleanup(dummyPowerShell)); - // Act - var installer = CreateDependenciesSnapshotInstallerWithMocks(); var caughtException = Assert.Throws( () => installer.InstallSnapshot(manifestEntries, _targetPathInstalled, dummyPowerShell, _mockLogger.Object)); - // Assert - Assert.Contains(injectedException.Message, caughtException.Message); _mockModuleProvider.Verify( @@ -212,8 +177,6 @@ public void DoesNotSaveModuleIfGetLatestPublishedModuleVersionThrows() [Fact] public void DoesNotPromoteDependenciesSnapshotIfSaveModuleKeepsThrowing() { - // Arrange - var manifestEntries = new[] { new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Version") }; @@ -229,14 +192,10 @@ public void DoesNotPromoteDependenciesSnapshotIfSaveModuleKeepsThrowing() _mockStorage.Setup(_ => _.RemoveSnapshot(_targetPathInstalling)); - // Act - var installer = CreateDependenciesSnapshotInstallerWithMocks(); var thrownException = Assert.Throws( () => installer.InstallSnapshot(manifestEntries, _targetPathInstalled, dummyPowerShell, _mockLogger.Object)); - // Assert - Assert.Contains(injectedException.Message, thrownException.Message); _mockStorage.Verify(_ => _.PromoteInstallingSnapshotToInstalledAtomically(It.IsAny()), Times.Never); From 03590234f4111b3e143139fa084e6dba3a567b3a Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 14:56:36 -0700 Subject: [PATCH 39/48] Fix formatting --- .../DependencyManagement/DependencySnapshotInstallerTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index e24d1c34..f7941e98 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -72,7 +72,7 @@ public void SavesLatestPublishedVersion_WhenMajorVersionIsSpecified() ExpectSnapshotCreationAndPromotion(); _mockModuleProvider.Setup( - _ => _.GetLatestPublishedModuleVersion("Module", "Major version")) + _ => _.GetLatestPublishedModuleVersion("Module", "Major version")) .Returns("Latest version"); var installer = CreateDependenciesSnapshotInstallerWithMocks(); @@ -187,7 +187,7 @@ public void DoesNotPromoteDependenciesSnapshotIfSaveModuleKeepsThrowing() var injectedException = new Exception("Couldn't save module"); _mockModuleProvider.Setup( - _ => _.SaveModule(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + _ => _.SaveModule(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Throws(injectedException); _mockStorage.Setup(_ => _.RemoveSnapshot(_targetPathInstalling)); From df2bccaf297790b3141d449ce3f0a24e7d191498 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 14:58:53 -0700 Subject: [PATCH 40/48] Move ExpectSnapshotCreationAndPromotion invocation to the ctor --- .../DependencySnapshotInstallerTests.cs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index f7941e98..1ad8168e 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -31,6 +31,7 @@ public DependencySnapshotInstallerTests() { _targetPathInstalled = DependencySnapshotFolderNameTools.CreateUniqueName(); _targetPathInstalling = DependencySnapshotFolderNameTools.ConvertInstalledToInstalling(_targetPathInstalled); + ExpectSnapshotCreationAndPromotion(); } [Fact] @@ -49,8 +50,6 @@ public void SavesSpecifiedVersion_WhenExactVersionIsSpecified() var manifestEntries = new[] { new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Exact version") }; - ExpectSnapshotCreationAndPromotion(); - var installer = CreateDependenciesSnapshotInstallerWithMocks(); installer.InstallSnapshot(manifestEntries, _targetPathInstalled, PowerShell.Create(), _mockLogger.Object); @@ -69,8 +68,6 @@ public void SavesLatestPublishedVersion_WhenMajorVersionIsSpecified() var manifestEntries = new[] { new DependencyManifestEntry("Module", VersionSpecificationType.MajorVersion, "Major version") }; - ExpectSnapshotCreationAndPromotion(); - _mockModuleProvider.Setup( _ => _.GetLatestPublishedModuleVersion("Module", "Major version")) .Returns("Latest version"); @@ -89,8 +86,6 @@ public void PromotesInstallingSnapshotToInstalledAfterSuccessfullySavingModule() var manifestEntries = new[] { new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Version") }; - ExpectSnapshotCreationAndPromotion(); - var installer = CreateDependenciesSnapshotInstallerWithMocks(); installer.InstallSnapshot(manifestEntries, _targetPathInstalled, PowerShell.Create(), _mockLogger.Object); @@ -104,8 +99,6 @@ public void CleansUpPowerShellRunspaceAfterSuccessfullySavingModule() var manifestEntries = new[] { new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Version") }; - ExpectSnapshotCreationAndPromotion(); - var dummyPowerShell = PowerShell.Create(); var installer = CreateDependenciesSnapshotInstallerWithMocks(); @@ -128,8 +121,6 @@ public void LogsInstallationStartAndFinish() _ => _.GetLatestPublishedModuleVersion(It.IsAny(), It.IsAny())) .Returns("Exact B version"); - ExpectSnapshotCreationAndPromotion(); - var installer = CreateDependenciesSnapshotInstallerWithMocks(); installer.InstallSnapshot(manifestEntries, _targetPathInstalled, PowerShell.Create(), _mockLogger.Object); @@ -145,8 +136,6 @@ public void DoesNotSaveModuleIfGetLatestPublishedModuleVersionThrows() var manifestEntries = new[] { new DependencyManifestEntry("Module", VersionSpecificationType.MajorVersion, "Version") }; - ExpectSnapshotCreationAndPromotion(); - var dummyPowerShell = PowerShell.Create(); var injectedException = new InvalidOperationException("Couldn't get latest published module version"); @@ -180,8 +169,6 @@ public void DoesNotPromoteDependenciesSnapshotIfSaveModuleKeepsThrowing() var manifestEntries = new[] { new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Version") }; - ExpectSnapshotCreationAndPromotion(); - var dummyPowerShell = PowerShell.Create(); var injectedException = new Exception("Couldn't save module"); From 9c2f686d0d3c40208bb9d79b2b784671c5c630a9 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 14:59:32 -0700 Subject: [PATCH 41/48] Inline ExpectSnapshotCreationAndPromotion --- .../DependencySnapshotInstallerTests.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index 1ad8168e..14372c12 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -31,7 +31,8 @@ public DependencySnapshotInstallerTests() { _targetPathInstalled = DependencySnapshotFolderNameTools.CreateUniqueName(); _targetPathInstalling = DependencySnapshotFolderNameTools.ConvertInstalledToInstalling(_targetPathInstalled); - ExpectSnapshotCreationAndPromotion(); + _mockStorage.Setup(_ => _.CreateInstallingSnapshot(_targetPathInstalled)).Returns(_targetPathInstalling); + _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(_targetPathInstalled)); } [Fact] @@ -195,12 +196,6 @@ private DependencySnapshotInstaller CreateDependenciesSnapshotInstallerWithMocks return new DependencySnapshotInstaller(_mockModuleProvider.Object, _mockStorage.Object); } - private void ExpectSnapshotCreationAndPromotion() - { - _mockStorage.Setup(_ => _.CreateInstallingSnapshot(_targetPathInstalled)).Returns(_targetPathInstalling); - _mockStorage.Setup(_ => _.PromoteInstallingSnapshotToInstalledAtomically(_targetPathInstalled)); - } - private void VerifyLoggedOnce(IEnumerable messageParts) { _mockLogger.Verify( From c4f7f26e59777f1f91070d0d1ea21cea855a98d1 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Thu, 25 Jul 2019 15:01:42 -0700 Subject: [PATCH 42/48] Rename test --- .../DependencyManagement/DependencySnapshotInstallerTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs index 14372c12..9bf52d1b 100644 --- a/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs +++ b/test/Unit/DependencyManagement/DependencySnapshotInstallerTests.cs @@ -82,7 +82,7 @@ public void SavesLatestPublishedVersion_WhenMajorVersionIsSpecified() } [Fact] - public void PromotesInstallingSnapshotToInstalledAfterSuccessfullySavingModule() + public void PromotesInstallingSnapshotToInstalledIfSaveModuleDoesNotThrow() { var manifestEntries = new[] { new DependencyManifestEntry("Module", VersionSpecificationType.ExactVersion, "Version") }; From d1166e140cf4eec80fd9f176bb2588a81e9f5057 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Tue, 30 Jul 2019 13:32:52 -0700 Subject: [PATCH 43/48] Inline local variables --- src/DependencyManagement/DependencyManifest.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DependencyManagement/DependencyManifest.cs b/src/DependencyManagement/DependencyManifest.cs index fca9366b..bb4eae76 100644 --- a/src/DependencyManagement/DependencyManifest.cs +++ b/src/DependencyManagement/DependencyManifest.cs @@ -42,10 +42,10 @@ public IEnumerable GetEntries() // 'ModuleName'='MajorVersion.*' // or // 'ModuleName'='ExactVersion' - var name = (string)entry.Key; - var version = (string)entry.Value; - yield return CreateDependencyManifestEntry(name, version); + yield return CreateDependencyManifestEntry( + name: (string)entry.Key, + version: (string)entry.Value); } } From 6432e86230470a8957c869df4a1c44f36774d64e Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Tue, 30 Jul 2019 13:39:24 -0700 Subject: [PATCH 44/48] Extract majorVersion variable --- src/DependencyManagement/DependencyManifest.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/DependencyManagement/DependencyManifest.cs b/src/DependencyManagement/DependencyManifest.cs index bb4eae76..7ecb8433 100644 --- a/src/DependencyManagement/DependencyManifest.cs +++ b/src/DependencyManagement/DependencyManifest.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // @@ -57,13 +57,14 @@ private static DependencyManifestEntry CreateDependencyManifestEntry(string name if (match.Success) { // Look for the 'MajorVersion.*' pattern first. + var majorVersion = match.Groups[1].Value; var afterMajorVersion = match.Groups[2].Value; if (afterMajorVersion == ".*") { return new DependencyManifestEntry( name, VersionSpecificationType.MajorVersion, - match.Groups[1].Value); + majorVersion); } // This is a very basic sanity check of the format that allows us detect some From b45f30cbdf0afe54c386065ffa48869c108c31ab Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Tue, 30 Jul 2019 13:39:34 -0700 Subject: [PATCH 45/48] Improve a comment --- src/DependencyManagement/DependencyManifest.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/DependencyManagement/DependencyManifest.cs b/src/DependencyManagement/DependencyManifest.cs index 7ecb8433..070af254 100644 --- a/src/DependencyManagement/DependencyManifest.cs +++ b/src/DependencyManagement/DependencyManifest.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // @@ -67,10 +67,12 @@ private static DependencyManifestEntry CreateDependencyManifestEntry(string name majorVersion); } - // This is a very basic sanity check of the format that allows us detect some + // At this point, we know this is not the 'MajorVersion.*' pattern. + // We want to perform a very basic sanity check of the format to detect some // obviously wrong cases: make sure afterMajorVersion starts with a dot, // does not contain * anywhere, and ends with a word character. - // Not even trying to match the actual version format rules, though. + // Not even trying to match the actual version format rules, + // as they are quite complex and controlled by the server side anyway. if (Regex.IsMatch(afterMajorVersion, @"^(\.[^\*]*?\w)?$")) { return new DependencyManifestEntry( From 231ff2494e41341897f22f5d5ffe1b245d34778c Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Tue, 30 Jul 2019 13:44:56 -0700 Subject: [PATCH 46/48] Allow installing explicitly specified prerelease versions --- src/DependencyManagement/PowerShellGalleryModuleProvider.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/DependencyManagement/PowerShellGalleryModuleProvider.cs b/src/DependencyManagement/PowerShellGalleryModuleProvider.cs index 57ba4b4b..70ff8f49 100644 --- a/src/DependencyManagement/PowerShellGalleryModuleProvider.cs +++ b/src/DependencyManagement/PowerShellGalleryModuleProvider.cs @@ -106,6 +106,7 @@ public void SaveModule(PowerShell pwsh, string moduleName, string version, strin .AddParameter("Repository", Repository) .AddParameter("Name", moduleName) .AddParameter("RequiredVersion", version) + .AddParameter("AllowPrerelease", version) .AddParameter("Path", path) .AddParameter("Force", Utils.BoxedTrue) .AddParameter("ErrorAction", "Stop") From 45a02433f9502c65c76b6caea4a2ad8d6d845d62 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Tue, 30 Jul 2019 14:04:33 -0700 Subject: [PATCH 47/48] Fix AllowPrerelease value --- src/DependencyManagement/PowerShellGalleryModuleProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DependencyManagement/PowerShellGalleryModuleProvider.cs b/src/DependencyManagement/PowerShellGalleryModuleProvider.cs index 70ff8f49..3b38e0b7 100644 --- a/src/DependencyManagement/PowerShellGalleryModuleProvider.cs +++ b/src/DependencyManagement/PowerShellGalleryModuleProvider.cs @@ -106,7 +106,7 @@ public void SaveModule(PowerShell pwsh, string moduleName, string version, strin .AddParameter("Repository", Repository) .AddParameter("Name", moduleName) .AddParameter("RequiredVersion", version) - .AddParameter("AllowPrerelease", version) + .AddParameter("AllowPrerelease", Utils.BoxedTrue) .AddParameter("Path", path) .AddParameter("Force", Utils.BoxedTrue) .AddParameter("ErrorAction", "Stop") From 5de92a0b6d85a736977d15822776a4eb02a332a4 Mon Sep 17 00:00:00 2001 From: Anatoli Beliaev Date: Tue, 30 Jul 2019 14:04:48 -0700 Subject: [PATCH 48/48] Update design doc --- docs/designs/PowerShell-AzF-Overall-Design.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/designs/PowerShell-AzF-Overall-Design.md b/docs/designs/PowerShell-AzF-Overall-Design.md index 7ecd1487..2e8fe53e 100644 --- a/docs/designs/PowerShell-AzF-Overall-Design.md +++ b/docs/designs/PowerShell-AzF-Overall-Design.md @@ -466,15 +466,20 @@ Note that, checking out a PowerShell Manager instance from the pool is a blockin The goal is to let the user declare the dependencies required by functions, and rely on the service automatically locating and installing the dependencies from the PowerShell Gallery or other sources, taking care of selecting the proper versions, and automatically upgrading the dependencies to the latest versions (if allowed by the version specifications provided by the user). -Dependencies are declared in the _requirements.psd1_ file (_manifest_) as a collection of pairs (<_name_>, <_version specification_>). Currently, the version specification should strictly match the following pattern: `.*`, so a typical manifest looks like this: +Dependencies are declared in the _requirements.psd1_ file (_manifest_) as a collection of pairs (<_name_>, <_version specification_>). Currently, the version specification should either be an exact and complete version, or strictly match the following pattern: `.*`. So, a typical manifest may look like this: ``` PowerShell @{ 'Az' = '2.*' 'PSDepend' = '0.*' + 'Pester' = '5.0.0-alpha3' } ``` +When the `.*` format is used, the worker will retrieve the latest available module version (within the specified major version) from the PowerShell Gallery, ignoring prerelease versions. + +When the exact version is specified, the worker will retrieve the specified version only, ignoring any other version. Prerelease versions are allowed in this case. + The number of entries in the _requirements.psd1_ file should not exceed **10**. This limit is not user-configurable. Installing and upgrading dependencies should be performed automatically, without requiring any interaction with the user, and without interfering with the currently running functions. This represents an important design challenge. In a different context, dependencies could be stored on a single location on the file system, managed by regular PowerShell tools (`Install-Module`/`Save-Module`, `PSDepend`, etc.), while having the same file system location added to _PSModulePath_ to make all the modules available to scripts running on this machine. This is what PowerShell users normally do, and this approach looks attractive because it is simple and conventional. However, in the contexts where multiple independent workers load modules and execute scripts concurrently, and at the same time some module versions are being added, upgraded, or removed, this simple approach causes many known problems. The root causes of these problems are in the fundamentals of PowerShell and PowerShell modules design. The managed dependencies design in Azure Functions must take this into account. The problems will be solved if we satisfy the following conditions: