Files
management-scripts/tests/Get-DSAManifest.Tests.ps1
T

161 lines
8.8 KiB
PowerShell

#Requires -Version 5.0
# Offline tests: collector and upload calls are mocked; only temporary files are deleted.
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
$DsaScriptUnderTest = Join-Path (Split-Path $PSScriptRoot -Parent) 'Get-DSAManifest.ps1'
$DsaTokens = $null
$DsaErrors = $null
$null = [Management.Automation.Language.Parser]::ParseFile($DsaScriptUnderTest, [ref]$DsaTokens, [ref]$DsaErrors)
If ($DsaErrors.Count) { Throw ($DsaErrors | Out-String) }
# Load only the real conversion helper, without running Tools.ps1 setup.
$DsaToolsPath = Join-Path (Split-Path $PSScriptRoot -Parent) 'Tools.ps1'
$DsaToolsAst = [Management.Automation.Language.Parser]::ParseFile($DsaToolsPath, [ref]$DsaTokens, [ref]$DsaErrors)
If ($DsaErrors.Count) { Throw ($DsaErrors | Out-String) }
$DsaBooleanDefinition = $DsaToolsAst.Find({
param($Node)
$Node -is [Management.Automation.Language.FunctionDefinitionAst] -and $Node.Name -eq 'ConvertTo-RmmBoolean'
}, $true)
If (!$DsaBooleanDefinition) { Throw 'Missing helper: ConvertTo-RmmBoolean' }
. ([scriptblock]::Create($DsaBooleanDefinition.Extent.Text))
Function Assert-Dsa { param([bool]$Condition, [string]$Description) If (!$Condition) { Throw $Description } }
Function Download-File { Throw 'DSA must use Download-FileDirectly.' }
Function Download-FileDirectly {
[CmdletBinding()]
param($URL, $File)
$script:DsaDownloadCalls++
If ($script:DsaTestMode -eq 'empty-download') {
[IO.File]::WriteAllBytes($File, [byte[]]@())
Return $File
}
If ($script:DsaTestMode -eq 'download-failure') { Throw 'Simulated BITS failure.' }
[IO.File]::WriteAllText($File, 'Mock executable; never executed.')
Return $File
}
Function Start-Process {
[CmdletBinding()]
param($FilePath, $ArgumentList, [switch]$Wait, [switch]$PassThru, $WindowStyle)
$script:DsaProcessCalls++
$DsaMatch = [regex]::Match($ArgumentList, '"--output"\s+"([^"]+)"')
If (!$DsaMatch.Success) { Throw 'Collector output argument missing.' }
$script:DsaCsvPath = $DsaMatch.Groups[1].Value
$script:DsaGeneratedPath = $DsaCsvPath
If ($ArgumentList -match '"--zip"') { $script:DsaGeneratedPath = [IO.Path]::ChangeExtension($DsaCsvPath, '.zip') }
If (!$DsaGeneratedPath.StartsWith($script:DsaTestRoot + '\', [StringComparison]::OrdinalIgnoreCase)) {
Throw 'Mock collector output is outside the test directory.'
}
If ($script:DsaTestMode -eq 'collector-failure') { Return [pscustomobject]@{ExitCode=7} }
If ($script:DsaTestMode -ne 'missing-output') {
[IO.File]::WriteAllText($DsaGeneratedPath, 'Synthetic manifest.')
If ($DsaGeneratedPath -ne $DsaCsvPath) {
# A same-basename CSV must not be deleted when the uploaded result is ZIP.
[IO.File]::WriteAllText($DsaCsvPath, 'Unrelated CSV sentinel.')
}
}
Return [pscustomobject]@{ExitCode=0}
}
Function Upload-File {
[CmdletBinding()]
param($Path, $Subject, $Message)
Assert-Dsa (Test-Path -LiteralPath $Path -PathType Leaf) 'File was deleted before upload.'
$script:DsaRecordedUploads += [pscustomobject]@{Path=$Path; Subject=$Subject; Message=$Message}
If ($script:DsaTestMode -eq 'upload-failure') { Throw 'Simulated upload failure.' }
If ($script:DsaTestMode -eq 'no-receipt') { Return }
If ($script:DsaTestMode -eq 'incomplete-receipt') { Return [pscustomobject]@{AttachmentId='test-attachment'} }
Return [pscustomobject]@{Path=$Path; AttachmentId='test-attachment'; Status='Filedrop message sent successfully'}
}
Function Invoke-DsaCase {
param([bool]$ZipExpected, [bool]$UploadExpected, [bool]$DeleteExpected, [string]$Mode='success', [hashtable]$Inputs)
$Zip = If ($ZipExpected) { ' YeS ' } Else { ' n ' }
$Upload = If ($UploadExpected) { ' $TrUe ' } Else { ' 0 ' }
$DeleteAfterUpload = If ($DeleteExpected) { ' 1 ' } Else { ' $FaLsE ' }
If ($null -ne $Inputs) {
$Zip = $Inputs.Zip
$Upload = $Inputs.Upload
$DeleteAfterUpload = $Inputs.DeleteAfterUpload
}
$DSAManifestSource = 'C:\Synthetic Source'
$Global:ToolsDirectory = Join-Path $script:DsaTestRoot 'Tools'
$Global:OutputDirectory = Join-Path $script:DsaTestRoot ([guid]::NewGuid().ToString('N'))
$script:DsaTestMode = $Mode
$script:DsaRecordedUploads = @()
$script:DsaDownloadCalls = 0
$script:DsaProcessCalls = 0
$script:DsaGeneratedPath = $null
$DsaCaught = $null
Try { . $DsaScriptUnderTest | Out-Null } Catch { $DsaCaught = $_ }
If ($Mode -eq 'invalid-input') {
Assert-Dsa ($null -ne $DsaCaught -and $DsaCaught.Exception.Message -like 'Invalid $* value*') 'Invalid flag was not rejected.'
Assert-Dsa ($script:DsaDownloadCalls -eq 0) 'Invalid flag must fail before downloading.'
Return
}
Assert-Dsa ($script:DsaDownloadCalls -eq 1) 'Expected one direct download.'
If ($Mode -in @('empty-download','download-failure')) {
Assert-Dsa ($null -ne $DsaCaught) 'Download failure must stop collection.'
Assert-Dsa ($script:DsaProcessCalls -eq 0 -and $script:DsaRecordedUploads.Count -eq 0) 'Download failure must prevent execution and upload.'
If ($Mode -eq 'empty-download') {
Assert-Dsa ($DsaCaught.Exception.Message -like '*0 bytes*') 'Empty download error must explain the failure.'
}
Return
}
If ($Mode -in @('collector-failure','missing-output')) {
Assert-Dsa ($null -ne $DsaCaught -and $script:DsaRecordedUploads.Count -eq 0) 'Collection failure must prevent upload.'
Return
}
If ($Mode -eq 'success') { Assert-Dsa ($null -eq $DsaCaught) "Unexpected failure: ${DsaCaught}" }
Else { Assert-Dsa ($null -ne $DsaCaught) 'Expected upload/receipt failure.' }
$DsaExpectedExtension = If ($ZipExpected) { '.zip' } Else { '.csv' }
Assert-Dsa ([IO.Path]::GetExtension($script:DsaGeneratedPath) -eq $DsaExpectedExtension) 'Incorrect collected file type.'
$DsaExpectedCalls = If ($UploadExpected) { 1 } Else { 0 }
Assert-Dsa ($script:DsaRecordedUploads.Count -eq $DsaExpectedCalls) 'Unexpected upload call count.'
If ($UploadExpected) {
Assert-Dsa ($DsaRecordedUploads[0].Path -eq $script:DsaGeneratedPath) 'Upload targeted the wrong file.'
Assert-Dsa ($DsaRecordedUploads[0].Subject -ceq "DSA manifest file uploaded from ${Env:COMPUTERNAME}") 'Subject changed.'
$DsaExpectedMessage = "DSA collection performed on ${DSAManifestSource} at $($DsaCollectionTime.ToString('yyyy-MM-dd HH:mm:ss zzz'))"
Assert-Dsa ($DsaRecordedUploads[0].Message -ceq $DsaExpectedMessage) 'Message changed.'
}
$DsaShouldRetain = !($UploadExpected -and $DeleteExpected -and $Mode -eq 'success')
Assert-Dsa ((Test-Path -LiteralPath $script:DsaGeneratedPath) -eq $DsaShouldRetain) 'Incorrect deletion or retention behavior.'
If ($ZipExpected) { Assert-Dsa (Test-Path -LiteralPath $script:DsaCsvPath) 'Cleanup incorrectly deleted the CSV neighbor.' }
}
$script:DsaTestRoot = Join-Path ([IO.Path]::GetTempPath()) ('dsa-options-tests-' + [guid]::NewGuid().ToString('N'))
$null = New-Item -Path $script:DsaTestRoot -ItemType Directory
$DsaOriginalToolsDirectory = $Global:ToolsDirectory
$DsaOriginalOutputDirectory = $Global:OutputDirectory
Try {
Foreach ($ZipExpected in @($false,$true)) {
Foreach ($UploadExpected in @($false,$true)) {
Foreach ($DeleteExpected in @($false,$true)) {
Invoke-DsaCase $ZipExpected $UploadExpected $DeleteExpected
Write-Host "PASS: ZIP=$ZipExpected Upload=$UploadExpected DeleteAfterUpload=$DeleteExpected"
}
}
Foreach ($Mode in @('upload-failure','no-receipt','incomplete-receipt','collector-failure','missing-output','empty-download','download-failure')) {
Invoke-DsaCase $ZipExpected $true $true $Mode
Write-Host "PASS: ZIP=$ZipExpected $Mode prevents deletion."
}
}
Invoke-DsaCase $false $false $false -Inputs @{}
Foreach ($Name in @('Zip','Upload','DeleteAfterUpload')) {
$Inputs = @{Zip='no'; Upload='no'; DeleteAfterUpload='no'}
$Inputs[$Name] = 'maybe'
Invoke-DsaCase $false $false $false 'invalid-input' $Inputs
}
Write-Host "PASS: empty options default to false; invalid options fail early."
Write-Host "All Get-DSAManifest tests passed on PowerShell $($PSVersionTable.PSVersion), $([IntPtr]::Size * 8)-bit."
}
Finally {
$Global:ToolsDirectory = $DsaOriginalToolsDirectory
$Global:OutputDirectory = $DsaOriginalOutputDirectory
$DsaResolvedRoot = (Resolve-Path -LiteralPath $script:DsaTestRoot).Path
$DsaTempRoot = [IO.Path]::GetFullPath([IO.Path]::GetTempPath()).TrimEnd('\') + '\'
If (!$DsaResolvedRoot.StartsWith($DsaTempRoot, [StringComparison]::OrdinalIgnoreCase) -or
(Split-Path $DsaResolvedRoot -Leaf) -notlike 'dsa-options-tests-*') { Throw 'Unexpected cleanup directory.' }
Remove-Item -LiteralPath $DsaResolvedRoot -Recurse -Force
}