private/Initialize-DeployOSDCloud.Tests.ps1

BeforeAll {
    . "$PSScriptRoot\Initialize-DeployOSDCloud.ps1"
    . "$PSScriptRoot\Update-OSDCloudCoreCatalogOS.ps1"
    . "$PSScriptRoot\..\public\WinOS\Deploy-OSDCloud.ps1"
    . "$PSScriptRoot\core-license\Initialize-OSDCoreLicense.ps1"
    . "$PSScriptRoot\core-license\Get-OSDCoreLicense.ps1"
    . "$PSScriptRoot\core-license\Test-OSDCoreLicense.ps1"
    . "$PSScriptRoot\core-device\Initialize-OSDCoreDevice.ps1"
    . "$PSScriptRoot\workflow\Invoke-OSDCloudWorkflowTask.ps1"
    . "$PSScriptRoot\workflow\Invoke-OSDCloudWorkflowUI.ps1"
    function Get-OSDCloudWorkflowRuntimeParameter {
        [CmdletBinding()]
        param ($ModuleBase, $WorkflowName, $ProcessorArchitecture)
        return [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
    }
    . "$PSScriptRoot\disk\Get-OSDCloudDiskParameters.ps1"
    . "$PSScriptRoot\disk\Test-OSDCloudDiskLayout.ps1"
    . "$PSScriptRoot\core-cache\Initialize-OSDCoreCache.ps1"
    . "$PSScriptRoot\core-driverpack\Initialize-ModuleCoreDriverPacks.ps1"
    . "$PSScriptRoot\core-driverpack\Get-OSDCoreModelDriversCacheObject.ps1"
    . "$PSScriptRoot\core-driverpack\Get-OSDCoreDriverPackCacheObject.ps1"
    . "$PSScriptRoot\core-driverpack\Test-OSDCoreDriverPackCloudObject.ps1"
    . "$PSScriptRoot\core-operatingsystem\Get-OSDCoreOperatingSystems.ps1"
    . "$PSScriptRoot\core-operatingsystem\Get-OSDCoreOperatingSystemCacheObject.ps1"
    . "$PSScriptRoot\core-operatingsystem\Test-OperatingSystemCloudObject.ps1"
    . "$PSScriptRoot\workflow\Initialize-OSDCloudWorkflowSettingsOS.ps1"
    . "$PSScriptRoot\workflow\Resolve-OSDCloudWorkflowOSActivation.ps1"
    . "$PSScriptRoot\workflow\Initialize-OSDCloudWorkflowTasks.ps1"
    $stateNames = @(
        'OSDCoreDevice', 'OSDCloudDeploy', 'OSDCloudEnv', 'OSDCoreDriverPacks',
        'ModuleCoreDriverPacks', 'OSDCoreOperatingSystems', 'OSDCloudWorkflowSettingsOS',
        'OSDCloudWorkflowTasks', 'OSDCoreLicense'
    )
    $originalState = @{}
    foreach ($name in $stateNames) {
        $originalState[$name] = Get-Variable -Name $name -Scope Global -ValueOnly -ErrorAction SilentlyContinue
    }
}

AfterAll {
    foreach ($name in $stateNames) {
        Set-Variable -Name $name -Value $originalState[$name] -Scope Global
    }
}

Describe 'Initialize-DeployOSDCloud ModelDrivers defaults' {
    BeforeEach {
        $entry = [pscustomobject]@{ Type = 'ModelDrivers'; FullName = 'E:\OSDCloud\modeldrivers-amd64\HP_8CD1_Any model_28000.1' }
        $global:OSDCoreDevice = [pscustomobject]@{
            OSDManufacturer = 'HP'
            OSDProduct = '8CD1'
            OSDModel = 'A different device model'
            ProcessorArchitecture = 'amd64'
            LocalDisk = @([pscustomobject]@{ Number = 0; FriendlyName = 'Test disk'; Size = 100GB })
        }
        $global:OSDCloudEnv = $null
        $global:OSDCoreLicense = [pscustomobject]@{ IsRegistered = $true }
        $script:licenseResult = [pscustomobject]@{ IsValid = $true }
        Mock Get-OSDCoreLicense {
            if ($global:OSDCoreLicense.IsIncognito -ne $true) { $script:licenseResult }
        }
        $global:ModuleCoreDriverPacks = @([pscustomobject]@{
            Name = 'OEM pack'; SystemId = '8CD1'; OSArchitecture = 'amd64'; FileName = 'pack.cab'
            Url = 'https://example.invalid/pack.cab'; HashMD5 = 'not-used-for-modeldrivers'
        })
        $global:OSDCloudWorkflowSettingsOS = [pscustomobject]@{
            OperatingSystem = [pscustomobject]@{ default = 'Windows 11 25H2'; values = @('Windows 11 25H2') }
            OSActivation = [pscustomobject]@{ default = 'Retail'; values = @('Retail') }
            OSEdition = [pscustomobject]@{ default = 'Pro'; values = @([pscustomobject]@{ Edition = 'Pro'; EditionId = 'Professional' }) }
            OSLanguageCode = [pscustomobject]@{ default = 'en-us'; values = @('en-us') }
        }
        $global:OSDCloudWorkflowTasks = @([pscustomobject]@{ name = 'OSDCloud'; steps = @() })
        Mock Initialize-OSDCoreCache {}
        Mock Update-OSDCloudCoreCatalogOS {}
        Mock Initialize-OSDCoreLicense { $global:OSDCoreLicense = [pscustomobject]@{ IsRegistered = $true } }
        Mock Initialize-OSDCoreDevice {}
        Mock Invoke-OSDCloudWorkflowTask {}
        Mock Invoke-OSDCloudWorkflowUI {}
        Mock Initialize-ModuleCoreDriverPacks {}
        Mock Get-OSDCoreModelDriversCacheObject { $entry }
        Mock Test-OSDCoreDriverPackCloudObject { $true }
        Mock Get-OSDCoreDriverPackCacheObject {}
        Mock Get-OSDCoreOperatingSystems {
            [pscustomobject]@{
                OperatingSystem = 'Windows 11 25H2'; OSActivation = 'Retail'; OSLanguageCode = 'en-us'
                OSArchitecture = $global:OSDCoreDevice.ProcessorArchitecture; OSBuild = '26200'; OSBuildVersion = '26200.9457'
            }
        }
        Mock Initialize-OSDCloudWorkflowSettingsOS {}
        Mock Resolve-OSDCloudWorkflowOSActivation { $OSActivation }
        Mock Test-OperatingSystemCloudObject { $true }
        Mock Get-OSDCoreOperatingSystemCacheObject {}
        Mock Initialize-OSDCloudWorkflowTasks {}
        Mock Export-Clixml {}
        Mock Out-File {}
        Mock Out-Host {}
        Mock Remove-Item {}
        Mock Get-FileHash { throw 'Unexpected archive hashing' }
        Mock Write-Host {}
    }

    It 'refreshes before loading OS choices for <Architecture>' -TestCases @(
        @{ Architecture = 'amd64' }
        @{ Architecture = 'arm64' }
    ) {
        param ($Architecture)
        $script:catalogRefreshed = $false
        Mock Update-OSDCloudCoreCatalogOS {
            $script:catalogRefreshed = $true
            [pscustomobject]@{ Published = $true }
        }
        Mock Get-OSDCoreOperatingSystems {
            $script:catalogRefreshed | Should -BeTrue
            [pscustomobject]@{
                OperatingSystem = 'Windows 11 25H2'; OSActivation = 'Retail'; OSLanguageCode = 'en-us'
                OSArchitecture = $global:OSDCoreDevice.ProcessorArchitecture
                OSBuild = '26200'; OSBuildVersion = '26200.9999'
            }
        }
        $result = @(Initialize-DeployOSDCloud -ProcessorArchitecture $Architecture)
        $result | Where-Object { $_.PSObject.Properties.Name -contains 'Published' } | Should -BeNullOrEmpty
        $global:OSDCloudDeploy.OSBuildVersion | Should -Be '26200.9999'
        Should -Invoke Update-OSDCloudCoreCatalogOS -Times 1 -Exactly -ParameterFilter { $Confirm -eq $false }
    }

    It 'continues loading existing catalogs when the updater <Behavior>' -TestCases @(
        @{ Behavior = 'skips' }
        @{ Behavior = 'warns' }
        @{ Behavior = 'throws' }
    ) {
        param ($Behavior)
        Mock Update-OSDCloudCoreCatalogOS {
            if ($Behavior -eq 'warns') { Write-Warning 'Synthetic update failure' }
            if ($Behavior -eq 'throws') { throw 'Synthetic update exception' }
        }
        Initialize-DeployOSDCloud -WarningVariable warnings -WarningAction SilentlyContinue
        $global:OSDCloudDeploy.OSBuildVersion | Should -Be '26200.9457'
        @($warnings).Count | Should -Be $(if ($Behavior -eq 'skips') { 0 } else { 1 })
        Should -Invoke Get-OSDCoreOperatingSystems -Times 1 -Exactly
    }

    It 'defaults to ModelDrivers and retains OEM fallback without testing its URL or archive' {
        Initialize-DeployOSDCloud
        $global:OSDCloudDeploy.DriverPackName | Should -Be 'ModelDrivers'
        $global:OSDCloudDeploy.ModelDriversCacheObject | Should -Be $entry
        $global:OSDCloudDeploy.DriverPackCloudObject.Name | Should -Be 'OEM pack'
        $global:OSDCloudDeploy.OSBuildVersion | Should -Be '26200.9457'
        Should -Invoke Test-OSDCoreDriverPackCloudObject -Times 0 -Exactly
        Should -Invoke Get-OSDCoreDriverPackCacheObject -Times 0 -Exactly
        Should -Invoke Get-FileHash -Times 0 -Exactly
    }

    It 'preserves the default disk layout and automatic Windows sizing' {
        Initialize-DeployOSDCloud
        $global:OSDCloudDeploy.DiskLabelOS | Should -Be 'OS'
        $global:OSDCloudDeploy.DiskLabelRecovery | Should -Be 'Recovery'
        $global:OSDCloudDeploy.DiskSizeSystem | Should -Be 500MB
        $global:OSDCloudDeploy.DiskSizeRecovery | Should -Be 2000MB
        $global:OSDCloudDeploy.DiskSizeOS | Should -BeNullOrEmpty
    }

    It 'retains all disk overrides without losing disk number zero' {
        Initialize-DeployOSDCloud -DiskNumber 0 -DiskLabelOS Windows -DiskLabelRecovery WinRE `
            -DiskSizeSystem 1GB -DiskSizeOS 32GB -DiskSizeRecovery 3GB -WarningVariable warnings
        $global:OSDCloudDeploy.DeploymentDiskNumber | Should -Be 0
        $global:OSDCloudDeploy.DiskLabelOS | Should -Be 'Windows'
        $global:OSDCloudDeploy.DiskLabelRecovery | Should -Be 'WinRE'
        $global:OSDCloudDeploy.DiskSizeSystem | Should -Be 1GB
        $global:OSDCloudDeploy.DiskSizeOS | Should -Be 32GB
        $global:OSDCloudDeploy.DiskSizeRecovery | Should -Be 3GB
        @($warnings).Count | Should -Be 0
        Should -Invoke Initialize-OSDCoreLicense -Times 0 -Exactly
    }

    It 'rejects an oversized OS partition during initialization' {
        { Initialize-DeployOSDCloud -DiskSizeOS 100GB } | Should -Throw '*target disk capacity*'
        Should -Invoke Initialize-OSDCloudWorkflowTasks -Times 0 -Exactly
    }

    It 'falls back for all overrides with exactly one warning for <Case>' -TestCases @(
        @{ Case = 'invalid registered license'; License = [pscustomobject]@{ IsValid = $false } }
        @{ Case = 'missing registered license'; License = $null }
        @{ Case = 'registration only'; License = [pscustomobject]@{ IsRegistered = $true } }
    ) {
        param ($License)
        $script:licenseResult = $License
        Initialize-DeployOSDCloud -DiskNumber 0 -DiskLabelOS Windows -DiskLabelRecovery WinRE `
            -DiskSizeSystem 1GB -DiskSizeOS 100GB -DiskSizeRecovery 3GB `
            -WarningVariable warnings -WarningAction SilentlyContinue
        $global:OSDCloudDeploy.DiskLabelOS | Should -Be 'OS'
        $global:OSDCloudDeploy.DiskLabelRecovery | Should -Be 'Recovery'
        $global:OSDCloudDeploy.DiskSizeSystem | Should -Be 500MB
        $global:OSDCloudDeploy.DiskSizeRecovery | Should -Be 2000MB
        $global:OSDCloudDeploy.DiskSizeOS | Should -BeNullOrEmpty
        $global:OSDCloudDeploy.DeploymentDiskNumber | Should -Be 0
        $diskParameters = Get-OSDCloudDiskParameters -Settings $global:OSDCloudDeploy
        $diskParameters.ContainsKey('SizeWindows') | Should -BeFalse
        @($warnings).Count | Should -Be 1
        "$warnings" | Should -Match 'valid Recast license.*default partition labels and sizes'
        Should -Invoke Initialize-OSDCoreLicense -Times 0 -Exactly
    }

    It 'warns once for each explicitly supplied unlicensed customization: <Name>' -TestCases @(
        @{ Name = 'DiskLabelOS'; Value = 'Windows' }
        @{ Name = 'DiskLabelRecovery'; Value = 'WinRE' }
        @{ Name = 'DiskSizeSystem'; Value = 1GB }
        @{ Name = 'DiskSizeOS'; Value = 100GB }
        @{ Name = 'DiskSizeRecovery'; Value = 3GB }
        @{ Name = 'DiskLabelOS'; Value = 'OS' }
        @{ Name = 'DiskSizeSystem'; Value = 500MB }
    ) {
        param ($Name, $Value)
        $script:licenseResult = $null
        $parameters = @{ $Name = $Value }
        Initialize-DeployOSDCloud @parameters -WarningVariable warnings -WarningAction SilentlyContinue
        @($warnings).Count | Should -Be 1
        "$warnings" | Should -Match $Name
        $global:OSDCloudDeploy.DiskLabelOS | Should -Be 'OS'
        $global:OSDCloudDeploy.DiskLabelRecovery | Should -Be 'Recovery'
        $global:OSDCloudDeploy.DiskSizeSystem | Should -Be 500MB
        $global:OSDCloudDeploy.DiskSizeRecovery | Should -Be 2000MB
        $global:OSDCloudDeploy.DiskSizeOS | Should -BeNullOrEmpty
    }

    It 'does not warn or gate DiskNumber without customization parameters' {
        $global:OSDCoreLicense.IsRegistered = $false
        $script:licenseResult = $null
        $global:OSDCoreDevice.LocalDisk += [pscustomobject]@{ Number = 1; FriendlyName = 'Second disk'; Size = 100GB }
        Initialize-DeployOSDCloud -DiskNumber 1 -WarningVariable warnings
        @($warnings).Count | Should -Be 0
        $global:OSDCloudDeploy.DeploymentDiskNumber | Should -Be 1
        Should -Invoke Initialize-OSDCoreLicense -Times 0 -Exactly
    }

    It 'initializes missing license state and checks validity: valid=<Valid>' -TestCases @(
        @{ Valid = $true }
        @{ Valid = $false }
    ) {
        param ($Valid)
        $global:OSDCoreLicense = $null
        $script:licenseResult = [pscustomobject]@{ IsValid = $Valid }
        Mock Initialize-OSDCoreLicense { $global:OSDCoreLicense = [pscustomobject]@{ IsRegistered = -not $Valid } }
        Initialize-DeployOSDCloud -DiskSizeOS 32GB -WarningVariable warnings -WarningAction SilentlyContinue
        Should -Invoke Initialize-OSDCoreLicense -Times 1 -Exactly
        if ($Valid) {
            $global:OSDCloudDeploy.DiskSizeOS | Should -Be 32GB
            @($warnings).Count | Should -Be 0
        }
        else {
            $global:OSDCloudDeploy.DiskSizeOS | Should -BeNullOrEmpty
            @($warnings).Count | Should -Be 1
        }
    }

    It 'allows valid licenses even when global registration is missing' {
        $global:OSDCoreLicense = [pscustomobject]@{}
        Initialize-DeployOSDCloud -DiskSizeOS 32GB -WarningVariable warnings
        $global:OSDCloudDeploy.DiskSizeOS | Should -Be 32GB
        @($warnings).Count | Should -Be 0
    }

    It 'falls back once on lookup failure without exposing exception details' {
        Mock Get-OSDCoreLicense { throw 'synthetic-private-license-data' }
        $output = @(Initialize-DeployOSDCloud -DiskSizeOS 100GB -Verbose -WarningVariable warnings 4>&1 3>&1)
        $global:OSDCloudDeploy.DiskSizeOS | Should -BeNullOrEmpty
        @($warnings).Count | Should -Be 1
        "$output" | Should -Not -Match 'synthetic-private-license-data'
        "$output" | Should -Match 'License lookup failed'
    }

    It 'does not warn on failed entitlement lookup when customization is omitted' {
        Mock Get-OSDCoreLicense { throw 'synthetic-private-license-data' }
        Initialize-DeployOSDCloud -WarningVariable warnings
        @($warnings).Count | Should -Be 0
        $global:OSDCloudDeploy.DiskSizeOS | Should -BeNullOrEmpty
    }

    It 'surfaces license initialization failure without proceeding' {
        $global:OSDCoreLicense = $null
        Mock Initialize-OSDCoreLicense { throw 'License initialization failed' }
        { Initialize-DeployOSDCloud -DiskSizeOS 32GB } | Should -Throw '*License initialization failed*'
        Should -Invoke Initialize-OSDCoreCache -Times 0 -Exactly
    }

    It 'preserves binding validation for unregistered invalid input' {
        $global:OSDCoreLicense.IsRegistered = $false
        { Initialize-DeployOSDCloud -DiskSizeOS 0 } | Should -Throw
        { Initialize-DeployOSDCloud -DiskLabelOS "bad`nlabel" } | Should -Throw
        Should -Invoke Initialize-OSDCoreCache -Times 0 -Exactly
    }

    It 'uses the real shared gate for public deployment: CLI=<CLI>, valid=<Valid>' -TestCases @(
        @{ CLI = $true; Valid = $true }
        @{ CLI = $false; Valid = $true }
        @{ CLI = $true; Valid = $false }
        @{ CLI = $false; Valid = $false }
    ) {
        param ($CLI, $Valid)
        $script:licenseResult = [pscustomobject]@{ IsValid = $Valid }
        Mock Initialize-OSDCoreLicense { $global:OSDCoreLicense = [pscustomobject]@{ IsRegistered = -not $Valid } }
        Deploy-OSDCloud -CLI:$CLI -DiskNumber 0 -DiskLabelOS Windows -DiskLabelRecovery WinRE `
            -DiskSizeSystem 1GB -DiskSizeOS 32GB -DiskSizeRecovery 3GB `
            -WarningVariable warnings -WarningAction SilentlyContinue
        Should -Invoke Initialize-OSDCoreLicense -Times 1 -Exactly
        $global:OSDCloudDeploy.DeploymentDiskNumber | Should -Be 0
        if ($Valid) {
            $global:OSDCloudDeploy.DiskLabelOS | Should -Be 'Windows'
            $global:OSDCloudDeploy.DiskLabelRecovery | Should -Be 'WinRE'
            $global:OSDCloudDeploy.DiskSizeSystem | Should -Be 1GB
            $global:OSDCloudDeploy.DiskSizeOS | Should -Be 32GB
            $global:OSDCloudDeploy.DiskSizeRecovery | Should -Be 3GB
            @($warnings).Count | Should -Be 0
        }
        else {
            $global:OSDCloudDeploy.DiskLabelOS | Should -Be 'OS'
            $global:OSDCloudDeploy.DiskLabelRecovery | Should -Be 'Recovery'
            $global:OSDCloudDeploy.DiskSizeSystem | Should -Be 500MB
            $global:OSDCloudDeploy.DiskSizeOS | Should -BeNullOrEmpty
            $global:OSDCloudDeploy.DiskSizeRecovery | Should -Be 2000MB
            @($warnings).Count | Should -Be 1
        }
    }

    It 'applies unlicensed disk fallback in Incognito then restores normal licensed behavior: CLI=<CLI>' -TestCases @(
        @{ CLI = $true }
        @{ CLI = $false }
    ) {
        param ($CLI)
        Deploy-OSDCloud -CLI:$CLI -Incognito -DiskNumber 0 -DiskLabelOS Windows -DiskLabelRecovery WinRE `
            -DiskSizeSystem 1GB -DiskSizeOS 100GB -DiskSizeRecovery 3GB `
            -WarningVariable warnings -WarningAction SilentlyContinue
        $global:OSDCoreLicense.IsRegistered | Should -BeFalse
        $global:OSDCloudDeploy.DiskLabelOS | Should -Be 'OS'
        $global:OSDCloudDeploy.DiskLabelRecovery | Should -Be 'Recovery'
        $global:OSDCloudDeploy.DiskSizeSystem | Should -Be 500MB
        $global:OSDCloudDeploy.DiskSizeOS | Should -BeNullOrEmpty
        $global:OSDCloudDeploy.DiskSizeRecovery | Should -Be 2000MB
        $global:OSDCloudDeploy.DeploymentDiskNumber | Should -Be 0
        @($warnings).Count | Should -Be 1
        Should -Invoke Initialize-OSDCoreLicense -Times 1 -Exactly
        Deploy-OSDCloud -CLI:$CLI -DiskSizeOS 32GB -WarningVariable normalWarnings
        $global:OSDCoreLicense.IsIncognito | Should -BeFalse
        $global:OSDCloudDeploy.DiskSizeOS | Should -Be 32GB
        @($normalWarnings).Count | Should -Be 0
    }

    It 'defaults to ModelDrivers when the device has no OEM catalog entry' {
        $global:ModuleCoreDriverPacks = @()
        Initialize-DeployOSDCloud
        $global:OSDCloudDeploy.DriverPackName | Should -Be 'ModelDrivers'
        $global:OSDCloudDeploy.ModelDriversCacheObject | Should -Be $entry
        $global:OSDCloudDeploy.DriverPackCloudObject | Should -BeNullOrEmpty
    }

    It 'selects ModelDrivers after device overrides and with the effective architecture' {
        Initialize-DeployOSDCloud -OSDManufacturer Microsoft -OSDProduct Surface_Product -OSDModel 'Ignored override' -ProcessorArchitecture arm64
        Should -Invoke Get-OSDCoreModelDriversCacheObject -Times 1 -Exactly -ParameterFilter {
            $OSArchitecture -eq 'arm64' -and $global:OSDCoreDevice.OSDManufacturer -eq 'Microsoft' -and
            $global:OSDCoreDevice.OSDProduct -eq 'Surface_Product'
        }
        $global:OSDCloudDeploy.OSArchitecture | Should -Be 'arm64'
    }

    It 'retains the original OEM validation behavior when no safe ModelDrivers match' {
        Mock Get-OSDCoreModelDriversCacheObject {}
        Initialize-DeployOSDCloud
        $global:OSDCloudDeploy.DriverPackName | Should -Be 'OEM pack'
        $global:OSDCloudDeploy.ModelDriversCacheObject | Should -BeNullOrEmpty
        Should -Invoke Test-OSDCoreDriverPackCloudObject -Times 1 -Exactly
        Should -Invoke Get-OSDCoreDriverPackCacheObject -Times 1 -Exactly
    }
}