tests/Invoke-AzureScout.Tests.ps1
|
#Requires -Modules Pester <# .SYNOPSIS Pester tests for Invoke-AzureScout (parameter validation & scope routing). .DESCRIPTION Validates the main orchestrator function: - Parameter validation (ValidateSet for Scope, OutputFormat, AzureEnvironment) - Scope routing gates extraction phases - Parameter aliases resolve correctly - Help switch returns without executing .NOTES Author: thisismydemo Version: 1.0.0 Created: 2026-02-23 #> BeforeAll { $ModuleRoot = Split-Path -Parent $PSScriptRoot Import-Module (Join-Path $ModuleRoot 'AzureScout.psd1') -Force -ErrorAction Stop } Describe 'Invoke-AzureScout — Parameter Validation' { # ── ValidateSet Enforcement ─────────────────────────────────────── Context 'Scope ValidateSet' { It 'Accepts "All" as a valid Scope value' { $cmd = Get-Command Invoke-AzureScout $scopeAttr = $cmd.Parameters['Scope'].Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } $scopeAttr.ValidValues | Should -Contain 'All' } It 'Accepts "ArmOnly" as a valid Scope value' { $cmd = Get-Command Invoke-AzureScout $scopeAttr = $cmd.Parameters['Scope'].Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } $scopeAttr.ValidValues | Should -Contain 'ArmOnly' } It 'Accepts "EntraOnly" as a valid Scope value' { $cmd = Get-Command Invoke-AzureScout $scopeAttr = $cmd.Parameters['Scope'].Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } $scopeAttr.ValidValues | Should -Contain 'EntraOnly' } It 'Rejects an invalid Scope value' { { Invoke-AzureScout -TenantID 'test' -Scope 'InvalidScope' } | Should -Throw } } Context 'OutputFormat ValidateSet' { It 'Accepts "All" as a valid OutputFormat value' { $cmd = Get-Command Invoke-AzureScout $attr = $cmd.Parameters['OutputFormat'].Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } $attr.ValidValues | Should -Contain 'All' } It 'Accepts "Excel" as a valid OutputFormat value' { $cmd = Get-Command Invoke-AzureScout $attr = $cmd.Parameters['OutputFormat'].Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } $attr.ValidValues | Should -Contain 'Excel' } It 'Accepts "Json" as a valid OutputFormat value' { $cmd = Get-Command Invoke-AzureScout $attr = $cmd.Parameters['OutputFormat'].Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } $attr.ValidValues | Should -Contain 'Json' } It 'Accepts "PowerBI" as a valid OutputFormat value' { $cmd = Get-Command Invoke-AzureScout $attr = $cmd.Parameters['OutputFormat'].Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } $attr.ValidValues | Should -Contain 'PowerBI' } It 'Rejects an invalid OutputFormat value' { { Invoke-AzureScout -TenantID 'test' -OutputFormat 'Csv' } | Should -Throw } } Context 'AzureEnvironment ValidateSet' { It 'Accepts standard Azure environment names' { $cmd = Get-Command Invoke-AzureScout $attr = $cmd.Parameters['AzureEnvironment'].Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } $attr.ValidValues | Should -Contain 'AzureCloud' } It 'Rejects an invalid AzureEnvironment value' { { Invoke-AzureScout -TenantID 'test' -AzureEnvironment 'FakeCloud' } | Should -Throw } } # ── Parameter Aliases ───────────────────────────────────────────── Context 'Parameter Aliases' { It 'Has aliases defined for SkipAdvisory' { $cmd = Get-Command Invoke-AzureScout $aliases = $cmd.Parameters['SkipAdvisory'].Aliases $aliases | Should -Not -BeNullOrEmpty } It 'SkipAdvisory includes SkipAdvisories alias' { $cmd = Get-Command Invoke-AzureScout $aliases = $cmd.Parameters['SkipAdvisory'].Aliases $aliases | Should -Contain 'SkipAdvisories' } } # ── Required Parameters ─────────────────────────────────────────── Context 'Required Parameters' { It 'TenantID is not mandatory (can default from context)' { $cmd = Get-Command Invoke-AzureScout $isMandatory = $cmd.Parameters['TenantID'].Attributes | Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] } | ForEach-Object { $_.Mandatory } # TenantID should not be mandatory — the function can discover it via Get-AzContext $isMandatory | Should -Not -Contain $true } } # ── Switch Parameters ───────────────────────────────────────────── Context 'Switch Parameters' { It 'SkipPermissionCheck is a switch parameter' { $cmd = Get-Command Invoke-AzureScout $cmd.Parameters['SkipPermissionCheck'].SwitchParameter | Should -BeTrue } It 'DeviceLogin is a switch parameter' { $cmd = Get-Command Invoke-AzureScout $cmd.Parameters['DeviceLogin'].SwitchParameter | Should -BeTrue } It 'SecurityCenter is a switch parameter' { $cmd = Get-Command Invoke-AzureScout $cmd.Parameters['SecurityCenter'].SwitchParameter | Should -BeTrue } } # ── Key Parameters Exist ────────────────────────────────────────── Context 'Key Parameters Exist' { It 'Has a Scope parameter' { $cmd = Get-Command Invoke-AzureScout $cmd.Parameters.Keys | Should -Contain 'Scope' } It 'Has an OutputFormat parameter' { $cmd = Get-Command Invoke-AzureScout $cmd.Parameters.Keys | Should -Contain 'OutputFormat' } It 'Has SubscriptionID parameter' { $cmd = Get-Command Invoke-AzureScout $cmd.Parameters.Keys | Should -Contain 'SubscriptionID' } It 'Has ManagementGroup parameter' { $cmd = Get-Command Invoke-AzureScout $cmd.Parameters.Keys | Should -Contain 'ManagementGroup' } It 'Has ReportName parameter' { $cmd = Get-Command Invoke-AzureScout $cmd.Parameters.Keys | Should -Contain 'ReportName' } It 'Has ReportDir parameter' { $cmd = Get-Command Invoke-AzureScout $cmd.Parameters.Keys | Should -Contain 'ReportDir' } } } # =================================================================== # PowerShell edition guard (fail-fast on Windows PowerShell 5.1 Desktop) # # The test host here runs PowerShell 7 (Core), so the guard cannot be # exercised end-to-end in-process against a real Desktop session. These # tests instead pin: (1) the guard exists in source, is unconditional, # and fires before any Azure/Graph work begins, (2) it does NOT fire on # the Core edition this suite actually runs under, and (3) the manifest # backstop (PowerShellVersion = 7.0) rejects 5.1 at Import-Module time — # verified live against powershell.exe in a separate check (see repo # engineer session notes / manual verification). # =================================================================== Describe 'Invoke-AzureScout — PowerShell edition guard' { BeforeAll { $script:InvokeSource = Get-Content -Path (Join-Path -Path $ModuleRoot -ChildPath 'Modules' -AdditionalChildPath 'Public', 'PublicFunctions', 'Invoke-AzureScout.ps1') -Raw } It 'Source contains an explicit PSEdition guard' { $script:InvokeSource | Should -Match "PSVersionTable\.PSEdition\s+-ne\s+'Core'" } It 'The guard throws an actionable, specific error message' { $script:InvokeSource | Should -Match "AzureScout requires PowerShell 7\+" $script:InvokeSource | Should -Match "Run in 'pwsh'" } It 'The guard runs before Test-AZSCPS (i.e. before any inventory/audit work begins)' { $guardIndex = $script:InvokeSource.IndexOf('PSVersionTable.PSEdition') $platOsIndex = $script:InvokeSource.IndexOf('$PlatOS = Test-AZSCPS') $guardIndex | Should -BeGreaterThan -1 $platOsIndex | Should -BeGreaterThan -1 $guardIndex | Should -BeLessThan $platOsIndex } It 'The guard condition itself evaluates to $false on this (Core) test host' { # Deliberately does NOT invoke Invoke-AzureScout end-to-end: beyond the guard, # the function calls Exit (on -Help) or attempts a live Connect-AzAccount / # device-code sign-in (on any other path), either of which is unsafe inside a # Pester run. Evaluating the guard's own condition is a safe, faithful proxy — # it is the exact expression used in the source (see previous test). ($PSVersionTable.PSEdition -ne 'Core') | Should -BeFalse } } |