Tests/Unit/MSFT_xVHD.tests.ps1

#region HEADER

# Unit Test Template Version: 1.2.0
$script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or `
    (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) )
{
    & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\'))
}

Import-Module -Name (Join-Path -Path $script:moduleRoot -ChildPath (Join-Path -Path 'DSCResource.Tests' -ChildPath 'TestHelper.psm1')) -Force

$TestEnvironment = Initialize-TestEnvironment `
    -DSCModuleName 'xHyper-V' `
    -DSCResourceName 'MSFT_xVHD' `
    -TestType Unit

#endregion HEADER

function Invoke-TestSetup
{

}

function Invoke-TestCleanup
{
    Restore-TestEnvironment -TestEnvironment $TestEnvironment
}

# Begin Testing
try
{
    Invoke-TestSetup

    InModuleScope 'MSFT_xVHD' {
        Describe 'MSFT_xVHD\Get-TargetResource' {
            # Create an empty function to be able to mock the missing Hyper-V cmdlet
            function Get-VHD
            {

            }

            Context 'Should stop when Hyper-V module is missing' {
                Mock -CommandName Get-Module -ParameterFilter { ($Name -eq 'Hyper-V') -and ($ListAvailable -eq $true) } -MockWith {
                    return $false
                }

                It 'Should throw when the module is missing' {
                    { Test-TargetResource -Name 'server.vhdx' -Path 'C:\VMs' -Type 'Fixed' -MaximumSizeBytes 1GB } |
                        Should Throw 'Please ensure that Hyper-V role is installed with its PowerShell module'
                }
            }

            # Mocks "Get-Module -Name Hyper-V" so that the DSC resource thinks the Hyper-V module is on the test system
            Mock -CommandName Get-Module -ParameterFilter { ($Name -eq 'Hyper-V') -and ($ListAvailable -eq $true) } -MockWith {
                return $true
            }

            Mock -CommandName GetNameWithExtension -MockWith { 'server.vhdx' }

            Context 'VHD Present' {
                It 'Should return a hashtable with Ensure being Present' {
                    Mock -CommandName Get-VHD -MockWith {
                        [pscustomobject]@{
                            Path = 'server.vhdx'
                        }
                    }

                    $getTargetResult = Get-TargetResource -Name 'server' -Path 'c:\boguspath' -Generation 'vhdx'
                    $getTargetResult.Ensure | Should Be 'Present'
                    $getTargetResult | Should BeOfType hashtable
                }
            }

            Context 'VHD Not Present' {
                It 'Should return a hashtable with Ensure being Absent' {
                    Mock -CommandName Get-VHD

                    $getTargetResult = Get-TargetResource -Name 'server' -Path 'c:\boguspath' -Generation 'vhdx'
                    $getTargetResult.Ensure | Should Be 'Absent'
                    $getTargetResult | Should BeOfType hashtable
                }
            }
        }

        Describe 'MSFT_xVHD\GetNameWithExtension' {
            Context 'Name does not have extension' {
                It 'Should return server.vhdx with generation vhdx' {
                    GetNameWithExtension -Name 'server' -Generation 'vhdx' |
                        Should Be 'server.vhdx'
                }

                It 'Should return server.vhd with generation vhd' {
                    GetNameWithExtension -Name 'server' -Generation 'vhd' |
                        Should Be 'server.vhd'
                }

                It 'Should not throw' {
                    { GetNameWithExtension -Name 'server' -Generation 'vhd' } |
                        Should Not Throw
                }
            }

            Context 'Name has extension' {
                It 'Should return server.vhdx with Name server.vhdx and generation vhdx' {
                    GetNameWithExtension -Name 'server.vhd' -Generation 'vhd' |
                        Should Be 'server.vhd'
                }

                It 'Should throw with mismatch with extension from name and generation' {
                    { GetNameWithExtension -Name 'server.vhdx' -Generation 'vhd' } |
                        Should Throw 'the extension vhdx on the name does not match the generation vhd'
                }
            }
        }

        Describe 'MSFT_xVHD\Test-TargetResource' {
            # Create an empty function to be able to mock the missing Hyper-V cmdlet
            function Test-VHD
            {

            }

            Context 'Should stop when Hyper-V module is missing' {
                Mock -CommandName Get-Module -ParameterFilter { ($Name -eq 'Hyper-V') -and ($ListAvailable -eq $true) } -MockWith {
                    return $false
                }

                It 'Should throw when the module is missing' {
                    { Test-TargetResource -Name 'server.vhdx' -Path 'C:\VMs' -Type 'Fixed' -MaximumSizeBytes 1GB } |
                        Should Throw 'Please ensure that Hyper-V role is installed with its PowerShell module'
                }
            }

            # Mocks "Get-Module -Name Hyper-V" so that the DSC resource thinks the Hyper-V module is on the test system
            Mock -CommandName Get-Module -ParameterFilter { ($Name -eq 'Hyper-V') -and ($ListAvailable -eq $true) } -MockWith {
                return $true
            }

            Context 'Parameter validation' {
                It 'Fixed and Dynamic VHDs need MaximumSizeBytes specified' {
                    { Test-TargetResource -Name 'server' -Path 'C:\VMs' -Type 'Dynamic' } |
                        Should Throw 'Specify MaximumSizeBytes property for Fixed and Dynamic VHDs.'
                }

                It 'Parent Path is passed for a non Differencing disk' {
                    { Test-TargetResource -Name 'server' -Path 'C:\VMs' -ParentPath 'C:\VMs\Parent' -Type 'Fixed' -MaximumSizeBytes 1GB } |
                        Should Throw 'Parent path is only supported for Differencing disks'
                }

                It 'Differencing disk needs a Parent Path' {
                    { Test-TargetResource -Name 'server' -Path 'C:\VMs' -Type 'Differencing' } |
                        Should Throw 'Differencing requires a parent path'
                }
            }

            Context 'ParentPath specified' {
                It 'Should throw when ParentPath does not exist' {
                    Mock -CommandName Test-Path -MockWith { $false }

                    { Test-TargetResource -Name 'server' -Path 'C:\VMs' -Type 'Differencing' -ParentPath 'c:\boguspath' } |
                        Should Throw 'c:\boguspath does not exists'
                }

                # "Generation $Generation should match ParentPath extension $($ParentPath.Split('.')[-1])"
                It 'Should throw when file extension and generation have a mismatch' {
                    Mock -CommandName Test-Path -MockWith { $true }

                    { Test-TargetResource -Name 'server' -Path 'C:\VMs' -Type 'Differencing' -ParentPath 'c:\boguspath.vhd' -Generation 'Vhdx' } |
                        Should Throw 'Generation Vhdx should match ParentPath extension vhd'
                }
            }

            Context 'Path does not exist' {
                It 'Should throw when the path does not exist' {
                    Mock -CommandName Test-Path -MockWith { $false }

                    { Test-TargetResource -Name 'server.vhdx' -Path 'C:\VMs' -Type 'Fixed' -MaximumSizeBytes 1GB } |
                        Should Throw 'C:\VMs does not exists'
                }
            }

            Context 'Vhd exists' {
                BeforeEach {
                    Mock -CommandName Test-Path -MockWith { $true }
                    Mock -CommandName GetNameWithExtension -MockWith { 'server.vhdx' }
                    Mock -CommandName Test-VHD -MockWith { $true }
                }

                It 'Should not throw' {
                    { Test-TargetResource -Name 'server.vhdx' -Path 'C:\VMs' -Type 'Fixed' -MaximumSizeBytes 1GB } |
                        Should not Throw
                }

                It 'Should return a boolean and it should be true' {
                    $testResult = Test-TargetResource -Name 'server.vhdx' -Path 'C:\VMs' -Type 'Fixed' -MaximumSizeBytes 1GB
                    $testResult | Should BeOfType bool
                    $testResult -eq $true | Should Be $true
                }
            }

            Context 'Vhd does not exist' {
                BeforeEach {
                    Mock -CommandName Test-Path -MockWith { $true }
                    Mock -CommandName GetNameWithExtension -MockWith { 'server.vhdx' }
                    Mock -CommandName Test-VHD -MockWith { $false }
                }

                It 'Should not throw' {
                    { Test-TargetResource -Name 'server.vhdx' -Path 'C:\VMs' -Type 'Fixed' -MaximumSizeBytes 1GB } |
                        Should not Throw
                }

                It 'Should return a boolean and it should be false' {
                    $testResult = Test-TargetResource -Name 'server.vhdx' -Path 'C:\VMs' -Type 'Fixed' -MaximumSizeBytes 1GB
                    $testResult | Should BeOfType bool
                    $testResult -eq $true | Should Be $false
                }
            }
        }

        Describe 'MSFT_xVHD\Set-TargetResource' {
            # Create an empty function to be able to mock the missing Hyper-V cmdlet
            function Get-VHD
            {

            }

            function Set-VHD
            {

            }

            function Resize-VHD
            {

            }

            function New-VHD
            {

            }

            Context 'Ensure is Absent' {
                Mock -CommandName Test-Path -MockWith { $true }
                Mock -CommandName Remove-Item
                Mock -CommandName GetNameWithExtension -MockWith { 'server.vhdx' }

                It 'Should remove when Ensure is Absent and vhdx exists' {
                    $null = Set-TargetResource -Name 'server.vhdx' -Path 'TestDrive:\' -Ensure 'Absent'
                    Assert-MockCalled -CommandName Remove-Item -Times 1 -Exactly
                }
            }

            Context 'Ensure is Present' {
                BeforeEach {
                    Mock -CommandName Get-VHD -MockWith {
                        [pscustomobject]@{
                            Path = 'server.vhdx'
                            ParentPath = 'c:\boguspath\server.vhdx'
                            Size = 1073741824
                            Type = 'Differencing'
                        }
                    }

                    Mock -CommandName Set-VHD
                    Mock -CommandName Resize-VHD
                    Mock -CommandName GetNameWithExtension -MockWith { 'server.vhdx' }
                    Mock -CommandName New-VHD -MockWith { }
                }

                It 'Should Create a VHD when Ensure is present and no VHD exists yet for non Differencing disk' {
                    Mock -CommandName Get-VHD -MockWith { throw }

                    $null = Set-TargetResource -Name 'server.vhdx' -Path 'TestDrive:\' -Ensure 'Present'
                    Assert-MockCalled -CommandName New-VHD -Exactly -Times 1 -Scope It
                }

                It 'Should Create a VHD when Ensure is present and no VHD exists yet for Differencing disk' {
                    Mock -CommandName Get-VHD -MockWith { throw }

                    $null = Set-TargetResource -Name 'server.vhdx' -Path 'TestDrive:\' -Ensure 'Present' -ParentPath 'c:\boguspath\server.vhdx' -Type 'Differencing'
                    Assert-MockCalled -CommandName New-VHD -Exactly -Times 1 -Scope It
                }

                It 'Should resize a VHD which has a different size as intended' {
                    $null = Set-TargetResource -Name 'server.vhdx' -Path 'TestDrive:\' -MaximumSizeBytes 2GB -Ensure 'Present'
                    Assert-MockCalled -CommandName Resize-VHD -Exactly -Times 1 -Scope It
                }

                It 'Should update the parentpath if it is different from intent' {
                    $null = Set-TargetResource -Name 'server.vhdx' -Path 'TestDrive:\' -ParentPath 'c:\boguspath2\server.vhdx' -Ensure 'Present'
                    Assert-MockCalled -CommandName Set-VHD -Exactly -Times 1 -Scope It
                }
            }
        }
    }
}
finally
{
    Invoke-TestCleanup
}