tests/Integration.Module.Tests.ps1

#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' }
#Requires -Modules ImportExcel

<#
.SYNOPSIS
    Pester tests for Integration inventory modules (APIM, ServiceBus).
#>


$IntegrationPath = Join-Path (Split-Path -Parent $PSScriptRoot) 'Modules' 'Public' 'InventoryModules' 'Integration'

$IntegrationModules = @(
    @{ Name = 'APIM';       File = 'APIM.ps1';       Type = 'microsoft.apimanagement/service';  Worksheet = 'APIM' }
    @{ Name = 'ServiceBUS'; File = 'ServiceBUS.ps1'; Type = 'microsoft.servicebus/namespaces';  Worksheet = 'Service BUS' }
)

BeforeAll {
    $script:ModuleRoot       = Split-Path -Parent $PSScriptRoot
    $script:IntegrationPath  = Join-Path $script:ModuleRoot 'Modules' 'Public' 'InventoryModules' 'Integration'
    $script:TempDir          = Join-Path $env:TEMP 'AZSC_IntegrationTests'
    if (Test-Path $script:TempDir) { Remove-Item $script:TempDir -Recurse -Force }
    New-Item -ItemType Directory -Path $script:TempDir -Force | Out-Null

    $script:MockResources = @()

    # --- APIM ---
    $script:MockResources += [PSCustomObject]@{
        id             = '/subscriptions/sub-00000001/resourceGroups/rg-int/providers/microsoft.apimanagement/service/apim-prod'
        NAME           = 'apim-prod'
        TYPE           = 'microsoft.apimanagement/service'
        KIND           = ''
        LOCATION       = 'eastus'
        RESOURCEGROUP  = 'rg-int'
        subscriptionId = 'sub-00000001'
        tags           = [PSCustomObject]@{ env = 'prod' }
        sku            = [PSCustomObject]@{ name = 'Premium'; capacity = 2 }
        PROPERTIES     = [PSCustomObject]@{
            gatewayUrl = 'https://apim-prod.azure-api.net'
            virtualNetworkType = 'External'
            virtualNetworkConfiguration = [PSCustomObject]@{
                subnetResourceId = '/subscriptions/sub-00000001/resourceGroups/rg-net/providers/Microsoft.Network/virtualNetworks/vnet-apim/subnets/apim-subnet'
            }
            customProperties = [PSCustomObject]@{
                'Microsoft.WindowsAzure.ApiManagement.Gateway.Protocols.Server.Http2' = 'True'
                'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30' = 'False'
                'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10' = 'False'
                'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11' = 'False'
                'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168' = 'False'
                'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30' = 'False'
                'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10' = 'False'
                'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11' = 'False'
            }
            publicIPAddresses = @('20.1.2.3')
        }
    }

    # --- ServiceBus ---
    $script:MockResources += [PSCustomObject]@{
        id             = '/subscriptions/sub-00000001/resourceGroups/rg-int/providers/microsoft.servicebus/namespaces/sb-prod'
        NAME           = 'sb-prod'
        TYPE           = 'microsoft.servicebus/namespaces'
        KIND           = ''
        LOCATION       = 'eastus'
        RESOURCEGROUP  = 'rg-int'
        subscriptionId = 'sub-00000001'
        tags           = [PSCustomObject]@{ env = 'prod' }
        SKU            = [PSCustomObject]@{ name = 'Premium'; capacity = 1 }
        PROPERTIES     = [PSCustomObject]@{
            createdAt = '2025-01-15T08:00:00Z'
            status = 'Active'
            zoneRedundant = $true
            serviceBusEndpoint = 'https://sb-prod.servicebus.windows.net:443/'
        }
    }
    # Add lowercase sku alias for APIM (it uses $1.sku.name)
    $script:MockResources[0] | Add-Member -NotePropertyName 'SKU' -NotePropertyValue $script:MockResources[0].sku -Force
}

AfterAll {
    if (Test-Path $script:TempDir) { Remove-Item $script:TempDir -Recurse -Force }
}

Describe 'Integration Module Files Exist' {
    It 'Integration module folder exists' { $script:IntegrationPath | Should -Exist }
    It '<Name> module file exists' -ForEach $IntegrationModules { Join-Path $script:IntegrationPath $File | Should -Exist }
}

Describe 'Integration Module Processing Phase — <Name>' -ForEach $IntegrationModules {
    BeforeAll {
        $script:ModFile = Join-Path $script:IntegrationPath $File
        $script:ResType = $Type
    }
    It 'Processing returns results when matching resources are present' {
        $matchedResources = $script:MockResources | Where-Object { $_.TYPE -eq $script:ResType }
        if ($matchedResources) {
            $content = Get-Content -Path $script:ModFile -Raw
            $sb = [ScriptBlock]::Create($content)
            $result = Invoke-Command -ScriptBlock $sb -ArgumentList $null, $null, $null, $script:MockResources, $null, 'Processing', $null, $null, 'Light20', $null
            $result | Should -Not -BeNullOrEmpty
        } else { Set-ItResult -Skipped -Because "No mock resource of type '$script:ResType'" }
    }
    It 'Processing does not throw when given an empty resource list' {
        $content = Get-Content -Path $script:ModFile -Raw
        $sb = [ScriptBlock]::Create($content)
        { Invoke-Command -ScriptBlock $sb -ArgumentList $null, $null, $null, @(), $null, 'Processing', $null, $null, 'Light20', $null } | Should -Not -Throw
    }
}

Describe 'Integration Module Reporting Phase — <Name>' -ForEach $IntegrationModules {
    BeforeAll {
        $script:ModFile  = Join-Path $script:IntegrationPath $File
        $script:ResType  = $Type
        $script:XlsxFile = Join-Path $script:TempDir ("Int_{0}_{1}.xlsx" -f $Name, [System.IO.Path]::GetRandomFileName())
        $matchedResources = $script:MockResources | Where-Object { $_.TYPE -eq $script:ResType }
        if ($matchedResources) {
            $content = Get-Content -Path $script:ModFile -Raw
            $sb = [ScriptBlock]::Create($content)
            $script:ProcessedData = Invoke-Command -ScriptBlock $sb -ArgumentList $null, $null, $null, $script:MockResources, $null, 'Processing', $null, $null, 'Light20', $null
        } else { $script:ProcessedData = $null }
    }
    It 'Reporting phase does not throw' {
        if ($script:ProcessedData) {
            $content = Get-Content -Path $script:ModFile -Raw
            $sb = [ScriptBlock]::Create($content)
            { Invoke-Command -ScriptBlock $sb -ArgumentList $null, $null, $null, $null, $null, 'Reporting', $script:XlsxFile, $script:ProcessedData, 'Light20', $null } | Should -Not -Throw
        } else { Set-ItResult -Skipped -Because 'No processed data' }
    }
    It 'Excel file is created' {
        if ($script:ProcessedData) { $script:XlsxFile | Should -Exist }
        else { Set-ItResult -Skipped -Because 'No processed data' }
    }
}