Tests/Embed.Tests.ps1
|
Import-Module "$PSScriptRoot/../ChocoForge.psd1" -Force Describe 'Embedded Installer Support' { InModuleScope 'ChocoForge' { BeforeEach { Mock Expand-EnvironmentVariables { 'fake-api-key' } Mock Invoke-RestMethod { Get-Content "$PSScriptRoot/assets/qemu-img-mocks/github-releases.json" -Raw | ConvertFrom-Json } Mock Invoke-Chocolatey { if ($PesterBoundParameters.Arguments[0] -eq 'search') { [PSCustomObject]@{ ExitCode = 0 StdOut = Get-Content "$PSScriptRoot/assets/qemu-img-mocks/github-packages.txt" -Raw StdErr = '' } } else { $realCommand = Get-Command -CommandType Function -Name Invoke-Chocolatey & $realCommand @PesterBoundParameters } } } It 'Reads embed flag from configuration' { $configPath = "$PSScriptRoot/assets/embed-package/test-embed.forge.yaml" $config = Read-ForgeConfiguration -Path $configPath $config.releases.embed | Should -Be $true } It 'Copies and renders legal folder contents' { $configPath = "$PSScriptRoot/assets/embed-package/test-embed.forge.yaml" $config = Read-ForgeConfiguration -Path $configPath | Resolve-ForgeConfiguration # Build WITHOUT embed (skip download) — just test that legal/ folder is rendered $nuspecPath = "$PSScriptRoot/assets/embed-package/test-embed.nuspec" $versionsToTest = @($config.versions | Select-Object -First 1) $versionsToTest | Should -Not -BeNullOrEmpty $packageBuilt = $versionsToTest | Build-ChocolateyPackage -NuspecPath $nuspecPath $packageBuilt | Should -Not -BeNullOrEmpty # Extract and verify VERIFICATION.txt was included and rendered $extractDir = Join-Path $env:TEMP 'chocoforge-embed-test' if (Test-Path $extractDir) { Remove-Item -Recurse -Force $extractDir } Expand-Archive -Path $packageBuilt -DestinationPath $extractDir -Force $verFile = Join-Path $extractDir 'legal/VERIFICATION.txt' Test-Path $verFile | Should -Be $true $verContent = Get-Content $verFile -Raw # Should have the download URL rendered (not template placeholder) $verContent | Should -Not -Match '\{\{assets\.browser_download_url\}\}' $verContent | Should -Match 'https://' # Clean up Remove-Item -Recurse -Force $extractDir } It 'Downloads assets into tools when -Embed is specified' { $configPath = "$PSScriptRoot/assets/embed-package/test-embed.forge.yaml" $config = Read-ForgeConfiguration -Path $configPath | Resolve-ForgeConfiguration $nuspecPath = "$PSScriptRoot/assets/embed-package/test-embed.nuspec" $versionsToTest = @($config.versions | Select-Object -First 1) $versionsToTest | Should -Not -BeNullOrEmpty # Mock Invoke-WebRequest to avoid actual downloads Mock Invoke-WebRequest { # Create a dummy file at the OutFile path Set-Content -Path $OutFile -Value 'dummy-content' } $packageBuilt = $versionsToTest | Build-ChocolateyPackage -NuspecPath $nuspecPath -Embed $packageBuilt | Should -Not -BeNullOrEmpty # Verify that Invoke-WebRequest was called (download was attempted) Should -Invoke Invoke-WebRequest -Times 1 -Exactly # Extract and check embedded file exists in tools/ $extractDir = Join-Path $env:TEMP 'chocoforge-embed-test-dl' if (Test-Path $extractDir) { Remove-Item -Recurse -Force $extractDir } Expand-Archive -Path $packageBuilt -DestinationPath $extractDir -Force # There should be files in tools/ beyond the install script $toolsFiles = Get-ChildItem (Join-Path $extractDir 'tools') -File $toolsFiles.Count | Should -BeGreaterThan 1 # .zip assets should NOT have .ignore files $ignoreFiles = Get-ChildItem (Join-Path $extractDir 'tools') -Filter '*.ignore' -File $ignoreFiles.Count | Should -Be 0 Remove-Item -Recurse -Force $extractDir } It 'Copies binary files in tools/ through the build byte-for-byte' { # Template expansion is a text round-trip; binaries must bypass it entirely. $srcDir = Join-Path $env:TEMP 'chocoforge-binary-test-src' if (Test-Path $srcDir) { Remove-Item -Recurse -Force $srcDir } New-Item -ItemType Directory -Path (Join-Path $srcDir 'tools') -Force | Out-Null Copy-Item "$PSScriptRoot/assets/embed-package/test-embed.nuspec" (Join-Path $srcDir 'test-embed.nuspec') Copy-Item "$PSScriptRoot/assets/embed-package/tools/chocolateyInstall.ps1" (Join-Path $srcDir 'tools/chocolateyInstall.ps1') # The nuspec declares a legal\** file entry, so the folder has to be present. Copy-Item "$PSScriptRoot/assets/embed-package/legal" (Join-Path $srcDir 'legal') -Recurse # Bytes that are not valid UTF-8; a text round-trip turns each into U+FFFD. $binaryPath = Join-Path $srcDir 'tools/payload.bin' $originalBytes = [byte[]]@(0x4D, 0x5A, 0x90, 0x00, 0xFF, 0xFE, 0x00, 0x01, 0x80, 0xC3, 0xA9) [System.IO.File]::WriteAllBytes($binaryPath, $originalBytes) $originalHash = (Get-FileHash -Path $binaryPath -Algorithm SHA256).Hash $ctx = [PSCustomObject]@{ version = [version]'1.0.0' } $packageBuilt = $ctx | Build-ChocolateyPackage -NuspecPath (Join-Path $srcDir 'test-embed.nuspec') $packageBuilt | Should -Not -BeNullOrEmpty # List what the .nupkg really holds, so a packaging change reports the contents # rather than a bare "expected True". Add-Type -AssemblyName System.IO.Compression.FileSystem $archive = [System.IO.Compression.ZipFile]::OpenRead($packageBuilt) try { $entries = @($archive.Entries | ForEach-Object { $_.FullName }) } finally { $archive.Dispose() } $extractDir = Join-Path $env:TEMP 'chocoforge-binary-test-out' if (Test-Path $extractDir) { Remove-Item -Recurse -Force $extractDir } Expand-Archive -Path $packageBuilt -DestinationPath $extractDir -Force $extractedBinary = Join-Path $extractDir 'tools/payload.bin' Test-Path $extractedBinary | Should -BeTrue -Because "the package should contain tools/payload.bin; it holds: $($entries -join ', ')" (Get-FileHash -Path $extractedBinary -Algorithm SHA256).Hash | Should -Be $originalHash [System.IO.File]::ReadAllBytes($extractedBinary).Count | Should -Be $originalBytes.Count Remove-Item -Recurse -Force $srcDir, $extractDir } It 'Places files correctly when the source path is an 8.3 short name' { # Get-ChildItem reports canonical paths. If the relative path were computed by # character offset against a caller-supplied short path, entries would land under a # truncated folder name (tools/ols/...), which is what $env:TEMP does on CI runners. $srcDir = Join-Path $env:TEMP 'ChocoForge ShortName Test' if (Test-Path -LiteralPath $srcDir) { Remove-Item -LiteralPath $srcDir -Recurse -Force } New-Item -ItemType Directory -Path (Join-Path $srcDir 'tools') -Force | Out-Null Copy-Item "$PSScriptRoot/assets/embed-package/test-embed.nuspec" (Join-Path $srcDir 'test-embed.nuspec') Copy-Item "$PSScriptRoot/assets/embed-package/tools/chocolateyInstall.ps1" (Join-Path $srcDir 'tools/chocolateyInstall.ps1') Copy-Item "$PSScriptRoot/assets/embed-package/legal" (Join-Path $srcDir 'legal') -Recurse # Address the same directory through its 8.3 alias. $fso = New-Object -ComObject Scripting.FileSystemObject $shortDir = $fso.GetFolder($srcDir).ShortPath $shortDir | Should -Not -Be $srcDir -Because 'the probe needs a genuinely different path form' $ctx = [PSCustomObject]@{ version = [version]'1.0.0' } $packageBuilt = $ctx | Build-ChocolateyPackage -NuspecPath (Join-Path $shortDir 'test-embed.nuspec') Add-Type -AssemblyName System.IO.Compression.FileSystem $archive = [System.IO.Compression.ZipFile]::OpenRead($packageBuilt) try { $entries = @($archive.Entries | ForEach-Object { $_.FullName }) } finally { $archive.Dispose() } $entries | Should -Contain 'tools/chocolateyInstall.ps1' -Because "entries were: $($entries -join ', ')" $entries | Should -Contain 'legal/VERIFICATION.txt' -Because "entries were: $($entries -join ', ')" Remove-Item -LiteralPath $srcDir -Recurse -Force } It 'Creates .ignore files for embedded .exe assets to prevent shimming' { $configPath = "$PSScriptRoot/assets/embed-package/test-embed.forge.yaml" $config = Read-ForgeConfiguration -Path $configPath | Resolve-ForgeConfiguration $nuspecPath = "$PSScriptRoot/assets/embed-package/test-embed.nuspec" # Take the first version's context and override assets with .exe URLs $ctx = $config.versions | Select-Object -First 1 $ctx | Add-Member -NotePropertyName 'assets' -NotePropertyValue @{ x64 = [PSCustomObject]@{ browser_download_url = 'https://example.com/Installer_x64.exe'; sha256 = 'abc123' } Win32 = [PSCustomObject]@{ browser_download_url = 'https://example.com/Installer_Win32.exe'; sha256 = 'def456' } } -Force # Mock Invoke-WebRequest to create dummy .exe files Mock Invoke-WebRequest { Set-Content -Path $OutFile -Value 'dummy-exe-content' } $packageBuilt = $ctx | Build-ChocolateyPackage -NuspecPath $nuspecPath -Embed $packageBuilt | Should -Not -BeNullOrEmpty # Extract the package $extractDir = Join-Path $env:TEMP 'chocoforge-embed-test-ignore' if (Test-Path $extractDir) { Remove-Item -Recurse -Force $extractDir } Expand-Archive -Path $packageBuilt -DestinationPath $extractDir -Force $toolsDir = Join-Path $extractDir 'tools' # Both .exe files should exist Test-Path (Join-Path $toolsDir 'Installer_x64.exe') | Should -Be $true Test-Path (Join-Path $toolsDir 'Installer_Win32.exe') | Should -Be $true # Both .ignore files should exist Test-Path (Join-Path $toolsDir 'Installer_x64.exe.ignore') | Should -Be $true Test-Path (Join-Path $toolsDir 'Installer_Win32.exe.ignore') | Should -Be $true # No .ignore file for non-.exe files (e.g., the install script) Test-Path (Join-Path $toolsDir 'chocolateyInstall.ps1.ignore') | Should -Be $false Remove-Item -Recurse -Force $extractDir } } } |