Files
PowerShell-PSDscResources/Tests/Unit/MSFT_ServiceResource.Tests.ps1
T
2016-11-02 11:54:04 -07:00

2045 lines
86 KiB
PowerShell

# Need to be able to create a password from plain text for testing purposes
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')]
param ()
Import-Module -Name (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) `
-ChildPath (Join-Path -Path 'TestHelpers' `
-ChildPath 'CommonTestHelper.psm1'))
$script:testEnvironment = Enter-DscResourceTestEnvironment `
-DSCResourceModuleName 'PSDscResources' `
-DSCResourceName 'MSFT_ServiceResource' `
-TestType Unit
# Begin Testing
try
{
# This is needed so that the ServiceControllerStatus enum is recognized as a valid type
Add-Type -AssemblyName 'System.ServiceProcess'
InModuleScope 'MSFT_ServiceResource' {
Import-Module -Name (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) `
-ChildPath 'TestHelpers\MSFT_ServiceResource.TestHelper.psm1') `
-Force
$script:DscResourceName = 'MSFT_ServiceResource'
$script:testServiceName = 'DscTestService'
$script:testServiceDisplayName = 'DSC test service display name'
$script:testServiceDescription = 'This is the DSC test service used for unit testing MSFT_ServiceResource'
$script:testServiceDependsOn = @('winrm','spooler')
$script:testServiceDependsOnHash = @( @{ name = 'winrm' }, @{ name = 'spooler' } )
$script:testServiceExecutablePath = Join-Path -Path $ENV:Temp -ChildPath 'DscTestService.exe'
$script:testServiceStartupType = 'Automatic'
$script:testServiceStartupTypeWin32 = 'Auto'
$script:testServiceStatusRunning = [System.ServiceProcess.ServiceControllerStatus]::Running
$script:testServiceStatusStopped = [System.ServiceProcess.ServiceControllerStatus]::Stopped
$script:testUsername = 'TestUser'
$script:testPassword = 'DummyPassword'
$script:testCredential = New-Object `
-TypeName System.Management.Automation.PSCredential `
-ArgumentList ($script:testUsername, `
(ConvertTo-SecureString $script:testPassword -AsPlainText -Force))
$script:testNewUsername = 'DifferentUser'
$script:testNewCredential = New-Object `
-TypeName System.Management.Automation.PSCredential `
-ArgumentList ($script:testNewUsername, `
(ConvertTo-SecureString $script:testPassword -AsPlainText -Force))
$script:testServiceMockRunning = New-Object -TypeName PSObject -Property @{
Name = $script:testServiceName
ServiceName = $script:testServiceName
DisplayName = $script:testServiceDisplayName
StartType = $script:testServiceStartupType
Status = $script:testServiceStatusRunning
ServicesDependedOn = $script:testServiceDependsOnHash
}
Add-Member -InputObject $script:testServiceMockRunning `
-MemberType ScriptMethod `
-Name Stop -Value { $global:ServiceStopped = $true }
Add-Member -InputObject $script:testServiceMockRunning `
-MemberType ScriptMethod `
-Name WaitForStatus -Value { param( $Status, $WaitTimeSpan ) }
$script:testServiceMockStopped = New-Object -TypeName PSObject -Property @{
Name = $script:testServiceName
ServiceName = $script:testServiceName
DisplayName = $script:testServiceDisplayName
StartType = $script:testServiceStartupType
Status = $script:testServiceStatusStopped
ServicesDependedOn = $script:testServiceDependsOnHash
}
Add-Member -InputObject $script:testServiceMockStopped `
-MemberType ScriptMethod `
-Name Start -Value { $global:ServiceStarted = $true }
Add-Member -InputObject $script:testServiceMockStopped `
-MemberType ScriptMethod `
-Name WaitForStatus -Value { param( $Status, $WaitTimeSpan ) }
$script:testWin32ServiceMockRunningLocalSystem = New-Object -TypeName PSObject -Property @{
Name = $script:testServiceName
Status = 'OK'
DesktopInteract = $true
PathName = $script:testServiceExecutablePath
StartMode = $script:testServiceStartupTypeWin32
Description = $script:testServiceDescription
Started = $true
DisplayName = $script:testServiceDisplayName
StartName = 'LocalSystem'
State = $script:testServiceStatusRunning
}
$script:splatServiceExistsAutomatic = @{
Name = $script:testServiceName
StartupType = $script:testServiceStartupType
BuiltInAccount = 'LocalSystem'
DesktopInteract = $true
State = $script:testServiceStatusRunning
Ensure = 'Present'
Path = $script:testServiceExecutablePath
DisplayName = $script:testServiceDisplayName
Description = $script:testServiceDescription
}
Describe "$script:DscResourceName\Get-TargetResource" {
Context 'Service exists' {
# Mocks that should be called
Mock -CommandName Test-ServiceExists `
-MockWith { $true } `
-Verifiable
Mock -CommandName Get-ServiceResource `
-MockWith { $script:testServiceMockRunning } `
-Verifiable
Mock -CommandName Get-Win32ServiceObject `
-MockWith { $script:testWin32ServiceMockRunningLocalSystem } `
-Verifiable
It 'Should not throw an exception' {
{ $script:service = Get-TargetResource `
-Name $script:testServiceName `
} | Should Not Throw
}
It 'Should return the correct hashtable properties' {
$service.Ensure | Should Be 'Present'
$service.Name | Should Be $script:testServiceName
$service.StartupType | Should Be $script:testServiceStartupType
$service.BuiltInAccount | Should Be 'LocalSystem'
$service.State | Should Be $script:testServiceStatusRunning
$service.Path | Should Be $script:testServiceExecutablePath
$service.DisplayName | Should Be $script:testServiceDisplayName
$service.Description | Should Be $script:testServiceDescription
$service.DesktopInteract | Should Be $true
$service.Dependencies | Should Be $script:testServiceDependsOn
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Test-ServiceExists -Exactly 1
Assert-MockCalled -CommandName Get-ServiceResource -Exactly 1
Assert-MockCalled -CommandName Get-Win32ServiceObject -Exactly 1
}
}
Context 'Service does not exist' {
# Mocks that should be called
Mock -CommandName Test-ServiceExists -MockWith { $false } -Verifiable
# Mocks that should not be called
Mock -CommandName Get-serviceResource
Mock -CommandName Get-Win32ServiceObject
It 'Should not throw an exception' {
{ $script:service = Get-TargetResource `
-Name $script:testServiceName `
} | Should Not Throw
}
It 'Should return the correct hashtable properties' {
$service.Ensure | Should Be 'Absent'
$service.Name | Should Be $script:testServiceName
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Test-ServiceExists -Exactly 1
Assert-MockCalled -CommandName Get-serviceResource -Exactly 0
Assert-MockCalled -CommandName Get-Win32ServiceObject -Exactly 0
}
}
}
Describe "$script:DscResourceName\Set-TargetResource" {
Context 'Service exists and should not' {
# Mocks that should be called
Mock -CommandName Test-StartupType `
-Verifiable
Mock -CommandName Test-ServiceExists `
-MockWith { $true } `
-Verifiable
Mock -CommandName Stop-ServiceResource `
-Verifiable
Mock -CommandName Remove-Service `
-Verifiable
# Mocks that should not be called
Mock -CommandName Start-ServiceResource
Mock -CommandName New-Service
Mock -CommandName Compare-ServicePath
Mock -CommandName Write-WriteProperty
It 'Should not throw an exception' {
$Splat = $script:splatServiceExistsAutomatic.Clone()
$Splat.Ensure = 'Absent'
{ Set-TargetResource @Splat } | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Test-StartupType -Exactly 1
Assert-MockCalled -CommandName Test-ServiceExists -Exactly 1
Assert-MockCalled -CommandName Start-ServiceResource -Exactly 0
Assert-MockCalled -CommandName Stop-ServiceResource -Exactly 1
Assert-MockCalled -CommandName Remove-Service -Exactly 1
Assert-MockCalled -CommandName New-Service -Exactly 0
Assert-MockCalled -CommandName Compare-ServicePath -Exactly 0
Assert-MockCalled -CommandName Write-WriteProperty -Exactly 0
}
}
Context 'Service exists and should, should be running, all parameters passed' {
# Mocks that should be called
Mock -CommandName Test-StartupType `
-Verifiable
Mock -CommandName Test-ServiceExists `
-MockWith { $true } `
-Verifiable
Mock -CommandName Compare-ServicePath `
-MockWith { $true } `
-Verifiable
Mock -CommandName Write-WriteProperty `
-MockWith { $false } `
-Verifiable
Mock -CommandName Start-ServiceResource `
-Verifiable
# Mocks that should not be called
Mock -CommandName Stop-ServiceResource
Mock -CommandName New-Service
Mock -CommandName Remove-Service
It 'Should not throw an exception' {
$Splat = $script:splatServiceExistsAutomatic.Clone()
{ Set-TargetResource @Splat } | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Test-StartupType -Exactly 1
Assert-MockCalled -CommandName Test-ServiceExists -Exactly 1
Assert-MockCalled -CommandName Start-ServiceResource -Exactly 1
Assert-MockCalled -CommandName Stop-ServiceResource -Exactly 0
Assert-MockCalled -CommandName Remove-Service -Exactly 0
Assert-MockCalled -CommandName New-Service -Exactly 0
Assert-MockCalled -CommandName Compare-ServicePath -Exactly 1
Assert-MockCalled -CommandName Write-WriteProperty -Exactly 1
}
}
Context 'Service exists and should, should be running, path needs change' {
# Mocks that should be called
Mock -CommandName Test-StartupType `
-Verifiable
Mock -CommandName Test-ServiceExists `
-MockWith { $true } `
-Verifiable
Mock -CommandName Compare-ServicePath `
-MockWith { $false } `
-Verifiable
Mock -CommandName Write-WriteProperty `
-MockWith { $false } `
-Verifiable
Mock -CommandName Start-ServiceResource `
-Verifiable
# Mocks that should not be called
Mock -CommandName Stop-ServiceResource
Mock -CommandName New-Service
Mock -CommandName Remove-Service
It 'Should not throw an exception' {
$Splat = $script:splatServiceExistsAutomatic.Clone()
$Splat.Path = 'c:\NewServicePath.exe'
{ Set-TargetResource @Splat } | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Test-StartupType -Exactly 1
Assert-MockCalled -CommandName Test-ServiceExists -Exactly 1
Assert-MockCalled -CommandName Start-ServiceResource -Exactly 1
Assert-MockCalled -CommandName Stop-ServiceResource -Exactly 0
Assert-MockCalled -CommandName Remove-Service -Exactly 0
Assert-MockCalled -CommandName New-Service -Exactly 0
Assert-MockCalled -CommandName Compare-ServicePath -Exactly 1
Assert-MockCalled -CommandName Write-WriteProperty -Exactly 1
}
}
Context 'Service exists and should, should be running but needs restart, all parameters passed' {
# Mocks that should be called
Mock -CommandName Test-StartupType `
-Verifiable
Mock -CommandName Test-ServiceExists `
-MockWith { $true } `
-Verifiable
Mock `
-CommandName Compare-ServicePath `
-MockWith { $true } `
-Verifiable
Mock -CommandName Write-WriteProperty `
-MockWith { $true } `
-Verifiable
Mock `
-CommandName Start-ServiceResource `
-Verifiable
Mock -CommandName Stop-ServiceResource `
-Verifiable
# Mocks that should not be called
Mock -CommandName New-Service
Mock -CommandName Remove-Service
It 'Should not throw an exception' {
$Splat = $script:splatServiceExistsAutomatic.Clone()
{ Set-TargetResource @Splat } | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Test-StartupType -Exactly 1
Assert-MockCalled -CommandName Test-ServiceExists -Exactly 1
Assert-MockCalled -CommandName Start-ServiceResource -Exactly 1
Assert-MockCalled -CommandName Stop-ServiceResource -Exactly 1
Assert-MockCalled -CommandName Remove-Service -Exactly 0
Assert-MockCalled -CommandName New-Service -Exactly 0
Assert-MockCalled -CommandName Compare-ServicePath -Exactly 1
Assert-MockCalled -CommandName Write-WriteProperty -Exactly 1
}
}
Context 'Service exists and should, should be stopped, all parameters passed' {
# Mocks that should be called
Mock -CommandName Test-StartupType `
-Verifiable
Mock -CommandName Test-ServiceExists `
-MockWith { $true } `
-Verifiable
Mock -CommandName Compare-ServicePath `
-MockWith { $true } `
-Verifiable
Mock -CommandName Write-WriteProperty `
-MockWith { $false } `
-Verifiable
Mock -CommandName Stop-ServiceResource `
-Verifiable
# Mocks that should not be called
Mock -CommandName New-Service
Mock -CommandName Remove-Service
Mock -CommandName Start-ServiceResource
It 'Should not throw an exception' {
$Splat = $script:splatServiceExistsAutomatic.Clone()
$Splat.State = 'Stopped'
{ Set-TargetResource @Splat } | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Test-StartupType -Exactly 1
Assert-MockCalled -CommandName Test-ServiceExists -Exactly 1
Assert-MockCalled -CommandName Start-ServiceResource -Exactly 0
Assert-MockCalled -CommandName Stop-ServiceResource -Exactly 1
Assert-MockCalled -CommandName Remove-Service -Exactly 0
Assert-MockCalled -CommandName New-Service -Exactly 0
Assert-MockCalled -CommandName Compare-ServicePath -Exactly 1
Assert-MockCalled -CommandName Write-WriteProperty -Exactly 1
}
}
Context 'Service exists and should, State is Ignore, all parameters passed' {
# Mocks that should be called
Mock -CommandName Test-StartupType `
-Verifiable
Mock -CommandName Test-ServiceExists `
-MockWith { $true } `
-Verifiable
Mock -CommandName Compare-ServicePath `
-MockWith { $true } `
-Verifiable
Mock -CommandName Write-WriteProperty `
-MockWith { $false } `
-Verifiable
# Mocks that should not be called
Mock -CommandName New-Service
Mock -CommandName Remove-Service
Mock -CommandName Start-ServiceResource
Mock -CommandName Stop-ServiceResource
It 'Should not throw an exception' {
$Splat = $script:splatServiceExistsAutomatic.Clone()
$Splat.State = 'Ignore'
{ Set-TargetResource @Splat } | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Test-StartupType -Exactly 1
Assert-MockCalled -CommandName Test-ServiceExists -Exactly 1
Assert-MockCalled -CommandName Start-ServiceResource -Exactly 0
Assert-MockCalled -CommandName Stop-ServiceResource -Exactly 0
Assert-MockCalled -CommandName Remove-Service -Exactly 0
Assert-MockCalled -CommandName New-Service -Exactly 0
Assert-MockCalled -CommandName Compare-ServicePath -Exactly 1
Assert-MockCalled -CommandName Write-WriteProperty -Exactly 1
}
}
Context 'Service does not exist but should' {
# Mocks that should be called
Mock -CommandName Test-StartupType `
-Verifiable
Mock -CommandName Test-ServiceExists `
-MockWith { $false } `
-Verifiable
Mock -CommandName New-Service `
-Verifiable
Mock -CommandName Write-WriteProperty `
-MockWith { $false } `
-Verifiable
Mock -CommandName Start-ServiceResource `
-MockWith { $false } `
-Verifiable
# Mocks that should not be called
Mock -CommandName Compare-ServicePath
Mock -CommandName Remove-Service
Mock -CommandName Stop-ServiceResource
It 'Should not throw an exception' {
$Splat = $script:splatServiceExistsAutomatic.Clone()
{ Set-TargetResource @Splat } | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Test-StartupType -Exactly 1
Assert-MockCalled -CommandName Test-ServiceExists -Exactly 1
Assert-MockCalled -CommandName Start-ServiceResource -Exactly 1
Assert-MockCalled -CommandName Stop-ServiceResource -Exactly 0
Assert-MockCalled -CommandName Remove-Service -Exactly 0
Assert-MockCalled -CommandName New-Service -Exactly 1
Assert-MockCalled -CommandName Compare-ServicePath -Exactly 0
Assert-MockCalled -CommandName Write-WriteProperty -Exactly 1
}
}
Context 'Service does not exist but should, but no path specified' {
# Mocks that should be called
Mock -CommandName Test-StartupType `
-Verifiable
Mock -CommandName Test-ServiceExists `
-MockWith { $false } `
-Verifiable
# Mocks that should not be called
Mock -CommandName New-Service
Mock -CommandName Compare-ServicePath
Mock -CommandName Start-ServiceResource
Mock -CommandName Remove-Service
Mock -CommandName Stop-ServiceResource
Mock -CommandName Write-WriteProperty
$errorRecord = Get-InvalidArgumentRecord `
-ErrorId 'ServiceDoesNotExistPathMissingError' `
-ErrorMessage ($script:localizedData.ServiceDoesNotExistPathMissingError `
-f $script:testServiceName)
It 'Should throw an exception' {
$Splat = $script:splatServiceExistsAutomatic.Clone()
$Splat.Remove('Path')
{ Set-TargetResource @Splat } | Should Throw $errorRecord
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Test-StartupType -Exactly 1
Assert-MockCalled -CommandName Test-ServiceExists -Exactly 1
Assert-MockCalled -CommandName Start-ServiceResource -Exactly 0
Assert-MockCalled -CommandName Stop-ServiceResource -Exactly 0
Assert-MockCalled -CommandName Remove-Service -Exactly 0
Assert-MockCalled -CommandName New-Service -Exactly 0
Assert-MockCalled -CommandName Compare-ServicePath -Exactly 0
Assert-MockCalled -CommandName Write-WriteProperty -Exactly 0
}
}
}
Describe "$script:DscResourceName\Test-TargetResource" {
# Mocks that should be called
Mock -CommandName Test-ServiceExists `
-MockWith { $true } `
-Verifiable
Mock -CommandName Test-StartupType `
-Verifiable
Mock -CommandName Get-ServiceResource `
-MockWith { $script:testServiceMockRunning } `
-Verifiable
Mock -CommandName Get-Win32ServiceObject `
-MockWith { $script:testWin32ServiceMockRunningLocalSystem } `
-Verifiable
Mock -CommandName Compare-ServicePath `
-MockWith { $true } `
-Verifiable
Mock -CommandName Test-UserName `
-MockWith { $true } `
-Verifiable
Context 'Service exists and should, and all parameters match' {
It 'Should not throw an exception' {
$Splat = $script:splatServiceExistsAutomatic.Clone()
{ $script:result = Test-TargetResource @Splat } | Should Not Throw
}
It 'Should return true' {
$script:result | Should Be $true
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Test-ServiceExists -Exactly 1
Assert-MockCalled -CommandName Get-ServiceResource -Exactly 1
Assert-MockCalled -CommandName Get-Win32ServiceObject -Exactly 1
Assert-MockCalled -CommandName Test-StartupType -Exactly 1
Assert-MockCalled -CommandName Compare-ServicePath -Exactly 1
Assert-MockCalled -CommandName Test-UserName -Exactly 1
}
}
Context 'Service exists and should, path mistmatches' {
# Mocks that should be called
Mock -CommandName Compare-ServicePath `
-MockWith { $false } `
-Verifiable
# Mocks that should not be called
Mock -CommandName Test-UserName
It 'Should not throw an exception' {
$Splat = $script:splatServiceExistsAutomatic.Clone()
$Splat.Path = 'c:\ANewPath.exe'
{ $script:result = Test-TargetResource @Splat } | Should Not Throw
}
It 'Should return false' {
$script:result | Should Be $false
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Test-ServiceExists -Exactly 1
Assert-MockCalled -CommandName Get-ServiceResource -Exactly 1
Assert-MockCalled -CommandName Get-Win32ServiceObject -Exactly 1
Assert-MockCalled -CommandName Test-StartupType -Exactly 1
Assert-MockCalled -CommandName Compare-ServicePath -Exactly 1
Assert-MockCalled -CommandName Test-UserName -Exactly 0
}
}
Context 'Service exists and should, startup type mistmatches' {
# Mocks that should be called
Mock -CommandName Test-UserName `
-MockWith { $true } `
-Verifiable
It 'Should not throw an exception' {
$Splat = $script:splatServiceExistsAutomatic.Clone()
$Splat.StartupType = 'Manual'
{ $script:result = Test-TargetResource @Splat } | Should Not Throw
}
It 'Should return false' {
$script:result | Should Be $false
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Test-ServiceExists -Exactly 1
Assert-MockCalled -CommandName Get-ServiceResource -Exactly 1
Assert-MockCalled -CommandName Get-Win32ServiceObject -Exactly 1
Assert-MockCalled -CommandName Test-StartupType -Exactly 1
Assert-MockCalled -CommandName Compare-ServicePath -Exactly 1
Assert-MockCalled -CommandName Test-UserName -Exactly 1
}
}
Context 'Service exists and should, credential mistmatches' {
# Mocks that should be called
Mock -CommandName Test-UserName `
-MockWith { $false } `
-Verifiable
It 'Should not throw an exception' {
$Splat = $script:splatServiceExistsAutomatic.Clone()
$Splat.Credential = $script:testNewCredential
{ $script:result = Test-TargetResource @Splat } | Should Not Throw
}
It 'Should return false' {
$script:result | Should Be $false
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Test-ServiceExists -Exactly 1
Assert-MockCalled -CommandName Get-ServiceResource -Exactly 1
Assert-MockCalled -CommandName Get-Win32ServiceObject -Exactly 1
Assert-MockCalled -CommandName Test-StartupType -Exactly 1
Assert-MockCalled -CommandName Compare-ServicePath -Exactly 1
Assert-MockCalled -CommandName Test-UserName -Exactly 1
}
}
Context 'Service exists and should, is running but should be stopped' {
# Mocks that should be called
Mock -CommandName Compare-ServicePath `
-MockWith { $true } `
-Verifiable
Mock -CommandName Test-UserName `
-MockWith { $true } `
-Verifiable
It 'Should not throw an exception' {
$Splat = $script:splatServiceExistsAutomatic.Clone()
$Splat.State = 'Stopped'
{ $script:result = Test-TargetResource @Splat } | Should Not Throw
}
It 'Should return false' {
$script:result | Should Be $false
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Test-ServiceExists -Exactly 1
Assert-MockCalled -CommandName Get-ServiceResource -Exactly 1
Assert-MockCalled -CommandName Get-Win32ServiceObject -Exactly 1
Assert-MockCalled -CommandName Test-StartupType -Exactly 1
Assert-MockCalled -CommandName Compare-ServicePath -Exactly 1
Assert-MockCalled -CommandName Test-UserName -Exactly 1
}
}
Context 'Service exists and should, everything matches and State is set to Ignore' {
# Mocks that should be called
Mock -CommandName Compare-ServicePath `
-MockWith { $true } `
-Verifiable
Mock -CommandName Test-UserName `
-MockWith { $true } `
-Verifiable
It 'Should not throw an exception' {
$Splat = $script:splatServiceExistsAutomatic.Clone()
$Splat.State = 'Ignore'
{ $script:result = Test-TargetResource @Splat } | Should Not Throw
}
It 'Should return true' {
$script:result | Should Be $true
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Test-ServiceExists -Exactly 1
Assert-MockCalled -CommandName Get-ServiceResource -Exactly 1
Assert-MockCalled -CommandName Get-Win32ServiceObject -Exactly 1
Assert-MockCalled -CommandName Test-StartupType -Exactly 1
Assert-MockCalled -CommandName Compare-ServicePath -Exactly 1
Assert-MockCalled -CommandName Test-UserName -Exactly 1
}
}
Context 'Service exists and should not' {
# Mocks that should not be called
Mock -CommandName Compare-ServicePath
Mock -CommandName Test-UserName
Mock -CommandName Get-ServiceResource
Mock -CommandName Get-Win32ServiceObject
It 'Should not throw an exception' {
$Splat = $script:splatServiceExistsAutomatic.Clone()
$Splat.Ensure = 'Absent'
{ $script:result = Test-TargetResource @Splat } | Should Not Throw
}
It 'Should return false' {
$script:result | Should Be $false
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Test-ServiceExists -Exactly 1
Assert-MockCalled -CommandName Get-ServiceResource -Exactly 0
Assert-MockCalled -CommandName Get-Win32ServiceObject -Exactly 0
Assert-MockCalled -CommandName Test-StartupType -Exactly 1
Assert-MockCalled -CommandName Compare-ServicePath -Exactly 0
Assert-MockCalled -CommandName Test-UserName -Exactly 0
}
}
}
Describe "$script:DscResourceName\Test-StartupType" {
Context 'Service is stopped, startup is automatic' {
$errorRecord = Get-InvalidArgumentRecord `
-ErrorId 'CannotStopServiceSetToStartAutomatically' `
-ErrorMessage ($script:localizedData.CannotStopServiceSetToStartAutomatically `
-f $script:testServiceName)
It 'Should throw CannotStopServiceSetToStartAutomatically exception' {
{ Test-StartupType `
-Name $script:testServiceName `
-StartupType 'Automatic' `
-State 'Stopped' `
} | Should Throw $errorRecord
}
}
Context 'Service is stopped, startup is not automatic' {
It 'Should not throw an exception' {
{ Test-StartupType `
-Name $script:testServiceName `
-StartupType 'Disabled' `
-State 'Stopped' `
} | Should Not Throw
}
}
Context 'Service is running, startup is disabled' {
$errorRecord = Get-InvalidArgumentRecord `
-ErrorId 'CannotStartAndDisable' `
-ErrorMessage ($script:localizedData.CannotStartAndDisable -f $script:testServiceName)
It 'Should throw CannotStartAndDisable exception' {
{ Test-StartupType `
-Name $script:testServiceName `
-StartupType 'Disabled' `
-State 'Running' `
} | Should Throw $errorRecord
}
}
Context 'Service is running, startup is not disabled' {
It 'Should not throw exception' {
{ Test-StartupType `
-Name $script:testServiceName `
-StartupType 'Manual' `
-State 'Running' `
} | Should Not Throw
}
}
Context 'State is Ignore' {
It 'Should not throw exception for Disabled' {
{ Test-StartupType `
-Name $script:testServiceName `
-StartupType 'Disabled' `
-State 'Ignore' `
} | Should Not Throw
}
It 'Should not throw exception for Automatic' {
{ Test-StartupType `
-Name $script:testServiceName `
-StartupType 'Automatic' `
-State 'Ignore' `
} | Should Not Throw
}
}
}
Describe "$script:DscResourceName\ConvertTo-StartModeString" {
Context 'StartupType is Automatic' {
It 'Should return Auto' {
ConvertTo-StartModeString -StartupType 'Automatic' | Should Be 'Auto'
}
}
Context 'StartupType is Disabled' {
It 'Should return Disabled' {
ConvertTo-StartModeString -StartupType 'Disabled' | Should Be 'Disabled'
}
}
}
Describe "$script:DscResourceName\ConvertTo-StartupTypeString" {
Context 'StartupType is Auto' {
It 'Should return Automatic' {
ConvertTo-StartupTypeString -StartMode 'Auto' | Should Be 'Automatic'
}
}
Context 'StartupType is Disabled' {
It 'Should return Disabled' {
ConvertTo-StartupTypeString -StartMode 'Disabled' | Should Be 'Disabled'
}
}
}
Describe "$script:DscResourceName\Get-Win32ServiceObject" {
Context 'Service exists' {
Mock -CommandName Get-CimInstance `
-MockWith { $script:testWin32ServiceMockRunningLocalSystem } `
-Verifiable
It 'Should not throw an exception' {
{ $script:result = Get-Win32ServiceObject `
-Name $script:testServiceName } | Should Not Throw
}
It 'Should return expected hash table' {
$script:result = $script:testWin32ServiceMockRunningLocalSystem
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-CimInstance -Exactly 1
}
}
Context 'Service does not exist' {
Mock -CommandName Get-CimInstance `
-Verifiable
It 'Should not throw an exception' {
{ $script:result = Get-Win32ServiceObject `
-Name $script:testServiceName } | Should Not Throw
}
It 'Should return $null' {
$script:result | Should BeNullOrEmpty
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-CimInstance -Exactly 1
}
}
}
Describe "$script:DscResourceName\Set-ServiceStartMode" {
# Stub Functions for Mocking
function Invoke-CimMethod { param ( $InputObject, $MethodName, $Arguments ) }
Context 'Current StartMode is set to Auto and should be' {
Mock -CommandName Invoke-CimMethod
It 'Should not throw an exception' {
{ Set-ServiceStartMode `
-Win32ServiceObject $script:testWin32ServiceMockRunningLocalSystem `
-StartupType $script:testServiceStartupType `
} | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Invoke-CimMethod -Exactly 0
}
}
Context 'Current StartMode needs to be changed, and is changed successfully' {
Mock -CommandName Invoke-CimMethod `
-MockWith { return @{ ReturnValue = 0 } } `
-Verifiable
It 'Should not throw an exception' {
{ Set-ServiceStartMode `
-Win32ServiceObject $script:testWin32ServiceMockRunningLocalSystem `
-StartupType 'Manual' `
} | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Invoke-CimMethod -Exactly 1
}
}
Context 'Current StartMode needs to be changed but an error occured' {
Mock -CommandName Invoke-CimMethod `
-MockWith { return @{ ReturnValue = 99 } } `
-Verifiable
$innerMessage = ($script:localizedData.MethodFailed `
-f 'Change', 'Win32_Service', '99' )
$errorMessage = ($script:localizedData.ErrorChangingProperty `
-f 'StartupType', $innerMessage)
$errorRecord = Get-InvalidArgumentRecord `
-ErrorId 'ChangeStartupTypeFailed' `
-ErrorMessage $errorMessage
It 'Should throw an exception' {
{ Set-ServiceStartMode `
-Win32ServiceObject $script:testWin32ServiceMockRunningLocalSystem `
-StartupType 'Manual' `
} | Should Throw $errorMessage
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Invoke-CimMethod -Exactly 1
}
}
}
Describe "$script:DscResourceName\Write-WriteProperty" {
# Stub Functions for Mocking
function Invoke-CimMethod { param ( $InputObject, $MethodName, $Arguments ) }
# Mocks that should be called
Mock -CommandName Get-Win32ServiceObject `
-MockWith { $script:testServiceStartupTypeWin32 } `
-Verifiable
Mock -CommandName Get-Service `
-MockWith { $script:testServiceMockRunning } `
-Verifiable
# Mocks that should not be called
Mock -CommandName Set-Service
Context 'No parameters passed' {
It 'Should not throw an exception' {
{ Write-WriteProperty -Name $script:testServiceName } | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-Win32ServiceObject -Exactly 1
Assert-MockCalled -CommandName Get-Service -Exactly 1
Assert-MockCalled -CommandName Set-Service -Exactly 0
}
}
Context 'Different DisplayName passed, will not trigger restart' {
# Mocks that should be called
Mock -CommandName Set-Service `
-Verifiable
It 'Should not throw an exception' {
{ $script:Result = Write-WriteProperty `
-Name $script:testServiceName `
-DisplayName 'NewDisplayName' `
} | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-Win32ServiceObject -Exactly 1
Assert-MockCalled -CommandName Get-Service -Exactly 1
Assert-MockCalled -CommandName Set-Service -Exactly 1
}
}
Context 'Different Description passed, will not trigger restart' {
# Mocks that should be called
Mock -CommandName Set-Service `
-Verifiable
It 'Should not throw an exception' {
{ $script:Result = Write-WriteProperty `
-Name $script:testServiceName `
-Description 'NewDescription' `
} | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-Win32ServiceObject -Exactly 1
Assert-MockCalled -CommandName Get-Service -Exactly 1
Assert-MockCalled -CommandName Set-Service -Exactly 1
}
}
Context 'Different Dependencies passed and set successfully, will not trigger restart' {
# Mocks that should be called
Mock -CommandName Invoke-CimMethod `
-MockWith { @{ ReturnValue = 0 } } `
-Verifiable
It 'Should not throw an exception' {
{ $script:Result = Write-WriteProperty `
-Name $script:testServiceName `
-Dependencies 'DepService1','DepService2' `
} | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-Win32ServiceObject -Exactly 1
Assert-MockCalled -CommandName Get-Service -Exactly 1
Assert-MockCalled -CommandName Invoke-CimMethod -Exactly 1
}
}
Context 'Different Dependencies passed and set failed, will not trigger restart' {
# Mocks that should be called
Mock -CommandName Invoke-CimMethod `
-MockWith { @{ ReturnValue = 99 } } `
-Verifiable
$innerMessage = ($script:localizedData.MethodFailed `
-f 'Change','Win32_Service','99')
$errorMessage = ($script:localizedData.ErrorChangingProperty `
-f 'Dependencies',$innerMessage)
$errorRecord = Get-InvalidArgumentRecord `
-ErrorId 'ChangeCredentialFailed' `
-ErrorMessage $errorMessage
It 'Should throw an exception' {
{ $script:Result = Write-WriteProperty `
-Name $script:testServiceName `
-Dependencies 'DepService1','DepService2' `
} | Should Throw $errorRecord
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-Win32ServiceObject -Exactly 1
Assert-MockCalled -CommandName Get-Service -Exactly 1
Assert-MockCalled -CommandName Invoke-CimMethod -Exactly 1
}
}
Context 'Path passed, will trigger restart' {
# Mocks that should be called
Mock -CommandName Write-BinaryProperty `
-MockWith { $true } `
-Verifiable
It 'Should not throw an exception' {
{ $script:Result = Write-WriteProperty `
-Name $script:testServiceName `
-Path 'c:\NewExecutable.exe' `
} | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-Win32ServiceObject -Exactly 1
Assert-MockCalled -CommandName Get-Service -Exactly 1
Assert-MockCalled -CommandName Write-BinaryProperty -Exactly 1
}
}
Context 'StartupType passed, will not trigger restart' {
# Mocks that should be called
Mock -CommandName Set-ServiceStartMode `
-Verifiable
It 'Should not throw an exception' {
{ $script:Result = Write-WriteProperty `
-Name $script:testServiceName `
-StartupType 'Manual' `
} | Should Not Throw
}
It 'Should return false' {
$script:Result | Should Be $false
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-Win32ServiceObject -Exactly 1
Assert-MockCalled -CommandName Get-Service -Exactly 1
Assert-MockCalled -CommandName Set-ServiceStartMode -Exactly 1
}
}
Context 'Credential passed, will not trigger restart' {
# Mocks that should be called
Mock -CommandName Write-CredentialProperty `
-Verifiable
It 'Should not throw an exception' {
{ $script:Result = Write-WriteProperty `
-Name $script:testServiceName `
-Credential $script:testCredential `
} | Should Not Throw
}
It 'Should return false' {
$script:Result | Should Be $false
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-Win32ServiceObject -Exactly 1
Assert-MockCalled -CommandName Get-Service -Exactly 1
Assert-MockCalled -CommandName Write-CredentialProperty -Exactly 1
}
}
Context 'BuildinAccount passed, will not trigger restart' {
# Mocks that should be called
Mock -CommandName Write-CredentialProperty `
-Verifiable
It 'Should not throw an exception' {
{ $script:Result = Write-WriteProperty `
-Name $script:testServiceName `
-BuiltInAccount 'LocalSystem' `
} | Should Not Throw
}
It 'Should return false' {
$script:Result | Should Be $false
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-Win32ServiceObject -Exactly 1
Assert-MockCalled -CommandName Get-Service -Exactly 1
Assert-MockCalled -CommandName Write-CredentialProperty -Exactly 1
}
}
Context 'DesktopInteract passed, will not trigger restart' {
# Mocks that should be called
Mock -CommandName Write-CredentialProperty `
-Verifiable
It 'Should not throw an exception' {
{ $script:Result = Write-WriteProperty `
-Name $script:testServiceName `
-DesktopInteract $true `
} | Should Not Throw
}
It 'Should return false' {
$script:Result | Should Be $false
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-Win32ServiceObject -Exactly 1
Assert-MockCalled -CommandName Get-Service -Exactly 1
Assert-MockCalled -CommandName Write-CredentialProperty -Exactly 1
}
}
}
Describe "$script:DscResourceName\Write-CredentialProperty" {
# Dummy Functions
function Invoke-CimMethod { param ( $InputObject, $MethodName, $Arguments ) }
Context 'No parameters to be changed passed in' {
# Mocks that should not be called
Mock -CommandName Get-UserNameAndPassword
Mock -CommandName Test-UserName
Mock -CommandName Set-LogOnAsServicePolicy
Mock -CommandName Invoke-CimMethod
It 'Should not throw an exception' {
{ Write-CredentialProperty `
-ServiceWmi $script:testWin32ServiceMockRunningLocalSystem `
} | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-UserNameAndPassword -Exactly 0
Assert-MockCalled -CommandName Test-UserName -Exactly 0
Assert-MockCalled -CommandName Set-LogOnAsServicePolicy -Exactly 0
Assert-MockCalled -CommandName Invoke-CimMethod -Exactly 0
}
}
Context 'Desktop interact passed but does not need to be changed' {
# Mocks that should be called
Mock -CommandName Get-UserNameAndPassword `
-MockWith { $null, $null } `
-Verifiable
# Mocks that should not be called
Mock -CommandName Test-UserName
Mock -CommandName Set-LogOnAsServicePolicy
Mock -CommandName Invoke-CimMethod
It 'Should not throw an exception' {
{ Write-CredentialProperty `
-ServiceWmi $script:testWin32ServiceMockRunningLocalSystem `
-DesktopInteract $true `
} | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-UserNameAndPassword -Exactly 1
Assert-MockCalled -CommandName Test-UserName -Exactly 0
Assert-MockCalled -CommandName Set-LogOnAsServicePolicy -Exactly 0
Assert-MockCalled -CommandName Invoke-CimMethod -Exactly 0
}
}
Context 'Desktop interact passed and does need to be changed' {
# Mocks that should be called
Mock -CommandName Get-UserNameAndPassword `
-MockWith { $null, $null } `
-Verifiable
Mock -CommandName Invoke-CimMethod `
-MockWith { @{ returnValue = 0 } } `
-Verifiable
# Mocks that should not be called
Mock -CommandName Test-UserName
Mock -CommandName Set-LogOnAsServicePolicy
It 'Should not throw an exception' {
{ Write-CredentialProperty `
-ServiceWmi $script:testWin32ServiceMockRunningLocalSystem `
-DesktopInteract $false `
} | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-UserNameAndPassword -Exactly 1
Assert-MockCalled -CommandName Test-UserName -Exactly 0
Assert-MockCalled -CommandName Set-LogOnAsServicePolicy -Exactly 0
Assert-MockCalled -CommandName Invoke-CimMethod -Exactly 1
}
}
Context 'Desktop interact passed and does need to be changed but fails' {
# Mocks that should be called
Mock -CommandName Get-UserNameAndPassword `
-MockWith { $null,$null } `
-Verifiable
Mock -CommandName Invoke-CimMethod `
-MockWith { @{ returnValue = 99 } } `
-Verifiable
# Mocks that should not be called
Mock -CommandName Test-UserName
Mock -CommandName Set-LogOnAsServicePolicy
$innerMessage = ($script:localizedData.MethodFailed `
-f 'Change','Win32_Service','99')
$errorMessage = ($script:localizedData.ErrorChangingProperty `
-f 'Credential',$innerMessage)
$errorRecord = Get-InvalidArgumentRecord `
-ErrorId 'ChangeCredentialFailed' `
-ErrorMessage $errorMessage
It 'Should throw an exception' {
{ Write-CredentialProperty `
-ServiceWmi $script:testWin32ServiceMockRunningLocalSystem `
-DesktopInteract $false `
} | Should Throw $errorRecord
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-UserNameAndPassword -Exactly 1
Assert-MockCalled -CommandName Test-UserName -Exactly 0
Assert-MockCalled -CommandName Set-LogOnAsServicePolicy -Exactly 0
Assert-MockCalled -CommandName Invoke-CimMethod -Exactly 1
}
}
Context 'Both credential and BuiltInAccount passed' {
# Mocks that should not be called
Mock -CommandName Get-UserNameAndPassword
Mock -CommandName Invoke-CimMethod
Mock -CommandName Test-UserName
Mock -CommandName Set-LogOnAsServicePolicy
$errorRecord = Get-InvalidArgumentRecord `
-ErrorId 'OnlyCredentialOrBuiltInAccount' `
-ErrorMessage ($script:localizedData.OnlyOneParameterCanBeSpecified `
-f 'Credential','BuiltInAccount')
It 'Should throw an exception' {
{ Write-CredentialProperty `
-ServiceWmi $script:testWin32ServiceMockRunningLocalSystem `
-Credential $script:testCredential `
-BuiltInAccount 'LocalSystem' } | Should Throw $errorRecord
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-UserNameAndPassword -Exactly 0
Assert-MockCalled -CommandName Test-UserName -Exactly 0
Assert-MockCalled -CommandName Set-LogOnAsServicePolicy -Exactly 0
Assert-MockCalled -CommandName Invoke-CimMethod -Exactly 0
}
}
Context 'Credential passed but does not need to be changed' {
# Mocks that should be called
Mock -CommandName Get-UserNameAndPassword `
-MockWith { $script:testUsername,$script:testPassword } `
-Verifiable
Mock -CommandName Test-UserName `
-MockWith { $true } `
-Verifiable
# Mocks that should not be called
Mock -CommandName Set-LogOnAsServicePolicy
Mock -CommandName Invoke-CimMethod
It 'Should not throw an exception' {
{ Write-CredentialProperty `
-ServiceWmi $script:testWin32ServiceMockRunningLocalSystem `
-Credential $script:testCredential } | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-UserNameAndPassword -Exactly 1
Assert-MockCalled -CommandName Test-UserName -Exactly 1
Assert-MockCalled -CommandName Set-LogOnAsServicePolicy -Exactly 0
Assert-MockCalled -CommandName Invoke-CimMethod -Exactly 0
}
}
Context 'Credential passed and needs to be changed' {
# Mocks that should be called
Mock -CommandName Get-UserNameAndPassword `
-MockWith { $script:testUsername,$script:testPassword } `
-Verifiable
Mock -CommandName Test-UserName `
-MockWith { $false } `
-Verifiable
Mock -CommandName Set-LogOnAsServicePolicy `
-Verifiable
Mock -CommandName Invoke-CimMethod `
-MockWith { @{ returnValue = 0 } } `
-Verifiable
It 'Should not throw an exception' {
{ Write-CredentialProperty `
-ServiceWmi $script:testWin32ServiceMockRunningLocalSystem `
-Credential $script:testCredential } | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-UserNameAndPassword -Exactly 1
Assert-MockCalled -CommandName Test-UserName -Exactly 1
Assert-MockCalled -CommandName Set-LogOnAsServicePolicy -Exactly 1
Assert-MockCalled -CommandName Invoke-CimMethod -Exactly 1
}
}
Context 'Credential passed and needs to be changed, but throws exception' {
# Mocks that should be called
Mock -CommandName Get-UserNameAndPassword `
-MockWith { $script:testUsername,$script:testPassword } `
-Verifiable
Mock -CommandName Test-UserName `
-MockWith { $false } `
-Verifiable
Mock -CommandName Set-LogOnAsServicePolicy `
-Verifiable
Mock -CommandName Invoke-CimMethod `
-MockWith { @{ returnValue = 99 } } `
-Verifiable
$innerMessage = ($script:localizedData.MethodFailed `
-f 'Change','Win32_Service','99')
$errorMessage = ($script:localizedData.ErrorChangingProperty `
-f 'Credential',$innerMessage)
$errorRecord = Get-InvalidArgumentRecord `
-ErrorId 'ChangeCredentialFailed' `
-ErrorMessage $errorMessage
It 'Should not throw an exception' {
{ Write-CredentialProperty `
-ServiceWmi $script:testWin32ServiceMockRunningLocalSystem `
-Credential $script:testCredential } | Should Throw $errorRecord
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-UserNameAndPassword -Exactly 1
Assert-MockCalled -CommandName Test-UserName -Exactly 1
Assert-MockCalled -CommandName Set-LogOnAsServicePolicy -Exactly 1
Assert-MockCalled -CommandName Invoke-CimMethod -Exactly 1
}
}
Context 'BuiltInAccount passed but does not need to be changed' {
# Mocks that should be called
Mock -CommandName Get-UserNameAndPassword `
-MockWith { '.\LocalSystem','' } `
-Verifiable
Mock -CommandName Test-UserName `
-MockWith { $true } `
-Verifiable
# Mocks that should not be called
Mock -CommandName Set-LogOnAsServicePolicy
Mock -CommandName Invoke-CimMethod
It 'Should not throw an exception' {
{ Write-CredentialProperty `
-ServiceWmi $script:testWin32ServiceMockRunningLocalSystem `
-BuiltInAccount 'LocalSystem' } | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-UserNameAndPassword -Exactly 1
Assert-MockCalled -CommandName Test-UserName -Exactly 1
Assert-MockCalled -CommandName Set-LogOnAsServicePolicy -Exactly 0
Assert-MockCalled -CommandName Invoke-CimMethod -Exactly 0
}
}
Context 'BuiltInAccount passed and needs to be changed' {
# Mocks that should be called
Mock -CommandName Get-UserNameAndPassword `
-MockWith { '.\LocalSystem',$null } `
-Verifiable
Mock -CommandName Test-UserName `
-MockWith { $false } `
-Verifiable
Mock -CommandName Invoke-CimMethod `
-MockWith { @{ returnValue = 0 } } `
-Verifiable
# Mocks that should not be called
Mock -CommandName Set-LogOnAsServicePolicy
It 'Should not throw an exception' {
{ Write-CredentialProperty `
-ServiceWmi $script:testWin32ServiceMockRunningLocalSystem `
-BuiltInAccount 'LocalSystem' } | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-UserNameAndPassword -Exactly 1
Assert-MockCalled -CommandName Test-UserName -Exactly 1
Assert-MockCalled -CommandName Set-LogOnAsServicePolicy -Exactly 0
Assert-MockCalled -CommandName Invoke-CimMethod -Exactly 1
}
}
Context 'BuiltInAccount passed and needs to be changed, but throws exception' {
# Mocks that should be called
Mock -CommandName Get-UserNameAndPassword `
-MockWith { '.\LocalSystem',$null } `
-Verifiable
Mock -CommandName Test-UserName `
-MockWith { $false } `
-Verifiable
Mock -CommandName Invoke-CimMethod `
-MockWith { @{ returnValue = 99 } } `
-Verifiable
# Mocks that should not be called
Mock -CommandName Set-LogOnAsServicePolicy
$innerMessage = ($script:localizedData.MethodFailed `
-f 'Change','Win32_Service','99')
$errorMessage = ($script:localizedData.ErrorChangingProperty `
-f 'Credential',$innerMessage)
$errorRecord = Get-InvalidArgumentRecord `
-ErrorId 'ChangeCredentialFailed' `
-ErrorMessage $errorMessage
It 'Should throw an exception' {
{ Write-CredentialProperty `
-ServiceWmi $script:testWin32ServiceMockRunningLocalSystem `
-BuiltInAccount 'LocalSystem' } | Should Throw $errorRecord
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-UserNameAndPassword -Exactly 1
Assert-MockCalled -CommandName Test-UserName -Exactly 1
Assert-MockCalled -CommandName Set-LogOnAsServicePolicy -Exactly 0
Assert-MockCalled -CommandName Invoke-CimMethod -Exactly 1
}
}
}
Describe "$script:DscResourceName\Write-BinaryProperty" {
# Stub Functions for Mocking
function Invoke-CimMethod { param ( $InputObject, $MethodName, $Arguments ) }
Context 'Path is already correct' {
# Mocks that should not be called
Mock -CommandName Invoke-CimMethod
It 'Should not throw an exception' {
{ $script:result = Write-BinaryProperty `
-ServiceWmi $script:testWin32ServiceMockRunningLocalSystem `
-Path $script:testServiceExecutablePath } | Should Not Throw
}
It 'Should return false' {
$script:result = $false
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Invoke-CimMethod -Exactly 0
}
}
Context 'Path needs to be changed and is changed without error' {
# Mocks that should be called
Mock -CommandName Invoke-CimMethod `
-MockWith { @{ returnValue = 0 } } `
-Verifiable
It 'Should not throw an exception' {
{ $script:result = Write-BinaryProperty `
-ServiceWmi $script:testWin32ServiceMockRunningLocalSystem `
-Path 'c:\NewServicePath.exe' } | Should Not Throw
}
It 'Should return true' {
$script:result = $true
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Invoke-CimMethod -Exactly 1
}
}
Context 'Path needs to be changed but an error occurs changing it' {
# Mocks that should be called
Mock -CommandName Invoke-CimMethod `
-MockWith { @{ returnValue = 99 } } `
-Verifiable
$innerMessage = ($script:localizedData.MethodFailed `
-f 'Change', 'Win32_Service', 99)
$errorMessage = ($script:localizedData.ErrorChangingProperty `
-f 'Binary Path', $innerMessage)
$errorRecord = Get-InvalidArgumentRecord `
-ErrorId 'ChangeBinaryPathFailed' `
-ErrorMessage $errorMessage
It 'Should throw an exception' {
{ $script:result = Write-BinaryProperty `
-ServiceWmi $script:testWin32ServiceMockRunningLocalSystem `
-Path 'c:\NewServicePath.exe' } | Should Throw $errorRecord
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Invoke-CimMethod -Exactly 1
}
}
}
Describe "$script:DscResourceName\Test-UserName" {
Context 'Username matches' {
It 'Should not throw an exception' {
{ $script:result = Test-Username `
-ServiceWmi $script:testWin32ServiceMockRunningLocalSystem `
-Username $script:testUsername } | Should Not Throw
}
It 'Should return true' {
$script:result = $true
}
}
Context 'Username does not match' {
It 'Should not throw an exception' {
{ $script:result = Test-Username `
-ServiceWmi $script:testWin32ServiceMockRunningLocalSystem `
-Username 'mismatch' } | Should Not Throw
}
It 'Should return false' {
$script:result = $false
}
}
}
Describe "$script:DscResourceName\Get-UserNameAndPassword" {
Context 'Built-in account provided' {
$script:result = Get-UserNameAndPassword -BuiltInAccount 'LocalService'
It 'Should return: NT Authority\LocalService and $null' {
$script:result[0] | Should Be 'NT Authority\LocalService'
$script:result[1] | Should BeNullOrEmpty
}
}
Context 'Credential provided' {
$script:result = Get-UserNameAndPassword -Credential $script:testCredential
It 'Should return the correct username and password' {
$script:result[0] | Should Be ".\$script:testUsername"
$script:result[1] | Should Be $script:testPassword
}
}
Context 'Neither built-in account or credential provided' {
$script:result = Get-UserNameAndPassword
It 'Should return both results as null/empty' {
$script:result[0] | Should BeNullOrEmpty
$script:result[1] | Should BeNullOrEmpty
}
}
}
Describe "$script:DscResourceName\Remove-Service" {
# Mocks that should be called
Mock -CommandName 'sc.exe' -Verifiable
Mock -CommandName Test-ServiceExists -MockWith { $false } -Verifiable
Context 'Service is deleted successfully' {
# Mocks that should not be called
Mock -CommandName Start-Sleep
It 'Should not throw exception' {
{
Remove-Service -Name $script:testServiceName
} | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName 'sc.exe' -Exactly 1
Assert-MockCalled -CommandName Test-ServiceExists -Exactly 1
Assert-MockCalled -CommandName Start-Sleep -Exactly 0
}
}
Context 'Service can not be deleted (will take 5 seconds)' {
Mock -CommandName Test-ServiceExists -MockWith { $true } -Verifiable
Mock -CommandName Start-Sleep -Verifiable
$errorRecord = Get-InvalidArgumentRecord `
-ErrorId 'ErrorDeletingService' `
-ErrorMessage ($script:localizedData.ErrorDeletingService -f $script:testServiceName)
It 'Should throw ErrorDeletingService exception' {
{ Remove-Service -Name $script:testServiceName } | Should Throw $errorRecord
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName 'sc.exe' -Exactly 1
}
}
}
Describe "$script:DscResourceName\Start-ServiceResource" {
Context 'Service is already running' {
# Mocks that should be called
Mock -CommandName Get-ServiceResource `
-MockWith { $script:testServiceMockRunning } `
-Verifiable
# Mocks that should not be called
Mock -CommandName New-Object
It 'Should not throw exception' {
{
Start-ServiceResource -Name $script:testServiceName -StartUpTimeout 30000
} | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-ServiceResource -Exactly 1
Assert-MockCalled -CommandName New-Object -Exactly 0
}
}
Context 'Service is stopped' {
# Mocks that should be called
Mock -CommandName Get-ServiceResource `
-MockWith { $script:testServiceMockStopped } `
-Verifiable
Mock -CommandName New-Object `
-Verifiable
$global:ServiceStarted = $false
It 'Should not throw exception' {
{
Start-ServiceResource -Name $script:testServiceName -StartUpTimeout 30000
} | Should Not Throw
}
It 'Should call start method' {
$global:ServiceStarted | Should Be $true
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-ServiceResource -Exactly 1
Assert-MockCalled -CommandName New-Object -Exactly 1
}
Remove-Variable -Name ServiceStarted -Scope Global
}
}
Describe "$script:DscResourceName\Stop-ServiceResource" {
Context 'Service is already stopped' {
# Mocks that should be called
Mock -CommandName Get-ServiceResource `
-MockWith { $script:testServiceMockStopped } `
-Verifiable
# Mocks that should not be called
Mock -CommandName New-Object
It 'Should not throw exception' {
{
Stop-ServiceResource -Name $script:testServiceName -TerminateTimeout 30000
} | Should Not Throw
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-ServiceResource -Exactly 1
Assert-MockCalled -CommandName New-Object -Exactly 0
}
}
Context 'Service is running' {
# Mocks that should be called
Mock -CommandName Get-ServiceResource `
-MockWith { $script:testServiceMockRunning } `
-Verifiable
Mock -CommandName New-Object `
-Verifiable
$global:ServiceStopped = $false
It 'Should not throw exception' {
{
Stop-ServiceResource -Name $script:testServiceName -TerminateTimeout 30000
} | Should Not Throw
}
It 'Should call stop method' {
$global:ServiceStopped | Should Be $true
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled -CommandName Get-ServiceResource -Exactly 1
Assert-MockCalled -CommandName New-Object -Exactly 1
}
Remove-Variable -Name ServiceStopped -Scope Global
}
}
Describe "$script:DscResourceName\Resolve-UserName" {
Context 'Username is NetworkService' {
It 'Should return NT Authority\NetworkService' {
Resolve-UserName -Username 'NetworkService' | Should Be 'NT Authority\NetworkService'
}
}
Context 'Username is LocalService' {
It 'Should return NT Authority\LocalService' {
Resolve-UserName -Username 'LocalService' | Should Be 'NT Authority\LocalService'
}
}
Context 'Username is LocalSystem' {
It 'Should return .\LocalSystem' {
Resolve-UserName -Username 'LocalSystem' | Should Be '.\LocalSystem'
}
}
Context 'Username is Domain\svcAccount' {
It 'Should return Domain\svcAccount' {
Resolve-UserName -Username 'Domain\svcAccount' | Should Be 'Domain\svcAccount'
}
}
Context 'Username is svcAccount' {
It 'Should return .\svcAccount' {
Resolve-UserName -Username 'svcAccount' | Should Be '.\svcAccount'
}
}
}
Describe "$script:DscResourceName\Test-ServiceExists" {
Context 'Service exists' {
# Mocks that should be called
Mock -CommandName Get-Service `
-ParameterFilter { $Name -eq $script:testServiceName } `
-MockWith { $script:testServiceMockRunning } `
-Verifiable
It 'Should not throw an exception' {
{
$script:result = Test-ServiceExists -Name $script:testServiceName
} | Should Not Throw
}
It 'Should return true' {
$script:Result | Should Be $true
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled `
-CommandName Get-Service `
-ParameterFilter { $Name -eq $script:testServiceName } `
-Exactly 1
}
}
Context 'Service does not exist' {
# Mocks that should be called
Mock -CommandName Get-Service `
-ParameterFilter { $Name -eq $script:testServiceName } `
-Verifiable
It 'Should not throw an exception' {
{
$script:result = Test-ServiceExists -Name $script:testServiceName
} | Should Not Throw
}
It 'Should return false' {
$script:Result | Should Be $false
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled `
-CommandName Get-Service `
-ParameterFilter { $Name -eq $script:testServiceName } `
-Exactly 1
}
}
}
Describe "$script:DscResourceName\Compare-ServicePath" {
Context 'Service exists, path matches' {
# Mocks that should be called
Mock -CommandName Get-CimInstance `
-MockWith { $script:testWin32ServiceMockRunningLocalSystem } `
-Verifiable
It 'Should not throw an exception' {
{ $script:result = Compare-ServicePath `
-Name $script:testServiceName `
-Path $script:testServiceExecutablePath `
} | Should Not Throw
}
It 'Should return true' {
$script:Result | Should Be $true
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled `
-CommandName Get-CimInstance `
-Exactly 1
}
}
Context 'Service exists, path does not match' {
# Mocks that should be called
Mock -CommandName Get-CimInstance `
-MockWith { $script:testWin32ServiceMockRunningLocalSystem } `
-Verifiable
It 'Should not throw an exception' {
{ $script:result = Compare-ServicePath `
-Name $script:testServiceName `
-Path 'c:\differentpath' `
} | Should Not Throw
}
It 'Should return false' {
$script:Result | Should Be $false
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled `
-CommandName Get-CimInstance `
-Exactly 1
}
}
Context 'Service does not exist' {
# Mocks that should be called
Mock -CommandName Get-CimInstance `
-Verifiable
It 'Should not throw an exception' {
{ $script:result = Compare-ServicePath `
-Name $script:testServiceName `
-Path 'c:\differentpath' `
} | Should Not Throw
}
It 'Should return false' {
$script:Result | Should Be $false
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled `
-CommandName Get-CimInstance `
-Exactly 1
}
}
}
Describe "$script:DscResourceName\Get-ServiceResource" {
Context 'Service exists' {
# Mocks that should be called
Mock -CommandName Get-Service `
-ParameterFilter { $Name -eq $script:testServiceName } `
-MockWith { $script:testServiceMockRunning } `
-Verifiable
It 'Should not throw an exception' {
{
$script:service = Get-ServiceResource -Name $script:testServiceName
} | Should Not Throw
}
It 'Should return the correct hashtable properties' {
$script:service.Name | Should Be $script:testServiceName
$script:service.ServiceName | Should Be $script:testServiceName
$script:service.DisplayName | Should Be $script:testServiceDisplayName
$script:service.StartType | Should Be $script:testServiceStartupType
$script:service.Status | Should Be $script:testServiceStatusRunning
$script:service.ServicesDependedOn | Should Be $script:testServiceDependsOnHash
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled `
-CommandName Get-Service `
-ParameterFilter { $Name -eq $script:testServiceName } `
-Exactly 1
}
}
Context 'Service does not exist' {
# Mocks that should be called
Mock -CommandName Get-Service `
-ParameterFilter { $Name -eq $script:testServiceName } `
-Verifiable
$errorRecord = Get-InvalidArgumentRecord `
-ErrorId 'ServiceNotFound' `
-ErrorMessage ($script:localizedData.ServiceNotFound -f $script:testServiceName)
It 'Should throw a ServiceNotFound exception' {
{
$script:service = Get-ServiceResource -Name $script:testServiceName
} | Should Throw $errorRecord
}
It 'Should call expected Mocks' {
Assert-VerifiableMocks
Assert-MockCalled `
-CommandName Get-Service `
-ParameterFilter { $Name -eq $script:testServiceName } `
-Exactly 1
}
}
}
}
}
finally
{
Exit-DscResourceTestEnvironment -TestEnvironment $script:testEnvironment
}