tests/Collect.OperationalCollectorEnrichment.Tests.ps1

#Requires -Version 7.0
#Requires -Modules Pester

BeforeAll {
    $script:Root = Split-Path -Parent $PSScriptRoot
    . "$script:Root/src/collect/Get-ScoutOperationalCollectorEnrichment.ps1"
    $script:Calls = [System.Collections.Generic.List[string]]::new()
    $script:Resources = @(
        [pscustomobject]@{ id='/subscriptions/sub-1/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/vm-1'; type='microsoft.compute/virtualmachines'; name='vm-1'; subscriptionId='sub-1'; resourceGroup='rg' },
        [pscustomobject]@{ id='/subscriptions/sub-1/resourceGroups/rg/providers/Microsoft.HybridCompute/machines/arc-1'; type='microsoft.hybridcompute/machines'; name='arc-1'; subscriptionId='sub-1'; resourceGroup='rg' },
        [pscustomobject]@{ id='/subscriptions/sub-1/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/store-1'; type='microsoft.storage/storageaccounts'; name='store-1'; subscriptionId='sub-1'; resourceGroup='rg' },
        [pscustomobject]@{ id='/subscriptions/sub-1/resourceGroups/rg-rg'; type='microsoft.resources/subscriptions/resourcegroups'; name='rg-rg'; subscriptionId='sub-1'; resourceGroup='rg-rg' }
    )
    function Initialize-OperationalStubs {
        $script:Calls.Clear()
        function global:Invoke-AzRestMethod { param($Path,$Method,$Payload,$ErrorAction) $null=$Method,$Payload,$ErrorAction; $script:Calls.Add($Path); [pscustomobject]@{StatusCode=200;Content='{"value":[]}' } }
        function global:Get-AzStorageBlobServiceProperty { param($ResourceGroupName,$Name,$ErrorAction) $null=$ResourceGroupName,$ErrorAction; [pscustomobject]@{ Name=$Name; DeleteRetentionPolicy=[pscustomobject]@{Enabled=$true} } }
        function global:Get-AzStorageFileServiceProperty { param($ResourceGroupName,$Name,$ErrorAction) $null=$ResourceGroupName,$ErrorAction; [pscustomobject]@{ Name=$Name; ShareDeleteRetentionPolicy=[pscustomobject]@{Enabled=$true} } }
        function global:Search-AzGraph { param($Query,$First,$ErrorAction) $null=$Query,$First,$ErrorAction; [pscustomobject]@{subscriptionId='sub-1';mgChain=@([pscustomobject]@{displayName='Platform'},[pscustomobject]@{displayName='Root'})} }
    }
    function Clear-OperationalStubs { foreach($Name in 'Invoke-AzRestMethod','Get-AzStorageBlobServiceProperty','Get-AzStorageFileServiceProperty','Search-AzGraph'){ Remove-Item "Function:\$Name" -Force -ErrorAction SilentlyContinue } }
}
Describe 'Get-ScoutOperationalCollectorEnrichment' {
    BeforeEach { Initialize-OperationalStubs }
    AfterEach { Clear-OperationalStubs }
    It 'returns stable envelopes for all six live-access collector contracts' {
        $Rows=@(Get-ScoutOperationalCollectorEnrichment -Resources $script:Resources -Subscriptions @([pscustomobject]@{Id='sub-1';Name='Subscription One'}))
        $Rows.type | Should -Be @('AZSC/Operational/VirtualMachine','AZSC/Operational/VMOperationalData','AZSC/Operational/ArcServerOperationalData','AZSC/Operational/ARCServers','AZSC/Operational/StorageAccount','AZSC/Management/SubscriptionEnrichment')
        ($Rows | Where-Object type -eq 'AZSC/Management/SubscriptionEnrichment').properties.ManagementGroupPath | Should -Be 'Root / Platform'
        ($Rows | Where-Object type -eq 'AZSC/Management/SubscriptionEnrichment').properties.ResourceCount | Should -Be 4
    }
    It 'uses parent-scoped REST/cmdlet calls and preserves their raw payloads' {
        $Rows=@(Get-ScoutOperationalCollectorEnrichment -Resources $script:Resources -Subscriptions @())
        ($script:Calls -join "`n") | Should -Match '/virtualMachines/vm-1/providers/microsoft.insights/metrics'
        ($script:Calls -join "`n") | Should -Match '/virtualMachines/vm-1/assessPatches'
        ($script:Calls -join "`n") | Should -Match '/machines/arc-1/assessPatches'
        ($Rows | Where-Object type -eq 'AZSC/Operational/StorageAccount').properties.BlobService.Name | Should -Be 'store-1'
        ($Rows | Where-Object type -eq 'AZSC/Operational/StorageAccount').properties.FileService.Name | Should -Be 'store-1'
    }
    It 'contains a failed parent request while producing other envelopes' {
        function global:Invoke-AzRestMethod { param($Path,$Method,$Payload,$ErrorAction) $null=$Method,$Payload,$ErrorAction; if($Path -match 'vm-1/assessPatches'){throw 'patch denied'}; [pscustomobject]@{StatusCode=200;Content='{}'} }
        $Rows=@(Get-ScoutOperationalCollectorEnrichment -Resources $script:Resources -Subscriptions @() -WarningVariable Warnings)
        ($Rows | Where-Object type -eq 'AZSC/Operational/VMOperationalData').properties.PatchAssessment.__AZSCError | Should -Be 'patch denied'
        ($Rows | Where-Object type -eq 'AZSC/Operational/ARCServers') | Should -Not -BeNullOrEmpty
        ($Warnings -join "`n") | Should -Match 'VMOperationalData.PatchAssessment'
    }
    It 'returns no envelopes and does not call Azure for empty inputs' {
        $Rows=@(Get-ScoutOperationalCollectorEnrichment -Resources @() -Subscriptions @())
        $Rows | Should -BeNullOrEmpty
        $script:Calls.Count | Should -Be 0
    }
}