tests/UnifiedEntryPoint.Tests.ps1
|
#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } <# .SYNOPSIS Pester tests for the single Invoke-AzureScout entry point and its wizard. .DESCRIPTION Inventory and assessment are one command with different switches, not two products. These tests pin that contract: - Invoke-AzureScout exposes the assessment-mode parameters. - Mixing an inventory-only format with -Assessment (and vice versa) fails with an actionable message instead of silently producing nothing. - The wizard never fires in a non-interactive host, so CI and scheduled runs of a bare `Invoke-AzureScout` cannot block on a prompt. - The wizard's checklist/selection primitives behave. No live Azure authentication is required. .NOTES Tracks AB#5540 (single entry point, per AB#5024) and AB#5541 (guided wizard). #> BeforeAll { $script:ModuleRoot = Split-Path -Parent $PSScriptRoot Import-Module (Join-Path $script:ModuleRoot 'AzureScout.psd1') -Force $script:Module = Get-Module AzureScout $script:Cmd = Get-Command Invoke-AzureScout } Describe 'Single entry point — parameter surface' { It 'Invoke-AzureScout exposes -<Name>' -ForEach @( @{ Name = 'Assessment' } @{ Name = 'CollectOnly' } @{ Name = 'FromCollect' } @{ Name = 'NoWizard' } ) { $script:Cmd.Parameters.ContainsKey($Name) | Should -BeTrue } It '-Assessment accepts multiple assessments' { $script:Cmd.Parameters['Assessment'].ParameterType | Should -Be ([string[]]) } It '-Assessment has the short -Assess alias' { $alias = $script:Cmd.Parameters['Assessment'].Aliases $alias | Should -Contain 'Assess' } It 'the deprecated Invoke-ScoutAssessment name still resolves' { # Published in v2.3.0 — kept working until the next major so existing # scripts do not break on upgrade. Get-Command Invoke-ScoutAssessment -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty } } Describe 'Single entry point — output format guards' { It 'rejects an inventory-only format in assessment mode' { { Invoke-AzureScout -Assessment LandingZone -OutputFormat Markdown } | Should -Throw -ExpectedMessage '*inventory-only*' } It 'rejects an assessment-only format in inventory mode' { { Invoke-AzureScout -OutputFormat Html -NoWizard } | Should -Throw -ExpectedMessage '*assessment format*' } It 'names the valid alternatives in the rejection message' { # The point of the guard is to redirect, not just to fail. { Invoke-AzureScout -OutputFormat Pptx -NoWizard } | Should -Throw -ExpectedMessage '*-Assessment*' } It 'accepts a format valid in both modes' { $vs = $script:Cmd.Parameters['OutputFormat'].Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } foreach ($shared in @('All', 'Excel', 'Json', 'PowerBI')) { $vs.ValidValues | Should -Contain $shared } } } Describe 'Wizard — interactive-host gate' { It 'does not prompt when stdin is redirected or the host is non-interactive' { # This Pester run is itself non-interactive, which is exactly the # condition a CI runner hits. & $script:Module { Test-AZSCInteractiveHost } | Should -BeFalse } It 'returns false when a CI environment variable is set' { $original = $env:GITHUB_ACTIONS try { $env:GITHUB_ACTIONS = 'true' & $script:Module { Test-AZSCInteractiveHost } | Should -BeFalse } finally { $env:GITHUB_ACTIONS = $original } } It 'exports Start-AZSCWizard so operators can re-run it on demand' { Get-Command Start-AZSCWizard -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty } It 'keeps the console primitives internal to the module' { Get-Command Read-AZSCWizardChecklist -ErrorAction SilentlyContinue | Should -BeNullOrEmpty } } Describe 'Wizard — checklist primitive' { It 'pre-selects every item and accepts on a bare Enter' { $result = & $script:Module { Mock -CommandName Read-Host -MockWith { '' } Read-AZSCWizardChecklist -Title 'x' -Items @('A', 'B', 'C') } $result | Should -Be @('A', 'B', 'C') } It 'unchecks the items the operator names, then accepts' { $result = & $script:Module { $script:calls = 0 Mock -CommandName Read-Host -MockWith { $script:calls++ if ($script:calls -eq 1) { '2' } else { '' } } Read-AZSCWizardChecklist -Title 'x' -Items @('A', 'B', 'C') } $result | Should -Be @('A', 'C') } It 'returns null when the operator quits' { $result = & $script:Module { Mock -CommandName Read-Host -MockWith { 'q' } Read-AZSCWizardChecklist -Title 'x' -Items @('A', 'B') } $result | Should -BeNullOrEmpty } It 'preserves the original item order regardless of toggle order' { $result = & $script:Module { $script:calls = 0 Mock -CommandName Read-Host -MockWith { $script:calls++ if ($script:calls -eq 1) { 'n' } elseif ($script:calls -eq 2) { '3,1' } else { '' } } Read-AZSCWizardChecklist -Title 'x' -Items @('A', 'B', 'C') } $result | Should -Be @('A', 'C') } } Describe 'Wizard — equivalent command rendering' { It 'renders the answers as a runnable Invoke-AzureScout line' { $line = & $script:Module { Format-AZSCWizardCommand -Answers @{ TenantID = '00000000-0000-0000-0000-000000000000' Assessment = @('LandingZone') OutputFormat = @('Html') } } $line | Should -BeLike 'Invoke-AzureScout *' $line | Should -BeLike '*-Assessment LandingZone*' $line | Should -BeLike '*-OutputFormat Html*' $line | Should -BeLike '*-TenantID 00000000-0000-0000-0000-000000000000*' } It 'quotes values containing spaces' { $line = & $script:Module { Format-AZSCWizardCommand -Answers @{ ReportDir = 'C:\My Reports\Scout' } } $line | Should -BeLike "*-ReportDir 'C:\My Reports\Scout'*" } It 'renders switches as bare flags' { $line = & $script:Module { Format-AZSCWizardCommand -Answers @{ IncludeTags = [switch]$true } } $line | Should -Be 'Invoke-AzureScout -IncludeTags' } } |