Merge branch 'dev' into markdownlint-fix

This commit is contained in:
Simon Heather
2019-06-03 08:10:59 +01:00
committed by GitHub
9 changed files with 895 additions and 877 deletions
+16 -13
View File
@@ -8,23 +8,26 @@ function Test-IsNanoServer
[CmdletBinding()]
param ()
$isNanoServer = $false
if (Test-CommandExists -Name 'Get-ComputerInfo')
$serverLevelsRegKey = 'HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Server\ServerLevels'
if (Test-Path -Path $serverLevelsRegKey)
{
$computerInfo = Get-ComputerInfo -ErrorAction 'SilentlyContinue'
if ($null -ne $computerInfo)
$serverLevels = Get-ItemProperty -Path $serverLevelsRegKey
if ($serverLevels.NanoServer -eq 1)
{
$computerIsServer = 'Server' -ieq $computerInfo.OsProductType
if ($computerIsServer)
{
$isNanoServer = 'NanoServer' -ieq $computerInfo.OsServerLevel
}
$isNanoServer = $true
}
else
{
$isNanoServer = $false
}
}
else
{
$isNanoServer = $false
}
return $isNanoServer
}
@@ -269,7 +269,7 @@ function Set-TargetResource
}
finally
{
if ($null -ne $responseStream)
if ((Test-Path -Path variable:responseStream) -and ($null -ne $responseStream))
{
Close-Stream -Stream $responseStream
}
+9 -2
View File
@@ -593,9 +593,16 @@ The following parameters will be the same for each process in the set:
### Unreleased
* Fixes issue where MsiPackage Integration tests fail to make an HTTPS
connection if Strong Crypto for .NET is not enabled. Fixes
* Fixes issue where MsiPackage Integration tests fail if the test HttpListener
fails to start. Moves the test HttpListener objects to dynamically assigned,
higher numbered ports to avoid conflicts with other services, and also checks
to ensure that the ports are available before using them. Adds checks to
ensure that no outstanding HTTP server jobs are running before attempting to
setup a new one. Also adds additional instrumentation to make it easier to
troubleshoot issues with the test HttpListener objects in the future.
Specifically fixes
[Issue #142](https://github.com/PowerShell/PSDscResources/issues/142)
* Improved speed of Test-IsNanoServer function
### 2.11.0.0
File diff suppressed because it is too large Load Diff
@@ -19,9 +19,6 @@ $script:testEnvironment = Enter-DscResourceTestEnvironment `
try
{
# Make sure strong crypto is enabled in .NET for HTTPS tests
$originalStrongCryptoSettings = Enable-StrongCryptoForDotNetFour
InModuleScope 'MSFT_MsiPackage' {
Describe 'MSFT_MsiPackage Integration Tests' {
BeforeAll {
@@ -40,7 +37,7 @@ try
<#
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.
most of the work of the mock server is done within a separate process.
#>
$script:logFile = Join-Path -Path $PSScriptRoot -ChildPath 'PackageTestLogFile.txt'
@@ -52,6 +49,9 @@ try
$null = New-TestMsi -DestinationPath $script:msiLocation
$script:testHttpPort = Get-UnusedTcpPort
$script:testHttpsPort = Get-UnusedTcpPort -ExcludePorts @($script:testHttpPort)
# Clear the log file
'Beginning integration tests' > $script:logFile
}
@@ -174,8 +174,11 @@ try
}
It 'Should correctly install and remove a package from a HTTP URL' {
$baseUrl = 'http://localhost:1242/'
$msiUrl = "$baseUrl" + 'package.msi'
$uriBuilder = [System.UriBuilder]::new('http', 'localhost', $script:testHttpPort)
$baseUrl = $uriBuilder.Uri.AbsoluteUri
$uriBuilder.Path = 'package.msi'
$msiUrl = $uriBuilder.Uri.AbsoluteUri
$fileServerStarted = $null
$job = $null
@@ -184,7 +187,10 @@ try
{
'Http tests:' >> $script:logFile
$serverResult = Start-Server -FilePath $script:msiLocation -LogPath $script:logFile -Https $false
# Make sure no existing HTTP(S) test servers are running
Stop-EveryTestServerInstance
$serverResult = Start-Server -FilePath $script:msiLocation -LogPath $script:logFile -Https $false -HttpPort $script:testHttpPort -HttpsPort $script:testHttpsPort
$fileServerStarted = $serverResult.FileServerStarted
$job = $serverResult.Job
@@ -199,6 +205,12 @@ try
Set-TargetResource -Ensure 'Absent' -Path $msiUrl -ProductId $script:packageId
Test-PackageInstalledById -ProductId $script:packageId | Should Be $false
}
catch
{
Write-Warning -Message 'Caught exception performing HTTP server tests. Outputting HTTP server log.' -Verbose
Get-Content -Path $script:logFile | Write-Verbose -Verbose
throw $_
}
finally
{
<#
@@ -210,9 +222,11 @@ try
}
It 'Should correctly install and remove a package from a HTTPS URL' -Skip:$script:skipHttpsTest {
$uriBuilder = [System.UriBuilder]::new('https', 'localhost', $script:testHttpsPort)
$baseUrl = $uriBuilder.Uri.AbsoluteUri
$baseUrl = 'https://localhost:1243/'
$msiUrl = "$baseUrl" + 'package.msi'
$uriBuilder.Path = 'package.msi'
$msiUrl = $uriBuilder.Uri.AbsoluteUri
$fileServerStarted = $null
$job = $null
@@ -221,9 +235,12 @@ try
{
'Https tests:' >> $script:logFile
$serverResult = Start-Server -FilePath $script:msiLocation -LogPath $script:logFile -Https $true
# Make sure no existing HTTP(S) test servers are running
Stop-EveryTestServerInstance
$serverResult = Start-Server -FilePath $script:msiLocation -LogPath $script:logFile -Https $true -HttpPort $script:testHttpPort -HttpsPort $script:testHttpsPort
$fileServerStarted = $serverResult.FileServerStarted
$job = $serverResult.Job
$job = $serverResult.Job
# Wait for the file server to be ready to receive requests
$fileServerStarted.WaitOne(30000)
@@ -236,6 +253,12 @@ try
Set-TargetResource -Ensure 'Absent' -Path $msiUrl -ProductId $script:packageId
Test-PackageInstalledById -ProductId $script:packageId | Should Be $false
}
catch
{
Write-Warning -Message 'Caught exception performing HTTPS server tests. Outputting HTTPS server log.' -Verbose
Get-Content -Path $script:logFile | Write-Verbose -Verbose
throw $_
}
finally
{
<#
@@ -309,6 +332,4 @@ try
finally
{
Exit-DscResourceTestEnvironment -TestEnvironment $script:testEnvironment
Undo-ChangesToStrongCryptoForDotNetFour -OriginalSettings $originalStrongCryptoSettings
}
+72 -92
View File
@@ -157,7 +157,7 @@ function Invoke-ExpectedMocksAreCalledTest
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 will only work if the command has a corresponding variable in the
string data name.
.PARAMETER ShouldThrow
@@ -227,7 +227,7 @@ function Invoke-GenericUnitTest {
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 will only work if the command has a corresponding variable in the
string data name.
.PARAMETER ExpectedReturnValue
@@ -288,7 +288,7 @@ function Invoke-GetTargetResourceUnitTest
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 will only work if the command has a corresponding variable in the
string data name.
.PARAMETER ShouldThrow
@@ -354,7 +354,7 @@ function Invoke-SetTargetResourceUnitTest {
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 will only work if the command has a corresponding variable in the
string data name.
.PARAMETER ExpectedReturnValue
@@ -573,7 +573,7 @@ function Test-IsFileLocked
.PARAMETER ExpectedOutput
The output expected to be in the output from running WhatIf with the Set-TargetResource cmdlet.
If this parameter is empty or null, this cmdlet will check that there was no output from
Set-TargetResource with WhatIf specified.
Set-TargetResource with WhatIf specified.
#>
function Test-SetTargetResourceWithWhatIf
{
@@ -584,7 +584,7 @@ function Test-SetTargetResourceWithWhatIf
[Parameter(Mandatory = $true)]
[Hashtable]
$Parameters,
[String[]]
$ExpectedOutput
)
@@ -792,101 +792,82 @@ function Exit-DscResourceTestEnvironment
<#
.SYNOPSIS
Enables strong crypto for .NET v4.0.30319. Returns a Hashtable
containing the relevant, original registry settings, prior to
modification.
Finds an unused TCP port in the specified port range. By default,
searches within ports 38473 - 38799, which at the time of writing, show
as unassigned in:
https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml
.PARAMETER LowestPortNumber
The TCP port number at which to begin the unused port search. Must be
greater than 0.
.PARAMETER HighestPortNumber
The highest TCP port number to search for unused ports within. Must be
greater than 0, and greater than LowestPortNumber.
.PARAMETER ExcludePorts
TCP ports to exclude from the search, even if they fall within the
LowestPortNumber and HighestPortNumber range.
#>
function Enable-StrongCryptoForDotNetFour
{
[OutputType([System.Collections.Hashtable])]
[CmdletBinding()]
param ()
$originalValues = @{}
$regBases = @(
'HKLM:\SOFTWARE'
'HKLM:\SOFTWARE\Wow6432Node'
)
$net4Suffix = 'Microsoft\.NetFramework\v4.0.30319'
foreach ($regBase in $regBases)
{
$regPath = Join-Path -Path $regBase -ChildPath $net4Suffix
if ($null -ne (Get-Item -Path $regPath -ErrorAction SilentlyContinue))
{
$useStrongCryptoProp = Get-ItemProperty -Path $regPath -Name 'SchUseStrongCrypto' -ErrorAction SilentlyContinue
$needsUpdate = $false
if ($null -eq $useStrongCryptoProp)
{
$originalValues.Add($regPath, $null)
$needsUpdate = $true
}
else
{
$originalValues.Add($regPath, $useStrongCryptoProp.SchUseStrongCrypto)
if ($useStrongCryptoProp.SchUseStrongCrypto -ne 1)
{
$needsUpdate = $true
}
}
if ($needsUpdate)
{
Write-Verbose -Message "Setting property SchUseStrongCrypto at path '$regPath' to 1"
Set-ItemProperty -Path $regPath -Name 'SchUseStrongCrypto' -Value '1' -Type DWord -ErrorAction Stop
}
}
else
{
Write-Warning -Message "Failed to find registry key at path: $regPath. Skipping setting SchUseStrongCrypto to 1. This may cause Integration tests to fail that depend on this setting."
continue
}
}
return $originalValues
}
<#
.SYNOPSIS
Restores strong crypto for .NET v4.0.30319 settings to what they were
prior to calling Enable-StrongCryptoForDotNetFour.
.PARAMETER OriginalSettings
A Hashtable containing the original registry settings for strong crypto
for .NET v4.0.30319.
#>
function Undo-ChangesToStrongCryptoForDotNetFour
function Get-UnusedTcpPort
{
[OutputType([System.UInt16])]
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.Collections.Hashtable]
$OriginalSettings
[Parameter()]
[ValidateScript({$_ -gt 0})]
[System.UInt16]
$LowestPortNumber = 38473,
[Parameter()]
[ValidateScript({$_ -gt $0})]
[System.UInt16]
$HighestPortNumber = 38799,
[Parameter()]
[System.UInt16[]]
$ExcludePorts = @()
)
foreach ($regPath in $OriginalSettings.Keys)
if ($HighestPortNumber -lt $LowestPortNumber)
{
$useStrongCryptoProp = Get-ItemProperty -Path $regPath -Name 'SchUseStrongCrypto' -ErrorAction SilentlyContinue
throw 'HighestPortNumber must be greater than or equal to LowestPortNumber'
}
# The SchUseStrongCrypto value previously didn't exist, but now does. Delete it.
if ($null -eq $OriginalSettings[$regPath] -and $null -ne $useStrongCryptoProp)
[System.UInt16] $unusedPort = 0
[System.Collections.ArrayList] $usedAndExcludedPorts = (Get-NetTCPConnection).LocalPort | Where-Object -FilterScript {
$_ -ge $LowestPortNumber -and $_ -le $HighestPortNumber
}
if (!(Test-Path -Path variable:usedAndExcludedPorts) -or ($null -eq $usedAndExcludedPorts))
{
[System.Collections.ArrayList] $usedAndExcludedPorts = @()
}
if (!(Test-Path -Path variable:ExcludePorts) -or ($null -eq $ExcludePorts))
{
$ExcludePorts = @()
}
$null = $usedAndExcludedPorts.Add($ExcludePorts)
foreach ($port in $LowestPortNumber..$HighestPortNumber)
{
if (!($usedAndExcludedPorts.Contains($port)))
{
Remove-ItemProperty -Path $regPath -Name 'SchUseStrongCrypto' -ErrorAction Stop
}
# The SchUseStrongCrypto value previously existed but had a different value.
elseif($OriginalSettings[$regPath] -ne $useStrongCryptoProp.SchUseStrongCrypto)
{
Set-ItemProperty -Path $regPath -Name 'SchUseStrongCrypto' -Value $OriginalSettings[$regPath] -Type DWord -ErrorAction Stop
$unusedPort = $port
break
}
}
if ($unusedPort -eq 0)
{
throw "Failed to find unused TCP port between ports $LowestPortNumber and $HighestPortNumber."
}
return $unusedPort
}
Export-ModuleMember -Function @(
@@ -901,7 +882,6 @@ Export-ModuleMember -Function @(
'Invoke-SetTargetResourceUnitTest', `
'Invoke-TestTargetResourceUnitTest', `
'Invoke-ExpectedMocksAreCalledTest', `
'Invoke-GenericUnitTest',
'Enable-StrongCryptoForDotNetFour',
'Undo-ChangesToStrongCryptoForDotNetFour'
'Invoke-GenericUnitTest', `
'Get-UnusedTcpPort'
)
@@ -1,6 +1,8 @@
$errorActionPreference = 'Stop'
Set-StrictMode -Version 'Latest'
$testJobPrefix = 'MsiPackageTestJob'
<#
.SYNOPSIS
Tests if the package with the given Id is installed.
@@ -55,9 +57,15 @@ function Test-PackageInstalledById
.PARAMETER Https
Indicates whether the server should use Https. If True then the file server will use Https
and listen on port 'https://localhost:1243'. Otherwise the file server will use Http and
listen on port 'http://localhost:1242'
and listen on port 'https://localhost:HttpsPort'. Otherwise the file server will use Http and
listen on port 'http://localhost:HttpPort'
Default value is False (Http).
.PARAMETER HttpPort
Specifies the TCP port to register an Http based HttpListener on.
.PARAMETER HttspPort
Specifies the TCP port to register an Https based HttpListener on.
#>
function Start-Server
{
@@ -74,7 +82,17 @@ function Start-Server
$LogPath = (Join-Path -Path $PSScriptRoot -ChildPath 'PackageTestLogFile.txt'),
[System.Boolean]
$Https = $false
$Https = $false,
[Parameter(Mandatory = $true)]
[ValidateScript({$_ -gt 0})]
[System.UInt16]
$HttpPort,
[Parameter(Mandatory = $true)]
[ValidateScript({$_ -gt 0})]
[System.UInt16]
$HttpsPort
)
# Create an event object to let the client know when the server is ready to begin receiving requests.
@@ -82,7 +100,7 @@ function Start-Server
'HttpIntegrationTest.FileServerStarted')
$null = $fileServerStarted.Reset()
<#
<#
The server is run on a separate process so that it can receive requests
while the tests continue to run. It takes in the same parameterss that are passed
in to this function. All helper functions that the server uses have to be
@@ -90,7 +108,7 @@ function Start-Server
#>
$server =
{
param($FilePath, $LogPath, $Https)
param($FilePath, $LogPath, $Https, $HttpPort, $HttpsPort)
<#
.SYNOPSIS
@@ -101,6 +119,9 @@ function Start-Server
.PARAMETER Https
Indicates whether https was used and if so, removes the SSL binding.
.PARAMETER HttspPort
Specifies the TCP port to de-register an Https based HttpListener from.
#>
function Stop-Listener
{
@@ -109,16 +130,21 @@ function Start-Server
(
[Parameter(Mandatory = $true)]
[System.Net.HttpListener]
$HttpListener,
$HttpListener,
[Parameter(Mandatory = $true)]
[System.Boolean]
$Https
$Https,
[Parameter(Mandatory = $true)]
[ValidateScript({$_ -gt 0})]
[System.UInt16]
$HttpsPort
)
Write-Log -LogFile $LogPath -Message 'Finished listening for requests. Shutting down HTTP server.'
$ipPort = '0.0.0.0:1243'
$ipPort = "0.0.0.0:$HttpsPort"
if ($null -eq $HttpListener)
{
@@ -159,16 +185,25 @@ function Start-Server
<#
.SYNOPSIS
Creates and registers an SSL certificate for Https connections.
.PARAMETER HttspPort
Specifies the TCP port to register an Https based HttpListener on.
#>
function Register-Ssl
{
[CmdletBinding()]
param()
param
(
[Parameter(Mandatory = $true)]
[ValidateScript({$_ -gt 0})]
[System.UInt16]
$HttpsPort
)
# Create certificate
$certificate = New-SelfSignedCertificate -CertStoreLocation 'Cert:\LocalMachine\My' -DnsName localhost
Write-Log -LogFile $LogPath -Message 'Created certificate'
$hash = $certificate.Thumbprint
$certPassword = ConvertTo-SecureString -String 'password12345' -AsPlainText -Force
$tempPath = 'C:\certForTesting'
@@ -176,11 +211,11 @@ function Start-Server
$null = Export-PfxCertificate -Cert $certificate -FilePath $tempPath -Password $certPassword
$null = Import-PfxCertificate -CertStoreLocation 'Cert:\LocalMachine\Root' -FilePath 'C:\certForTesting' -Password $certPassword
Remove-Item -Path $tempPath
Write-Log -LogFile $LogPath -Message 'Finished importing certificate into root. About to bind it to port.'
# Use net shell command to directly bind certificate to designated testing port
$null = netsh http add sslcert ipport=0.0.0.0:1243 certhash=$hash appid='{833f13c2-319a-4799-9d1a-5b267a0c3593}' clientcertnegotiation=enable
$null = netsh http add sslcert ipport=0.0.0.0:$HttpsPort certhash=$hash appid='{833f13c2-319a-4799-9d1a-5b267a0c3593}' clientcertnegotiation=enable
}
<#
@@ -204,32 +239,32 @@ function Start-Server
# Add the CallbackEventBridge type if it's not already defined
if (-not ('CallbackEventBridge' -as [Type]))
{
Add-Type @'
using System;
public sealed class CallbackEventBridge {
public event AsyncCallback CallbackComplete = delegate { };
private CallbackEventBridge() {}
Add-Type @'
using System;
public sealed class CallbackEventBridge {
public event AsyncCallback CallbackComplete = delegate { };
private CallbackEventBridge() {}
private void CallbackInternal(IAsyncResult result)
{
CallbackComplete(result);
}
{
CallbackComplete(result);
}
public AsyncCallback Callback
{
get { return new AsyncCallback(CallbackInternal); }
}
{
get { return new AsyncCallback(CallbackInternal); }
}
public static CallbackEventBridge Create()
{
return new CallbackEventBridge();
}
}
{
return new CallbackEventBridge();
}
}
'@
}
$bridge = [CallbackEventBridge]::Create()
Register-ObjectEvent -InputObject $bridge -EventName 'CallbackComplete' -Action $Callback -MessageData $args > $null
$bridge.Callback
@@ -249,7 +284,7 @@ function Start-Server
.PARAMETER ScriptBlock
The code to execute.
#>
function Invoke-ConsoleCommand
{
@@ -277,7 +312,7 @@ function Start-Server
$message = ('Failed action ''{0}'' on target ''{1}'' (exit code {2}): {3}' -f $Action,$Target,$LASTEXITCODE,$output)
Write-Error -Message $message
Write-Log -LogFile $LogPath -Message "Error from Invoke-ConsoleCommand: $message"
}
}
else
{
$nonNullOutput = $output | Where-Object { $_ -ne $null }
@@ -309,7 +344,7 @@ function Start-Server
[String]
$Message
)
$Message >> $LogFile
}
@@ -332,11 +367,11 @@ function Start-Server
# Set up the listener
if ($Https)
{
$HttpListener.Prefixes.Add([Uri]'https://localhost:1243')
$HttpListener.Prefixes.Add([Uri] "https://localhost:$HttpsPort")
try
{
Register-SSL
Register-SSL -HttpsPort $HttpsPort
}
catch
{
@@ -349,7 +384,7 @@ function Start-Server
}
else
{
$HttpListener.Prefixes.Add([Uri]'http://localhost:1242')
$HttpListener.Prefixes.Add([Uri] "http://localhost:$HttpPort")
}
Write-Log -LogFile $LogPath -Message 'Finished listener setup - about to start listener'
@@ -374,7 +409,7 @@ function Start-Server
.PARAMETER Result
th IAsyncResult containing the listener object and path to the MSI file.
#>
$requestListener =
{
@@ -450,8 +485,22 @@ function Start-Server
catch
{
$errorMessage = "There were problems setting up the HTTP(s) listener. Error: $_"
Write-Log -LogFile $LogPath -Message $errorMessage
throw $errorMessage
'Error Record Info' >> $LogPath
$_ | ConvertTo-Xml -As String >> $LogPath
'Exception Info' >> $LogPath
$_.Exception | ConvertTo-Xml -As String >> $LogPath
'Running Process Info' >> $LogPath
Get-Process | Format-List | Out-String >> $LogPath
'Open TCP Connections Info' >> $LogPath
Get-NetTCPConnection | Format-List | Out-String >> $LogPath
throw $_
}
finally
{
@@ -459,19 +508,39 @@ function Start-Server
{
$fileServerStarted.Dispose()
}
Write-Log -LogFile $LogPath -Message 'Stopping the Server'
Stop-Listener -HttpListener $HttpListener -Https $Https
Stop-Listener -HttpListener $HttpListener -Https $Https -HttpsPort $HttpsPort
}
}
$job = Start-Job -ScriptBlock $server -ArgumentList @( $FilePath, $LogPath, $Https )
if ($Https)
{
$jobName = $testJobPrefix + 'Https'
}
else
{
$jobName = $testJobPrefix + 'Http'
}
$job = Start-Job -ScriptBlock $server -Name $jobName -ArgumentList @( $FilePath, $LogPath, $Https, $HttpPort, $HttpsPort )
# Verify that the job is receivable and does not contain an exception. If it does, re-throw it.
try
{
$receivedJob = $job | Receive-Job
}
catch
{
Write-Error -Message 'Failed to setup HTTP(S) listener for MsiPackage Tests'
throw $_
}
<#
Return the event object so that client knows when it can start sending requests and
Return the event object so that client knows when it can start sending requests and
the job object so that the client can stop the job once it is done sending requests.
#>
return @{
return @{
FileServerStarted = $fileServerStarted
Job = $job
}
@@ -501,7 +570,7 @@ function Stop-Server
$FileServerStarted,
[System.Management.Automation.Job]
$Job
$Job
)
if ($null -ne $FileServerStarted)
@@ -516,6 +585,20 @@ function Stop-Server
}
}
<#
.SYNOPSIS
Removes any jobs associated with HTTP(S) servers that were created
for MsiPackage tests.
#>
function Stop-EveryTestServerInstance
{
[CmdletBinding()]
param ()
Get-Job -Name "$($testJobPrefix)*" | Stop-Job
Get-Job -Name "$($testJobPrefix)*" | Remove-Job
}
<#
.SYNOPSIS
Creates a new MSI package for testing.
@@ -526,7 +609,7 @@ function Stop-Server
function New-TestMsi
{
[CmdletBinding()]
param
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
@@ -1061,4 +1144,5 @@ Export-ModuleMember -Function `
New-TestMsi, `
Start-Server, `
Stop-Server, `
Test-PackageInstalledById
Test-PackageInstalledById, `
Stop-EveryTestServerInstance
+19 -101
View File
@@ -14,112 +14,38 @@ Describe 'CommonResourceHelper Unit Tests' {
InModuleScope 'CommonResourceHelper' {
Describe 'Test-IsNanoServer' {
$testComputerInfoNanoServer = @{
OsProductType = 'Server'
OsServerLevel = 'NanoServer'
NanoServer = 1
}
$testComputerInfoServerNotNano = @{
OsProductType = 'Server'
OsServerLevel = 'NotNano'
}
$testComputerInfoNotServer = @{
OsProductType = 'NotServer'
OsServerLevel = 'NotNano'
}
Mock -CommandName 'Test-CommandExists' -MockWith { return $true }
Mock -CommandName 'Get-ComputerInfo' -MockWith { return $testComputerInfoNanoServer }
Context 'Get-ComputerInfo command exists and succeeds' {
Context 'Computer OS type is Server and OS server level is NanoServer' {
It 'Should not throw' {
{ $null = Test-IsNanoServer } | Should -Not -Throw
}
It 'Should test if the Get-ComputerInfo command exists' {
$testCommandExistsParameterFilter = {
return $Name -eq 'Get-ComputerInfo'
}
Assert-MockCalled -CommandName 'Test-CommandExists' -ParameterFilter $testCommandExistsParameterFilter -Exactly 1 -Scope 'Context'
}
It 'Should retrieve the computer info' {
Assert-MockCalled -CommandName 'Get-ComputerInfo' -Exactly 1 -Scope 'Context'
}
It 'Should return true' {
Test-IsNanoServer | Should -Be $true
}
Context 'Computer OS type is Server and OS server level is NanoServer' {
Mock -CommandName 'Test-Path' -MockWith { return $true }
Mock -CommandName 'Get-ItemProperty' -MockWith { return $testComputerInfoNanoServer }
It 'Should not throw' {
{ $null = Test-IsNanoServer } | Should -Not -Throw
}
Context 'Computer OS type is Server and OS server level is not NanoServer' {
Mock -CommandName 'Get-ComputerInfo' -MockWith { return $testComputerInfoServerNotNano }
It 'Should not throw' {
{ $null = Test-IsNanoServer } | Should -Not -Throw
}
It 'Should test if the Get-ComputerInfo command exists' {
$testCommandExistsParameterFilter = {
return $Name -eq 'Get-ComputerInfo'
}
Assert-MockCalled -CommandName 'Test-CommandExists' -ParameterFilter $testCommandExistsParameterFilter -Exactly 1 -Scope 'Context'
}
It 'Should retrieve the computer info' {
Assert-MockCalled -CommandName 'Get-ComputerInfo' -Exactly 1 -Scope 'Context'
}
It 'Should return false' {
Test-IsNanoServer | Should -Be $false
}
It 'Should check the ServerLevels registry path' {
Assert-MockCalled -CommandName 'Get-ItemProperty' -Exactly 1 -Scope 'Context'
}
Context 'Computer OS type is not Server' {
Mock -CommandName 'Get-ComputerInfo' -MockWith { return $testComputerInfoNotServer }
It 'Should not throw' {
{ $null = Test-IsNanoServer } | Should -Not -Throw
}
It 'Should test if the Get-ComputerInfo command exists' {
$testCommandExistsParameterFilter = {
return $Name -eq 'Get-ComputerInfo'
}
Assert-MockCalled -CommandName 'Test-CommandExists' -ParameterFilter $testCommandExistsParameterFilter -Exactly 1 -Scope 'Context'
}
It 'Should retrieve the computer info' {
Assert-MockCalled -CommandName 'Get-ComputerInfo' -Exactly 1 -Scope 'Context'
}
It 'Should return false' {
Test-IsNanoServer | Should -Be $false
}
It 'Should return true' {
Test-IsNanoServer | Should -Be $true
}
}
Context 'Get-ComputerInfo command exists but throws an error and returns null' {
Mock -CommandName 'Get-ComputerInfo' -MockWith { return $null }
Context 'Computer OS type is Server and OS server level is not NanoServer' {
Mock -CommandName 'Test-Path' -MockWith { return $true }
Mock -CommandName 'Get-ItemProperty' -MockWith { return $testComputerInfoServerNotNano }
It 'Should not throw' {
{ $null = Test-IsNanoServer } | Should -Not -Throw
}
It 'Should test if the Get-ComputerInfo command exists' {
$testCommandExistsParameterFilter = {
return $Name -eq 'Get-ComputerInfo'
}
Assert-MockCalled -CommandName 'Test-CommandExists' -ParameterFilter $testCommandExistsParameterFilter -Exactly 1 -Scope 'Context'
}
It 'Should retrieve the computer info' {
Assert-MockCalled -CommandName 'Get-ComputerInfo' -Exactly 1 -Scope 'Context'
It 'Should check the ServerLevels registry path' {
Assert-MockCalled -CommandName 'Get-ItemProperty' -Exactly 1 -Scope 'Context'
}
It 'Should return false' {
@@ -127,23 +53,15 @@ Describe 'CommonResourceHelper Unit Tests' {
}
}
Context 'Get-ComputerInfo command does not exist' {
Mock -CommandName 'Test-CommandExists' -MockWith { return $false }
Context 'Computer OS type is not Server' {
Mock -CommandName 'Test-Path' -MockWith { return $false }
It 'Should not throw' {
{ $null = Test-IsNanoServer } | Should -Not -Throw
}
It 'Should test if the Get-ComputerInfo command exists' {
$testCommandExistsParameterFilter = {
return $Name -eq 'Get-ComputerInfo'
}
Assert-MockCalled -CommandName 'Test-CommandExists' -ParameterFilter $testCommandExistsParameterFilter -Exactly 1 -Scope 'Context'
}
It 'Should not attempt to retrieve the computer info' {
Assert-MockCalled -CommandName 'Get-ComputerInfo' -Exactly 0 -Scope 'Context'
It 'Should check the ServerLevels registry path' {
Assert-MockCalled -CommandName 'Test-Path' -Exactly 1 -Scope 'Context'
}
It 'Should return false' {
+23 -21
View File
@@ -123,7 +123,7 @@ Describe 'MsiPackage Unit Tests' {
-ExpectedReturnValue $expectedReturnValue
}
}
Describe 'Set-TargetResource' {
$setTargetResourceParameters = @{
ProductId = 'TestProductId'
@@ -138,7 +138,7 @@ Describe 'MsiPackage Unit Tests' {
ServerCertificateValidationCallback = 'TestValidationCallback'
RunAsCredential = $script:testCredential
}
Mock -CommandName 'Convert-PathToUri' -MockWith { return $script:testUriNonUnc }
Mock -CommandName 'Convert-ProductIdToIdentifyingNumber' -MockWith { return $script:testIdentifyingNumber }
Mock -CommandName 'Assert-PathExtensionValid' -MockWith {}
@@ -158,7 +158,7 @@ Describe 'MsiPackage Unit Tests' {
Mock -CommandName 'Invoke-CimMethod' -MockWith {}
Mock -CommandName 'Get-ItemProperty' -MockWith { return $null }
Mock -CommandName 'Get-ProductEntry' -MockWith { return $script:mockProductEntry }
Context 'Uri scheme is non-UNC file and installation succeeds' {
$mocksCalled = @(
@{ Command = 'Convert-PathToUri'; Times = 1 }
@@ -219,13 +219,13 @@ Describe 'MsiPackage Unit Tests' {
@{ Command = 'Assert-PathExtensionValid'; Times = 1 }
@{ Command = 'New-LogFile'; Times = 1 }
@{ Command = 'New-PSDrive'; Times = 0 }
@{ Command = 'Test-Path'; Times = 2; Custom = 'to the package cache' }
@{ Command = 'Test-Path'; Times = 3; Custom = 'to the package cache' }
@{ Command = 'New-Item'; Times = 0; Custom = 'directory for the package cache' }
@{ Command = 'New-Object'; Times = 1; Custom = 'file stream to copy the response to' }
@{ Command = 'Get-WebRequestResponse'; Times = 1 }
@{ Command = 'Copy-ResponseStreamToFileStream'; Times = 1 }
@{ Command = 'Close-Stream'; Times = 2 }
@{ Command = 'Test-Path'; Times = 2; Custom = 'to the MSI file' }
@{ Command = 'Test-Path'; Times = 3; Custom = 'to the MSI file' }
@{ Command = 'Assert-FileValid'; Times = 1 }
@{ Command = 'Get-MsiProductCode'; Times = 1 }
@{ Command = 'Start-MsiProcess'; Times = 1 }
@@ -250,13 +250,13 @@ Describe 'MsiPackage Unit Tests' {
@{ Command = 'Assert-PathExtensionValid'; Times = 1 }
@{ Command = 'New-LogFile'; Times = 1 }
@{ Command = 'New-PSDrive'; Times = 0 }
@{ Command = 'Test-Path'; Times = 2; Custom = 'to the package cache' }
@{ Command = 'Test-Path'; Times = 3; Custom = 'to the package cache' }
@{ Command = 'New-Item'; Times = 0; Custom = 'directory for the package cache' }
@{ Command = 'New-Object'; Times = 1; Custom = 'file stream to copy the response to' }
@{ Command = 'Get-WebRequestResponse'; Times = 1 }
@{ Command = 'Copy-ResponseStreamToFileStream'; Times = 1 }
@{ Command = 'Close-Stream'; Times = 2 }
@{ Command = 'Test-Path'; Times = 2; Custom = 'to the MSI file' }
@{ Command = 'Test-Path'; Times = 3; Custom = 'to the MSI file' }
@{ Command = 'Assert-FileValid'; Times = 1 }
@{ Command = 'Get-MsiProductCode'; Times = 1 }
@{ Command = 'Start-MsiProcess'; Times = 1 }
@@ -349,7 +349,7 @@ Describe 'MsiPackage Unit Tests' {
-ErrorMessage ($script:localizedData.PostValidationError -f $setTargetResourceParameters.Path) `
-ErrorTestName $script:errorMessageTitles.PostValidationError
}
Mock -CommandName 'Get-MsiProductCode' -MockWith { return $script:testWrongProductId }
Context 'Product code from downloaded MSI package does not match specified ID' {
@@ -491,25 +491,27 @@ Describe 'MsiPackage Unit Tests' {
Convert-PathToUri -Path $filePath | Should Be $expectedReturnValue
}
It 'Should return the expected URI when scheme is http' {
$filePath = 'http://localhost:1242/testMsi.msi'
$expectedReturnValue = [Uri] $filePath
Convert-PathToUri -Path $filePath | Should Be $expectedReturnValue
It 'Should return the expected URI when scheme is http' {
$uriBuilder = [System.UriBuilder]::new('http', 'localhost')
$uriBuilder.Path = 'testMsi.msi'
$filePath = $uriBuilder.Uri.AbsoluteUri
Convert-PathToUri -Path $filePath | Should Be $uriBuilder.Uri
}
It 'Should return the expected URI when scheme is https' {
$filePath = 'https://localhost:1243/testMsi.msi'
$expectedReturnValue = [Uri] $filePath
$uriBuilder = [System.UriBuilder]::new('https', 'localhost')
$uriBuilder.Path = 'testMsi.msi'
$filePath = $uriBuilder.Uri.AbsoluteUri
Convert-PathToUri -Path $filePath | Should Be $expectedReturnValue
Convert-PathToUri -Path $filePath | Should Be $uriBuilder.Uri
}
}
Context 'Invalid path passed in' {
It 'Should throw an error when uri scheme is invalid' {
$filePath = 'ht://localhost:1243/testMsi.msi'
$filePath = 'ht://localhost/testMsi.msi'
$expectedErrorMessage = ($script:localizedData.InvalidPath -f $filePath)
{ Convert-PathToUri -Path $filePath } | Should Throw $expectedErrorMessage
@@ -686,7 +688,7 @@ Describe 'MsiPackage Unit Tests' {
-ErrorTestName $script:errorMessageTitles.CouldNotOpenLog
}
}
Describe 'Get-WebRequestResponse' {
Mock -CommandName 'Get-WebRequest' -MockWith { return $script:mockWebRequest }
Mock -CommandName 'Get-ScriptBlock' -MockWith { return { Write-Verbose 'Hello World' } }
@@ -702,7 +704,7 @@ Describe 'MsiPackage Unit Tests' {
It 'Should return the expected response stream' {
Get-WebRequestResponse -Uri $script:testUriHttp | Should Be $script:mockStream
}
Invoke-ExpectedMocksAreCalledTest -MocksCalled $mocksCalled
}
@@ -716,7 +718,7 @@ Describe 'MsiPackage Unit Tests' {
It 'Should return the expected response stream' {
Get-WebRequestResponse -Uri $script:testUriHttps | Should Be $script:mockStream
}
Invoke-ExpectedMocksAreCalledTest -MocksCalled $mocksCalled
}
@@ -859,7 +861,7 @@ Describe 'MsiPackage Unit Tests' {
Describe 'Assert-FileSignatureValid' {
$mockThumbprint = 'mockThumbprint'
$mockSubject = 'mockSubject'
$mockSignature = @{
$mockSignature = @{
Status = [System.Management.Automation.SignatureStatus]::Valid
SignerCertificate = @{ Thumbprint = $mockThumbprint; Subject = $mockSubject }
}