mirror of https://github.com/raandree/NTFSSecurity
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
188 lines
10 KiB
188 lines
10 KiB
<#
|
|
Tests Gallery publication recovery offline. Every network and publication command is mocked; the fake API key
|
|
exists only in the test process and is restored afterwards. Package hashes come from a sandbox file.
|
|
#>
|
|
[Diagnostics.CodeAnalysis.SuppressMessageAttribute(
|
|
'PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Pester shares variables between blocks.'
|
|
)]
|
|
param ()
|
|
|
|
BeforeAll {
|
|
Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath 'TestHelpers.psm1') -Force
|
|
$sandbox = New-TestSandbox -Name 'PublishPackage'
|
|
$package = Join-Path -Path $sandbox -ChildPath 'NTFSSecurity.5.0.0-rc7.nupkg'
|
|
Assert-TestSandboxPath -Sandbox $sandbox -Path $package
|
|
[IO.File]::WriteAllBytes($package, [Text.Encoding]::UTF8.GetBytes('The package that CI built.'))
|
|
$sha512 = [Security.Cryptography.SHA512]::Create()
|
|
try { $hash = [Convert]::ToBase64String($sha512.ComputeHash([IO.File]::ReadAllBytes($package))) }
|
|
finally { $sha512.Dispose() }
|
|
$metadata = [pscustomobject]@{ entry = [pscustomobject]@{ properties = [pscustomobject]@{
|
|
Id = 'NTFSSecurity'; Version = '5.0.0-rc7'; PackageHashAlgorithm = 'SHA512'; PackageHash = $hash
|
|
} } }
|
|
$published = [pscustomobject]@{ Name = 'NTFSSecurity'; Version = [version]'5.0.0'; Prerelease = 'rc7' }
|
|
$scriptPath = Join-Path -Path $PSScriptRoot -ChildPath '..\.github\scripts\Publish-ModulePackage.ps1'
|
|
$originalKey = $env:PSGALLERY_API_KEY
|
|
|
|
# Stubs keep these tests available in Windows PowerShell, where PSResourceGet might not be installed.
|
|
function Find-PSResource {
|
|
[Diagnostics.CodeAnalysis.SuppressMessageAttribute(
|
|
'PSReviewUnusedParameter', '', Justification = 'Command stub supplies parameter metadata for Pester mocks.'
|
|
)]
|
|
[CmdletBinding()]
|
|
param ([string] $Name, [string] $Version, [switch] $Prerelease, [string] $Repository)
|
|
throw 'Find-PSResource must be mocked in this test.'
|
|
}
|
|
function Publish-PSResource {
|
|
[Diagnostics.CodeAnalysis.SuppressMessageAttribute(
|
|
'PSReviewUnusedParameter', '', Justification = 'Command stub supplies parameter metadata for Pester mocks.'
|
|
)]
|
|
[CmdletBinding()]
|
|
param ([string] $NupkgPath, [string] $Repository, [string] $ApiKey)
|
|
throw 'Publish-PSResource must be mocked in this test.'
|
|
}
|
|
}
|
|
|
|
AfterAll {
|
|
$env:PSGALLERY_API_KEY = $originalKey
|
|
Remove-TestSandbox -Sandbox $sandbox
|
|
}
|
|
|
|
Describe 'Publish-ModulePackage.ps1' {
|
|
BeforeEach {
|
|
$env:PSGALLERY_API_KEY = 'test-only-api-key'
|
|
Mock -CommandName Find-PSResource
|
|
Mock -CommandName Publish-PSResource
|
|
Mock -CommandName Invoke-RestMethod -MockWith { $metadata }
|
|
}
|
|
|
|
It 'Should publish a new version using the environment key and the verified package path' {
|
|
& $scriptPath -NupkgPath $package -Version '5.0.0-rc7'
|
|
|
|
Should -Invoke -CommandName Publish-PSResource -Times 1 -Exactly -ParameterFilter {
|
|
$NupkgPath -eq $package -and $Repository -eq 'PSGallery' -and $ApiKey -eq 'test-only-api-key'
|
|
}
|
|
}
|
|
|
|
It 'Should skip publication only after checking the existing package hash' {
|
|
Mock -CommandName Find-PSResource -MockWith { $published }
|
|
|
|
& $scriptPath -NupkgPath $package -Version '5.0.0-rc7'
|
|
|
|
Should -Invoke -CommandName Publish-PSResource -Times 0 -Exactly
|
|
Should -Invoke -CommandName Invoke-RestMethod -Times 1 -Exactly -ParameterFilter {
|
|
$Uri -eq "https://www.powershellgallery.com/api/v2/Packages(Id='NTFSSecurity',Version='5.0.0-rc7')"
|
|
}
|
|
}
|
|
|
|
It 'Should refuse an existing version containing a different package' {
|
|
Mock -CommandName Find-PSResource -MockWith { $published }
|
|
Mock -CommandName Invoke-RestMethod -MockWith {
|
|
[pscustomobject]@{ entry = [pscustomobject]@{ properties = [pscustomobject]@{ PackageHashAlgorithm = 'SHA512'; PackageHash = 'different-package' } } }
|
|
}
|
|
|
|
{ & $scriptPath -NupkgPath $package -Version '5.0.0-rc7' } | Should -Throw -ExpectedMessage '*different package*'
|
|
Should -Invoke -CommandName Publish-PSResource -Times 0 -Exactly
|
|
}
|
|
|
|
It 'Should refuse metadata without a usable SHA512 package hash: <Case>' -ForEach @(
|
|
@{ Case = 'missing hash'; Algorithm = 'SHA512'; PackageHash = '' }
|
|
@{ Case = 'wrong algorithm'; Algorithm = 'SHA256'; PackageHash = 'not-sha512' }
|
|
) {
|
|
Mock -CommandName Find-PSResource -MockWith { $published }
|
|
Mock -CommandName Invoke-RestMethod -MockWith {
|
|
[pscustomobject]@{ entry = [pscustomobject]@{ properties = [pscustomobject]@{ PackageHashAlgorithm = $Algorithm; PackageHash = $PackageHash } } }
|
|
}
|
|
|
|
{ & $scriptPath -NupkgPath $package -Version '5.0.0-rc7' } | Should -Throw -ExpectedMessage '*SHA512*'
|
|
Should -Invoke -CommandName Publish-PSResource -Times 0 -Exactly
|
|
}
|
|
|
|
It 'Should recover an uncertain upload only when the exact package appears in the Gallery' {
|
|
$script:lookups = 0
|
|
Mock -CommandName Find-PSResource -MockWith { $script:lookups++; if ($script:lookups -gt 1) { $published } }
|
|
Mock -CommandName Publish-PSResource -MockWith { throw '409: a package with this version already exists.' }
|
|
|
|
& $scriptPath -NupkgPath $package -Version '5.0.0-rc7' -WarningVariable uploadWarnings -WarningAction SilentlyContinue
|
|
|
|
Should -Invoke -CommandName Publish-PSResource -Times 1 -Exactly
|
|
Should -Invoke -CommandName Find-PSResource -Times 2 -Exactly
|
|
Should -Invoke -CommandName Invoke-RestMethod -Times 1 -Exactly
|
|
$uploadWarnings | Should -HaveCount 1
|
|
$uploadWarnings[0].Message | Should -BeLike '*verified*exact package*'
|
|
}
|
|
|
|
It 'Should preserve the upload error when the version remains absent' {
|
|
Mock -CommandName Publish-PSResource -MockWith { throw 'Upload failed: the server is unavailable.' }
|
|
|
|
{ & $scriptPath -NupkgPath $package -Version '5.0.0-rc7' } | Should -Throw -ExpectedMessage '*Upload failed: the server is unavailable*'
|
|
Should -Invoke -CommandName Publish-PSResource -Times 1 -Exactly
|
|
Should -Invoke -CommandName Find-PSResource -Times 2 -Exactly
|
|
}
|
|
|
|
It 'Should preserve the upload error when verification finds a different package' {
|
|
$script:lookups = 0
|
|
Mock -CommandName Find-PSResource -MockWith { $script:lookups++; if ($script:lookups -gt 1) { $published } }
|
|
Mock -CommandName Publish-PSResource -MockWith { throw 'Upload failed: version collision.' }
|
|
Mock -CommandName Invoke-RestMethod -MockWith {
|
|
[pscustomobject]@{ entry = [pscustomobject]@{ properties = [pscustomobject]@{ PackageHashAlgorithm = 'SHA512'; PackageHash = 'different-package' } } }
|
|
}
|
|
|
|
{ & $scriptPath -NupkgPath $package -Version '5.0.0-rc7' -WarningAction SilentlyContinue } | Should -Throw -ExpectedMessage '*Upload failed: version collision*'
|
|
Should -Invoke -CommandName Invoke-RestMethod -Times 1 -Exactly
|
|
}
|
|
|
|
It 'Should not publish after a lookup fails for a reason other than a missing version' {
|
|
Mock -CommandName Find-PSResource -MockWith { Write-Error -Message 'Lookup failed.' -ErrorId RepositoryUnavailable }
|
|
|
|
{ & $scriptPath -NupkgPath $package -Version '5.0.0-rc7' } | Should -Throw -ExpectedMessage '*Lookup failed*'
|
|
Should -Invoke -CommandName Publish-PSResource -Times 0 -Exactly
|
|
}
|
|
|
|
It 'Should compare Base64 hashes case-sensitively' {
|
|
Mock -CommandName Find-PSResource -MockWith { $published }
|
|
$differentCase = $hash.ToLowerInvariant()
|
|
($hash -ceq $differentCase) | Should -BeFalse
|
|
Mock -CommandName Invoke-RestMethod -MockWith {
|
|
[pscustomobject]@{ entry = [pscustomobject]@{ properties = [pscustomobject]@{ PackageHashAlgorithm = 'SHA512'; PackageHash = $differentCase } } }
|
|
}
|
|
|
|
{ & $scriptPath -NupkgPath $package -Version '5.0.0-rc7' } | Should -Throw -ExpectedMessage '*different package*'
|
|
Should -Invoke -CommandName Publish-PSResource -Times 0 -Exactly
|
|
}
|
|
|
|
It 'Should preserve the upload error when post-upload metadata is unavailable' {
|
|
$script:lookups = 0
|
|
Mock -CommandName Find-PSResource -MockWith { $script:lookups++; if ($script:lookups -gt 1) { $published } }
|
|
Mock -CommandName Publish-PSResource -MockWith { throw 'Upload failed: original error.' }
|
|
Mock -CommandName Invoke-RestMethod -MockWith { throw 'Metadata is unavailable.' }
|
|
|
|
{ & $scriptPath -NupkgPath $package -Version '5.0.0-rc7' -WarningAction SilentlyContinue } | Should -Throw -ExpectedMessage '*Upload failed: original error*'
|
|
Should -Invoke -CommandName Invoke-RestMethod -Times 1 -Exactly
|
|
}
|
|
|
|
It 'Should preserve the upload error when the post-upload lookup fails' {
|
|
$script:lookups = 0
|
|
Mock -CommandName Find-PSResource -MockWith {
|
|
$script:lookups++
|
|
if ($script:lookups -gt 1) { Write-Error -Message 'Post-upload lookup failed.' -ErrorId RepositoryUnavailable }
|
|
}
|
|
Mock -CommandName Publish-PSResource -MockWith { throw 'Upload failed: original error.' }
|
|
|
|
{ & $scriptPath -NupkgPath $package -Version '5.0.0-rc7' -WarningAction SilentlyContinue } | Should -Throw -ExpectedMessage '*Upload failed: original error*'
|
|
Should -Invoke -CommandName Find-PSResource -Times 2 -Exactly
|
|
}
|
|
It 'Should allow the expected PackageNotFound probe result before publishing' {
|
|
Mock -CommandName Find-PSResource -MockWith { Write-Error -Message 'Not published yet.' -ErrorId PackageNotFound }
|
|
|
|
{ & $scriptPath -NupkgPath $package -Version '5.0.0-rc7' } | Should -Not -Throw
|
|
Should -Invoke -CommandName Publish-PSResource -Times 1 -Exactly
|
|
}
|
|
|
|
It 'Should reject a missing API key before contacting the Gallery' {
|
|
$env:PSGALLERY_API_KEY = $null
|
|
|
|
{ & $scriptPath -NupkgPath $package -Version '5.0.0-rc7' } | Should -Throw -ExpectedMessage '*PSGALLERY_API_KEY*not set*'
|
|
Should -Invoke -CommandName Find-PSResource -Times 0 -Exactly
|
|
Should -Invoke -CommandName Publish-PSResource -Times 0 -Exactly
|
|
}
|
|
}
|
|
|