Tests/Integration/MSFT_MsiPackage.Integration.Tests.ps1
$errorActionPreference = 'Stop' Set-StrictMode -Version 'Latest' if ($PSVersionTable.PSVersion.Major -lt 5 -or $PSVersionTable.PSVersion.Minor -lt 1) { Write-Warning -Message 'Cannot run PSDscResources integration tests on PowerShell versions lower than 5.1' return } $script:testsFolderFilePath = Split-Path $PSScriptRoot -Parent $testHelperFolderFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'TestHelpers' $commonTestHelperFilePath = Join-Path -Path $testHelperFolderFilePath -ChildPath 'CommonTestHelper.psm1' Import-Module -Name $script:commonTestHelperFilePath $script:testEnvironment = Enter-DscResourceTestEnvironment ` -DscResourceModuleName 'PSDscResources' ` -DscResourceName 'MSFT_MsiPackage' ` -TestType 'Unit' try { InModuleScope 'MSFT_MsiPackage' { Describe 'MSFT_MsiPackage Integration Tests' { BeforeAll { $testsFolderFilePath = Split-Path $PSScriptRoot -Parent $testHelperFolderFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'TestHelpers' $commonTestHelperFilePath = Join-Path -Path $testHelperFolderFilePath -ChildPath 'CommonTestHelper.psm1' $packageTestHelperFilePath = Join-Path -Path $testHelperFolderFilePath -ChildPath 'MSFT_MsiPackageResource.TestHelper.psm1' $commonTestHelperFilePath = Join-Path -Path $testHelperFolderFilePath -ChildPath 'CommonTestHelper.psm1' Import-Module -Name $packageTestHelperFilePath -Force # The common test helper file needs to be imported twice because of the InModuleScope Import-Module -Name $commonTestHelperFilePath $script:skipHttpsTest = $false <# This log file is used to log messages from the mock server which is important for debugging since most of the work of the mock server is done within a separate process. #> $script:logFile = Join-Path -Path $PSScriptRoot -ChildPath 'PackageTestLogFile.txt' $script:msiName = 'DSCSetupProject.msi' $script:msiLocation = Join-Path -Path $TestDrive -ChildPath $script:msiName $script:msiArguments = '/NoReboot' $script:packageId = '{deadbeef-80c6-41e6-a1b9-8bdb8a05027f}' $null = New-TestMsi -DestinationPath $script:msiLocation # Clear the log file 'Beginning integration tests' > $script:logFile } BeforeEach { if (Test-PackageInstalledById -ProductId $script:packageId) { $null = Start-Process -FilePath 'msiexec.exe' -ArgumentList @("/x$script:packageId", '/passive') -Wait $null = Start-Sleep -Seconds 1 } if (Test-PackageInstalledById -ProductId $script:packageId) { throw 'Test package could not be uninstalled after running all tests. It may cause errors in subsequent test runs.' } } AfterAll { if (Test-PackageInstalledById -ProductId $script:packageId) { $null = Start-Process -FilePath 'msiexec.exe' -ArgumentList @("/x$script:packageId", '/passive') -Wait $null = Start-Sleep -Seconds 1 } if (Test-PackageInstalledById -ProductId $script:packageId) { throw 'Test package could not be uninstalled after running test' } } Context 'Get-TargetResource' { It 'Should return only basic properties for absent package' { $packageParameters = @{ Path = $script:msiLocation ProductId = $script:packageId } $getTargetResourceResult = Get-TargetResource @packageParameters $getTargetResourceResultProperties = @( 'Ensure', 'ProductId' ) Test-GetTargetResourceResult -GetTargetResourceResult $getTargetResourceResult -GetTargetResourceResultProperties $getTargetResourceResultProperties } It 'Should return full package properties for present package without registry check parameters specified' { $packageParameters = @{ Path = $script:msiLocation ProductId = $script:packageId } Set-TargetResource -Ensure 'Present' @packageParameters $getTargetResourceResult = Get-TargetResource @packageParameters $getTargetResourceResultProperties = @( 'Ensure', 'Name', 'InstallSource', 'InstalledOn', 'ProductId', 'Size', 'Version', 'PackageDescription', 'Publisher' ) Test-GetTargetResourceResult -GetTargetResourceResult $getTargetResourceResult -GetTargetResourceResultProperties $getTargetResourceResultProperties } } Context 'Test-TargetResource' { It 'Should return correct value when package is absent' { $testTargetResourceResult = Test-TargetResource ` -Ensure 'Present' ` -Path $script:msiLocation ` -ProductId $script:packageId $testTargetResourceResult | Should Be $false $testTargetResourceResult = Test-TargetResource ` -Ensure 'Absent' ` -Path $script:msiLocation ` -ProductId $script:packageId $testTargetResourceResult | Should Be $true } It 'Should return correct value when package is present' { Set-TargetResource -Ensure 'Present' -Path $script:msiLocation -ProductId $script:packageId Test-PackageInstalledById -ProductId $script:packageId | Should Be $true $testTargetResourceResult = Test-TargetResource ` -Ensure 'Present' ` -Path $script:msiLocation ` -ProductId $script:packageId ` $testTargetResourceResult | Should Be $true $testTargetResourceResult = Test-TargetResource ` -Ensure 'Absent' ` -Path $script:msiLocation ` -ProductId $script:packageId ` $testTargetResourceResult | Should Be $false } } Context 'Set-TargetResource' { It 'Should correctly install and remove a .msi package' { Set-TargetResource -Ensure 'Present' -Path $script:msiLocation -ProductId $script:packageId Test-PackageInstalledById -ProductId $script:packageId | Should Be $true $getTargetResourceResult = Get-TargetResource -Path $script:msiLocation -ProductId $script:packageId $getTargetResourceResult.Version | Should Be '1.2.3.4' $getTargetResourceResult.InstalledOn | Should Be ('{0:d}' -f [DateTime]::Now.Date) $getTargetResourceResult.ProductId | Should Be $script:packageId [Math]::Round($getTargetResourceResult.Size, 2) | Should Be 0.03 Set-TargetResource -Ensure 'Absent' -Path $script:msiLocation -ProductId $script:packageId Test-PackageInstalledById -ProductId $script:packageId | Should Be $false } It 'Should throw with incorrect product id' { $wrongPackageId = '{deadbeef-80c6-41e6-a1b9-8bdb8a050272}' { Set-TargetResource -Ensure 'Present' -Path $script:msiLocation -ProductId $wrongPackageId } | Should Throw } It 'Should correctly install and remove a package from a HTTP URL' { $baseUrl = 'http://localhost:1242/' $msiUrl = "$baseUrl" + 'package.msi' $fileServerStarted = $null $job = $null try { 'Http tests:' >> $script:logFile $serverResult = Start-Server -FilePath $script:msiLocation -LogPath $script:logFile -Https $false $fileServerStarted = $serverResult.FileServerStarted $job = $serverResult.Job # Wait for the file server to be ready to receive requests $fileServerStarted.WaitOne(30000) { Set-TargetResource -Ensure 'Present' -Path $baseUrl -ProductId $script:packageId } | Should Throw Set-TargetResource -Ensure 'Present' -Path $msiUrl -ProductId $script:packageId Test-PackageInstalledById -ProductId $script:packageId | Should Be $true Set-TargetResource -Ensure 'Absent' -Path $msiUrl -ProductId $script:packageId Test-PackageInstalledById -ProductId $script:packageId | Should Be $false } finally { <# This must be called after Start-Server to ensure the listening port is closed, otherwise subsequent tests may fail until the machine is rebooted. #> Stop-Server -FileServerStarted $fileServerStarted -Job $job } } It 'Should correctly install and remove a package from a HTTPS URL' -Skip:$script:skipHttpsTest { $baseUrl = 'https://localhost:1243/' $msiUrl = "$baseUrl" + 'package.msi' $fileServerStarted = $null $job = $null try { 'Https tests:' >> $script:logFile $serverResult = Start-Server -FilePath $script:msiLocation -LogPath $script:logFile -Https $true $fileServerStarted = $serverResult.FileServerStarted $job = $serverResult.Job # Wait for the file server to be ready to receive requests $fileServerStarted.WaitOne(30000) { Set-TargetResource -Ensure 'Present' -Path $baseUrl -ProductId $script:packageId } | Should Throw Set-TargetResource -Ensure 'Present' -Path $msiUrl -ProductId $script:packageId Test-PackageInstalledById -ProductId $script:packageId | Should Be $true Set-TargetResource -Ensure 'Absent' -Path $msiUrl -ProductId $script:packageId Test-PackageInstalledById -ProductId $script:packageId | Should Be $false } finally { <# This must be called after Start-Server to ensure the listening port is closed, otherwise subsequent tests may fail until the machine is rebooted. #> Stop-Server -FileServerStarted $fileServerStarted -Job $job } } It 'Should write to the specified log path' { $logPath = Join-Path -Path $TestDrive -ChildPath 'TestMsiLog.txt' if (Test-Path -Path $logPath) { Remove-Item -Path $logPath -Force } Set-TargetResource -Ensure 'Present' -Path $script:msiLocation -LogPath $logPath -ProductId $script:packageId Test-Path -Path $logPath | Should Be $true Get-Content -Path $logPath | Should Not Be $null } It 'Should add space after .MSI installation arguments' { Mock Invoke-Process -ParameterFilter { $Process.StartInfo.Arguments.EndsWith($script:msiArguments) } { return @{ ExitCode = 0 } } Mock Get-ProductEntry { return $script:packageId } $packageParameters = @{ Path = $script:msiLocation ProductId = $script:packageId Arguments = $script:msiArguments } Set-TargetResource -Ensure 'Present' @packageParameters Assert-MockCalled Invoke-Process -ParameterFilter { $Process.StartInfo.Arguments.EndsWith(" $script:msiArguments") } -Scope It } It 'Should not check for product installation when rebooted is required' { Mock Invoke-Process { return 3010 } Mock Get-ProductEntry { } $packageParameters = @{ Path = $script:msiLocation ProductId = $script:packageId } { Set-TargetResource -Ensure 'Present' @packageParameters } | Should Not Throw } It 'Should install package using user credentials when specified' { Mock Invoke-PInvoke { } Mock Get-ProductEntry { return $script:packageId } $packageCredential = [System.Management.Automation.PSCredential]::Empty $packageParameters = @{ Path = $script:msiLocation ProductId = $script:packageId RunAsCredential = $packageCredential } Set-TargetResource -Ensure 'Present' @packageParameters Assert-MockCalled Invoke-PInvoke -ParameterFilter { $RunAsCredential -eq $packageCredential} -Scope It } } } } } finally { Exit-DscResourceTestEnvironment -TestEnvironment $script:testEnvironment } |