functions/_assertExtensionLockSatisfied.Tests.ps1
|
# <copyright file="_assertExtensionLockSatisfied.Tests.ps1" company="Endjin Limited"> # Copyright (c) Endjin Limited. All rights reserved. # </copyright> BeforeAll { # sut . $PSCommandPath.Replace('.Tests.ps1','.ps1') # in-module dependencies . (Join-Path (Split-Path -Parent $PSCommandPath) '_getExtensionLockEntries.ps1') . (Join-Path (Split-Path -Parent $PSCommandPath) '_getExtensionContentHash.ps1') . (Join-Path (Split-Path -Parent $PSCommandPath) '_testExtensionLockEntryMatch.ps1') . (Join-Path (Split-Path -Parent $PSCommandPath) '_getExtensionVersionRange.ps1') . (Join-Path (Split-Path -Parent $PSCommandPath) '_getCompleteExtensionVersion.ps1') . (Join-Path (Split-Path -Parent $PSCommandPath) '_testExtensionVersionInRange.ps1') . (Join-Path (Split-Path -Parent $PSCommandPath) '_getVersionFromGitTag.ps1') . (Join-Path (Split-Path -Parent $PSCommandPath) '_getNormalizedGitRepositoryUrl.ps1') Set-StrictMode -Version Latest # Creates the metadata of a registered extension, as produced by Register-ExtensionAndDependencies function newRegisteredExtension([string] $Name, [string] $Type = 'git', [string] $Requested = 'main', [string] $Resolved = 'abc123', [bool] $Enabled = $true) { $source = $Type -eq 'path' ? [ordered]@{ type = 'path'; path = "/src/$Name" } : [ordered]@{ type = 'git'; folderPath = 'module'; repository = "https://github.com/org/$Name" } @{ Name = $Name Path = Join-Path $TestDrive $Name Enabled = $Enabled LockEntry = [ordered]@{ source = $source requested = $Requested resolved = $Resolved ? $Resolved : $null direct = $true requiredBy = @() } } } # Creates a lock file entry, as read from the lock file function newLockedExtension([string] $Name, [string] $Type = 'git', [string] $Requested = 'main', [string] $Resolved = 'abc123', [string] $ContentHash = 'sha256:content') { $source = $Type -eq 'path' ? @{ type = 'path'; path = "/src/$Name" } : @{ type = 'git'; folderPath = 'module'; repository = "https://github.com/org/$Name" } @{ source = $source requested = $Requested resolved = $Resolved ? $Resolved : $null contentHash = $ContentHash ? $ContentHash : $null direct = $true requiredBy = @() } } } Describe '_assertExtensionLockSatisfied' { BeforeAll { Mock _getExtensionContentHash { 'sha256:content' } Mock Write-Warning {} $lockFilePath = '/repo/.zf/extensions.lock.json' } It 'Succeeds when the extensions match the lock file' { $extensions = @( newRegisteredExtension -Name 'Build' newRegisteredExtension -Name 'Local' -Type 'path' -Requested '' -Resolved '' ) $locked = @{ 'Build' = newLockedExtension -Name 'Build' 'Local' = newLockedExtension -Name 'Local' -Type 'path' -Requested '' -Resolved '' -ContentHash '' } { _assertExtensionLockSatisfied -Extensions $extensions -LockedExtensions $locked -LockFilePath $lockFilePath } | Should -Not -Throw Should -Invoke Write-Warning -Times 0 -Scope It } It 'Fails when <Description>' -ForEach @( @{ Description = 'an extension is not in the lock file' Extension = @{ Name = 'Build' } Locked = @{} ExpectedProblem = "'Build' is not in the lock file" } @{ Description = 'an extension is requested differently' Extension = @{ Name = 'Build'; Requested = 'develop' } Locked = @{ Name = 'Build'; Requested = 'main' } ExpectedProblem = "'Build' is requested as 'develop' from git 'https://github.com/org/Build', but the lock file has 'main' from git 'https://github.com/org/Build'" } @{ Description = 'the lock file has no resolved version' Extension = @{ Name = 'Build' } Locked = @{ Name = 'Build'; Resolved = '' } ExpectedProblem = "'Build' has no resolved version in the lock file; delete its installed copy, then run in 'Write' mode*" } @{ Description = 'an extension resolved to a different version' Extension = @{ Name = 'Build'; Resolved = 'other' } Locked = @{ Name = 'Build' } ExpectedProblem = "'Build' resolved to 'other', but the lock file has 'abc123'" } @{ Description = 'the content of an extension differs' Extension = @{ Name = 'Build' } Locked = @{ Name = 'Build'; ContentHash = 'sha256:other' } ExpectedProblem = "'Build' (abc123) does not have the content recorded in the lock file*" } @{ Description = 'an extension could not be installed' Extension = @{ Name = 'Build'; Enabled = $false } Locked = @{ Name = 'Build' } ExpectedProblem = "'Build' could not be installed" } ) { $extensions = @( newRegisteredExtension @Extension ) $lockedExtensions = $Locked.Count -gt 0 ? @{ $Locked.Name = (newLockedExtension @Locked) } : @{} { _assertExtensionLockSatisfied -Extensions $extensions -LockedExtensions $lockedExtensions -LockFilePath $lockFilePath } | Should -Throw "*$ExpectedProblem*" } It 'Fails, without reporting that it could not be installed, when an extension was not installed because <Description>' -ForEach @( @{ Description = 'it is not in the lock file' Locked = @{} ExpectedProblem = "'Build' is not in the lock file" } @{ Description = 'it is requested differently' Locked = @{ Name = 'Build'; Requested = 'develop' } ExpectedProblem = "'Build' is requested as 'main' from git 'https://github.com/org/Build', but the lock file has 'develop' from git 'https://github.com/org/Build'" } @{ Description = 'the lock file has no resolved version' Locked = @{ Name = 'Build'; Resolved = '' } ExpectedProblem = "'Build' has no resolved version in the lock file*" } ) { $extension = newRegisteredExtension -Name 'Build' -Resolved '' -Enabled $false $extension.LockMismatch = $true $lockedExtensions = $Locked.Count -gt 0 ? @{ $Locked.Name = (newLockedExtension @Locked) } : @{} $errorMessage = try { _assertExtensionLockSatisfied -Extensions @($extension) -LockedExtensions $lockedExtensions -LockFilePath $lockFilePath } catch { $_.Exception.Message } $errorMessage | Should -BeLike "*$ExpectedProblem*" $errorMessage | Should -Not -BeLike '*could not be installed*' } It 'Does not warn about unused lock entries when there are problems' { $extension = newRegisteredExtension -Name 'Build' -Resolved '' -Enabled $false $extension.LockMismatch = $true $locked = @{ 'Build' = newLockedExtension -Name 'Build' -Requested 'develop' 'Dependency' = newLockedExtension -Name 'Dependency' } { _assertExtensionLockSatisfied -Extensions @($extension) -LockedExtensions $locked -LockFilePath $lockFilePath } | Should -Throw Should -Invoke Write-Warning -Times 0 -Scope It } It 'Reports every problem, with how to update the lock file' { $extensions = @( newRegisteredExtension -Name 'Build' -Resolved 'other' newRegisteredExtension -Name 'Common' ) $locked = @{ 'Build' = newLockedExtension -Name 'Build' } $errorMessage = try { _assertExtensionLockSatisfied -Extensions $extensions -LockedExtensions $locked -LockFilePath $lockFilePath } catch { $_.Exception.Message } $errorMessage | Should -BeLike "The extensions do not match the lock file '$lockFilePath':*" $errorMessage | Should -BeLike "*'Build' resolved to 'other'*" $errorMessage | Should -BeLike "*'Common' is not in the lock file*" $errorMessage | Should -BeLike "*ZF_EXTENSIONS_LOCK_MODE = 'Write'*" } It 'Only checks the request of local extensions' { $extensions = @( newRegisteredExtension -Name 'Local' -Type 'path' -Requested '' -Resolved '' ) $locked = @{ 'Local' = newLockedExtension -Name 'Local' -Type 'path' -Requested '' -Resolved '' -ContentHash 'sha256:old' } { _assertExtensionLockSatisfied -Extensions $extensions -LockedExtensions $locked -LockFilePath $lockFilePath } | Should -Not -Throw } It 'Only checks the first extension found with each name' { $extensions = @( newRegisteredExtension -Name 'Build' newRegisteredExtension -Name 'Build' -Requested 'other' -Resolved 'other' -Enabled $false ) $locked = @{ 'Build' = newLockedExtension -Name 'Build' } { _assertExtensionLockSatisfied -Extensions $extensions -LockedExtensions $locked -LockFilePath $lockFilePath } | Should -Not -Throw } It 'Warns about extensions in the lock file that are no longer used' { $extensions = @( newRegisteredExtension -Name 'Build' ) $locked = @{ 'Build' = newLockedExtension -Name 'Build' 'Removed' = newLockedExtension -Name 'Removed' } { _assertExtensionLockSatisfied -Extensions $extensions -LockedExtensions $locked -LockFilePath $lockFilePath } | Should -Not -Throw Should -Invoke Write-Warning -Times 1 -Scope It -ParameterFilter { $Message -like '*no longer used: Removed' } } Context 'Minimum versions' { BeforeEach { # Another extension that required the same extension with a different minimum version was locked $locked = @{ 'Common' = newLockedExtension -Name 'Common' -Requested '[1.5.0,2.0.0)' -Resolved 'commit16' } $locked.Common.resolvedRef = 'refs/tags/1.6.0' } It 'Succeeds when the locked release tag is in the range of the minimum version' { $extension = newRegisteredExtension -Name 'Common' -Requested '[1.2.0,2.0.0)' -Resolved 'commit16' $extension.MinimumVersion = '1.2.0' { _assertExtensionLockSatisfied -Extensions @($extension) -LockedExtensions $locked -LockFilePath $lockFilePath } | Should -Not -Throw } It 'Fails when the locked release tag is not in the range of the minimum version' { $extension = newRegisteredExtension -Name 'Common' -Requested '[1.7.0,2.0.0)' -Resolved 'commit17' $extension.MinimumVersion = '1.7.0' { _assertExtensionLockSatisfied -Extensions @($extension) -LockedExtensions $locked -LockFilePath $lockFilePath } | Should -Throw "*'Common' is requested as '``[1.7.0,2.0.0)' from git *, but the lock file has '``[1.5.0,2.0.0)' from git *" } } } |