microsoft/hve-core

Public

mirrored from https://github.com/microsoft/hve-coreAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
hve-core-v2.0.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

scripts/tests/lib/Get-VerifiedDownload.Tests.ps1

533lines · modecode

1#Requires -Modules Pester
2
3BeforeAll {
4 . $PSScriptRoot/../../lib/Get-VerifiedDownload.ps1
5}
6
7Describe 'Get-FileHashValue' {
8 It 'Returns uppercase hash string for valid file' {
9 $tempFile = New-TemporaryFile
10 try {
11 'test content' | Set-Content -Path $tempFile.FullName -NoNewline
12 $result = Get-FileHashValue -Path $tempFile.FullName -Algorithm 'SHA256'
13 $result | Should -BeOfType [string]
14 $result | Should -Match '^[A-F0-9]{64}$'
15 } finally {
16 Remove-Item -Path $tempFile.FullName -Force -ErrorAction SilentlyContinue
17 }
18 }
19
20 It 'Supports SHA384 algorithm' {
21 $tempFile = New-TemporaryFile
22 try {
23 'test' | Set-Content -Path $tempFile.FullName -NoNewline
24 $result = Get-FileHashValue -Path $tempFile.FullName -Algorithm 'SHA384'
25 $result | Should -Match '^[A-F0-9]{96}$'
26 } finally {
27 Remove-Item -Path $tempFile.FullName -Force -ErrorAction SilentlyContinue
28 }
29 }
30
31 It 'Supports SHA512 algorithm' {
32 $tempFile = New-TemporaryFile
33 try {
34 'test' | Set-Content -Path $tempFile.FullName -NoNewline
35 $result = Get-FileHashValue -Path $tempFile.FullName -Algorithm 'SHA512'
36 $result | Should -Match '^[A-F0-9]{128}$'
37 } finally {
38 Remove-Item -Path $tempFile.FullName -Force -ErrorAction SilentlyContinue
39 }
40 }
41}
42
43Describe 'Test-HashMatch' {
44 It 'Returns true when hashes match (case-insensitive)' {
45 $result = Test-HashMatch -ComputedHash 'ABC123' -ExpectedHash 'abc123'
46 $result | Should -BeTrue
47 }
48
49 It 'Returns false when hashes do not match' {
50 $result = Test-HashMatch -ComputedHash 'ABC123' -ExpectedHash 'DEF456'
51 $result | Should -BeFalse
52 }
53
54 It 'Returns true when hashes match exactly' {
55 $result = Test-HashMatch -ComputedHash 'ABC123DEF456' -ExpectedHash 'ABC123DEF456'
56 $result | Should -BeTrue
57 }
58}
59
60Describe 'Get-DownloadTargetPath' {
61 BeforeAll {
62 $script:testDir = [System.IO.Path]::GetTempPath().TrimEnd([System.IO.Path]::DirectorySeparatorChar)
63 }
64
65 It 'Uses filename from URL when FileName not specified' {
66 $result = Get-DownloadTargetPath -Url 'https://example.com/file.zip' -DestinationDirectory $script:testDir
67 $expected = [System.IO.Path]::Combine($script:testDir, 'file.zip')
68 $result | Should -Be $expected
69 }
70
71 It 'Uses explicit FileName when specified' {
72 $result = Get-DownloadTargetPath -Url 'https://example.com/file.zip' -DestinationDirectory $script:testDir -FileName 'custom.zip'
73 $expected = [System.IO.Path]::Combine($script:testDir, 'custom.zip')
74 $result | Should -Be $expected
75 }
76
77 It 'Handles URL with query parameters' {
78 $result = Get-DownloadTargetPath -Url 'https://example.com/file.zip?token=abc' -DestinationDirectory $script:testDir
79 $expected = [System.IO.Path]::Combine($script:testDir, 'file.zip')
80 $result | Should -Be $expected
81 }
82}
83
84Describe 'Test-ExistingFileValid' {
85 It 'Returns true when file exists with matching hash' {
86 $tempFile = New-TemporaryFile
87 try {
88 'known content' | Set-Content -Path $tempFile.FullName -NoNewline
89 $expectedHash = (Get-FileHash -Path $tempFile.FullName -Algorithm SHA256).Hash
90 $result = Test-ExistingFileValid -Path $tempFile.FullName -ExpectedHash $expectedHash -Algorithm 'SHA256'
91 $result | Should -BeTrue
92 } finally {
93 Remove-Item -Path $tempFile.FullName -Force -ErrorAction SilentlyContinue
94 }
95 }
96
97 It 'Returns false when file exists with non-matching hash' {
98 $tempFile = New-TemporaryFile
99 try {
100 'some content' | Set-Content -Path $tempFile.FullName -NoNewline
101 $result = Test-ExistingFileValid -Path $tempFile.FullName -ExpectedHash 'INVALID_HASH' -Algorithm 'SHA256'
102 $result | Should -BeFalse
103 } finally {
104 Remove-Item -Path $tempFile.FullName -Force -ErrorAction SilentlyContinue
105 }
106 }
107
108 It 'Returns false when file does not exist' {
109 $nonexistentPath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), 'nonexistent-dir-12345', 'file.txt')
110 $result = Test-ExistingFileValid -Path $nonexistentPath -ExpectedHash 'ABC123' -Algorithm 'SHA256'
111 $result | Should -BeFalse
112 }
113}
114
115Describe 'New-DownloadResult' {
116 It 'Creates hashtable with all properties' {
117 $result = New-DownloadResult -Path 'C:\file.zip' -WasDownloaded $true -HashVerified $true
118 $result | Should -BeOfType [hashtable]
119 $result.Path | Should -Be 'C:\file.zip'
120 $result.WasDownloaded | Should -BeTrue
121 $result.HashVerified | Should -BeTrue
122 }
123
124 It 'Handles false values correctly' {
125 $result = New-DownloadResult -Path 'C:\cached.zip' -WasDownloaded $false -HashVerified $true
126 $result.WasDownloaded | Should -BeFalse
127 $result.HashVerified | Should -BeTrue
128 }
129}
130
131Describe 'Get-ArchiveType' {
132 It 'Returns tar.gz for .tar.gz files' {
133 $result = Get-ArchiveType -Path 'archive.tar.gz'
134 $result | Should -Be 'tar.gz'
135 }
136
137 It 'Returns tar.gz for .tgz files' {
138 $result = Get-ArchiveType -Path 'archive.tgz'
139 $result | Should -Be 'tar.gz'
140 }
141
142 It 'Returns tar for plain .tar files' {
143 $result = Get-ArchiveType -Path 'archive.tar'
144 $result | Should -Be 'tar'
145 }
146
147 It 'Returns zip for .zip files' {
148 $result = Get-ArchiveType -Path 'archive.zip'
149 $result | Should -Be 'zip'
150 }
151
152 It 'Returns unknown for unrecognized extensions' {
153 $result = Get-ArchiveType -Path 'file.txt'
154 $result | Should -Be 'unknown'
155 }
156
157 It 'Handles paths with directories' {
158 $result = Get-ArchiveType -Path 'C:\downloads\archive.tar.gz'
159 $result | Should -Be 'tar.gz'
160 }
161}
162
163Describe 'Test-TarAvailable' {
164 It 'Returns boolean indicating tar availability' {
165 $result = Test-TarAvailable
166 $result | Should -BeOfType [bool]
167 }
168}
169
170Describe 'Invoke-VerifiedDownload' {
171 BeforeAll {
172 $script:testDestDir = Join-Path $TestDrive 'downloads'
173 $script:testUrl = 'https://example.com/tool.zip'
174 $script:testHash = 'ABC123DEF456ABC123DEF456ABC123DEF456ABC123DEF456ABC123DEF456ABCD'
175 }
176
177 BeforeEach {
178 # Clean test directory before each test
179 if (Test-Path $script:testDestDir) {
180 Remove-Item -Path $script:testDestDir -Recurse -Force
181 }
182 }
183
184 Context 'Skip when valid file exists' {
185 BeforeAll {
186 Mock Write-Verbose { }
187 }
188
189 It 'Skips download when existing file hash matches' {
190 # Create a pre-existing file with matching hash
191 New-Item -ItemType Directory -Path $script:testDestDir -Force | Out-Null
192 $prePath = Join-Path $script:testDestDir 'tool.zip'
193 'existing content' | Set-Content -Path $prePath -NoNewline
194 $actualHash = (Get-FileHash -Path $prePath -Algorithm SHA256).Hash
195
196 $result = Invoke-VerifiedDownload `
197 -Url $script:testUrl `
198 -DestinationDirectory $script:testDestDir `
199 -ExpectedHash $actualHash
200
201 $result.WasDownloaded | Should -BeFalse
202 $result.HashVerified | Should -BeTrue
203 $result.Path | Should -Be $prePath
204 }
205
206 It 'Returns existing file path in result' {
207 New-Item -ItemType Directory -Path $script:testDestDir -Force | Out-Null
208 $prePath = Join-Path $script:testDestDir 'tool.zip'
209 'existing content' | Set-Content -Path $prePath -NoNewline
210 $actualHash = (Get-FileHash -Path $prePath -Algorithm SHA256).Hash
211
212 $result = Invoke-VerifiedDownload `
213 -Url $script:testUrl `
214 -DestinationDirectory $script:testDestDir `
215 -ExpectedHash $actualHash
216
217 $result.Path | Should -Not -BeNullOrEmpty
218 $result.Path | Should -Exist
219 }
220 }
221
222 Context 'Successful download with valid hash' {
223 BeforeAll {
224 Mock Write-Host { }
225 }
226
227 It 'Downloads file and returns success result' {
228 $expectedHash = '2D8C2F6D7A37F9F6E8C5A4B3D2E1F0A9B8C7D6E5F4A3B2C1D0E9F8A7B6C5D4E3'
229
230 Mock Invoke-WebRequest {
231 param($OutFile)
232 Set-Content -Path $OutFile -Value 'mock downloaded content' -NoNewline
233 }
234 Mock Get-FileHashValue { return '2D8C2F6D7A37F9F6E8C5A4B3D2E1F0A9B8C7D6E5F4A3B2C1D0E9F8A7B6C5D4E3' }
235
236 $result = Invoke-VerifiedDownload `
237 -Url $script:testUrl `
238 -DestinationDirectory $script:testDestDir `
239 -ExpectedHash $expectedHash
240
241 $result.WasDownloaded | Should -BeTrue
242 $result.HashVerified | Should -BeTrue
243 $result.Path | Should -Exist
244 Should -Invoke -CommandName Invoke-WebRequest -Times 1 -Exactly
245 Should -Invoke -CommandName Get-FileHashValue -Times 1
246 }
247
248 It 'Creates destination directory if not exists' {
249 $expectedHash = 'AABBCC11223344556677889900AABBCC11223344556677889900AABBCC112233'
250
251 Mock Invoke-WebRequest {
252 param($OutFile)
253 Set-Content -Path $OutFile -Value 'mock content' -NoNewline
254 }
255 Mock Get-FileHashValue { return 'AABBCC11223344556677889900AABBCC11223344556677889900AABBCC112233' }
256
257 $newDir = Join-Path $TestDrive 'new-subdir'
258 $result = Invoke-VerifiedDownload `
259 -Url $script:testUrl `
260 -DestinationDirectory $newDir `
261 -ExpectedHash $expectedHash
262
263 $newDir | Should -Exist
264 $result.WasDownloaded | Should -BeTrue
265 }
266 }
267
268 Context 'Successful download with extraction' {
269 BeforeAll {
270 Mock Write-Host { }
271 Mock Write-Verbose { }
272 }
273
274 It 'Extracts ZIP archive to target path' {
275 $downloadedContent = 'mock zip content'
276 $expectedHash = 'ZIPARCHIVEHASH123456789012345678901234567890123456789012345678'
277
278 Mock Invoke-WebRequest {
279 param($OutFile)
280 Set-Content -Path $OutFile -Value $downloadedContent -NoNewline
281 }
282 Mock Get-FileHashValue { return $expectedHash }
283 Mock Expand-Archive { }
284 Mock Get-ArchiveType { return 'zip' }
285
286 $extractDir = Join-Path $TestDrive 'extracted'
287 $result = Invoke-VerifiedDownload `
288 -Url 'https://example.com/archive.zip' `
289 -DestinationDirectory $script:testDestDir `
290 -ExpectedHash $expectedHash `
291 -Extract `
292 -ExtractPath $extractDir
293
294 $result.HashVerified | Should -BeTrue
295 Should -Invoke -CommandName Expand-Archive -Times 1 -Exactly
296 }
297 }
298
299 Context 'Hash mismatch' {
300 BeforeAll {
301 Mock Write-Host { }
302 }
303
304 It 'Throws on hash verification failure' {
305 Mock Invoke-WebRequest {
306 param($OutFile)
307 Set-Content -Path $OutFile -Value 'bad content' -NoNewline
308 }
309 Mock Get-FileHashValue { return 'WRONGHASH123456789012345678901234567890123456789012345678901234' }
310
311 { Invoke-VerifiedDownload `
312 -Url $script:testUrl `
313 -DestinationDirectory $script:testDestDir `
314 -ExpectedHash 'EXPECTEDHASH567890123456789012345678901234567890123456789012345'
315 } | Should -Throw '*Checksum verification failed*'
316 }
317
318 It 'Cleans up temp file on hash failure' {
319 Mock Invoke-WebRequest {
320 param($OutFile)
321 Set-Content -Path $OutFile -Value 'bad content' -NoNewline
322 }
323 Mock Get-FileHashValue { return 'MISMATCHHASH23456789012345678901234567890123456789012345678901' }
324 Mock Remove-Item { } -Verifiable
325
326 { Invoke-VerifiedDownload `
327 -Url $script:testUrl `
328 -DestinationDirectory $script:testDestDir `
329 -ExpectedHash 'EXPECTEDHASH567890123456789012345678901234567890123456789012345'
330 } | Should -Throw
331
332 # The finally block should clean up temp files
333 Should -InvokeVerifiable
334 }
335 }
336
337 Context 'Network error' {
338 It 'Propagates network errors' {
339 Mock Invoke-WebRequest { throw 'Network unreachable' }
340
341 { Invoke-VerifiedDownload `
342 -Url $script:testUrl `
343 -DestinationDirectory $script:testDestDir `
344 -ExpectedHash $script:testHash
345 } | Should -Throw '*Network unreachable*'
346 }
347 }
348
349 Context 'Extraction error' {
350 BeforeAll {
351 Mock Write-Host { }
352 Mock Write-Verbose { }
353 }
354
355 It 'Propagates ZIP extraction errors' {
356 $downloadedContent = 'mock content'
357 $expectedHash = 'VALIDHASH1234567890123456789012345678901234567890123456789012'
358
359 Mock Invoke-WebRequest {
360 param($OutFile)
361 Set-Content -Path $OutFile -Value $downloadedContent -NoNewline
362 }
363 Mock Get-FileHashValue { return $expectedHash }
364 Mock Expand-Archive { throw 'Invalid archive format' }
365 Mock Get-ArchiveType { return 'zip' }
366
367 { Invoke-VerifiedDownload `
368 -Url 'https://example.com/archive.zip' `
369 -DestinationDirectory $script:testDestDir `
370 -ExpectedHash $expectedHash `
371 -Extract
372 } | Should -Throw '*Invalid archive format*'
373 }
374
375 It 'Throws for unsupported archive format' {
376 $downloadedContent = 'mock content'
377 $expectedHash = 'UNSUPPORTEDHASH89012345678901234567890123456789012345678901234'
378
379 Mock Invoke-WebRequest {
380 param($OutFile)
381 Set-Content -Path $OutFile -Value $downloadedContent -NoNewline
382 }
383 Mock Get-FileHashValue { return $expectedHash }
384 Mock Get-ArchiveType { return 'unknown' }
385
386 { Invoke-VerifiedDownload `
387 -Url 'https://example.com/file.xyz' `
388 -DestinationDirectory $script:testDestDir `
389 -ExpectedHash $expectedHash `
390 -Extract
391 } | Should -Throw '*Unsupported archive format*'
392 }
393 }
394
395 Context 'Tar extraction' {
396 BeforeAll {
397 Mock Write-Host { }
398 Mock Write-Verbose { }
399 }
400
401 It 'Throws when tar not available for tar.gz extraction' {
402 $downloadedContent = 'mock content'
403 $expectedHash = 'TARHASH123456789012345678901234567890123456789012345678901234'
404
405 Mock Invoke-WebRequest {
406 param($OutFile)
407 Set-Content -Path $OutFile -Value $downloadedContent -NoNewline
408 }
409 Mock Get-FileHashValue { return $expectedHash }
410 Mock Get-ArchiveType { return 'tar.gz' }
411 Mock Test-TarAvailable { return $false }
412
413 { Invoke-VerifiedDownload `
414 -Url 'https://example.com/archive.tar.gz' `
415 -DestinationDirectory $script:testDestDir `
416 -ExpectedHash $expectedHash `
417 -Extract
418 } | Should -Throw '*tar command not available*'
419 }
420
421 It 'Extracts tar.gz archive when tar is available' {
422 $downloadedContent = 'mock tar.gz content'
423 $expectedHash = 'TARGZHASH23456789012345678901234567890123456789012345678901234'
424
425 Mock Invoke-WebRequest {
426 param($OutFile)
427 Set-Content -Path $OutFile -Value $downloadedContent -NoNewline
428 }
429 Mock Get-FileHashValue { return $expectedHash }
430 Mock Get-ArchiveType { return 'tar.gz' }
431 Mock Test-TarAvailable { return $true }
432 Mock tar { $global:LASTEXITCODE = 0 }
433
434 $extractDir = Join-Path $TestDrive 'tar-gz-extracted'
435 $result = Invoke-VerifiedDownload `
436 -Url 'https://example.com/archive.tar.gz' `
437 -DestinationDirectory $script:testDestDir `
438 -ExpectedHash $expectedHash `
439 -Extract `
440 -ExtractPath $extractDir
441
442 $result.HashVerified | Should -BeTrue
443 Should -Invoke -CommandName tar -Times 1 -Exactly
444 }
445
446 It 'Extracts plain tar archive when tar is available' {
447 $downloadedContent = 'mock tar content'
448 $expectedHash = 'PLAINTARHASH456789012345678901234567890123456789012345678901234'
449
450 Mock Invoke-WebRequest {
451 param($OutFile)
452 Set-Content -Path $OutFile -Value $downloadedContent -NoNewline
453 }
454 Mock Get-FileHashValue { return $expectedHash }
455 Mock Get-ArchiveType { return 'tar' }
456 Mock Test-TarAvailable { return $true }
457 Mock tar { $global:LASTEXITCODE = 0 }
458
459 $extractDir = Join-Path $TestDrive 'tar-extracted'
460 $result = Invoke-VerifiedDownload `
461 -Url 'https://example.com/archive.tar' `
462 -DestinationDirectory $script:testDestDir `
463 -ExpectedHash $expectedHash `
464 -Extract `
465 -ExtractPath $extractDir
466
467 $result.HashVerified | Should -BeTrue
468 Should -Invoke -CommandName tar -Times 1 -Exactly
469 }
470
471 It 'Throws when tar not available for plain tar extraction' {
472 $downloadedContent = 'mock content'
473 $expectedHash = 'NOTARHASH789012345678901234567890123456789012345678901234567890'
474
475 Mock Invoke-WebRequest {
476 param($OutFile)
477 Set-Content -Path $OutFile -Value $downloadedContent -NoNewline
478 }
479 Mock Get-FileHashValue { return $expectedHash }
480 Mock Get-ArchiveType { return 'tar' }
481 Mock Test-TarAvailable { return $false }
482
483 { Invoke-VerifiedDownload `
484 -Url 'https://example.com/archive.tar' `
485 -DestinationDirectory $script:testDestDir `
486 -ExpectedHash $expectedHash `
487 -Extract
488 } | Should -Throw '*tar command not available*'
489 }
490
491 It 'Throws when tar.gz extraction fails with non-zero exit code' {
492 $downloadedContent = 'mock content'
493 $expectedHash = 'TARFAILHASH9012345678901234567890123456789012345678901234567890'
494
495 Mock Invoke-WebRequest {
496 param($OutFile)
497 Set-Content -Path $OutFile -Value $downloadedContent -NoNewline
498 }
499 Mock Get-FileHashValue { return $expectedHash }
500 Mock Get-ArchiveType { return 'tar.gz' }
501 Mock Test-TarAvailable { return $true }
502 Mock tar { $global:LASTEXITCODE = 1 }
503
504 { Invoke-VerifiedDownload `
505 -Url 'https://example.com/archive.tar.gz' `
506 -DestinationDirectory $script:testDestDir `
507 -ExpectedHash $expectedHash `
508 -Extract
509 } | Should -Throw '*tar extraction failed*'
510 }
511
512 It 'Throws when plain tar extraction fails with non-zero exit code' {
513 $downloadedContent = 'mock content'
514 $expectedHash = 'PLAINTARFAIL012345678901234567890123456789012345678901234567890'
515
516 Mock Invoke-WebRequest {
517 param($OutFile)
518 Set-Content -Path $OutFile -Value $downloadedContent -NoNewline
519 }
520 Mock Get-FileHashValue { return $expectedHash }
521 Mock Get-ArchiveType { return 'tar' }
522 Mock Test-TarAvailable { return $true }
523 Mock tar { $global:LASTEXITCODE = 2 }
524
525 { Invoke-VerifiedDownload `
526 -Url 'https://example.com/archive.tar' `
527 -DestinationDirectory $script:testDestDir `
528 -ExpectedHash $expectedHash `
529 -Extract
530 } | Should -Throw '*tar extraction failed*'
531 }
532 }
533}
534