microsoft/hve-core

Public

mirrored fromhttps://github.com/microsoft/hve-coreAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
feat/context-working

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

534lines · modecode

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