Tests/Unit/MSFT_xVMSwitch_BandwidthReservationMode.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_xVMSwitch' `
    -TestType Unit

#endregion HEADER

function Invoke-TestSetup
{

}

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

# Begin Testing
try
{
    Invoke-TestSetup

    InModuleScope 'MSFT_xVMSwitch' {

        <#
            Defines a variable that contains all the possible Bandwidth Reservation Modes which will be used
            for foreach loops later on
        #>

        New-Variable -Name 'BANDWIDTH_RESERVATION_MODES' -Option 'Constant' -Value @('Default', 'Weight', 'Absolute', 'None')

        # Function to create a exception object for testing output exceptions
        function Get-InvalidArgumentError
        {
            [CmdletBinding()]
            param
            (
                [Parameter(Mandatory = $true)]
                [ValidateNotNullOrEmpty()]
                [System.String]
                $ErrorId,

                [Parameter(Mandatory = $true)]
                [ValidateNotNullOrEmpty()]
                [System.String]
                $ErrorMessage
            )

            $exception = New-Object -TypeName System.ArgumentException `
                -ArgumentList $ErrorMessage
            $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidArgument
            $errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord `
                -ArgumentList $exception, $ErrorId, $errorCategory, $null
            return $errorRecord
        } # end function Get-InvalidArgumentError

        # A helper function to mock a VMSwitch
        function New-MockedVMSwitch
        {
            Param (
                [Parameter(Mandatory = $true)]
                [string]
                $Name,

                [Parameter(Mandatory = $true)]
                [ValidateSet('Default', 'Weight', 'Absolute', 'None', 'NA')]
                [string]
                $BandwidthReservationMode,

                [Parameter()]
                [bool]
                $AllowManagementOS = $false
            )

            $mockedVMSwitch = @{
                Name = $Name
                SwitchType = 'External'
                AllowManagementOS = $AllowManagementOS
                NetAdapterInterfaceDescription = 'Microsoft Network Adapter Multiplexor Driver'
            }

            if ($BandwidthReservationMode -ne 'NA')
            {
                $mockedVMSwitch['BandwidthReservationMode'] = $BandwidthReservationMode
            }

            return [PsObject]$mockedVMSwitch
        }

        Describe 'Validates Get-TargetResource Function' {
            # Create an empty function to be able to mock the missing Hyper-V cmdlet
            function Get-VMSwitch
            {

            }

            <#
                Mocks Get-VMSwitch and will return $global:mockedVMSwitch which is
                a variable that is created during most It statements to mock a VMSwitch
            #>

            Mock -CommandName Get-VMSwitch -MockWith {
                param
                (
                    [string]
                    $ErrorAction
                )

                if ($ErrorAction -eq 'Stop' -and $global:mockedVMSwitch -eq $null)
                {
                    throw [System.Management.Automation.ActionPreferenceStopException]'No switch can be found by given criteria.'
                }

                return $global:mockedVMSwitch
            }

            # Mocks Get-NetAdapter which returns a simplified network adapter
            Mock -CommandName Get-NetAdapter -MockWith {
                return [PSCustomObject]@{
                    Name = 'SomeNIC'
                    InterfaceDescription = 'Microsoft Network Adapter Multiplexor Driver'
                }
            }

            # 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
            }

            # Create all the test cases for Get-TargetResource
            $getTestCases = @()
            foreach ($brmMode in $BANDWIDTH_RESERVATION_MODES) {
                $getTestCases += @{
                    CurrentName = $brmMode + 'BRM'
                    CurrentBandwidthReservationMode = $brmMode
                }
            }

            # Test Get-TargetResource with the test cases created above
            It 'Current switch''s BandwidthReservationMode is set to <CurrentBandwidthReservationMode>' -TestCases $getTestCases {
                param
                (
                    [Parameter()]
                    [string]
                    $CurrentName,

                    [Parameter()]
                    [string]
                    $CurrentBandwidthReservationMode
                )

                # Set the mocked VMSwitch to be returned from Get-VMSwitch based on the input from $getTestCases
                $global:mockedVMSwitch = New-MockedVMSwitch -Name $CurrentName -BandwidthReservationMode $CurrentBandwidthReservationMode

                $targetResource = Get-TargetResource -Name $CurrentName -Type 'External'
                $targetResource -is [System.Collections.Hashtable] | Should Be $true
                $targetResource['BandwidthReservationMode'] | Should Be $CurrentBandwidthReservationMode

                Remove-Variable -Scope 'Global' -Name 'mockedVMSwitch' -ErrorAction 'SilentlyContinue'
            }

            <#
                Test Get-TargetResource when the VMSwitch's BandwidthReservationMode member variable is not
                set which simulates older versions of Windows that don't support it
            #>

            It 'BandwidthReservationMode is set to null' {
                # Set the mocked VMSwitch to be returned from Get-VMSwitch
                $global:mockedVMSwitch = New-MockedVMSwitch -Name 'NaBRM' -BandwidthReservationMode 'NA'

                $targetResource = Get-TargetResource -Name 'NaBRM' -Type 'External'
                $targetResource -is [System.Collections.Hashtable] | Should Be $true
                $targetResource['BandwidthReservationMode'] | Should Be "NA"

                Remove-Variable -Scope 'Global' -Name 'mockedVMSwitch' -ErrorAction 'SilentlyContinue'
            }
        }

        # Create all the test cases for Test-TargetResource and Set-TargetResource when the switch already exists
        $testSetTestCases = @()
        foreach ($currentBrmMode in $BANDWIDTH_RESERVATION_MODES)
        {
            foreach ($desiredBrmMode in $BANDWIDTH_RESERVATION_MODES)
            {
                foreach ($ensureOption in @('Present', 'Absent'))
                {
                    $case = @{
                        CurrentName = $currentBrmMode + 'BRM'
                        CurrentBandwidthReservationMode = $currentBrmMode
                        DesiredName = $desiredBrmMode + 'BRM'
                        DesiredBandwidthReservationMode = $desiredBrmMode
                        Ensure = $ensureOption
                        ExpectedResult = $ensureOption -eq 'Present' -and $currentBrmMode -eq $desiredBrmMode
                    }
                    $testSetTestCases += $case
                }
            }
        }

        # Create all the test cases for Test-TargetResource and Set-TargetResource when the switch does not exists
        foreach ($desiredBrmMode in $BANDWIDTH_RESERVATION_MODES)
        {
            foreach ($ensureOption in @('Present', 'Absent'))
            {
                $case = @{
                    CurrentName = $null
                    CurrentBandwidthReservationMode = $null
                    DesiredName = $desiredBrmMode + 'BRM'
                    DesiredBandwidthReservationMode = $desiredBrmMode
                    Ensure = $ensureOption
                    ExpectedResult = $ensureOption -eq 'Absent'
                }
                $testSetTestCases += $case
            }
        }

        Describe 'Validates Test-TargetResource Function' {
            # Create an empty function to be able to mock the missing Hyper-V cmdlet
            function Get-VMSwitch
            {

            }

            <#
                Mocks Get-VMSwitch and will return $global:mockedVMSwitch which is
                a variable that is created during most It statements to mock a VMSwitch
            #>

            Mock -CommandName Get-VMSwitch -MockWith {
                param (
                    [string]
                    $ErrorAction
                )

                if ($ErrorAction -eq 'Stop' -and $global:mockedVMSwitch -eq $null)
                {
                    throw [System.Management.Automation.ActionPreferenceStopException]'No switch can be found by given criteria.'
                }

                return $global:mockedVMSwitch
            }

            # Mocks Get-NetAdapter which returns a simplified network adapter
            Mock -CommandName Get-NetAdapter -MockWith {
                return [PSCustomObject]@{
                    Name = 'SomeNIC'
                    InterfaceDescription = 'Microsoft Network Adapter Multiplexor Driver'
                }
            }

            # 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 Get-OSVersion -MockWith {
                return [Version]::Parse('6.3.9600')
            }

            # Create all the test cases for Get-TargetResource
            $getTestCases = @()
            foreach ($brmMode in $BANDWIDTH_RESERVATION_MODES)
            {
                $getTestCases += @{
                    CurrentName = $brmMode + 'BRM'
                    CurrentBandwidthReservationMode = $brmMode
                }
            }

            # Test Test-TargetResource with the test cases created above
            It 'Current Name "<CurrentName>" | Current BandwidthReservationMode set to "<CurrentBandwidthReservationMode>" | Desired BandwidthReservationMode set to "<DesiredBandwidthReservationMode>" | Ensure "<Ensure>"' -TestCases $testSetTestCases {
                param
                (
                    [Parameter()]
                    [string]
                    $CurrentName,

                    [Parameter()]
                    [string]
                    $CurrentBandwidthReservationMode,

                    [Parameter()]
                    [string]
                    $DesiredName,

                    [Parameter()]
                    [string]
                    $DesiredBandwidthReservationMode,

                    [Parameter()]
                    [string]
                    $Ensure,

                    [Parameter()]
                    [bool]
                    $ExpectedResult
                )

                # Set the mocked VMSwitch to be returned from Get-VMSwitch if the switch exists
                if ($CurrentName)
                {
                    $global:mockedVMSwitch = New-MockedVMSwitch -Name $CurrentName -BandwidthReservationMode $CurrentBandwidthReservationMode -AllowManagementOS $true
                }

                $targetResource = Test-TargetResource -Name $DesiredName -BandwidthReservationMode $DesiredBandwidthReservationMode -Type 'External' -NetAdapterName 'SomeNIC' -Ensure $Ensure -AllowManagementOS $true
                $targetResource | Should Be $ExpectedResult

                Remove-Variable -Scope 'Global' -Name 'mockedVMSwitch' -ErrorAction 'SilentlyContinue'
            }

            Mock -CommandName Get-OSVersion -MockWith {
                return [Version]::Parse('6.1.7601')
            }

            # Test Test-TargetResource when the version of Windows doesn't support BandwidthReservationMode
            It 'Invalid Operating System Exception' {
                $errorRecord = Get-InvalidArgumentError `
                    -ErrorId 'BandwidthReservationModeError' `
                    -ErrorMessage $LocalizedData.BandwidthReservationModeError
                {Test-TargetResource -Name 'WeightBRM' -Type 'External' -NetAdapterName 'SomeNIC' -AllowManagementOS $true -BandwidthReservationMode 'Weight' -Ensure 'Present'} | Should Throw $errorRecord
            }

            # Test Test-TargetResource when the version of Windows doesn't support BandwidthReservationMode and specifies NA for BandwidthReservationMode
            It 'Simulates Windows Server 2008 R2 | Desired BandwidthReservationMode set to "NA" | Ensure Present | Expected Result is True' {
                $global:mockedVMSwitch = New-MockedVMSwitch -Name 'SomeSwitch' -BandwidthReservationMode 'NA' -AllowManagementOS $true
                $targetResource = Test-TargetResource -Name 'SomeSwitch' -BandwidthReservationMode 'NA' -Type 'External' -NetAdapterName 'SomeNIC' -Ensure 'Present' -AllowManagementOS $true
                $targetResource | Should Be $true
            }

            It 'Passes when "BandwidthReservationMode" does not match but is not specified (#48)' {
                $global:mockedVMSwitch = New-MockedVMSwitch -Name 'SomeSwitch' -BandwidthReservationMode 'Absolute'
                $targetResource = Test-TargetResource -Name 'SomeSwitch' -Type 'Internal' -Ensure 'Present'
                $targetResource | Should Be $true
            }
        }

        Describe 'Validates Set-TargetResource Function' {
            # Create empty functions to be able to mock the missing Hyper-V cmdlet
            function Get-VMSwitch
            {

            }

            function New-VMSwitch
            {

            }

            function Remove-VMSwitch
            {

            }

            function Set-VMSwitch
            {

            }

            <#
                Mocks Get-VMSwitch and will return $global:mockedVMSwitch which is
                a variable that is created during most It statements to mock a VMSwitch
            #>

            Mock -CommandName Get-VMSwitch -MockWith {
                param
                (
                    [string]
                    $Name,

                    [string]
                    $SwitchType,

                    [string]
                    $ErrorAction
                )

                if ($ErrorAction -eq 'Stop' -and $global:mockedVMSwitch -eq $null)
                {
                    throw [System.Management.Automation.ActionPreferenceStopException]'No switch can be found by given criteria.'
                }

                return $global:mockedVMSwitch
            }

            <#
                Mocks New-VMSwitch and will assign a mocked switch to $global:mockedVMSwitch. This returns $global:mockedVMSwitch
                which is a variable that is created during most It statements to mock a VMSwitch
            #>

            Mock -CommandName New-VMSwitch -MockWith {
                param
                (
                    [string]
                    $Name,

                    [string]
                    $NetAdapterName,

                    [string]
                    $MinimumBandwidthMode,

                    [bool]
                    $AllowManagementOS
                )

                $global:mockedVMSwitch = New-MockedVMSwitch -Name $Name -BandwidthReservationMode $MinimumBandwidthMode -AllowManagementOS $AllowManagementOS
                return $global:mockedVMSwitch
            }

            <#
                Mocks Set-VMSwitch and will modify $global:mockedVMSwitch which is
                a variable that is created during most It statements to mock a VMSwitch
            #>

            Mock -CommandName Set-VMSwitch -MockWith {
                param
                (
                    [bool]
                    $AllowManagementOS
                )

                if ($AllowManagementOS)
                {
                    $global:mockedVMSwitch['AllowManagementOS'] = $AllowManagementOS
                }
            }

            <#
                Mocks Remove-VMSwitch and will remove the variable $global:mockedVMSwitch which is
                a variable that is created during most It statements to mock a VMSwitch
            #>

            Mock -CommandName Remove-VMSwitch -MockWith {
                $global:mockedVMSwitch = $null
            }

            # Mocks Get-NetAdapter which returns a simplified network adapter
            Mock -CommandName Get-NetAdapter -MockWith {
                return [PSCustomObject]@{
                    Name = 'SomeNIC'
                    InterfaceDescription = 'Microsoft Network Adapter Multiplexor Driver'
                }
            }

            # 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 Get-OSVersion -MockWith {
                return [Version]::Parse('6.3.9600')
            }

            # Create all the test cases for Get-TargetResource
            $getTestCases = @()
            foreach ($brmMode in $BANDWIDTH_RESERVATION_MODES)
            {
                $getTestCases += @{
                    CurrentName = $brmMode + 'BRM'
                    CurrentBandwidthReservationMode = $brmMode
                }
            }

            It 'Current Name "<CurrentName>" | Current BandwidthReservationMode set to "<CurrentBandwidthReservationMode>" | Desired BandwidthReservationMode set to "<DesiredBandwidthReservationMode>" | Ensure "<Ensure>"' -TestCases $testSetTestCases {
                param
                (
                    [Parameter()]
                    [string]
                    $CurrentName,

                    [Parameter()]
                    [string]
                    $CurrentBandwidthReservationMode,

                    [Parameter()]
                    [string]
                    $DesiredName,

                    [Parameter()]
                    [string]
                    $DesiredBandwidthReservationMode,

                    [Parameter()]
                    [string]
                    $Ensure,

                    [Parameter()]
                    [bool]
                    $ExpectedResult
                )

                # Set the mocked VMSwitch to be returned from Get-VMSwitch if the switch exists
                if ($CurrentName)
                {
                    $global:mockedVMSwitch = New-MockedVMSwitch -Name $CurrentName -BandwidthReservationMode $CurrentBandwidthReservationMode -AllowManagementOS $true
                }

                $targetResource = Set-TargetResource -Name $DesiredName -BandwidthReservationMode $DesiredBandwidthReservationMode -Type 'External' -NetAdapterName 'SomeNIC' -Ensure $Ensure -AllowManagementOS $true
                $targetResource | Should Be $null

                if ($CurrentName -and $Ensure -eq 'Present')
                {
                    if ($DesiredBandwidthReservationMode -ne $CurrentBandwidthReservationMode)
                    {
                        Assert-MockCalled -CommandName Get-VMSwitch -Times 2 -Scope 'It'
                        Assert-MockCalled -CommandName Remove-VMSwitch -Times 1 -Scope 'It'
                        Assert-MockCalled -CommandName New-VMSwitch -Times 1 -Scope 'It'
                        Assert-MockCalled -CommandName Set-VMSwitch -Times 0 -Scope 'It'
                    }
                    else
                    {
                        Assert-MockCalled -CommandName Get-VMSwitch -Times 1 -Scope 'It'
                    }
                }
                elseif ($Ensure -eq 'Present')
                {
                    Assert-MockCalled -CommandName Get-VMSwitch -Times 1 -Scope 'It'
                    Assert-MockCalled -CommandName New-VMSwitch -Times 1 -Scope 'It'
                }
                else
                {
                    Assert-MockCalled -CommandName Get-VMSwitch -Times 1 -Scope 'It'
                    Assert-MockCalled -CommandName Remove-VMSwitch -Times 1 -Scope 'It'
                }
                Remove-Variable -Scope 'Global' -Name 'mockedVMSwitch' -ErrorAction 'SilentlyContinue'
            }

            # Test Set-TargetResource when the version of Windows doesn't support BandwidthReservationMode
            It 'Invalid Operating System Exception' {
                Mock -CommandName Get-OSVersion -MockWith {
                    return [Version]::Parse('6.1.7601')
                }

                $errorRecord = Get-InvalidArgumentError `
                    -ErrorId 'BandwidthReservationModeError' `
                    -ErrorMessage $LocalizedData.BandwidthReservationModeError
                {Set-TargetResource -Name 'WeightBRM' -Type 'External' -NetAdapterName 'SomeNIC' -AllowManagementOS $true -BandwidthReservationMode 'Weight' -Ensure 'Present'} | Should Throw $errorRecord
            }
        }
    }
}
finally
{
    Invoke-TestCleanup
}