Tests/Unit/MSFT_ScriptResource.Tests.ps1
$errorActionPreference = 'Stop' Set-StrictMode -Version 'Latest' # Import CommonTestHelper for Enter-DscResourceTestEnvironment, Exit-DscResourceTestEnvironment $script:moduleRootPath = Split-Path -Path $PSScriptRoot -Parent $script:testHelpersPath = Join-Path -Path $script:moduleRootPath -ChildPath 'TestHelpers' Import-Module -Name (Join-Path -Path $script:testHelpersPath -ChildPath 'CommonTestHelper.psm1') $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DSCResourceModuleName 'PSDscResources' ` -DSCResourceName 'MSFT_ScriptResource' ` -TestType 'Unit' try { InModuleScope 'MSFT_ScriptResource' { $testUsername = 'TestUsername' $testPassword = 'TestPassword' $secureTestPassword = ConvertTo-SecureString -String $testPassword -AsPlainText -Force $script:testCredenital = New-Object -TypeName 'System.Management.Automation.PSCredential' -ArgumentList @( $testUsername, $secureTestPassword ) Describe 'Script\Get-TargetResource' { Mock -CommandName 'Invoke-Script' -MockWith { } Context 'Specified get script returns null' { $getTargetResourceParameters = @{ GetScript = 'return $null' TestScript = 'NotUsed' SetScript = 'NotUsed' } It 'Should throw an error for malformed get script' { $errorMessage = $script:localizedData.GetScriptDidNotReturnHashtable { $null = Get-TargetResource @getTargetResourceParameters } | Should -Throw -ExpectedMessage $errorMessage } } Mock -CommandName 'Invoke-Script' -MockWith { return "String" } Context 'Specified get script returns a string' { $getTargetResourceParameters = @{ GetScript = 'return "String"' TestScript = 'NotUsed' SetScript = 'NotUsed' } It 'Should throw an error for malformed get script' { $errorMessage = $script:localizedData.GetScriptDidNotReturnHashtable { $null = Get-TargetResource @getTargetResourceParameters } | Should -Throw -ExpectedMessage $errorMessage } } $testException = New-Object -TypeName 'System.Exception' -ArgumentList @() $newErrorRecoredArguments = @( $testException, 'Test', [System.Management.Automation.ErrorCategory]::InvalidOperation, $null ) $testErrorRecord = New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList $newErrorRecoredArguments Mock -CommandName 'Invoke-Script' -MockWith { return $testErrorRecord } Context 'Specified get script throws an error' { $getTargetResourceParameters = @{ GetScript = 'throw "Error"' TestScript = 'NotUsed' SetScript = 'NotUsed' } It 'Should throw error from get script' { { $null = Get-TargetResource @getTargetResourceParameters } | Should -Throw -ExpectedMessage $testErrorRecord } } $testScriptResult = @{ TestResult = 'Value1' } Mock -CommandName 'Invoke-Script' -MockWith { return $testScriptResult } Context 'Specified get script returns a hashtable and Credential not specified' { $getTargetResourceParameters = @{ GetScript = 'return "something"' TestScript = 'NotUsed' SetScript = 'NotUsed' } It 'Should not throw' { { $null = Get-TargetResource @getTargetResourceParameters } | Should -Not -Throw } It 'Should use script execution helper to run script' { $expectedScriptBlock = [System.Management.Automation.ScriptBlock]::Create($getTargetResourceParameters.GetScript) $null = Get-TargetResource @getTargetResourceParameters $invokeScriptParameterFilter = { $scriptBlockParameterCorrect = $null -eq (Compare-Object -ReferenceObject $expectedScriptBlock.Ast -DifferenceObject $ScriptBlock.Ast) return $scriptBlockParameterCorrect } Assert-MockCalled -CommandName 'Invoke-Script' -ParameterFilter $invokeScriptParameterFilter -Times 1 -Scope 'It' } It 'Should return a hashtable' { $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters $getTargetResourceResult -is [System.Collections.Hashtable] | Should -BeTrue } It 'Should return the output from the specified get script' { $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters Compare-Object -ReferenceObject $testScriptResult -DifferenceObject $getTargetResourceResult | Should -Be $null } } Context 'Specified get script returns a hashtable and Credential specified' { $getTargetResourceParameters = @{ GetScript = 'return "something"' TestScript = 'NotUsed' SetScript = 'NotUsed' Credential = $script:testCredenital } It 'Should not throw' { { $null = Get-TargetResource @getTargetResourceParameters } | Should -Not -Throw } It 'Should use script execution helper to run script with the specified Credential' { $expectedScriptBlock = [System.Management.Automation.ScriptBlock]::Create($getTargetResourceParameters.GetScript) $null = Get-TargetResource @getTargetResourceParameters $invokeScriptParameterFilter = { $scriptBlockParameterCorrect = $null -eq (Compare-Object -ReferenceObject $expectedScriptBlock.Ast -DifferenceObject $ScriptBlock.Ast) $credentialParameterCorrect = $null -eq (Compare-Object -ReferenceObject $getTargetResourceParameters.Credential -DifferenceObject $Credential) return $scriptBlockParameterCorrect -and $credentialParameterCorrect } Assert-MockCalled -CommandName 'Invoke-Script' -ParameterFilter $invokeScriptParameterFilter -Times 1 -Scope 'It' } It 'Should return a hashtable' { $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters $getTargetResourceResult -is [System.Collections.Hashtable] | Should -BeTrue } It 'Should return the output from the specified get script' { $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters Compare-Object -ReferenceObject $testScriptResult -DifferenceObject $getTargetResourceResult | Should -Be $null } } } Describe 'Script\Set-TargetResource' { Mock -CommandName 'Invoke-Script' -MockWith { } Context 'Specified set script returns correctly and Credential not specified' { $setTargetResourceParameters = @{ GetScript = 'NotUsed' TestScript = 'NotUsed' SetScript = '$assignedVariable = "Value1"' } It 'Should not throw' { { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw } It 'Should use script execution helper to run script' { $expectedScriptBlock = [System.Management.Automation.ScriptBlock]::Create($setTargetResourceParameters.SetScript) Set-TargetResource @setTargetResourceParameters $invokeScriptParameterFilter = { $scriptBlockParameterCorrect = $null -eq (Compare-Object -ReferenceObject $expectedScriptBlock.Ast -DifferenceObject $ScriptBlock.Ast) return $scriptBlockParameterCorrect } Assert-MockCalled -CommandName 'Invoke-Script' -ParameterFilter $invokeScriptParameterFilter -Times 1 -Scope 'It' } } Context 'Specified set script returns correctly and Credential specified' { $setTargetResourceParameters = @{ GetScript = 'NotUsed' TestScript = 'NotUsed' SetScript = '$assignedVariable = "Value1"' Credential = $script:testCredenital } It 'Should not throw' { { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw } It 'Should use script execution helper to run script with specified Credential' { $expectedScriptBlock = [System.Management.Automation.ScriptBlock]::Create($setTargetResourceParameters.SetScript) Set-TargetResource @setTargetResourceParameters $invokeScriptParameterFilter = { $scriptBlockParameterCorrect = $null -eq (Compare-Object -ReferenceObject $expectedScriptBlock.Ast -DifferenceObject $ScriptBlock.Ast) $credentialParameterCorrect = $null -eq (Compare-Object -ReferenceObject $setTargetResourceParameters.Credential -DifferenceObject $Credential) return $scriptBlockParameterCorrect -and $credentialParameterCorrect } Assert-MockCalled -CommandName 'Invoke-Script' -ParameterFilter $invokeScriptParameterFilter -Times 1 -Scope 'It' } } $testException = New-Object -TypeName 'System.Exception' -ArgumentList @() $newErrorRecoredArguments = @( $testException, 'Test', [System.Management.Automation.ErrorCategory]::InvalidOperation, $null ) $testErrorRecord = New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList $newErrorRecoredArguments Mock -CommandName 'Invoke-Script' -MockWith { return $testErrorRecord } Context 'Specified set script returns an error' { $setTargetResourceParameters = @{ GetScript = 'NotUsed' TestScript = 'NotUsed' SetScript = 'throw "Error"' } It 'Should throw error from set script' { { Set-TargetResource @setTargetResourceParameters } | Should -Throw -ExpectedMessage $testErrorRecord } } } Describe 'Script\Test-TargetResource' { Mock -CommandName 'Invoke-Script' -MockWith { } Context 'Specified test script returns null' { $testTargetResourceParameters = @{ GetScript = 'NotUsed' TestScript = 'return $null' SetScript = 'NotUsed' } It 'Should throw an error for malformed test script' { $errorMessage = $script:localizedData.TestScriptDidNotReturnBoolean { $null = Test-TargetResource @testTargetResourceParameters } | Should -Throw -ExpectedMessage $errorMessage } } $testException = New-Object -TypeName 'System.Exception' -ArgumentList @() $newErrorRecoredArguments = @( $testException, 'Test', [System.Management.Automation.ErrorCategory]::InvalidOperation, $null ) $testErrorRecord = New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList $newErrorRecoredArguments Mock -CommandName 'Invoke-Script' -MockWith { return $testErrorRecord } Context 'Specified test script returns an error' { $testTargetResourceParameters = @{ GetScript = 'NotUsed' TestScript = 'throw "Error"' SetScript = 'NotUsed' } It 'Should throw error from test script' { { $null = Test-TargetResource @testTargetResourceParameters } | Should -Throw -ExpectedMessage $testErrorRecord } } $expectedBoolean = $true Mock -CommandName 'Invoke-Script' -MockWith { return $expectedBoolean } Context 'Specified test script returns one boolean and Credential not specified' { $testTargetResourceParameters = @{ GetScript = 'NotUsed' TestScript = 'return $true' SetScript = 'NotUsed' } It 'Should not throw' { { $null = Test-TargetResource @testTargetResourceParameters } | Should -Not -Throw } It 'Should use script execution helper to run script' { $expectedScriptBlock = [System.Management.Automation.ScriptBlock]::Create($testTargetResourceParameters.TestScript) $null = Test-TargetResource @testTargetResourceParameters $invokeScriptParameterFilter = { $scriptBlockParameterCorrect = $null -eq (Compare-Object -ReferenceObject $expectedScriptBlock.Ast -DifferenceObject $ScriptBlock.Ast) return $scriptBlockParameterCorrect } Assert-MockCalled -CommandName 'Invoke-Script' -ParameterFilter $invokeScriptParameterFilter -Times 1 -Scope 'It' } It 'Should return the expected boolean' { $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters $testTargetResourceResult | Should -Be $expectedBoolean } } Context 'Specified test script returns one boolean and Credential specified' { $testTargetResourceParameters = @{ GetScript = 'NotUsed' TestScript = 'return $true' SetScript = 'NotUsed' Credential = $script:testCredenital } It 'Should not throw' { { $null = Test-TargetResource @testTargetResourceParameters } | Should -Not -Throw } It 'Should use script execution helper to run script with specified Credential' { $expectedScriptBlock = [System.Management.Automation.ScriptBlock]::Create($testTargetResourceParameters.TestScript) $null = Test-TargetResource @testTargetResourceParameters $invokeScriptParameterFilter = { $scriptBlockParameterCorrect = $null -eq (Compare-Object -ReferenceObject $expectedScriptBlock.Ast -DifferenceObject $ScriptBlock.Ast) $credentialParameterCorrect = $null -eq (Compare-Object -ReferenceObject $testTargetResourceParameters.Credential -DifferenceObject $Credential) return $scriptBlockParameterCorrect -and $credentialParameterCorrect } Assert-MockCalled -CommandName 'Invoke-Script' -ParameterFilter $invokeScriptParameterFilter -Times 1 -Scope 'It' } It 'Should return the expected boolean' { $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters $testTargetResourceResult | Should -Be $expectedBoolean } } $expectedBoolean = $false Mock -CommandName 'Invoke-Script' -MockWith { return @( (-not $expectedBoolean), $expectedBoolean ) } Context 'Specified test script returns multiple booleans' { $testTargetResourceParameters = @{ GetScript = 'NotUsed' TestScript = 'return $true, $false' SetScript = 'NotUsed' } It 'Should not throw' { { $null = Test-TargetResource @testTargetResourceParameters } | Should -Not -Throw } It 'Should use script execution helper to run script' { $expectedScriptBlock = [System.Management.Automation.ScriptBlock]::Create($testTargetResourceParameters.TestScript) $null = Test-TargetResource @testTargetResourceParameters $invokeScriptParameterFilter = { $scriptBlockParameterCorrect = $null -eq (Compare-Object -ReferenceObject $expectedScriptBlock.Ast -DifferenceObject $ScriptBlock.Ast) return $scriptBlockParameterCorrect } Assert-MockCalled -CommandName 'Invoke-Script' -ParameterFilter $invokeScriptParameterFilter -Times 1 -Scope 'It' } It 'Should return the expected boolean' { $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters $testTargetResourceResult | Should -Be $expectedBoolean } } Mock -CommandName 'Invoke-Script' -MockWith { return 'Value1' } Context 'Specified test script returns a string' { $testTargetResourceParameters = @{ GetScript = 'NotUsed' TestScript = 'return "MyString"' SetScript = 'NotUsed' } It 'Should throw an error for malformed test script' { $errorMessage = $script:localizedData.TestScriptDidNotReturnBoolean { $null = Test-TargetResource @testTargetResourceParameters } | Should -Throw -ExpectedMessage $errorMessage } } } Describe 'Script\Invoke-Script' { Mock -CommandName 'Invoke-Command' -MockWith { } Context 'Specified script throws an error' { $testErrorMessage = 'Script execution helper test error message' $scriptExecutionHelperParameters = @{ ScriptBlock = { throw $testErrorMessage } } It 'Should not throw' { { $null = Invoke-Script @scriptExecutionHelperParameters } | Should -Not -Throw } It 'Should return an error record' { $scriptExecutionHelperResult = Invoke-Script @scriptExecutionHelperParameters $scriptExecutionHelperResult -is [System.Management.Automation.ErrorRecord] | Should -BeTrue } It 'Should return an error record' { $scriptExecutionHelperResult = Invoke-Script @scriptExecutionHelperParameters $scriptExecutionHelperResult -is [System.Management.Automation.ErrorRecord] | Should -BeTrue } It 'Should return error with expected message from script' { $scriptExecutionHelperResult = Invoke-Script @scriptExecutionHelperParameters $scriptExecutionHelperResult.Exception.Message | Should -Be $testErrorMessage } } Context 'Specified script returns nothing and Credential specified' { $scriptExecutionHelperParameters = @{ ScriptBlock = { return $null } Credential = $script:testCredenital } It 'Should not throw' { { $null = Invoke-Script @scriptExecutionHelperParameters } | Should -Not -Throw } It 'Should run script through Invoke-Command using the specified Credential' { $null = Invoke-Script @scriptExecutionHelperParameters $invokeCommandParameterFilter = { $scriptBlockParameterCorrect = $null -eq (Compare-Object -ReferenceObject $scriptExecutionHelperParameters.ScriptBlock.Ast -DifferenceObject $ScriptBlock.Ast) $credentialParameterCorrect = $null -eq (Compare-Object -ReferenceObject $scriptExecutionHelperParameters.Credential -DifferenceObject $Credential) return $scriptBlockParameterCorrect -and $credentialParameterCorrect } Assert-MockCalled -CommandName 'Invoke-Command' -ParameterFilter $invokeCommandParameterFilter -Times 1 -Scope 'It' } It 'Should return nothing' { $scriptExecutionHelperResult = Invoke-Script @scriptExecutionHelperParameters $scriptExecutionHelperResult | Should -Be $null } } Context 'Specified script returns a result and Credential not specified' { $testScriptResult = 'Script result' $scriptExecutionHelperParameters = @{ ScriptBlock = { return $testScriptResult } } It 'Should not run script through Invoke-Command' { $null = Invoke-Script @scriptExecutionHelperParameters Assert-MockCalled -CommandName 'Invoke-Command' -Times 0 -Scope 'It' } It 'Should return result of script' { $scriptExecutionHelperResult = Invoke-Script @scriptExecutionHelperParameters $scriptExecutionHelperResult | Should -Be $testScriptResult } } } } } finally { Exit-DscResourceTestEnvironment -TestEnvironment $script:testEnvironment } |