Tests/Public/Select-IntegrityReportFile.Tests.ps1
|
BeforeAll { $modulePath = "$PSScriptRoot/../.." Get-ChildItem -Path "$modulePath/Private/*.ps1" | ForEach-Object { . $_.FullName } Get-ChildItem -Path "$modulePath/Public/*.ps1" | ForEach-Object { . $_.FullName } } Describe 'Select-IntegrityReportFile' { BeforeAll { $testDir = Join-Path ([System.IO.Path]::GetTempPath()) "SelectTests_$([guid]::NewGuid().ToString('N'))" New-Item -ItemType Directory -Path $testDir -Force | Out-Null # Create a sample CSV report $csvPath = Join-Path $testDir 'test-report.csv' @( '"RelativePath","Status","SourceSize","DestSize","SourceMD5","DestMD5","Error"' '"docs\readme.txt","OK","1024","1024","abc","abc",""' '"data\file1.xlsx","ByteMismatch","2048","2048","abc","def",""' '"data\file2.docx","SizeMismatch","1024","512","","",""' '"data\file3.pdf","HashMismatch","4096","4096","abc","xyz",""' '"backup\old.zip","MissingOnDestination","8192","","","",""' '"temp\extra.log","MissingOnSource","","512","","",""' '"data\subdir\report.xlsx","ByteMismatch","3072","3072","abc","ghi",""' ) | Set-Content -Path $csvPath -Encoding utf8 } AfterAll { Remove-Item -Path $testDir -Recurse -Force -ErrorAction SilentlyContinue } BeforeEach { Mock Write-Host {} Mock Write-Warning {} } Context 'Default status filter' { It 'Returns remediable statuses by default' { $result = @(Select-IntegrityReportFile -ReportPath $csvPath) # ByteMismatch(2) + SizeMismatch(1) + HashMismatch(1) + MissingOnDestination(1) = 5 $result.Count | Should -Be 5 } It 'Does not include OK or MissingOnSource by default' { $result = @(Select-IntegrityReportFile -ReportPath $csvPath) $result | Should -Not -Contain 'docs\readme.txt' $result | Should -Not -Contain 'temp\extra.log' } } Context 'Specific status filter' { It 'Returns only ByteMismatch files when specified' { $result = @(Select-IntegrityReportFile -ReportPath $csvPath -Status 'ByteMismatch') $result.Count | Should -Be 2 $result | Should -Contain 'data\file1.xlsx' $result | Should -Contain 'data\subdir\report.xlsx' } It 'Returns OK files when explicitly asked' { $result = @(Select-IntegrityReportFile -ReportPath $csvPath -Status 'OK') $result.Count | Should -Be 1 $result[0] | Should -Be 'docs\readme.txt' } It 'Returns multiple statuses' { $result = @(Select-IntegrityReportFile -ReportPath $csvPath -Status 'ByteMismatch', 'SizeMismatch') $result.Count | Should -Be 3 } } Context 'FilterPath mode' { It 'Filters by literal substring (case-insensitive)' { $result = @(Select-IntegrityReportFile -ReportPath $csvPath -Status 'ByteMismatch' -FilterPath 'subdir') $result.Count | Should -Be 1 $result[0] | Should -Be 'data\subdir\report.xlsx' } It 'Returns nothing when substring does not match' { $result = @(Select-IntegrityReportFile -ReportPath $csvPath -Status 'ByteMismatch' -FilterPath 'nonexistent') $result.Count | Should -Be 0 } } Context 'FilterRegex mode' { It 'Filters by regex pattern' { $result = @(Select-IntegrityReportFile -ReportPath $csvPath -FilterRegex '\.xlsx$') $result.Count | Should -Be 2 } It 'Is case-insensitive by default' { $result = @(Select-IntegrityReportFile -ReportPath $csvPath -FilterRegex '\.XLSX$') $result.Count | Should -Be 2 } } Context 'IncludePaths mode' { It 'Returns only explicitly listed paths' { $result = @(Select-IntegrityReportFile -ReportPath $csvPath -IncludePaths @('data\file1.xlsx')) $result.Count | Should -Be 1 $result[0] | Should -Be 'data\file1.xlsx' } It 'Warns about paths not found in report' { Select-IntegrityReportFile -ReportPath $csvPath -IncludePaths @('does\not\exist.txt') | Out-Null Should -Invoke Write-Warning -ParameterFilter { $Message -like '*not found*' } } } Context 'Mutually exclusive filter modes' { It 'Throws when multiple filter modes are specified' { { Select-IntegrityReportFile -ReportPath $csvPath -FilterPath 'data' -FilterRegex '\.xlsx$' } | Should -Throw '*Only one of*' } } Context 'Empty results' { It 'Returns nothing for a status with no matches' { $result = @(Select-IntegrityReportFile -ReportPath $csvPath -Status 'CheckError') $result.Count | Should -Be 0 } } Context 'Semicolon-delimited report' { BeforeAll { $semiCsvPath = Join-Path $testDir 'semi-report.csv' @( '"RelativePath";"Status";"SourceSize";"DestSize";"SourceMD5";"DestMD5";"Error"' '"data\file1.xlsx";"ByteMismatch";"2048";"2048";"abc";"def";""' ) | Set-Content -Path $semiCsvPath -Encoding utf8 } It 'Detects and parses semicolon delimiter' { $result = @(Select-IntegrityReportFile -ReportPath $semiCsvPath -Status 'ByteMismatch') $result.Count | Should -Be 1 } } Context 'Empty report file' { BeforeAll { $emptyReportPath = Join-Path $testDir 'empty-report.csv' @( '"RelativePath","Status","SourceSize","DestSize","SourceMD5","DestMD5","Error"' ) | Set-Content -Path $emptyReportPath -Encoding utf8 } It 'Returns nothing when CSV has only a header row' { $result = @(Select-IntegrityReportFile -ReportPath $emptyReportPath) $result.Count | Should -Be 0 Should -Invoke Write-Host -ParameterFilter { $Object -like '*empty*' } } } Context 'Interactive mode' { BeforeAll { # Define a dummy function so Pester can mock it (it doesn't exist locally) function Out-ConsoleGridView { param([Parameter(ValueFromPipeline)][object]$InputObject, [string]$Title, [string]$OutputMode) } } It 'Throws when neither Out-ConsoleGridView nor Out-GridView is available' { Mock Get-Command { $null } { Select-IntegrityReportFile -ReportPath $csvPath -Interactive } | Should -Throw '*requires*' } It 'Returns nothing when user picks no items in grid view' { Mock Get-Command { [PSCustomObject]@{ Name = 'Out-ConsoleGridView' } } -ParameterFilter { $Name -eq 'Out-ConsoleGridView' } Mock Out-ConsoleGridView { $input | Out-Null; return } $result = @(Select-IntegrityReportFile -ReportPath $csvPath -Interactive) $result.Count | Should -Be 0 Should -Invoke Write-Host -ParameterFilter { $Object -like '*No files selected*' } } It 'Returns only user-selected files from grid view' { Mock Get-Command { [PSCustomObject]@{ Name = 'Out-ConsoleGridView' } } -ParameterFilter { $Name -eq 'Out-ConsoleGridView' } Mock Out-ConsoleGridView { [PSCustomObject]@{ RelativePath = 'data\file1.xlsx'; Status = 'ByteMismatch'; SourceSize = '2048'; DestSize = '2048' } } $result = @(Select-IntegrityReportFile -ReportPath $csvPath -Interactive) $result.Count | Should -Be 1 $result[0] | Should -Be 'data\file1.xlsx' } } Context 'No files matched after path filter' { It 'Returns nothing when FilterPath removes all status-matched rows' { $result = @(Select-IntegrityReportFile -ReportPath $csvPath -Status 'ByteMismatch' -FilterPath 'ZZZZZ_nonexistent') $result.Count | Should -Be 0 Should -Invoke Write-Host -ParameterFilter { $Object -like '*No files matched*' } } It 'Returns nothing when FilterRegex removes all rows' { $result = @(Select-IntegrityReportFile -ReportPath $csvPath -Status 'ByteMismatch' -FilterRegex '^IMPOSSIBLE_PATTERN_999$') $result.Count | Should -Be 0 Should -Invoke Write-Host -ParameterFilter { $Object -like '*No files matched*' } } } } |