Tests/Unit/MSFT_xProcessResource.Tests.ps1
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "")] param () Import-Module -Name "$PSScriptRoot\..\CommonTestHelper.psm1" -Force $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'xPSDesiredStateConfiguration' ` -DscResourceName 'MSFT_xProcessResource' ` -TestType 'Unit' try { InModuleScope 'MSFT_xProcessResource' { Describe 'MSFT_xProcessResource Unit Tests' { BeforeAll { Import-Module -Name "$PSScriptRoot\MSFT_xProcessResource.TestHelper.psm1" -Force $script:cmdProcessShortName = 'ProcessTest' $script:cmdProcessFullName = 'ProcessTest.exe' $script:cmdProcessFullPath = "$env:winDir\system32\ProcessTest.exe" Copy-Item -Path "$env:winDir\system32\cmd.exe" -Destination $script:cmdProcessFullPath -ErrorAction 'SilentlyContinue' -Force $script:processTestFolder = Join-Path -Path (Get-Location) -ChildPath 'ProcessTestFolder' if (Test-Path -Path $script:processTestFolder) { Remove-Item -Path $script:processTestFolder -Recurse -Force } New-Item -Path $script:processTestFolder -ItemType 'Directory' | Out-Null Push-Location -Path $script:processTestFolder } AfterAll { Stop-ProcessByName -ProcessName $script:cmdProcessShortName if (Test-Path -Path $script:cmdProcessFullPath) { Remove-Item -Path $script:cmdProcessFullPath -ErrorAction 'SilentlyContinue' -Force } Pop-Location if (Test-Path -Path $script:processTestFolder) { Remove-Item -Path $script:processTestFolder -Recurse -Force } } BeforeEach { Stop-ProcessByName -ProcessName $script:cmdProcessShortName } Context 'Get-TargetResource' { It 'Should return the correct properties for a process that is absent with Arguments' { $processArguments = 'TestGetProperties' $getTargetResourceResult = Get-TargetResource -Path $script:cmdProcessFullPath -Arguments $processArguments $getTargetResourceProperties = @( 'Arguments', 'Ensure', 'Path' ) Test-GetTargetResourceResult -GetTargetResourceResult $getTargetResourceResult -GetTargetResourceResultProperties $getTargetResourceProperties $getTargetResourceResult.Arguments | Should Be $processArguments $getTargetResourceResult.Ensure | Should Be 'Absent' $getTargetResourceResult.Path -icontains $script:cmdProcessFullPath | Should Be $true $getTargetResourceResult.Count | Should Be 3 } It 'Should return the correct properties for a process that is absent without Arguments' { $processArguments = '' $getTargetResourceResult = Get-TargetResource -Path $script:cmdProcessFullPath -Arguments $processArguments $getTargetResourceProperties = @( 'Arguments', 'Ensure', 'Path' ) Test-GetTargetResourceResult -GetTargetResourceResult $getTargetResourceResult -GetTargetResourceResultProperties $getTargetResourceProperties $getTargetResourceResult.Arguments | Should Be $processArguments $getTargetResourceResult.Ensure | Should Be 'Absent' $getTargetResourceResult.Path -icontains $script:cmdProcessFullPath | Should Be $true $getTargetResourceResult.Count | Should Be 3 } It 'Should return the correct properties for a process that is present with Arguments' { $processArguments = 'TestGetProperties' Set-TargetResource -Path $script:cmdProcessFullPath -Arguments $processArguments $getTargetResourceResult = Get-TargetResource -Path $script:cmdProcessFullPath -Arguments $processArguments $getTargetResourceProperties = @( 'VirtualMemorySize', 'Arguments', 'Ensure', 'PagedMemorySize', 'Path', 'NonPagedMemorySize', 'HandleCount', 'ProcessId' ) Test-GetTargetResourceResult -GetTargetResourceResult $getTargetResourceResult -GetTargetResourceResultProperties $getTargetResourceProperties $getTargetResourceResult.VirtualMemorySize -le 0 | Should Be $false $getTargetResourceResult.Arguments | Should Be $processArguments $getTargetResourceResult.Ensure | Should Be 'Present' $getTargetResourceResult.PagedMemorySize -le 0 | Should Be $false $getTargetResourceResult.Path.IndexOf("ProcessTest.exe",[Stringcomparison]::OrdinalIgnoreCase) -le 0 | Should Be $false $getTargetResourceResult.NonPagedMemorySize -le 0 | Should Be $false $getTargetResourceResult.HandleCount -le 0 | Should Be $false $getTargetResourceResult.ProcessId -le 0 | Should Be $false $getTargetResourceResult.Count | Should Be 8 } It 'Should return the correct properties for a process that is present without Arguments' { $processArguments = '' Set-TargetResource -Path $script:cmdProcessFullPath -Arguments $processArguments $getTargetResourceResult = Get-TargetResource -Path $script:cmdProcessFullPath -Arguments $processArguments $getTargetResourceProperties = @( 'VirtualMemorySize', 'Arguments', 'Ensure', 'PagedMemorySize', 'Path', 'NonPagedMemorySize', 'HandleCount', 'ProcessId' ) Test-GetTargetResourceResult -GetTargetResourceResult $getTargetResourceResult -GetTargetResourceResultProperties $getTargetResourceProperties $getTargetResourceResult.VirtualMemorySize -le 0 | Should Be $false $getTargetResourceResult.Arguments | Should Be $processArguments $getTargetResourceResult.Ensure | Should Be 'Present' $getTargetResourceResult.PagedMemorySize -le 0 | Should Be $false $getTargetResourceResult.Path.IndexOf("ProcessTest.exe",[Stringcomparison]::OrdinalIgnoreCase) -le 0 | Should Be $false $getTargetResourceResult.NonPagedMemorySize -le 0 | Should Be $false $getTargetResourceResult.HandleCount -le 0 | Should Be $false $getTargetResourceResult.ProcessId -le 0 | Should Be $false $getTargetResourceResult.Count | Should Be 8 } It 'Should return correct Ensure value based on Arguments parameter with multiple processes' { $actualArguments = 'TestProcessResourceWithArguments' Set-TargetResource -Path $script:cmdProcessFullPath -Arguments $actualArguments $processes = @( Get-TargetResource -Path $script:cmdProcessFullPath -Arguments '') $processes.Count | Should Be 1 $processes[0].Ensure | Should Be 'Absent' $processes = @( Get-TargetResource -Path $script:cmdProcessFullPath -Arguments $actualArguments) $processes.Count | Should Be 1 $processes[0].Ensure | Should Be 'Present' $processes = @( Get-TargetResource -Path $script:cmdProcessFullPath -Arguments 'NotOrginalArguments') $processes.Count | Should Be 1 $processes[0].Ensure | Should Be 'Absent' Set-TargetResource -Path $script:cmdProcessFullPath -Arguments '' $processes = @( Get-TargetResource -Path $script:cmdProcessFullPath -Arguments '') $processes.Count | Should Be 1 $processes[0].Ensure | Should Be 'Present' $processes[0].Arguments.Length | Should Be 0 $processes = @( Get-TargetResource -Path $script:cmdProcessFullPath -Arguments $actualArguments) $processes.Count | Should Be 1 $processes[0].Ensure | Should Be 'Present' $processes[0].Arguments | Should Be $actualArguments } } Context 'Set-TargetResource' { It 'Should start and stop a process with no arguments' { Test-TargetResource -Path $script:cmdProcessFullPath -Arguments '' | Should Be $false Set-TargetResource -Path $script:cmdProcessFullPath -Arguments '' Test-TargetResource -Path $script:cmdProcessFullPath -Arguments '' | Should Be $true Set-TargetResource -Path $script:cmdProcessFullPath -Ensure 'Absent' -Arguments '' Test-TargetResource -Path $script:cmdProcessFullPath -Arguments '' | Should Be $false } It 'Should have correct output for absent process with WhatIf specified and default Ensure' { $setTargetResourceParameters = @{ Path = $script:cmdProcessFullPath Arguments = '' } $expectedWhatIfOutput = @( $LocalizedData.StartingProcessWhatif, $script:cmdProcessFullPath ) Test-SetTargetResourceWithWhatIf -Parameters $setTargetResourceParameters -ExpectedOutput $expectedWhatIfOutput if ($setTargetResourceParameters.ContainsKey('WhatIf')) { $setTargetResourceParameters.Remove('WhatIf') } $testTargetResourceResult = Test-TargetResource @setTargetResourceParameters $testTargetResourceResult | Should Be $false } It 'Should have no output for absent process with WhatIf specified and Ensure Absent' { $setTargetResourceParameters = @{ Ensure = 'Absent' Path = $script:cmdProcessFullPath Arguments = '' } Test-SetTargetResourceWithWhatIf -Parameters $setTargetResourceParameters -ExpectedOutput '' } It 'Should have correct output for existing process with WhatIf specified and Ensure Absent' { Set-TargetResource -Path $script:cmdProcessFullPath -Arguments '' $setTargetResourceParameters = @{ Ensure = 'Absent' Path = $script:cmdProcessFullPath Arguments = '' } $expectedWhatIfOutput = @( $LocalizedData.StoppingProcessWhatif, $script:cmdProcessFullPath ) Test-SetTargetResourceWithWhatIf -Parameters $setTargetResourceParameters -ExpectedOutput $expectedWhatIfOutput if ($setTargetResourceParameters.ContainsKey('WhatIf')) { $setTargetResourceParameters.Remove('WhatIf') } $testTargetResourceResult = Test-TargetResource @setTargetResourceParameters $testTargetResourceResult | Should Be $false } It 'Should have no output for existing process with WhatIf specified and default Ensure' { Set-TargetResource -Path $script:cmdProcessFullPath -Arguments '' $setTargetResourceParameters = @{ Path = $script:cmdProcessFullPath Arguments = '' } Test-SetTargetResourceWithWhatIf -Parameters $setTargetResourceParameters -ExpectedOutput '' } It 'Should provide correct error output to the specified error and output streams when using invalid input from the specified input stream' { $errorPath = Join-Path -Path (Get-Location) -ChildPath 'TestStreamsError.txt' $outputPath = Join-Path -Path (Get-Location) -ChildPath 'TestStreamsOutput.txt' $inputPath = Join-Path -Path (Get-Location) -ChildPath 'TestStreamsInput.txt' $workingDirectoryPath = Join-Path -Path (Get-Location) -ChildPath 'TestWorkingDirectory' foreach ($path in @( $errorPath, $outputPath, $inputPath, $workingDirectoryPath )) { if (Test-Path -Path $path) { Remove-Item -Path $path -Recurse -Force } } New-Item -Path $workingDirectoryPath -ItemType 'Directory' | Out-Null $inputFileText = "ECHO Testing ProcessTest.exe ` dir volumeSyntaxError:\ ` set /p waitforinput=Press [y/n]?: " Out-File -FilePath $inputPath -InputObject $inputFileText -Encoding 'ASCII' Set-TargetResource -Path $script:cmdProcessFullPath -WorkingDirectory $workingDirectoryPath -StandardOutputPath $outputPath -StandardErrorPath $errorPath -StandardInputPath $inputPath -Arguments '' Wait-ScriptBlockReturnTrue -ScriptBlock { (Get-TargetResource -Path $script:cmdProcessFullPath -Arguments '').Ensure -ieq 'Absent' } -TimeoutSeconds 10 Wait-ScriptBlockReturnTrue -ScriptBlock { Test-IsFileLocked -Path $errorPath } -TimeoutSeconds 2 $errorFileContent = Get-Content -Path $errorPath -Raw $errorFileContent | Should Not Be $null Wait-ScriptBlockReturnTrue -ScriptBlock { Test-IsFileLocked -Path $outputPath } -TimeoutSeconds 2 $outputFileContent = Get-Content -Path $outputPath -Raw $outputFileContent | Should Not Be $null if ((Get-Culture).Name -ieq 'en-us') { $errorFileContent.Contains('The filename, directory name, or volume label syntax is incorrect.') | Should Be $true $outputFileContent.Contains('Press [y/n]?:') | Should Be $true $outputFileContent.ToLower().Contains($workingDirectoryPath.ToLower()) | Should Be $true } else { $errorFileContent.Length -gt 0 | Should Be $true $outputFileContent.Length -gt 0 | Should Be $true } } It 'Should throw when trying to specify streams or working directory with Ensure Absent' { $invalidPropertiesWithAbsent = @( 'StandardOutputPath', 'StandardErrorPath', 'StandardInputPath', 'WorkingDirectory' ) foreach ($invalidPropertyWithAbsent in $invalidPropertiesWithAbsent) { $setTargetResourceArguments = @{ Path = $script:cmdProcessFullPath Ensure = 'Absent' Arguments = '' $invalidPropertyWithAbsent = 'Something' } { Set-TargetResource @setTargetResourceArguments } | Should Throw ($LocalizedData.ParameterShouldNotBeSpecified -f $invalidPropertyWithAbsent) } } It 'Should throw when passing a relative path to stream or working directory parameters' { $invalidRelativePath = '..\RelativePath' $pathParameters = @( 'StandardOutputPath', 'StandardErrorPath', 'StandardInputPath', 'WorkingDirectory' ) foreach($pathParameter in $pathParameters) { $setTargetResourceParameters = @{ Path = $script:cmdProcessFullPath Ensure = 'Present' Arguments = '' $pathParameter = $invalidRelativePath } { Set-TargetResource @setTargetResourceParameters } | Should Throw $LocalizedData.PathShouldBeAbsolute } } It 'Should throw when providing a nonexistent path for StandardInputPath or WorkingDirectory' { $invalidNonexistentPath = Join-Path -Path (Get-Location) -ChildPath 'NonexistentPath' if (Test-Path -Path $invalidNonexistentPath) { Remove-Item -Path $invalidNonexistentPath -Recurse -Force } $pathMustExistParameters = @( 'StandardInputPath', 'WorkingDirectory' ) foreach ($pathMustExistParameter in $pathMustExistParameters) { $setTargetResourceParameters = @{ Path = $script:cmdProcessFullPath Ensure = 'Present' Arguments = '' $pathMustExistParameter = $invalidNonexistentPath } { Set-TargetResource @setTargetResourceParameters } | Should Throw $LocalizedData.PathShouldExist } } It 'Should not throw when providing a nonexistent path for StandardOutputPath or StandardErrorPath' { $invalidNonexistentPath = Join-Path -Path (Get-Location) -ChildPath 'NonexistentPath' if (Test-Path -Path $invalidNonexistentPath) { Remove-Item -Path $invalidNonexistentPath -Recurse -Force } $pathNotNeedExistParameters = @( 'StandardOutputPath', 'StandardErrorPath' ) foreach ($pathNotNeedExistParameter in $pathNotNeedExistParameters) { $setTargetResourceParameters = @{ Path = $script:cmdProcessFullPath Ensure = 'Present' Arguments = '' $pathNotNeedExistParameter = $invalidNonexistentPath } { Set-TargetResource @setTargetResourceParameters } | Should Not Throw } } } Context 'Test-TargetResource' { It 'Should return correct value based on Arguments' { $actualArguments = 'TestProcessResourceWithArguments' Set-TargetResource -Path $script:cmdProcessFullPath -Arguments $actualArguments Test-TargetResource -Path $script:cmdProcessFullPath -Arguments '' | Should Be $false Test-TargetResource -Path $script:cmdProcessFullPath -Arguments 'NotTheOriginalArguments' | Should Be $false Test-TargetResource -Path $script:cmdProcessFullPath -Arguments $actualArguments | Should Be $true } It 'Should return false for absent process with directory arguments' { $testTargetResourceResult = Test-TargetResource ` -Path $script:cmdProcessFullPath ` -WorkingDirectory 'something' ` -StandardOutputPath 'something' ` -StandardErrorPath 'something' ` -StandardInputPath 'something' ` -Arguments '' $testTargetResourceResult | Should Be $false } } Context 'Get-Win32Process' { It 'Should only return one process when arguments were changed for that process' { Set-TargetResource -Path $script:cmdProcessFullPath -Arguments '' Set-TargetResource -Path $script:cmdProcessFullPath -Arguments 'abc' $processes = @( Get-Win32Process -Path $script:cmdProcessFullPath -UseGetCimInstanceThreshold 0 ) $processes.Count | Should Be 1 $processes = @( Get-Win32Process -Path $script:cmdProcessFullPath -UseGetCimInstanceThreshold 5 ) $processes.Count | Should Be 1 } } Context 'Get-ArgumentsFromCommandLineInput' { It 'Should retrieve expected arguments from command line input' { $testCases = @( @{ CommandLineInput = "c a " ExpectedArguments = "a" }, @{ CommandLineInput = '"c b d" e ' ExpectedArguments = "e" }, @{ CommandLineInput = " a b" ExpectedArguments = "b" }, @{ CommandLineInput = " abc " ExpectedArguments = "" } ) foreach ($testCase in $testCases) { $commandLineInput = $testCase.CommandLineInput $expectedArguments = $testCase.ExpectedArguments $actualArguments = Get-ArgumentsFromCommandLineInput -CommandLineInput $commandLineInput $actualArguments | Should Be $expectedArguments } } } Context 'Split-Credential' { It 'Should return correct domain and username with @ seperator' { $testUsername = 'user@domain' $testPassword = ConvertTo-SecureString -String 'dummy' -AsPlainText -Force $testCredential = New-Object -TypeName 'PSCredential' -ArgumentList @($testUsername, $testPassword) $splitCredentialResult = Split-Credential -Credential $testCredential $splitCredentialResult.Domain | Should Be 'domain' $splitCredentialResult.Username | Should Be 'user' } It 'Should return correct domain and username with \ seperator' { $testUsername = 'domain\user' $testPassword = ConvertTo-SecureString -String 'dummy' -AsPlainText -Force $testCredential = New-Object -TypeName 'PSCredential' -ArgumentList @($testUsername, $testPassword) $splitCredentialResult = Split-Credential -Credential $testCredential $splitCredentialResult.Domain | Should Be 'domain' $splitCredentialResult.Username | Should Be 'user' } It 'Should return correct domain and username with a local user' { $testUsername = 'localuser' $testPassword = ConvertTo-SecureString -String 'dummy' -AsPlainText -Force $testCredential = New-Object -TypeName 'PSCredential' -ArgumentList @($testUsername, $testPassword) $splitCredentialResult = Split-Credential -Credential $testCredential $splitCredentialResult.Username | Should Be 'localuser' } It 'Should throw when more than one \ in username' { $testUsername = 'user\domain\foo' $testPassword = ConvertTo-SecureString -String 'dummy' -AsPlainText -Force $testCredential = New-Object -TypeName 'PSCredential' -ArgumentList @($testUsername, $testPassword) { $splitCredentialResult = Split-Credential -Credential $testCredential } | Should Throw } It 'Should throw when more than one @ in username' { $testUsername = 'user@domain@foo' $testPassword = ConvertTo-SecureString -String 'dummy' -AsPlainText -Force $testCredential = New-Object -TypeName 'PSCredential' -ArgumentList @($testUsername, $testPassword) { $splitCredentialResult = Split-Credential -Credential $testCredential } | Should Throw } } } } } finally { Exit-DscResourceTestEnvironment -TestEnvironment $script:testEnvironment } |