Tests/Src/LabVMDiskFile.Tests.ps1

#requires -RunAsAdministrator
#requires -Version 4

$moduleName = 'Lability';
$repoRoot = (Resolve-Path "$PSScriptRoot\..\..").Path;
Import-Module (Join-Path -Path $RepoRoot -ChildPath "$moduleName.psm1") -Force;

Describe 'Src\LabVMDiskFile' {

    InModuleScope $moduleName {

        Context 'Validates "SetLabVMDiskModule" method' {

            $testModules = @(
                @{ Name = 'PowerShellModule'; }
                @{ Name = 'DscResourceModule'; }
            )

            It 'Calls "InvokeModuleCacheDownload" with "Force" when specified' {
                Mock InvokeModuleCacheDownload -MockWith { }
                Mock ExpandModuleCache -MockWith { }

                SetLabVMDiskModule -Module $testModules -DestinationPath ".\" -Force

                Assert-MockCalled InvokeModuleCacheDownload -ParameterFilter { $Force -eq $true } -Scope It;

            }

            It 'Calls "ExpandModuleCache" with "Clean" when specified' {
                Mock InvokeModuleCacheDownload -MockWith { }
                Mock ExpandModuleCache -MockWith { }

                SetLabVMDiskModule -Module $testModules -DestinationPath ".\" -Clean

                Assert-MockCalled ExpandModuleCache -ParameterFilter { $Clean -eq $true } -Scope It;

            }

        } #end context Validates "SetLabVMDiskModule" method

        Context 'Validates "SetLabVMDiskFileResource" method' {

            It 'Calls "ExpandLabResource" using "ResourceShare" path' {

                $testNode = 'TestNode';
                $testConfigurationData = @{}
                $testDriveLetter = 'Z';
                $testResourceShare = 'TestResourceShare';
                $testHostConfiguration = [PSCustomObject] @{
                    ResourceShareName = $testResourceShare;
                }
                Mock GetConfigurationData -MockWith { return $testHostConfiguration; }
                Mock ExpandLabResource -MockWith { }

                $testParams = @{
                    ConfigurationData = $testConfigurationData;
                    NodeName = $testNode;
                    VhdDriveLetter = $testDriveLetter;
                }
                SetLabVMDiskFileResource @testParams;

                $expectedDestinationPath = '{0}:\{1}' -f $testDriveLetter, $testResourceShare;
                Assert-MockCalled ExpandLabResource -ParameterFilter { $DestinationPath -eq $expectedDestinationPath } -Scope It
            }

        } #end context Validates "SetLabVMDiskFileResource" method

        Context 'Validates "SetLabVMDiskFileModule" method' {

            $testNode = 'TestNode';
            $testConfigurationData = @{}
            $testDriveLetter = $env:SystemDrive.Trim(':');

            $testModules = @(
                @{ Name = 'PowerShellModule'; }
                @{ Name = 'DscResourceModule'; }
            )

            It 'Calls "ResolveLabModule" to query DSC resource modules' {
                Mock ResolveLabModule -MockWith { return $testModules; }
                Mock SetLabVMDiskModule -MockWith { }

                $testParams = @{
                    ConfigurationData = $testConfigurationData;
                    NodeName = $testNode;
                    VhdDriveLetter = $testDriveLetter;
                }
                SetLabVMDiskFileModule @testParams;

                Assert-MockCalled ResolveLabModule -ParameterFilter { $ModuleType -eq 'DscResource'; } -Scope It;
            }

            It 'Calls "ResolveLabModule" to query PowerShell modules' {
                Mock ResolveLabModule -MockWith { return $testModules; }
                Mock SetLabVMDiskModule -MockWith { }

                $testParams = @{
                    ConfigurationData = $testConfigurationData;
                    NodeName = $testNode;
                    VhdDriveLetter = $testDriveLetter;
                }
                SetLabVMDiskFileModule @testParams;

                Assert-MockCalled ResolveLabModule -ParameterFilter { $ModuleType -eq 'Module'; } -Scope It;
            }

            It 'Calls "SetLabVMDiskModule" to expand modules' {
                Mock ResolveLabModule -MockWith { return $testModules; }
                Mock SetLabVMDiskModule -MockWith { }

                $testParams = @{
                    ConfigurationData = $testConfigurationData;
                    NodeName = $testNode;
                    VhdDriveLetter = $testDriveLetter;
                }
                SetLabVMDiskFileModule @testParams;

                Assert-MockCalled SetLabVMDiskModule -Scope It -Exactly 2;
            }

        } #end context Validates "SetLabVMDiskFileModule" method

        Context 'Validates "SetLabVMDiskFileUnattendXml" method' {

            $testNode = 'TestNode';
            $testConfigurationData = @{
                AllNodes = @(
                    @{
                        NodeName = $testNode;
                        CustomData = @{
                            ProductKey = 'ABCDE-12345-EDCBA-54321-ABCDE';
                        }
                    }
                )
            }
            $testDriveLetter = $env:SystemDrive.Trim(':');
            $testCredential = $testCredential = New-Object System.Management.Automation.PSCredential 'DummyUser', (ConvertTo-SecureString 'DummyPassword' -AsPlainText -Force);;

            It 'Calls "SetUnattendXml" to create "\Windows\System32\Sysprep\Unattend.xml" file' {

                $testNode = 'TestNode';
                $testConfigurationData = @{
                    AllNodes = @(
                        @{ NodeName = $testNode; }
                    )
                }
                $testDriveLetter = $env:SystemDrive.Trim(':');
                $testCredential = $testCredential = New-Object System.Management.Automation.PSCredential 'DummyUser', (ConvertTo-SecureString 'DummyPassword' -AsPlainText -Force);;

                Mock SetUnattendXml -MockWith { }

                $testParams = @{
                    ConfigurationData = $testConfigurationData;
                    NodeName = $testNode;
                    VhdDriveLetter = $testDriveLetter;
                    Credential = $testCredential;
                }
                SetLabVMDiskFileUnattendXml @testParams;

                $expectedPath = '{0}:\Windows\System32\Sysprep\Unattend.xml' -f $testDriveLetter;
                Assert-MockCalled SetUnattendXml -ParameterFilter { $Path -eq $expectedPath } -Scope It;
            }

            It 'Passes node "ProductKey" when specified (#134)' {

                $testNode = 'TestNode';
                $testProductKey = 'ABCDE-12345-EDCBA-54321-ABCDE';
                $testConfigurationData = @{
                    AllNodes = @(
                        @{
                            NodeName = $testNode;
                            CustomData = @{ ProductKey = $testProductKey; }
                        }
                    )
                }
                $testDriveLetter = $env:SystemDrive.Trim(':');
                $testCredential = $testCredential = New-Object System.Management.Automation.PSCredential 'DummyUser', (ConvertTo-SecureString 'DummyPassword' -AsPlainText -Force);;

                Mock SetUnattendXml -MockWith { }

                $testParams = @{
                    ConfigurationData = $testConfigurationData;
                    NodeName = $testNode;
                    VhdDriveLetter = $testDriveLetter;
                    Credential = $testCredential;
                }
                SetLabVMDiskFileUnattendXml @testParams;

                Assert-MockCalled SetUnattendXml -ParameterFilter { $ProductKey -eq $testProductKey } -Scope It;

            }

            It 'Passes media "ProductKey" when specified (#134)' {

                $testNode = 'TestNode';
                $testProductKey = 'ABCDE-12345-FGHIJ-67890-KLMNO';
                $testConfigurationData = @{
                    AllNodes = @(
                        @{
                            NodeName = $testNode;
                        }
                    )
                }
                $testDriveLetter = $env:SystemDrive.Trim(':');
                $testCredential = $testCredential = New-Object System.Management.Automation.PSCredential 'DummyUser', (ConvertTo-SecureString 'DummyPassword' -AsPlainText -Force);;

                Mock SetUnattendXml -MockWith { }

                $testParams = @{
                    ConfigurationData = $testConfigurationData;
                    NodeName = $testNode;
                    VhdDriveLetter = $testDriveLetter;
                    Credential = $testCredential;
                    ProductKey = $testProductKey;
                }
                SetLabVMDiskFileUnattendXml @testParams;

                Assert-MockCalled SetUnattendXml -ParameterFilter { $ProductKey -eq $testProductKey } -Scope It;

            }

            It 'Passes node "ProductKey" when node and media key specified (#134)' {

                $testNode = 'TestNode';
                $testNodeProductKey = 'ABCDE-12345-EDCBA-54321-ABCDE';
                $testProductKey = 'ABCDE-12345-FGHIJ-67890-KLMNO';
                $testConfigurationData = @{
                    AllNodes = @(
                        @{
                            NodeName = $testNode;
                            CustomData = @{ ProductKey = $testNodeProductKey; }
                        }
                    )
                }
                $testDriveLetter = $env:SystemDrive.Trim(':');
                $testCredential = $testCredential = New-Object System.Management.Automation.PSCredential 'DummyUser', (ConvertTo-SecureString 'DummyPassword' -AsPlainText -Force);;

                Mock SetUnattendXml -MockWith { }

                $testParams = @{
                    ConfigurationData = $testConfigurationData;
                    NodeName = $testNode;
                    VhdDriveLetter = $testDriveLetter;
                    Credential = $testCredential;
                    ProductKey = $testProductKey;
                }
                SetLabVMDiskFileUnattendXml @testParams;

                Assert-MockCalled SetUnattendXml -ParameterFilter { $ProductKey -eq $testNodeProductKey } -Scope It;

            }

        } #end context Validates "SetLabVMDiskFileUnattendXml" method

        Context 'Validates "SetLabVMDiskFileBootstrap" method' {

            $testDriveLetter = $env:SystemDrive.Trim(':');

            It 'Calls "SetBootStrap" to inject default bootstrap' {
                Mock SetBootStrap -MockWith { }
                Mock SetSetupCompleteCmd -MockWith { }

                $testParams = @{
                    VhdDriveLetter = $testDriveLetter;
                }
                SetLabVMDiskFileBootstrap @testParams;

                Assert-MockCalled SetBootStrap -ParameterFilter { $null -eq $CustomBootstrap } -Scope It;
            }

            It 'Calls "SetBootStrap" to inject custom bootstrap when specified' {
                Mock SetBootStrap -MockWith { }
                Mock SetSetupCompleteCmd -MockWith { }

                $testParams = @{
                    VhdDriveLetter = $testDriveLetter;
                    CustomBootStrap = 'Test Bootstrap';
                }
                SetLabVMDiskFileBootstrap @testParams;

                Assert-MockCalled SetBootStrap -ParameterFilter { $null -ne $CustomBootstrap } -Scope It;
            }

            It 'Calls "SetBootStrap" with "CoreCLR" to inject CoreCLR bootstrap when specified' {
                Mock SetBootStrap -MockWith { }
                Mock SetSetupCompleteCmd -MockWith { }

                $testParams = @{
                    VhdDriveLetter = $testDriveLetter;
                    CoreCLR = $true;
                }
                SetLabVMDiskFileBootstrap @testParams;

                Assert-MockCalled SetBootStrap -ParameterFilter { $CoreCLR -eq $true } -Scope It;
            }

            It 'Calls "SetSetupCompleteCmd" with "\Windows\Setup\Scripts" path' {
                Mock SetBootStrap -MockWith { }
                Mock SetSetupCompleteCmd -MockWith { }

                $testParams = @{
                    VhdDriveLetter = $testDriveLetter;
                }
                SetLabVMDiskFileBootstrap @testParams;

                $expectedPath = '{0}:\Windows\Setup\Scripts' -f $testDriveLetter;
                Assert-MockCalled SetSetupCompleteCmd -ParameterFilter { $Path -eq $expectedPath } -Scope It;
            }

        } #end context Validates "SetLabVMDiskFileBootstrap" method

        Context 'Validates "SetLabVMDiskFileMof" method' {

            $testNode = 'TestNode';
            $testDriveLetter = $env:SystemDrive.Trim(':');
            $testPath = $env:SystemDrive;
            $testParams = @{
                NodeName = $testNode;
                VhdDriveLetter = $testDriveLetter;
                Path = $testPath;
            }

            It 'Warns if .mof file cannot be found' {
                Mock Copy-Item -MockWith { }
                Mock Test-Path -MockWith { return $false; }

                { SetLabVMDiskFileMof @testParams -WarningAction Stop 3>&1 } | Should Throw;

            }

            It 'Copies .mof file if found' {
                $testMofPath = Join-Path -Path $testPath -ChildPath "$testNode.mof";
                Mock Copy-Item -MockWith { }
                Mock Test-Path -ParameterFilter { $Path -eq $testMofPath } -MockWith { return $true; }

                SetLabVMDiskFileMof @testParams;

                Assert-MockCalled Copy-Item -ParameterFilter { $Path -eq $testMofPath } -Scope It;
            }

            It 'Copies .meta.mof file if found' {
                $testMofPath = Join-Path -Path $testPath -ChildPath "$testNode.meta.mof";
                Mock Copy-Item -MockWith { }
                Mock Test-Path -ParameterFilter { $Path -eq $testMofPath } -MockWith { return $true; }

                SetLabVMDiskFileMof @testParams 3>&1;

                Assert-MockCalled Copy-Item -ParameterFilter { $Path -eq $testMofPath } -Scope It;
            }

        } #end context Validates "SetLabVMDiskFileMof" method

        Context 'Validates "SetLabVMDiskFileCertificate" method' {

            $testNode = 'TestNode';
            $testConfigurationData = @{ AllNodes = @( @{ NodeName = $testNode; } ) }
            $testDriveLetter = $env:SystemDrive.Trim(':');

            Mock Copy-Item -MockWith { }

            It 'Copies default client certificate' {
                $testParams = @{
                    NodeName = $testNode;
                    ConfigurationData = $testConfigurationData;
                    VhdDriveLetter = $testDriveLetter;
                }
                SetLabVMDiskFileCertificate @testParams;

                Assert-MockCalled Copy-Item -ParameterFilter { $Path.EndsWith('\Lability\Certificates\LabClient.pfx') } -Scope It;
            }

            It 'Copies default root certificate' {
                $testParams = @{
                    NodeName = $testNode;
                    ConfigurationData = $testConfigurationData;
                    VhdDriveLetter = $testDriveLetter;
                }
                SetLabVMDiskFileCertificate @testParams;

                Assert-MockCalled Copy-Item -ParameterFilter { $Path.EndsWith('\Lability\Certificates\LabRoot.cer') } -Scope It;
            }

            It 'Copies custom client certificate' {
                $testCertificate = '{0}:\TestClientCertificate.pfx' -f $testDriveLetter;
                $testParams = @{
                    NodeName = $testNode;
                    ConfigurationData = @{ AllNodes = @( @{ NodeName = $testNode; Lability_ClientCertificatePath = $testCertificate; } ) };
                    VhdDriveLetter = $testDriveLetter;
                }
                SetLabVMDiskFileCertificate @testParams;

                Assert-MockCalled Copy-Item -ParameterFilter { $Path.EndsWith($testCertificate) } -Scope It;
            }

            It 'Copies custom root certificate' {
                $testCertificate = '{0}:\TestRootCertificate.cer' -f $testDriveLetter;
                $testParams = @{
                    NodeName = $testNode;
                    ConfigurationData = @{ AllNodes = @( @{ NodeName = $testNode; Lability_RootCertificatePath = $testCertificate; } ) };
                    VhdDriveLetter = $testDriveLetter;
                }
                SetLabVMDiskFileCertificate @testParams;

                Assert-MockCalled Copy-Item -ParameterFilter { $Path.EndsWith($testCertificate) } -Scope It;
            }

        } #end context Validates "SetLabVMDiskFileCertificate" method

        Context 'Validates "SetLabVMDiskFile" method' {

            $testNode = 'TestNode';
            $testConfigurationData = @{ AllNodes = @( @{ NodeName = $testNode; } ) }
            $testDiskNumber = 42;
            $testDriveLetter = $env:SystemDrive.Trim(':');

            $testCredential = [System.Management.Automation.PSCredential]::Empty;


            $testVhdPath = 'TestDrive:\{0}.vhdx' -f $testNode;

            Mock ResolveLabVMDiskPath -MockWith { return $testVhdPath; }
            Mock Stop-Service -MockWith { }
            Mock Mount-Vhd -MockWith { return [PSCustomObject] @{ DiskNumber = $testDiskNumber; } }
            Mock Get-Partition -MockWith { return [PSCustomObject] @{ DriveLetter = $testDriveLetter; } }
            Mock Start-Service -MockWith { }
            Mock SetLabVMDiskFileResource -MockWith { }
            Mock SetLabVMDiskFileBootstrap -MockWith { }
            Mock SetLabVMDiskFileUnattendXml -MockWith { }
            Mock SetLabVMDiskFileMof -MockWith { }
            Mock SetLabVMDiskFileCertificate -MockWith { }
            Mock SetLabVMDiskFileModule -MockWith { }
            Mock Dismount-Vhd -MockWith { }

            $testParams = @{
                NodeName = $testNode;
                ConfigurationData = $testConfigurationData;
                Credential = $testCredential;
                Path = '.\';
            }

            It 'Stops "ShellHWDetection" service' {

                SetLabVMDiskFile @testParams;

                Assert-MockCalled Stop-Service -ParameterFilter { $Name -eq 'ShellHWDetection' } -Scope It;
            }

            It 'Mounts virtual machine VHDX file' {

                SetLabVMDiskFile @testParams;

                Assert-MockCalled Mount-Vhd -ParameterFilter { $Path -eq $testVhdPath } -Scope It;
            }

            It 'Starts "ShellHWDetection" service' {

                SetLabVMDiskFile @testParams;

                Assert-MockCalled Start-Service -ParameterFilter { $Name -eq 'ShellHWDetection' } -Scope It;
            }

            It 'Calls "SetLabVMDiskFileResource" to copy node resources' {
                SetLabVMDiskFile @testParams;

                Assert-MockCalled SetLabVMDiskFileResource -ParameterFilter { $VhdDriveLetter -eq $testDriveLetter } -Scope It;
            }

            It 'Calls "SetLabVMDiskFileBootstrap" to copy Lability bootstrap' {
                SetLabVMDiskFile @testParams;

                Assert-MockCalled SetLabVMDiskFileBootstrap -ParameterFilter { $VhdDriveLetter -eq $testDriveLetter } -Scope It;
            }

            It 'Calls "SetLabVMDiskFileUnattendXml" to copy unattended installation file' {
                SetLabVMDiskFile @testParams;

                Assert-MockCalled SetLabVMDiskFileUnattendXml -ParameterFilter { $VhdDriveLetter -eq $testDriveLetter } -Scope It;
            }

            It 'Calls "SetLabVMDiskFileMof" to copy node DSC configuaration files' {
                SetLabVMDiskFile @testParams;

                Assert-MockCalled SetLabVMDiskFileMof -ParameterFilter { $VhdDriveLetter -eq $testDriveLetter } -Scope It;
            }

            It 'Calls "SetLabVMDiskFileCertificate" to copy node certificate files' {
                SetLabVMDiskFile @testParams;

                Assert-MockCalled SetLabVMDiskFileCertificate -ParameterFilter { $VhdDriveLetter -eq $testDriveLetter } -Scope It;
            }

            It 'Calls "SetLabVMDiskFileModule" to copy PowerShell/DSC resource modules' {
                SetLabVMDiskFile @testParams;

                Assert-MockCalled SetLabVMDiskFileModule -ParameterFilter { $VhdDriveLetter -eq $testDriveLetter } -Scope It;
            }

            It 'Dismounts virtual machine VHDX file' {

                SetLabVMDiskFile @testParams;

                Assert-MockCalled Dismount-Vhd -ParameterFilter { $Path -eq $testVhdPath } -Scope It;
            }

        }  #end context Validates "SetLabVMDiskFile" method

    } #end InModuleScope

} #end describe Src\LabVMDiskFile