Testing/Unit/PowerShell/Orchestrator/Invoke-Cyber.Tests.ps1

$OrchestratorPath = '../../../../Modules/Orchestrator.psm1'
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath $OrchestratorPath) -Function 'Invoke-CyberAssessment' -Force

InModuleScope Orchestrator {
    Describe -Tag 'Orchestrator' -Name 'Invoke-Cyber' {
        BeforeAll {
            Mock -ModuleName Orchestrator Remove-Resources {}
            Mock -ModuleName Orchestrator Import-Resources {}
            Mock -ModuleName Orchestrator Invoke-Connection { @() }
            function Get-TenantDetail {throw 'this will be mocked'}
            Mock -ModuleName Orchestrator Get-TenantDetail { '{"DisplayName": "displayName"}' }
            function Invoke-ProviderList {throw 'this will be mocked'}
            Mock -ModuleName Orchestrator Invoke-ProviderList {}
            function Invoke-RunRego {throw 'this will be mocked'}
            Mock -ModuleName Orchestrator Invoke-RunRego {}

            Mock -ModuleName Orchestrator Invoke-ReportCreation {}
            Mock -ModuleName Orchestrator Merge-JsonOutput {}
            function Disconnect-CyberAssessmentTenant {throw 'this will be mocked'}
            Mock -ModuleName Orchestrator Disconnect-CyberAssessmentTenant {}

            function Get-CyberDefault {throw 'this will be mocked'}
            Mock -ModuleName Orchestrator Get-CyberDefault {"."}

            function Merge-JsonOutput {throw 'this will be mocked'}
            Mock -ModuleName Orchestrator Merge-JsonOutput {}

            function ConvertTo-ResultsCsv {throw 'this will be mocked'}
            Mock -ModuleName Orchestrator ConvertTo-ResultsCsv {}

            Mock -CommandName New-Item {}
            Mock -CommandName Copy-Item {}
        }
        Context 'When checking the conformance of commercial tenants' {
            BeforeAll {
                [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'SplatParams')]
                $SplatParams = @{
                    M365Environment = 'commercial';
                    KeepIndividualJSON = $true;
                }
            }
            It 'Do it quietly (Do not automatically show report)' {
                {Invoke-Cyber -Quiet} | Should -Not -Throw
                Should -Invoke -CommandName Invoke-ReportCreation -Exactly -Times 1 -ParameterFilter {$Quiet -eq $true}
            }
            It 'Show report' {
                {Invoke-Cyber} | Should -Not -Throw
                Should -Invoke -CommandName Invoke-ReportCreation -Exactly -Times 1 -ParameterFilter {$Quiet -eq $false}
            }
            It 'Given -ProductNames aad should not throw' {
                $SplatParams += @{
                    ProductNames = @("aad")
                }
                {Invoke-Cyber @SplatParams} | Should -Not -Throw
            }
            It 'Given -ProductNames defender should not throw' {
                $SplatParams += @{
                    ProductNames = @("defender")
                }
                {Invoke-Cyber @SplatParams} | Should -Not -Throw
            }
            It 'Given -ProductNames exo should not throw' {
                $SplatParams += @{
                    ProductNames = @("exo")
                }
                {Invoke-Cyber @SplatParams} | Should -Not -Throw
            }
            It 'Given -ProductNames powerplatform should not throw' {
                $SplatParams += @{
                    ProductNames = @("powerplatform")
                }
                {Invoke-Cyber @SplatParams} | Should -Not -Throw
            }
            It 'Given -ProductNames teams should not throw' {
                $SplatParams += @{
                    ProductNames = @("teams")
                }
                {Invoke-Cyber @SplatParams} | Should -Not -Throw
            }
            It 'Given -ProductNames * should not throw' {
                $SplatParams += @{
                    ProductNames = @("*")
                }
                {Invoke-Cyber @SplatParams} | Should -Not -Throw
            }
            It 'Given -ProductNames * and -DisconnectOnExit should not throw' {
                $SplatParams += @{
                    ProductNames = @("*")
                    DisconnectOnExit = $true
                }
                {Invoke-Cyber @SplatParams} | Should -Not -Throw
            }
            It 'Should only run each baseline once if provider names contains duplicates' {
                {Invoke-Cyber -ProductNames aad,aad} | Should -Not -Throw
                # After refactor, -ProductNames are consolidated into CyberConfig and duplicates removed
                # Validate only a single invocation and that consolidated ProductNames contains exactly one 'aad'
                Should -Invoke Invoke-ReportCreation -ParameterFilter {$CyberConfig.ProductNames -eq 'aad'}
            }
        }
        Context 'Service Principal provided'{
            It 'All items given as not null or empty'{
                $SplatParams += @{
                    AppID = "a"
                    CertificateThumbprint = "b"
                    Organization = "c"
                }
                {Invoke-Cyber @SplatParams} | Should -Not -Throw
                Should -Invoke -CommandName Invoke-Connection -Exactly -Times 1 -ParameterFilter {$CyberConfig.AppID -eq 'a'}
            }
            It 'Items given as empty string'{
                $SplatParams += @{
                    AppID = ""
                }
                {Invoke-Cyber @SplatParams} | Should -Throw
            }
            It 'Items given as null'{
                $SplatParams += @{
                    AppID = $null
                }
                {Invoke-Cyber @SplatParams} | Should -Throw
            }
        }
        Context 'When checking module version' {
            It 'Given -Version should not throw' {
                {Invoke-Cyber -Version} | Should -Not -Throw
            }
        }
        Context 'When modifying the CSV output files names' {
            It 'Given -OutCsvFileName should not throw' {
                $SplatParams += @{
                    OutCsvFileName = "a"
                }
                {Invoke-Cyber -Version} | Should -Not -Throw
            }
            It 'Given -OutActionPlanFileName should not throw' {
                $SplatParams += @{
                    OutActionPlanFileName = "a"
                }
                {Invoke-Cyber @SplatParams} | Should -Not -Throw
            }
            It 'Given both -OutCsvFileName and -OutActionPlanFileName should not throw' {
                $SplatParams += @{
                    OutCsvFileName = "a"
                    OutActionPlanFileName = "b"
                }
                {Invoke-Cyber @SplatParams} | Should -Not -Throw
            }
            It 'Given -OutCsvFileName and -OutActionPlanFileName equal should throw' {
                $SplatParams += @{
                    OutCsvFileName = "a"
                    OutActionPlanFileName = "a"
                }
                {Invoke-Cyber @SplatParams} | Should -Throw
            }
        }
    }
}
AfterAll {
    Remove-Module Orchestrator -ErrorAction SilentlyContinue
}