adding msiPackage

This commit is contained in:
Mariah Breakey
2017-04-13 12:35:27 -07:00
committed by Katie Keim
parent 19ffca486a
commit 8303c0e7fc
17 changed files with 5342 additions and 2 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,23 @@
[ClassVersion("1.0.0"),FriendlyName("MsiPackage")]
class MSFT_MsiPackage : OMI_BaseResource
{
[Key, Description("The identifying number used to find the package, usually a GUID.")] String ProductId;
[Required, Description("The path to the MSI file that should be installed or uninstalled.")] String Path;
[Write, Description("Specifies whether or not the MSI file should be installed or uninstalled."), ValueMap{"Present", "Absent"}, Values{"Present", "Absent"}] String Ensure;
[Write, Description("The arguments to be passed to the MSI package during installation or uninstallation.")] String Arguments;
[Write, Description("The credential of a user account to be used to mount a UNC path if needed."), EmbeddedInstance("MSFT_Credential")] String Credential;
[Write, Description("The path to the log file to log the output from the MSI execution.")] String LogPath;
[Write, Description("The expected hash value of the MSI file at the given path.")] String FileHash;
[Write, Description("The algorithm used to generate the given hash value."), ValueMap{"SHA1", "SHA256", "SHA384", "SHA512", "MD5", "RIPEMD160"}, Values{"SHA1", "SHA256", "SHA384", "SHA512", "MD5", "RIPEMD160"}] String HashAlgorithm;
[Write, Description("The subject that should match the signer certificate of the digital signature of the MSI file.")] String SignerSubject;
[Write, Description("The certificate thumbprint that should match the signer certificate of the digital signature of the MSI file.")] String SignerThumbprint;
[Write, Description("PowerShell code that should be used to validate SSL certificates for paths using HTTPS.")] String ServerCertificateValidationCallback;
[Write, Description("The credential of a user account under which to run the installation or uninstallation of the MSI package."), EmbeddedInstance("MSFT_Credential")] String RunAsCredential;
[Read, Description("The display name of the MSI package.")] String Name;
[Read, Description("The path to the MSI package.")] String InstallSource;
[Read, Description("The date that the MSI package was installed on or serviced on, whichever is later.")] String InstalledOn;
[Read, Description("The size of the MSI package in MB.")] UInt32 Size;
[Read, Description("The version number of the MSI package.")] String Version;
[Read, Description("The description of the MSI package.")] String PackageDescription;
[Read, Description("The publisher of the MSI package.")] String Publisher;
};
@@ -0,0 +1,44 @@
# Localized resources for MSFT_MsiPackage
ConvertFrom-StringData @'
CheckingFileHash = Checking file '{0}' for expected {2} hash value of {1}
CheckingFileSignature = Checking file '{0}' for valid digital signature
CopyingTheSchemeStreamBytesToTheDiskCache = Copying the stream bytes to the disk cache
CouldNotGetResponseFromWebRequest = An error occurred while trying to get the {0} response for file {1}
CouldNotOpenDestFile = Could not open the file {0} for writing
CouldNotOpenLog = The specified LogPath ({0}) could not be opened
CouldNotStartProcess = The process {0} could not be started
CreatingCacheLocation = Creating cache location
CreatingTheDestinationCacheFile = Creating the destination cache file
CreatingTheSchemeStream = Creating the {0} stream
ErrorCopyingDataToFile = Encountered an error while copying the response to the output stream
FileHasValidSignature = File '{0}' contains a valid digital signature. Signer Thumbprint: {1}, Subject: {2}
GetTargetResourceFound = Successfully retrieved package {0}
GetTargetResourceNotFound = Unable to find package: {0}
GettingTheSchemeResponseStream = Getting the {0} response stream
InvalidBinaryType = The specified Path ({0}) does not appear to specify an MSI file and as such is not supported
InvalidFileHash = File '{0}' does not match expected {2} hash value of {1}
InvalidFileSignature = File '{0}' does not have a valid Authenticode signature. Status: {1}
InvalidId = The specified IdentifyingNumber ({0}) does not match the IdentifyingNumber ({1}) in the MSI file
InvalidIdentifyingNumber = The specified IdentifyingNumber ({0}) is not a valid GUID
InvalidPath = The specified Path ({0}) is not in a valid format. Valid formats are local paths, UNC, HTTP, and HTTPS
MachineRequiresReboot = The machine requires a reboot
PackageAppearsInstalled = The package {0} is installed
PackageConfigurationStarting = Package configuration starting
PackageDoesNotAppearInstalled = The package {0} is not installed
PathDoesNotExist = The given Path ({0}) could not be found
PackageInstalled = Package has been installed
PackageUninstalled = Package has been uninstalled
ParsedProductIdAsIdentifyingNumber = Parsed {0} as {1}
ParsingProductIdAsAnIdentifyingNumber = Parsing {0} as an identifyingNumber
PostValidationError = Package from {0} was installed, but the specified ProductId does not match package details
RedirectingPackagePathToCacheFileLocation = Redirecting package path to cache file location
SettingAuthenticationLevel = Setting authentication level to None
SettingCertificateValidationCallback = Assigning user-specified certificate verification callback
SettingDefaultCredential = Setting default credential
StartingWithStartInfoFileNameStartInfoArguments = Starting {0} with {1}
ThePathExtensionWasPathExt = The path extension was {0}
TheUriSchemeWasUriScheme = The uri scheme was {0}
WrongSignerSubject = File '{0}' was not signed by expected signer subject '{1}'
WrongSignerThumbprint = File '{0}' was not signed by expected signer certificate thumbprint '{1}'
'@
@@ -0,0 +1,28 @@
<#
.SYNOPSIS
Installs the MSI file with the product ID: '{DEADBEEF-80C6-41E6-A1B9-8BDB8A05027F}'
at the path: 'file://Examples/example.msi'.
Note that the MSI file with the given product ID must already exist at the specified path.
The product ID and path value in this file are provided for example purposes only and will
need to be replaced with valid values.
You can run the following command to get a list of all available MSIs on
your system with the correct Path (LocalPackage) and product ID (IdentifyingNumber):
Get-WmiObject Win32_Product | Format-Table IdentifyingNumber, Name, LocalPackage
#>
Configuration Sample_MsiPackage_InstallPackageFromFile
{
Import-DscResource -ModuleName 'PSDscResources'
Node localhost
{
MsiPackage MsiPackage1
{
ProductId = '{DEADBEEF-80C6-41E6-A1B9-8BDB8A05027F}'
Path = 'file://Examples/example.msi'
Ensure = 'Present'
}
}
}
@@ -0,0 +1,23 @@
<#
.SYNOPSIS
Installs the MSI file with the product ID: '{DEADBEEF-80C6-41E6-A1B9-8BDB8A05027F}'
at the path: 'http://Examples/example.msi'.
Note that the MSI file with the given product ID must already exist on the server.
The product ID and path value in this file are provided for example purposes only and will
need to be replaced with valid values.
#>
Configuration Sample_MsiPackage_InstallPackageFromHttp
{
Import-DscResource -ModuleName 'PSDscResources'
Node localhost
{
MsiPackage MsiPackage1
{
ProductId = '{DEADBEEF-80C6-41E6-A1B9-8BDB8A05027F}'
Path = 'http://Examples/example.msi'
Ensure = 'Present'
}
}
}
@@ -0,0 +1,28 @@
<#
.SYNOPSIS
Uninstalls the MSI file with the product ID: '{DEADBEEF-80C6-41E6-A1B9-8BDB8A05027F}'
at the path: 'file://Examples/example.msi'.
Note that the MSI file with the given product ID must already exist at the specified path.
The product ID and path value in this file are provided for example purposes only and will
need to be replaced with valid values.
You can run the following command to get a list of all available MSIs on
your system with the correct Path (LocalPackage) and product ID (IdentifyingNumber):
Get-WmiObject Win32_Product | Format-Table IdentifyingNumber, Name, LocalPackage
#>
Configuration Sample_MsiPackage_UninstallPackageFromFile
{
Import-DscResource -ModuleName 'PSDscResources'
Node localhost
{
MsiPackage MsiPackage1
{
ProductId = '{DEADBEEF-80C6-41E6-A1B9-8BDB8A05027F}'
Path = 'file://Examples/example.msi'
Ensure = 'Absent'
}
}
}
@@ -0,0 +1,23 @@
<#
.SYNOPSIS
Uninstalls the MSI file with the product ID: '{DEADBEEF-80C6-41E6-A1B9-8BDB8A05027F}'
at the path: 'https://Examples/example.msi'.
Note that the MSI file with the given product ID must already exist on the server.
The product ID and path value in this file are provided for example purposes only and will
need to be replaced with valid values.
#>
Configuration Sample_MsiPackage_UninstallPackageFromHttps
{
Import-DscResource -ModuleName 'PSDscResources'
Node localhost
{
MsiPackage MsiPackage1
{
ProductId = '{DEADBEEF-80C6-41E6-A1B9-8BDB8A05027F}'
Path = 'https://Examples/example.msi'
Ensure = 'Absent'
}
}
}
+1 -1
View File
@@ -73,7 +73,7 @@ VariablesToExport = '*'
AliasesToExport = @()
# DSC resources to export from this module
DscResourcesToExport = @( 'Archive', 'Environment', 'Group', 'GroupSet', 'Registry', 'Script', 'Service', 'ServiceSet', 'User', 'WindowsFeature', 'WindowsFeatureSet', 'WindowsOptionalFeature', 'WindowsOptionalFeatureSet', 'WindowsPackageCab', 'WindowsProcess', 'ProcessSet' )
DscResourcesToExport = @( 'Archive', 'Environment', 'Group', 'GroupSet', 'MsiPackage', 'Registry', 'Script', 'Service', 'ServiceSet', 'User', 'WindowsFeature', 'WindowsFeatureSet', 'WindowsOptionalFeature', 'WindowsOptionalFeatureSet', 'WindowsPackageCab', 'WindowsProcess', 'ProcessSet' )
# List of all modules packaged with this module
# ModuleList = @()
+42
View File
@@ -41,6 +41,7 @@ Please check out the common DSC Resources [contributing guidelines](https://gith
* [Environment](#environment): Provides a mechanism to configure and manage environment variables for a machine or process.
* [Group](#group): Provides a mechanism to manage local groups on a target node.
* [GroupSet](#groupset): Provides a mechanism to configure and manage multiple Group resources with common settings but different names.
* [MsiPackage](#msipackage): Provides a mechanism to install and uninstall MSI packages.
* [Registry](#registry): Provides a mechanism to manage registry keys and values on a target node.
* [Script](#script): Provides a mechanism to run PowerShell script blocks on a target node.
* [Service](#service): Provides a mechanism to configure and manage Windows services on a target node.
@@ -177,6 +178,46 @@ None
* [Add members to multiple groups](https://github.com/PowerShell/PSDscResources/blob/master/Examples/Sample_GroupSet_AddMembers.ps1)
### MsiPackage
Provides a mechanism to install and uninstall MSI packages.
#### Requirements
None
#### Parameters
* **[String] ProductId** _(Key)_: The identifying number used to find the package, usually a GUID.
* **[String] Path** _(Required)_: The path to the MSI file that should be installed or uninstalled.
* **[String] Ensure** _(Write)_: Specifies whether or not the MSI file should be installed or not. To install the MSI file, specify this property as Present. To uninstall the .msi file, specify this property as Absent. The default value is Present. { *Present* | Absent }.
* **[String] Arguments** _(Write)_: The arguments to be passed to the MSI package during installation or uninstallation if needed.
* **[System.Management.Automation.PSCredential] Credential** _(Write)_: The credential of a user account to be used to mount a UNC path if needed.
* **[String] LogPath** _(Write)_: The path to the log file to log the output from the MSI execution.
* **[String] FileHash** _(Write)_: The expected hash value of the MSI file at the given path.
* **[String] HashAlgorithm** _(Write)_: The algorithm used to generate the given hash value.
* **[String] SignerSubject** _(Write)_: The subject that should match the signer certificate of the digital signature of the MSI file.
* **[String] SignerThumbprint** _(Write)_: The certificate thumbprint that should match the signer certificate of the digital signature of the MSI file.
* **[String] ServerCertificateValidationCallback** _(Write)_: PowerShell code that should be used to validate SSL certificates for paths using HTTPS.
* **[System.Management.Automation.PSCredential] RunAsCredential** _(Write)_: The credential of a user account under which to run the installation or uninstallation of the MSI package.
#### Read-Only Properties from Get-TargetResource
* **[String] Name** _(Read)_: The display name of the MSI package.
* **[String] InstallSource** _(Read)_: The path to the MSI package.
* **[String] InstalledOn** _(Read)_: The date that the MSI package was installed on or serviced on, whichever is later.
* **[UInt32] Size** _(Read)_: The size of the MSI package in MB.
* **[String] Version** _(Read)_: The version number of the MSI package.
* **[String] PackageDescription** _(Read)_: The description of the MSI package.
* **[String] Publisher** _(Read)_: The publisher of the MSI package.
#### Examples
* [Install the MSI file with the given ID at the given Path](https://github.com/PowerShell/PSDscResources/blob/dev/Examples/Sample_MsiPackage_InstallPackageFromFile.ps1)
* [Uninstall the MSI file with the given ID at the given Path](https://github.com/PowerShell/PSDscResources/blob/dev/Examples/Sample_MsiPackage_UninstallPackageFromFile.ps1)
* [Install the MSI file with the given ID at the given HTTP URL](https://github.com/PowerShell/PSDscResources/blob/dev/Examples/Sample_MsiPackage_InstallPackageFromHttp.ps1)
* [Uninstall the MSI file with the given ID at the given HTTPS URL](https://github.com/PowerShell/PSDscResources/blob/dev/Examples/Sample_MsiPackage_UnstallPackageFromHttps.ps1)
### Registry
Provides a mechanism to manage registry keys and values on a target node.
@@ -538,6 +579,7 @@ The following parameters will be the same for each process in the set:
* Archive:
* Fixed a minor bug in the unit tests where sometimes the incorrect DateTime format was used.
* Added MsiPackage
### 2.5.0.0
@@ -0,0 +1,643 @@
<#
Please note that some of these tests depend on each other.
They must be run in the order given - if one test fails, subsequent tests may
also fail.
#>
$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
}
Describe 'MsiPackage End to End Tests' {
BeforeAll {
# Import CommonTestHelper
$testsFolderFilePath = Split-Path $PSScriptRoot -Parent
$testHelperFolderFilePath = Join-Path -Path $testsFolderFilePath -ChildPath 'TestHelpers'
$commonTestHelperFilePath = Join-Path -Path $testHelperFolderFilePath -ChildPath 'CommonTestHelper.psm1'
Import-Module -Name $commonTestHelperFilePath
$script:testEnvironment = Enter-DscResourceTestEnvironment `
-DscResourceModuleName 'PSDscResources' `
-DscResourceName 'MSFT_MsiPackage' `
-TestType 'Integration'
# Import MsiPackage resource module for Test-TargetResource
$moduleRootFilePath = Split-Path -Path $testsFolderFilePath -Parent
$dscResourcesFolderFilePath = Join-Path -Path $moduleRootFilePath -ChildPath 'DscResources'
$msiPackageResourceFolderFilePath = Join-Path -Path $dscResourcesFolderFilePath -ChildPath 'MSFT_MsiPackage'
$msiPackageResourceModuleFilePath = Join-Path -Path $msiPackageResourceFolderFilePath -ChildPath 'MSFT_MsiPackage.psm1'
Import-Module -Name $msiPackageResourceModuleFilePath -Force
# Import the MsiPackage test helper
$packageTestHelperFilePath = Join-Path -Path $testHelperFolderFilePath -ChildPath 'MSFT_MsiPackageResource.TestHelper.psm1'
Import-Module -Name $packageTestHelperFilePath -Force
# Set up the paths to the test configurations
$script:configurationFilePathNoOptionalParameters = Join-Path -Path $PSScriptRoot -ChildPath 'MSFT_MsiPackage_NoOptionalParameters'
$script:configurationFilePathLogPath = Join-Path -Path $PSScriptRoot -ChildPath 'MSFT_MsiPackage_LogPath'
<#
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:environmentInIncorrectStateErrorMessage = 'The current environment is not in the expected state for this test - either something was setup incorrectly on your machine or a previous test failed - results after this may be invalid.'
$script:msiName = 'DSCSetupProject.msi'
$script:msiLocation = Join-Path -Path $TestDrive -ChildPath $script:msiName
$script:packageId = '{deadbeef-80c6-41e6-a1b9-8bdb8a05027f}'
$null = New-TestMsi -DestinationPath $script:msiLocation
# Clear the log file
'Beginning integration tests' > $script:logFile
}
AfterAll {
# Remove the test MSI if it is still installed
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.'
}
Exit-DscResourceTestEnvironment -TestEnvironment $script:testEnvironment
}
Context 'Uninstall package that is already Absent' {
$configurationName = 'RemoveAbsentMsiPackage'
$msiPackageParameters = @{
ProductId = $script:packageId
Path = $script:msiLocation
Ensure = 'Absent'
}
It 'Should return True from Test-TargetResource with the same parameters before configuration' {
$testTargetResourceInitialResult = MSFT_MsiPackage\Test-TargetResource @msiPackageParameters
$testTargetResourceInitialResult | Should Be $true
if ($testTargetResourceInitialResult -ne $true)
{
<#
Not throwing an error here since the tests should still run correctly after this,
we just want to notify the user that the tests aren't necessarily testing what
they should be
#>
Write-Error -Message $script:environmentInIncorrectStateErrorMessage
}
}
It 'Package should not exist on the machine before configuration is run' {
Test-PackageInstalledById -ProductId $script:packageId | Should Be $false
}
It 'Should compile and run configuration' {
{
. $script:configurationFilePathNoOptionalParameters -ConfigurationName $configurationName
& $configurationName -OutputPath $TestDrive @msiPackageParameters
Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force
} | Should Not Throw
}
It 'Should return True from Test-TargetResource with the same parameters after configuration' {
MSFT_MsiPackage\Test-TargetResource @msiPackageParameters | Should Be $true
}
It 'Package should not exist on the machine' {
Test-PackageInstalledById -ProductId $script:packageId | Should Be $false
}
}
Context 'Install package that is not installed yet' {
$configurationName = 'InstallMsiPackage'
$msiPackageParameters = @{
ProductId = $script:packageId
Path = $script:msiLocation
Ensure = 'Present'
}
It 'Should return False from Test-TargetResource with the same parameters before configuration' {
$testTargetResourceInitialResult = MSFT_MsiPackage\Test-TargetResource @msiPackageParameters
$testTargetResourceInitialResult | Should Be $false
if ($testTargetResourceInitialResult -ne $false)
{
<#
Not throwing an error here since the tests should still run correctly after this,
we just want to notify the user that the tests aren't necessarily testing what
they should be
#>
Write-Error -Message $script:environmentInIncorrectStateErrorMessage
}
}
It 'Package should not exist on the machine before configuration is run' {
Test-PackageInstalledById -ProductId $script:packageId | Should Be $false
}
It 'Should compile and run configuration' {
{
. $script:configurationFilePathNoOptionalParameters -ConfigurationName $configurationName
& $configurationName -OutputPath $TestDrive @msiPackageParameters
Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force
} | Should Not Throw
}
It 'Should return True from Test-TargetResource with the same parameters after configuration' {
MSFT_MsiPackage\Test-TargetResource @msiPackageParameters | Should Be $true
}
It 'Package should exist on the machine' {
Test-PackageInstalledById -ProductId $script:packageId | Should Be $true
}
}
Context 'Install package that is already installed' {
$configurationName = 'InstallExistingMsiPackage'
$msiPackageParameters = @{
ProductId = $script:packageId
Path = $script:msiLocation
Ensure = 'Present'
}
It 'Should return True from Test-TargetResource with the same parameters before configuration' {
$testTargetResourceInitialResult = MSFT_MsiPackage\Test-TargetResource @msiPackageParameters
$testTargetResourceInitialResult | Should Be $true
if ($testTargetResourceInitialResult -ne $true)
{
<#
Not throwing an error here since the tests should still run correctly after this,
we just want to notify the user that the tests aren't necessarily testing what
they should be
#>
Write-Error -Message $script:environmentInIncorrectStateErrorMessage
}
}
It 'Package should exist on the machine before configuration is run' {
Test-PackageInstalledById -ProductId $script:packageId | Should Be $true
}
It 'Should compile and run configuration' {
{
. $script:configurationFilePathNoOptionalParameters -ConfigurationName $configurationName
& $configurationName -OutputPath $TestDrive @msiPackageParameters
Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force
} | Should Not Throw
}
It 'Should return True from Test-TargetResource with the same parameters after configuration' {
MSFT_MsiPackage\Test-TargetResource @msiPackageParameters | Should Be $true
}
It 'Package should exist on the machine' {
Test-PackageInstalledById -ProductId $script:packageId | Should Be $true
}
}
Context 'Uninstall package that is installed' {
$configurationName = 'UninstallExistingMsiPackage'
$msiPackageParameters = @{
ProductId = $script:packageId
Path = $script:msiLocation
Ensure = 'Absent'
}
It 'Should return False from Test-TargetResource with the same parameters before configuration' {
$testTargetResourceInitialResult = MSFT_MsiPackage\Test-TargetResource @msiPackageParameters
$testTargetResourceInitialResult | Should Be $false
if ($testTargetResourceInitialResult -ne $false)
{
<#
Not throwing an error here since the tests should still run correctly after this,
we just want to notify the user that the tests aren't necessarily testing what
they should be
#>
Write-Error -Message $script:environmentInIncorrectStateErrorMessage
}
}
It 'Package should exist on the machine before configuration is run' {
Test-PackageInstalledById -ProductId $script:packageId | Should Be $true
}
It 'Should compile and run configuration' {
{
. $script:configurationFilePathNoOptionalParameters -ConfigurationName $configurationName
& $configurationName -OutputPath $TestDrive @msiPackageParameters
Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force
} | Should Not Throw
}
It 'Should return True from Test-TargetResource with the same parameters after configuration' {
MSFT_MsiPackage\Test-TargetResource @msiPackageParameters | Should Be $true
}
It 'Package should not exist on the machine' {
Test-PackageInstalledById -ProductId $script:packageId | Should Be $false
}
}
Context 'Install package that is not installed and write to specified log file' {
$configurationName = 'InstallWithLogFile'
$logPath = Join-Path -Path $TestDrive -ChildPath 'TestMsiLog.txt'
if (Test-Path -Path $logPath)
{
Remove-Item -Path $logPath -Force
}
$msiPackageParameters = @{
ProductId = $script:packageId
Path = $script:msiLocation
Ensure = 'Present'
LogPath = $logPath
}
It 'Should return False from Test-TargetResource with the same parameters before configuration' {
$testTargetResourceInitialResult = MSFT_MsiPackage\Test-TargetResource @msiPackageParameters
$testTargetResourceInitialResult | Should Be $false
if ($testTargetResourceInitialResult -ne $false)
{
<#
Not throwing an error here since the tests should still run correctly after this,
we just want to notify the user that the tests aren't necessarily testing what
they should be
#>
Write-Error -Message $script:environmentInIncorrectStateErrorMessage
}
}
It 'Package should not exist on the machine before configuration is run' {
Test-PackageInstalledById -ProductId $script:packageId | Should Be $false
}
It 'Should compile and run configuration' {
{
. $script:configurationFilePathLogPath -ConfigurationName $configurationName
& $configurationName -OutputPath $TestDrive @msiPackageParameters
Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force
} | Should Not Throw
}
It 'Should return True from Test-TargetResource with the same parameters after configuration' {
MSFT_MsiPackage\Test-TargetResource @msiPackageParameters | Should Be $true
}
It 'Should have created the log file' {
Test-Path -Path $logPath | Should Be $true
}
It 'Package should exist on the machine' {
Test-PackageInstalledById -ProductId $script:packageId | Should Be $true
}
}
Context 'Uninstall package that is installed and write to specified log file' {
$configurationName = 'InstallWithLogFile'
$logPath = Join-Path -Path $TestDrive -ChildPath 'TestMsiLog.txt'
if (Test-Path -Path $logPath)
{
Remove-Item -Path $logPath -Force
}
$msiPackageParameters = @{
ProductId = $script:packageId
Path = $script:msiLocation
Ensure = 'Absent'
LogPath = $logPath
}
It 'Should return False from Test-TargetResource with the same parameters before configuration' {
$testTargetResourceInitialResult = MSFT_MsiPackage\Test-TargetResource @msiPackageParameters
$testTargetResourceInitialResult | Should Be $false
if ($testTargetResourceInitialResult -ne $false)
{
<#
Not throwing an error here since the tests should still run correctly after this,
we just want to notify the user that the tests aren't necessarily testing what
they should be
#>
Write-Error -Message $script:environmentInIncorrectStateErrorMessage
}
}
It 'Package should exist on the machine before configuration is run' {
Test-PackageInstalledById -ProductId $script:packageId | Should Be $true
}
It 'Should compile and run configuration' {
{
. $script:configurationFilePathLogPath -ConfigurationName $configurationName
& $configurationName -OutputPath $TestDrive @msiPackageParameters
Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force
} | Should Not Throw
}
It 'Should return True from Test-TargetResource with the same parameters after configuration' {
MSFT_MsiPackage\Test-TargetResource @msiPackageParameters | Should Be $true
}
It 'Should have created the log file' {
Test-Path -Path $logPath | Should Be $true
}
It 'Package should not exist on the machine' {
Test-PackageInstalledById -ProductId $script:packageId | Should Be $false
}
}
Context 'Install package from HTTP Url' {
$configurationName = 'UninstallExistingMsiPackageFromHttp'
$baseUrl = 'http://localhost:1242/'
$msiUrl = "$baseUrl" + 'package.msi'
$fileServerStarted = $null
$job = $null
$msiPackageParameters = @{
ProductId = $script:packageId
Path = $msiUrl
Ensure = 'Present'
}
It 'Should return False from Test-TargetResource with the same parameters before configuration' {
$testTargetResourceInitialResult = MSFT_MsiPackage\Test-TargetResource @msiPackageParameters
$testTargetResourceInitialResult | Should Be $false
if ($testTargetResourceInitialResult -ne $false)
{
<#
Not throwing an error here since the tests should still run correctly after this,
we just want to notify the user that the tests aren't necessarily testing what
they should be
#>
Write-Error -Message $script:environmentInIncorrectStateErrorMessage
}
}
It 'Package should not exist on the machine before configuration is run' {
Test-PackageInstalledById -ProductId $script:packageId | Should Be $false
}
try
{
$serverResult = Start-Server -FilePath $script:msiLocation -LogPath $script:logFile -Https $false
$fileServerStarted = $serverResult.FileServerStarted
$job = $serverResult.Job
$fileServerStarted.WaitOne(30000)
It 'Should compile and run configuration' {
{
. $script:configurationFilePathNoOptionalParameters -ConfigurationName $configurationName
& $configurationName -OutputPath $TestDrive @msiPackageParameters
Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force
} | Should Not Throw
}
}
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 return True from Test-TargetResource with the same parameters after configuration' {
MSFT_MsiPackage\Test-TargetResource @msiPackageParameters | Should Be $true
}
It 'Package should exist on the machine' {
Test-PackageInstalledById -ProductId $script:packageId | Should Be $true
}
}
Context 'Uninstall Msi package from HTTP Url' {
$configurationName = 'InstallMsiPackageFromHttp'
$baseUrl = 'http://localhost:1242/'
$msiUrl = "$baseUrl" + 'package.msi'
$fileServerStarted = $null
$job = $null
$msiPackageParameters = @{
ProductId = $script:packageId
Path = $msiUrl
Ensure = 'Absent'
}
It 'Should return False from Test-TargetResource with the same parameters before configuration' {
$testTargetResourceInitialResult = MSFT_MsiPackage\Test-TargetResource @msiPackageParameters
$testTargetResourceInitialResult | Should Be $false
if ($testTargetResourceInitialResult -ne $false)
{
<#
Not throwing an error here since the tests should still run correctly after this,
we just want to notify the user that the tests aren't necessarily testing what
they should be
#>
Write-Error -Message $script:environmentInIncorrectStateErrorMessage
}
}
It 'Package should exist on the machine before configuration is run' {
Test-PackageInstalledById -ProductId $script:packageId | Should Be $true
}
try
{
$serverResult = Start-Server -FilePath $script:msiLocation -LogPath $script:logFile -Https $false
$fileServerStarted = $serverResult.FileServerStarted
$job = $serverResult.Job
$fileServerStarted.WaitOne(30000)
It 'Should compile and run configuration' {
{
. $script:configurationFilePathNoOptionalParameters -ConfigurationName $configurationName
& $configurationName -OutputPath $TestDrive @msiPackageParameters
Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force
} | Should Not Throw
}
}
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 return true from Test-TargetResource with the same parameters after configuration' {
MSFT_MsiPackage\Test-TargetResource @msiPackageParameters | Should Be $true
}
It 'Package should not exist on the machine' {
Test-PackageInstalledById -ProductId $script:packageId | Should Be $false
}
}
Context 'Install Msi package from HTTPS Url' {
$configurationName = 'InstallMsiPackageFromHttpS'
$baseUrl = 'https://localhost:1243/'
$msiUrl = "$baseUrl" + 'package.msi'
$fileServerStarted = $null
$job = $null
$msiPackageParameters = @{
ProductId = $script:packageId
Path = $msiUrl
Ensure = 'Present'
}
It 'Should return False from Test-TargetResource with the same parameters before configuration' {
$testTargetResourceInitialResult = MSFT_MsiPackage\Test-TargetResource @msiPackageParameters
$testTargetResourceInitialResult | Should Be $false
if ($testTargetResourceInitialResult -ne $false)
{
<#
Not throwing an error here since the tests should still run correctly after this,
we just want to notify the user that the tests aren't necessarily testing what
they should be
#>
Write-Error -Message $script:environmentInIncorrectStateErrorMessage
}
}
It 'Package should not exist on the machine before configuration is run' {
Test-PackageInstalledById -ProductId $script:packageId | Should Be $false
}
try
{
$serverResult = Start-Server -FilePath $script:msiLocation -LogPath $script:logFile -Https $true
$fileServerStarted = $serverResult.FileServerStarted
$job = $serverResult.Job
$fileServerStarted.WaitOne(30000)
It 'Should compile and run configuration' {
{
. $script:configurationFilePathNoOptionalParameters -ConfigurationName $configurationName
& $configurationName -OutputPath $TestDrive @msiPackageParameters
Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force
} | Should Not Throw
}
}
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 return true from Test-TargetResource with the same parameters after configuration' {
MSFT_MsiPackage\Test-TargetResource @msiPackageParameters | Should Be $true
}
It 'Package should exist on the machine' {
Test-PackageInstalledById -ProductId $script:packageId | Should Be $true
}
}
Context 'Uninstall Msi package from HTTPS Url' {
$configurationName = 'UninstallMsiPackageFromHttps'
$baseUrl = 'https://localhost:1243/'
$msiUrl = "$baseUrl" + 'package.msi'
$fileServerStarted = $null
$job = $null
$msiPackageParameters = @{
ProductId = $script:packageId
Path = $msiUrl
Ensure = 'Absent'
}
It 'Should return False from Test-TargetResource with the same parameters before configuration' {
$testTargetResourceInitialResult = MSFT_MsiPackage\Test-TargetResource @msiPackageParameters
$testTargetResourceInitialResult | Should Be $false
if ($testTargetResourceInitialResult -ne $false)
{
<#
Not throwing an error here since the tests should still run correctly after this,
we just want to notify the user that the tests aren't necessarily testing what
they should be
#>
Write-Error -Message $script:environmentInIncorrectStateErrorMessage
}
}
It 'Package should exist on the machine before configuration is run' {
Test-PackageInstalledById -ProductId $script:packageId | Should Be $true
}
try
{
$serverResult = Start-Server -FilePath $script:msiLocation -LogPath $script:logFile -Https $true
$fileServerStarted = $serverResult.FileServerStarted
$job = $serverResult.Job
$fileServerStarted.WaitOne(30000)
It 'Should compile and run configuration' {
{
. $script:configurationFilePathNoOptionalParameters -ConfigurationName $configurationName
& $configurationName -OutputPath $TestDrive @msiPackageParameters
Start-DscConfiguration -Path $TestDrive -ErrorAction 'Stop' -Wait -Force
} | Should Not Throw
}
}
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 return true from Test-TargetResource with the same parameters after configuration' {
MSFT_MsiPackage\Test-TargetResource @msiPackageParameters | Should Be $true
}
It 'Package should not exist on the machine' {
Test-PackageInstalledById -ProductId $script:packageId | Should Be $false
}
}
}
@@ -0,0 +1,309 @@
$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
}
@@ -0,0 +1,43 @@
param
(
[Parameter(Mandatory = $true)]
[String]
$ConfigurationName
)
Configuration $ConfigurationName
{
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[String]
$ProductId,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[String]
$Path,
[ValidateSet('Present', 'Absent')]
[String]
$Ensure = 'Present',
[Parameter(Mandatory = $true)]
[String]
$LogPath
)
Import-DscResource -ModuleName 'PSDscResources'
Node localhost
{
MsiPackage MsiPackage1
{
ProductId = $ProductId
Path = $Path
Ensure = $Ensure
LogPath = $LogPath
}
}
}
@@ -0,0 +1,38 @@
param
(
[Parameter(Mandatory = $true)]
[String]
$ConfigurationName
)
Configuration $ConfigurationName
{
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[String]
$ProductId,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[String]
$Path,
[ValidateSet('Present', 'Absent')]
[String]
$Ensure = 'Present'
)
Import-DscResource -ModuleName 'PSDscResources'
Node localhost
{
MsiPackage MsiPackage1
{
ProductId = $ProductId
Path = $Path
Ensure = $Ensure
}
}
}
+386 -1
View File
@@ -10,6 +10,386 @@ Set-StrictMode -Version 'Latest'
#>
$script:appVeyorAdministratorCredential = $null
<#
String data for unit test names to be used with the generic test functions.
Maps command names to the appropriate test names to insert when checking that
the correct mocks are called.
In the future we will move this data out of the commonTestHelper file and into the
corresponding test file or its own file. For now, it is much easier to access this
way rather than passing it around.
#>
data testStrings
{
ConvertFrom-StringData -StringData @'
Assert-FileHashValid = assert that the file hash is valid
Assert-FileSignatureValid = assert that the file signature is valid
Assert-FileValid = assert that the file is valid
Assert-PathExtensionValid = assert that the specified path extension is valid
Close-Stream = close the stream
Convert-PathToUri = convert the path to a URI
Convert-ProductIdToIdentifyingNumber = convert the product ID to the identifying number
Copy-ResponseStreamToFileStream = copy the response to the outstream
Get-ItemProperty = retrieve {0}
Get-MsiProductCode = retrieve the MSI product code
Get-ProductEntry = retrieve the product entry
Get-ProductEntryInfo = retrieve the product entry info
Get-ProductEntryValue = retrieve the value of the product entry property
Get-ScriptBlock = retrieve the script block
Get-WebRequest = retrieve the WebRequest object
Get-WebRequestResponse = retrieve the web request response
Get-WebRequestResponseStream = retrieve the WebRequest response stream
Invoke-CimMethod = attempt to invoke a cim method to check if reboot is required
Invoke-PInvoke = attempt to {0} the MSI package under the user
Invoke-Process = attempt to {0} the MSI package under the process
New-Item = create a new {0}
New-LogFile = create a new log file
New-Object = create a new {0}
New-PSDrive = create a new PS Drive
Remove-Item = remove {0}
Remove-PSDrive = remove the PS drive
Start-MsiProcess = start the MSI process
Test-Path = test that the path {0} exists
'@
}
<#
.SYNOPSIS
Retrieves the name of the test for asserting that the given function is called.
.PARAMETER IsCalled
Indicates whether the function should be called or not.
.PARAMETER Custom
An optional string to include in the test name to make the name more descriptive.
Can only be used by commands that have a variable in their string data name.
#>
function Get-TestName
{
[OutputType([String])]
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[String]
$Command,
[Boolean]
$IsCalled = $true,
[String]
$Custom = ''
)
$testName = ''
if (-not [String]::IsNullOrEmpty($Custom))
{
$testName = ($testStrings.$Command -f $Custom)
}
else
{
$testName = $testStrings.$Command
}
if ($IsCalled)
{
return 'Should ' + $testName
}
else
{
return 'Should not ' + $testName
}
}
<#
.SYNOPSIS
Tests that each mock in MocksCalled is called the expected number of times.
.PARAMETER MocksCalled
An array of the mocked commands that should be called or not called.
Each item in the array is a hashtable that contains the name of the command
being mocked and the number of times it is called (can be 0).
#>
function Invoke-ExpectedMocksAreCalledTest
{
[CmdletBinding()]
param
(
[Hashtable[]]
$MocksCalled
)
foreach ($mock in $MocksCalled)
{
$testName = Get-TestName -Command $mock.Command -IsCalled $mock.Times
if ($mock.Keys -contains 'Custom')
{
$testName = Get-TestName -Command $mock.Command -IsCalled $mock.Times -Custom $mock.Custom
}
It $testName {
Assert-MockCalled -CommandName $mock.Command -Exactly $mock.Times -Scope 'Context'
}
}
}
<#
.SYNOPSIS
Performs generic tests for the given function, including checking that the
function does not throw and checking that all mocks are called the expected
number of times.
.PARAMETER Function
The function to be called. Must be in format:
{ Param($hashTableOfParamsToPass) Function-Name @hashTableOfParamsToPass }
For example:
{ Param($testLogPath) New-LogFile $script:testPath }
or
{ Param($startMsiProcessParameters) Start-MsiProcess @startMsiProcessParameters }
.PARAMETER FunctionParameters
The parameters that should be passed to the function for this test. Should match
what is passed in the Function parameter.
.PARAMETER MocksCalled
An array of the mocked commands that should be called for this test.
Each item in the array is a hashtable that contains the name of the command
being mocked, the number of times it is called (can be 0) and, optionally,
an extra custom string to make the test name more descriptive. The custom
string will only work if the command has a corresponding variable in the
string data name.
.PARAMETER ShouldThrow
Indicates whether the function should throw or not. If this is set to True
then ErrorMessage and ErrorTestName should also be passed.
.PARAMETER ErrorMessage
The error message that should be thrown if the function is supposed to throw.
.PARAMETER ErrorTestName
The string that should be used to create the name of the test that checks for
the correct error being thrown.
#>
function Invoke-GenericUnitTest {
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[ScriptBlock]
$Function,
[Parameter(Mandatory = $true)]
[Hashtable]
$FunctionParameters,
[Hashtable[]]
$MocksCalled,
[Boolean]
$ShouldThrow = $false,
[String]
$ErrorMessage = '',
[String]
$ErrorTestName = ''
)
if ($ShouldThrow)
{
It "Should throw an error for $ErrorTestName" {
{ $null = $($Function.Invoke($FunctionParameters)) } | Should Throw $ErrorMessage
}
}
else
{
It 'Should not throw' {
{ $null = $($Function.Invoke($FunctionParameters)) } | Should Not Throw
}
}
Invoke-ExpectedMocksAreCalledTest -MocksCalled $MocksCalled
}
<#
.SYNOPSIS
Performs generic tests for Get-TargetResource, including checking that the
function does not throw, checking that all mocks are called the expected
number of times, and checking that the correct result is returned. If the function
is expected to throw, then this function should not be used.
.PARAMETER GetTargetResourceParameters
The parameters that should be passed to Get-TargetResource for this test.
.PARAMETER MocksCalled
An array of the mocked commands that should be called for this test.
Each item in the array is a hashtable that contains the name of the command
being mocked, the number of times it is called (can be 0) and, optionally,
an extra custom string to make the test name more descriptive. The custom
string will only work if the command has a corresponding variable in the
string data name.
.PARAMETER ExpectedReturnValue
The expected hashtable that Get-TargetResource should return for this test.
#>
function Invoke-GetTargetResourceUnitTest
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[Hashtable]
$GetTargetResourceParameters,
[Hashtable[]]
$MocksCalled,
[Parameter(Mandatory = $true)]
[Hashtable]
$ExpectedReturnValue
)
It 'Should not throw' {
{ $null = Get-TargetResource @GetTargetResourceParameters } | Should Not Throw
}
Invoke-ExpectedMocksAreCalledTest -MocksCalled $MocksCalled
$getTargetResourceResult = Get-TargetResource @GetTargetResourceParameters
It 'Should return a Hashtable' {
$getTargetResourceResult -is [Hashtable] | Should Be $true
}
It "Should return a Hashtable with $($ExpectedReturnValue.Keys.Count) properties" {
$getTargetResourceResult.Keys.Count | Should Be $ExpectedReturnValue.Keys.Count
}
foreach ($key in $ExpectedReturnValue.Keys)
{
It "Should return a Hashtable with the $key property as $($ExpectedReturnValue.$key)" {
$getTargetResourceResult.$key | Should Be $ExpectedReturnValue.$key
}
}
}
<#
.SYNOPSIS
Performs generic tests for Set-TargetResource, including checking that the
function does not throw and checking that all mocks are called the expected
number of times.
.PARAMETER SetTargetResourceParameters
The parameters that should be passed to Set-TargetResource for this test.
.PARAMETER MocksCalled
An array of the mocked commands that should be called for this test.
Each item in the array is a hashtable that contains the name of the command
being mocked, the number of times it is called (can be 0) and, optionally,
an extra custom string to make the test name more descriptive. The custom
string will only work if the command has a corresponding variable in the
string data name.
.PARAMETER ShouldThrow
Indicates whether Set-TargetResource should throw or not. If this is set to True
then ErrorMessage and ErrorTestName should also be passed.
.PARAMETER ErrorMessage
The error message that should be thrown if Set-TargetResource is supposed to throw.
.PARAMETER ErrorTestName
The string that should be used to create the name of the test that checks for
the correct error being thrown.
#>
function Invoke-SetTargetResourceUnitTest {
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[Hashtable]
$SetTargetResourceParameters,
[Hashtable[]]
$MocksCalled,
[Boolean]
$ShouldThrow = $false,
[String]
$ErrorMessage = '',
[String]
$ErrorTestName = ''
)
if ($ShouldThrow)
{
It "Should throw an error for $ErrorTestName" {
{ $null = Set-TargetResource @SetTargetResourceParameters } | Should Throw $ErrorMessage
}
}
else
{
It 'Should not throw' {
{ $null = Set-TargetResource @SetTargetResourceParameters } | Should Not Throw
}
}
Invoke-ExpectedMocksAreCalledTest -MocksCalled $MocksCalled
}
<#
.SYNOPSIS
Performs generic tests for Test-TargetResource, including checking that the
function does not throw, checking that all mocks are called the expected
number of times, and checking that the correct result is returned. If the function
is expected to throw, then this function should not be used.
.PARAMETER TestTargetResourceParameters
The parameters that should be passed to Test-TargetResource for this test.
.PARAMETER MocksCalled
An array of the mocked commands that should be called for this test.
Each item in the array is a hashtable that contains the name of the command
being mocked, the number of times it is called (can be 0) and, optionally,
an extra custom string to make the test name more descriptive. The custom
string will only work if the command has a corresponding variable in the
string data name.
.PARAMETER ExpectedReturnValue
The expected boolean value that should be returned
#>
function Invoke-TestTargetResourceUnitTest
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[Hashtable]
$TestTargetResourceParameters,
[Hashtable[]]
$MocksCalled,
[Parameter(Mandatory = $true)]
[Boolean]
$ExpectedReturnValue
)
It 'Should not throw' {
{ $null = Test-TargetResource @TestTargetResourceParameters } | Should Not Throw
}
Invoke-ExpectedMocksAreCalledTest -MocksCalled $MocksCalled
$testTargetResourceResult = Test-TargetResource @TestTargetResourceParameters
It "Should return $ExpectedReturnValue" {
$testTargetResourceResult | Should Be $ExpectedReturnValue
}
}
<#
.SYNOPSIS
Tests that the Get-TargetResource method of a DSC Resource is not null, can be converted to a hashtable, and has the correct properties.
@@ -417,5 +797,10 @@ Export-ModuleMember -Function @(
'Test-SetTargetResourceWithWhatIf', `
'Get-AppVeyorAdministratorCredential', `
'Enter-DscResourceTestEnvironment', `
'Exit-DscResourceTestEnvironment'
'Exit-DscResourceTestEnvironment', `
'Invoke-GetTargetResourceUnitTest', `
'Invoke-SetTargetResourceUnitTest', `
'Invoke-TestTargetResourceUnitTest', `
'Invoke-ExpectedMocksAreCalledTest', `
'Invoke-GenericUnitTest'
)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff