tests/GraphExtraction.StrictMode.Tests.ps1

#Requires -Version 7.0
#Requires -Modules Pester

<#
    AB#5547 — regression test for the unset $MGContainerExtension crash.

    Start-AZSCGraphExtraction consumed $MGContainerExtension unconditionally in the
    resourcecontainers query but only assigned it inside the -ManagementGroup branch, so an
    inventory run WITHOUT -ManagementGroup threw "The variable '$MGContainerExtension' cannot be
    retrieved because it has not been set" as soon as the caller had StrictMode on.

    This was found by a live tenant run, NOT by the suite: all 1688 tests passed while the bug was
    present, because nothing invoked the extractor from a StrictMode caller. That is the gap this
    file closes — it drives the real function under Set-StrictMode -Version Latest with the Azure
    calls stubbed out, so the variable-initialisation contract is enforced offline from now on.

    No Azure connection.
#>


BeforeAll {
    $root = Split-Path $PSScriptRoot -Parent
    . "$root/Modules/Private/Extraction/Start-AZTIGraphExtraction.ps1"

    # Stub the two Azure-touching dependencies. Each records the queries it was handed so the
    # tests can assert on what the extractor built.
    function Invoke-AZSCInventoryLoop {
        param($GraphQuery, $FSubscri, $LoopName)
        $script:capturedQueries += [pscustomobject]@{ LoopName = $LoopName; Query = $GraphQuery }
        return @()
    }
    function Get-AZSCManagementGroups {
        param($ManagementGroup, $Subscriptions)
        return $Subscriptions
    }

    $script:subs = @([pscustomobject]@{ id = 'sub1'; name = 'Sub One' })

    # The function's switch-ish parameters are declared untyped (legacy v1 signature) and are
    # tested with `.IsPresent`, so they only work when the caller passes a real SwitchParameter.
    # Invoke-AzureScout always does, via Start-AZSCExtractionOrchestration — mirror that contract
    # here rather than relying on the $null default, which is a separate latent fragility in this
    # file and out of scope for AB#5547.
    $script:switchArgs = @{
        IncludeTags    = [switch]$false
        SkipAdvisory   = [switch]$true
        SecurityCenter = [switch]$false
    }
}

Describe 'Start-AZSCGraphExtraction under StrictMode (AB#5547)' {

    BeforeEach { $script:capturedQueries = @() }

    It 'does not throw on an unset variable when no -ManagementGroup is supplied' {
        Set-StrictMode -Version Latest
        {
            Start-AZSCGraphExtraction -Subscriptions $script:subs -AzureEnvironment 'AzureCloud' `
                @script:switchArgs 3>$null 4>$null 6>$null | Out-Null
        } | Should -Not -Throw
    }

    It 'builds the resourcecontainers query with an empty management-group extension' {
        Set-StrictMode -Version Latest
        Start-AZSCGraphExtraction -Subscriptions $script:subs -AzureEnvironment 'AzureCloud' `
            @script:switchArgs 3>$null 4>$null 6>$null | Out-Null

        $containerQuery = ($script:capturedQueries |
                Where-Object { $_.LoopName -eq 'Subscriptions and Resource Groups' }).Query
        $containerQuery | Should -Not -BeNullOrEmpty
        $containerQuery | Should -Match 'resourcecontainers'
        # The management-group join must be absent entirely, not rendered as a literal null.
        $containerQuery | Should -Not -Match 'managementGroupAncestorsChain'
    }

    It 'still injects the management-group extension when -ManagementGroup IS supplied' {
        Set-StrictMode -Version Latest
        Start-AZSCGraphExtraction -Subscriptions $script:subs -AzureEnvironment 'AzureCloud' `
            -ManagementGroup 'mg-root' @script:switchArgs 3>$null 4>$null 6>$null | Out-Null

        $containerQuery = ($script:capturedQueries |
                Where-Object { $_.LoopName -eq 'Subscriptions and Resource Groups' }).Query
        $containerQuery | Should -Match 'managementGroupAncestorsChain'
        $containerQuery | Should -Match 'mg-root'
    }

    It 'returns the documented shape rather than a bare collection' {
        Set-StrictMode -Version Latest
        $result = Start-AZSCGraphExtraction -Subscriptions $script:subs -AzureEnvironment 'AzureCloud' `
            @script:switchArgs 3>$null 4>$null 6>$null

        foreach ($field in 'Resources', 'ResourceContainers', 'Advisories', 'Security', 'Retirements') {
            $result.PSObject.Properties.Name | Should -Contain $field
        }
    }
}