Tests/Get-Sha256FromUrlWithCache.Tests.ps1
|
Import-Module "$PSScriptRoot/../ChocoForge.psd1" -Force Describe 'Get-Sha256FromUrlWithCache' { InModuleScope 'ChocoForge' { BeforeEach { # The function caches into $env:TEMP/ChocoForge-HashCache.json. Point TEMP at a # scratch directory so tests neither read nor clobber the real cache. $script:realTemp = $env:TEMP $script:sandbox = Join-Path ([System.IO.Path]::GetTempPath()) "chocoforge-cache-test-$([Guid]::NewGuid())" New-Item -ItemType Directory -Path $script:sandbox -Force | Out-Null $env:TEMP = $script:sandbox $script:cacheFile = Join-Path $script:sandbox 'ChocoForge-HashCache.json' # Stand in for the download: write known bytes so the hash is predictable. Mock Invoke-WebRequest { Set-Content -Path $OutFile -Value 'known-content' -NoNewline } } AfterEach { $env:TEMP = $script:realTemp if (Test-Path $script:sandbox) { Remove-Item -Recurse -Force $script:sandbox } } It 'Downloads and hashes on a cache miss' { $hash = Get-Sha256FromUrlWithCache -Url 'https://example.com/file.zip' $hash | Should -Match '^[0-9a-f]{64}$' Should -Invoke Invoke-WebRequest -Times 1 -Exactly } It 'Returns the hash of the actual content' { $expected = (Get-FileHash -InputStream ([System.IO.MemoryStream]::new([System.Text.Encoding]::UTF8.GetBytes('known-content'))) -Algorithm SHA256).Hash.ToLower() Get-Sha256FromUrlWithCache -Url 'https://example.com/file.zip' | Should -Be $expected } It 'Writes the cache file' { $null = Get-Sha256FromUrlWithCache -Url 'https://example.com/file.zip' Test-Path $script:cacheFile | Should -BeTrue $cache = Get-Content $script:cacheFile -Raw | ConvertFrom-Json -AsHashtable $cache.Keys | Should -Contain 'https://example.com/file.zip' } It 'Serves a second request from cache without downloading again' { $first = Get-Sha256FromUrlWithCache -Url 'https://example.com/file.zip' $second = Get-Sha256FromUrlWithCache -Url 'https://example.com/file.zip' $second | Should -Be $first Should -Invoke Invoke-WebRequest -Times 1 -Exactly # still only the first call } It 'Re-downloads once the cached entry is older than 24 hours' { $stale = @{ 'https://example.com/file.zip' = @{ sha256 = 'stalehash' timestamp = (Get-Date).AddHours(-25).ToString('o') } } $stale | ConvertTo-Json -Depth 5 | Set-Content -Path $script:cacheFile $hash = Get-Sha256FromUrlWithCache -Url 'https://example.com/file.zip' $hash | Should -Not -Be 'stalehash' Should -Invoke Invoke-WebRequest -Times 1 -Exactly } It 'Prunes expired entries for other URLs when writing' { $seed = @{ 'https://example.com/expired.zip' = @{ sha256 = 'old'; timestamp = (Get-Date).AddHours(-48).ToString('o') } 'https://example.com/fresh.zip' = @{ sha256 = 'new'; timestamp = (Get-Date).AddMinutes(-5).ToString('o') } } $seed | ConvertTo-Json -Depth 5 | Set-Content -Path $script:cacheFile $null = Get-Sha256FromUrlWithCache -Url 'https://example.com/file.zip' $cache = Get-Content $script:cacheFile -Raw | ConvertFrom-Json -AsHashtable $cache.Keys | Should -Not -Contain 'https://example.com/expired.zip' # pruned $cache.Keys | Should -Contain 'https://example.com/fresh.zip' # retained $cache.Keys | Should -Contain 'https://example.com/file.zip' # just added } It 'Drops entries with an unparseable timestamp' { $seed = @{ 'https://example.com/corrupt.zip' = @{ sha256 = 'x'; timestamp = 'not-a-date' } } $seed | ConvertTo-Json -Depth 5 | Set-Content -Path $script:cacheFile $null = Get-Sha256FromUrlWithCache -Url 'https://example.com/file.zip' $cache = Get-Content $script:cacheFile -Raw | ConvertFrom-Json -AsHashtable $cache.Keys | Should -Not -Contain 'https://example.com/corrupt.zip' } It 'Recovers from a corrupt cache file instead of throwing' { Set-Content -Path $script:cacheFile -Value 'this is not json {{{' { Get-Sha256FromUrlWithCache -Url 'https://example.com/file.zip' } | Should -Not -Throw Should -Invoke Invoke-WebRequest -Times 1 -Exactly } It 'Trims surrounding whitespace from the URL' { $withSpace = Get-Sha256FromUrlWithCache -Url ' https://example.com/file.zip ' $cache = Get-Content $script:cacheFile -Raw | ConvertFrom-Json -AsHashtable $cache.Keys | Should -Contain 'https://example.com/file.zip' $withSpace | Should -Match '^[0-9a-f]{64}$' } } } |