Tests/Unit/MSFT_xServiceResource.Tests.ps1

# Suppressed as per PSSA Rule Severity guidelines for unit/integration tests:
# https://github.com/PowerShell/DscResources/blob/master/PSSARuleSeverities.md
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')]
param ()

$script:DSCModuleName      = 'xPSDesiredStateConfiguration'
$script:DSCResourceName    = 'MSFT_xServiceResource'

#region HEADER
# Integration Test Template Version: 1.1.0
[String] $script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or `
     (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) )
{
    & git @('clone','https://github.com/PowerShell/DscResource.Tests.git',(Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\'))
}

Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force
$TestEnvironment = Initialize-TestEnvironment `
    -DSCModuleName $script:DSCModuleName `
    -DSCResourceName $script:DSCResourceName `
    -TestType Unit
#endregion

# Begin Testing
try
{
    InModuleScope $script:DSCResourceName {
        $DSCResourceName = 'MSFT_xServiceResource'

        $script:testServiceName = "DscTestService"
        $script:testServiceDisplayName = "DSC test service display name"
        $script:testServiceDescription = "This is DSC test service used for integration testing MSFT_xServiceResource"
        $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
        }

        function Get-InvalidArgumentError
        {
            [CmdletBinding()]
            param
            (
                [Parameter(Mandatory)]
                [ValidateNotNullOrEmpty()]
                [System.String]
                $ErrorId,

                [Parameter(Mandatory)]
                [ValidateNotNullOrEmpty()]
                [System.String]
                $ErrorMessage
            )

            $exception = New-Object -TypeName System.InvalidOperationException `
                -ArgumentList $ErrorMessage
            $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidOperation
            $errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord `
                -ArgumentList $exception, $ErrorId, $errorCategory, $null
            return $errorRecord
        } # end function Get-InvalidArgumentError

        Describe "$DSCResourceName\Get-TargetResource" {
            Context 'Service exists' {
                # Mocks that should be called
                Mock `
                    -CommandName Test-ServiceExist `
                    -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 `
                            -Verbose } | 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-ServiceExist -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-ServiceExist -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 `
                        -Verbose } | 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-ServiceExist -Exactly 1
                    Assert-MockCalled -CommandName Get-serviceResource -Exactly 0
                    Assert-MockCalled -CommandName Get-Win32ServiceObject -Exactly 0
                }
            }
        }

        Describe "$DSCResourceName\Test-TargetResource" {
            # Mocks that should be called
            Mock `
                -CommandName Test-ServiceExist `
                -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 `
                        -Verbose } | Should Not Throw
                }

                It 'Should return true' {
                    $script:result | Should Be $True
                }

                It 'Should call expected Mocks' {
                    Assert-VerifiableMocks
                    Assert-MockCalled -CommandName Test-ServiceExist -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 `
                        -Verbose } | Should Not Throw
                }

                It 'Should return false' {
                    $script:result | Should Be $False
                }

                It 'Should call expected Mocks' {
                    Assert-VerifiableMocks
                    Assert-MockCalled -CommandName Test-ServiceExist -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 `
                        -Verbose } | Should Not Throw
                }

                It 'Should return false' {
                    $script:result | Should Be $False
                }

                It 'Should call expected Mocks' {
                    Assert-VerifiableMocks
                    Assert-MockCalled -CommandName Test-ServiceExist -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 `
                        -Verbose } | Should Not Throw
                }

                It 'Should return false' {
                    $script:result | Should Be $False
                }

                It 'Should call expected Mocks' {
                    Assert-VerifiableMocks
                    Assert-MockCalled -CommandName Test-ServiceExist -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 { $false } `
                    -Verifiable

                # Mocks that should not be called
                Mock -CommandName Test-UserName

                It 'Should not throw an exception' {
                    $Splat = $script:splatServiceExistsAutomatic.Clone()
                    $Splat.State = 'Stopped'
                    { $script:result = Test-TargetResource @Splat `
                        -Verbose } | Should Not Throw
                }

                It 'Should return false' {
                    $script:result | Should Be $False
                }

                It 'Should call expected Mocks' {
                    Assert-VerifiableMocks
                    Assert-MockCalled -CommandName Test-ServiceExist -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 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 `
                        -Verbose } | Should Not Throw
                }

                It 'Should return false' {
                    $script:result | Should Be $False
                }

                It 'Should call expected Mocks' {
                    Assert-VerifiableMocks
                    Assert-MockCalled -CommandName Test-ServiceExist -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 "$DSCResourceName\Set-TargetResource" {
            Context 'Service exists and should not' {
                # Mocks that should be called
                Mock `
                    -CommandName Test-StartupType `
                    -Verifiable

                Mock `
                    -CommandName Test-ServiceExist `
                    -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 `
                        -Verbose } | Should Not Throw
                }

                It 'Should call expected Mocks' {
                    Assert-VerifiableMocks
                    Assert-MockCalled -CommandName Test-StartupType -Exactly 1
                    Assert-MockCalled -CommandName Test-ServiceExist -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 and match' {
                # Mocks that should be called
                Mock `
                    -CommandName Test-StartupType `
                    -Verifiable

                Mock `
                    -CommandName Test-ServiceExist `
                    -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 `
                        -Verbose } | Should Not Throw
                }

                It 'Should call expected Mocks' {
                    Assert-VerifiableMocks
                    Assert-MockCalled -CommandName Test-StartupType -Exactly 1
                    Assert-MockCalled -CommandName Test-ServiceExist -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-ServiceExist `
                    -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 `
                        -Verbose } | Should Not Throw
                }

                It 'Should call expected Mocks' {
                    Assert-VerifiableMocks
                    Assert-MockCalled -CommandName Test-StartupType -Exactly 1
                    Assert-MockCalled -CommandName Test-ServiceExist -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 and match' {
                # Mocks that should be called
                Mock `
                    -CommandName Test-StartupType `
                    -Verifiable

                Mock `
                    -CommandName Test-ServiceExist `
                    -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 `
                        -Verbose } | Should Not Throw
                }

                It 'Should call expected Mocks' {
                    Assert-VerifiableMocks
                    Assert-MockCalled -CommandName Test-StartupType -Exactly 1
                    Assert-MockCalled -CommandName Test-ServiceExist -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 and match' {
                # Mocks that should be called
                Mock `
                    -CommandName Test-StartupType `
                    -Verifiable

                Mock `
                    -CommandName Test-ServiceExist `
                    -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 `
                        -Verbose } | Should Not Throw
                }

                It 'Should call expected Mocks' {
                    Assert-VerifiableMocks
                    Assert-MockCalled -CommandName Test-StartupType -Exactly 1
                    Assert-MockCalled -CommandName Test-ServiceExist -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 does not exist but should' {
                # Mocks that should be called
                Mock `
                    -CommandName Test-StartupType `
                    -Verifiable

                Mock `
                    -CommandName Test-ServiceExist `
                    -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 `
                        -Verbose } | Should Not Throw
                }

                It 'Should call expected Mocks' {
                    Assert-VerifiableMocks
                    Assert-MockCalled -CommandName Test-StartupType -Exactly 1
                    Assert-MockCalled -CommandName Test-ServiceExist -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-ServiceExist `
                    -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-InvalidArgumentError `
                    -ErrorId "ServiceDoesNotExistPathMissingError" `
                    -ErrorMessage ($LocalizedData.ServiceDoesNotExistPathMissingError `
                        -f $script:testServiceName)

                It 'Should not throw an exception' {
                    $Splat = $script:splatServiceExistsAutomatic.Clone()
                    $Splat.Remove('Path')
                    { Set-TargetResource @Splat `
                        -Verbose } | Should Throw $errorRecord
                }

                It 'Should call expected Mocks' {
                    Assert-VerifiableMocks
                    Assert-MockCalled -CommandName Test-StartupType -Exactly 1
                    Assert-MockCalled -CommandName Test-ServiceExist -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 "$DSCResourceName\Test-StartupType" {
            Context 'Service is stopped, startup is automatic' {
                $errorRecord = Get-InvalidArgumentError `
                    -ErrorId "CannotStopServiceSetToStartAutomatically" `
                    -ErrorMessage ($LocalizedData.CannotStopServiceSetToStartAutomatically `
                        -f $script:testServiceName)

                It 'Shoult throw CannotStopServiceSetToStartAutomatically exception' {
                    { Test-StartupType `
                        -Name $script:testServiceName `
                        -StartupType 'Automatic' `
                        -State 'Stopped' `
                        -Verbose } | Should Throw $errorRecord
                }
            }

            Context 'Service is stopped, startup is not automatic' {
                It 'Shoult not throw exception' {
                    { Test-StartupType `
                        -Name $script:testServiceName `
                        -StartupType 'Disabled' `
                        -State 'Stopped' `
                        -Verbose } | Should Not Throw
                }
            }

            Context 'Service is running, startup is disabled' {
                $errorRecord = Get-InvalidArgumentError `
                    -ErrorId "CannotStartAndDisable" `
                    -ErrorMessage ($LocalizedData.CannotStartAndDisable -f $script:testServiceName)

                It 'Shoult throw CannotStartAndDisable exception' {
                    { Test-StartupType `
                        -Name $script:testServiceName `
                        -StartupType 'Disabled' `
                        -State 'Running' `
                        -Verbose } | Should Throw $errorRecord
                }
            }

            Context 'Service is running, startup is not disabled' {
                It 'Shoult not throw exception' {
                    { Test-StartupType `
                        -Name $script:testServiceName `
                        -StartupType 'Manual' `
                        -State 'Running' `
                        -Verbose } | Should Not Throw
                }
            }
        }

        Describe "$DSCResourceName\ConvertTo-StartModeString" {
            Context "StartupType is 'Automatic'" {
                It "Should return 'Automatic'" {
                    ConvertTo-StartModeString -StartupType 'Automatic' | Should Be 'Auto'
                }
            }

            Context "StartupType is 'Disabled'" {
                It "Should return 'Disabled'" {
                    ConvertTo-StartModeString -StartupType 'Disabled' | Should Be 'Disabled'
                }
            }
        }

        Describe "$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 "$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 `
                        -Verbose } | 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 `
                        -Verbose } | 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 "$DSCResourceName\Set-ServiceStartMode" {
            # Dummy Functions
            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 `
                        -Verbose } | 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 OK' {
                Mock `
                    -CommandName Invoke-CimMethod `
                    -MockWith { return @{ ReturnValue = 0 } } `
                    -Verifiable

                It 'Should not throw an exception' {
                    { Set-ServiceStartMode `
                        -Win32ServiceObject $script:testWin32ServiceMockRunningLocalSystem `
                        -StartupType 'Manual' `
                        -Verbose } | 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 = ($LocalizedData.MethodFailed `
                    -f "Change", "Win32_Service", '99' )
                $errorMessage = ($LocalizedData.ErrorChangingProperty `
                    -f "StartupType", $innerMessage)
                $errorRecord = Get-InvalidArgumentError `
                    -ErrorId "ChangeStartupTypeFailed" `
                    -ErrorMessage $errorMessage

                It 'Should throw an exception' {
                    { Set-ServiceStartMode `
                        -Win32ServiceObject $script:testWin32ServiceMockRunningLocalSystem `
                        -StartupType 'Manual' `
                        -Verbose } | Should Throw $errorMessage
                }

                It 'Should call expected Mocks' {
                    Assert-VerifiableMocks
                    Assert-MockCalled -CommandName Invoke-CimMethod -Exactly 1
                }
            }
        }

        Describe "$DSCResourceName\Write-WriteProperty" {
            # Dummy Functions
            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 `
                        -Verbose } | 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' `
                        -Verbose } | 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' `
                        -Verbose } | 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 OK, 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' `
                        -Verbose } | 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 = ($LocalizedData.MethodFailed `
                    -f "Change","Win32_Service","99")
                $errorMessage = ($LocalizedData.ErrorChangingProperty `
                    -f "Dependencies",$innerMessage)
                $errorRecord = Get-InvalidArgumentError `
                    -ErrorId "ChangeCredentialFailed" `
                    -ErrorMessage $errorMessage

                It 'Should throw an exception' {
                    { $script:Result = Write-WriteProperty `
                        -Name $script:testServiceName `
                        -Dependencies 'DepService1','DepService2' `
                        -Verbose } | 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' `
                        -Verbose } | 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' `
                        -Verbose } | Should Not Throw
                }

                It 'Should return true' {
                    $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 `
                        -Verbose } | Should Not Throw
                }

                It 'Should return true' {
                    $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' `
                        -Verbose } | Should Not Throw
                }

                It 'Should return true' {
                    $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 `
                        -Verbose } | Should Not Throw
                }

                It 'Should return true' {
                    $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 "$DSCResourceName\Write-CredentialProperty" {
            # Dummy Functions
            function Invoke-CimMethod { param ( $InputObject,$MethodName,$Arguments ) }

            Context 'No parameters to be changed passed' {
                # 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 `
                        -Verbose } | 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 `
                        -Verbose } | 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 `
                        -Verbose } | 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 = ($LocalizedData.MethodFailed `
                    -f "Change","Win32_Service","99")
                $errorMessage = ($LocalizedData.ErrorChangingProperty `
                    -f "Credential",$innerMessage)
                $errorRecord = Get-InvalidArgumentError `
                    -ErrorId "ChangeCredentialFailed" `
                    -ErrorMessage $errorMessage

                It 'Should throw an exception' {
                    { Write-CredentialProperty `
                        -ServiceWmi $script:testWin32ServiceMockRunningLocalSystem `
                        -DesktopInteract $false `
                        -Verbose } | 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 buildinaccount 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-InvalidArgumentError `
                   -ErrorId "OnlyCredentialOrBuiltInAccount" `
                    -ErrorMessage ($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 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 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 = ($LocalizedData.MethodFailed `
                    -f "Change","Win32_Service","99")
                $errorMessage = ($LocalizedData.ErrorChangingProperty `
                    -f "Credential",$innerMessage)
                $errorRecord = Get-InvalidArgumentError `
                    -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 = ($LocalizedData.MethodFailed `
                    -f "Change","Win32_Service","99")
                $errorMessage = ($LocalizedData.ErrorChangingProperty `
                    -f "Credential",$innerMessage)
                $errorRecord = Get-InvalidArgumentError `
                    -ErrorId "ChangeCredentialFailed" `
                    -ErrorMessage $errorMessage

                It 'Should not 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 "$DSCResourceName\Write-BinaryProperty" {
            # Dummy Functions
            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 = ($LocalizedData.MethodFailed `
                    -f "Change", "Win32_Service", 99)
                $errorMessage = ($LocalizedData.ErrorChangingProperty `
                    -f "Binary Path", $innerMessage)
                $errorRecord = Get-InvalidArgumentError `
                    -ErrorId "ChangeBinaryPathFailed" `
                    -ErrorMessage $errorMessage

                It 'Should not 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 "$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 "$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 '.\$($script:testUsername)' and '$($script:testPassword)'" {
                     $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 '$null' and '$null'" {
                     $script:result[0] | Should BeNullOrEmpty
                     $script:result[1] | Should BeNullOrEmpty
                }
            }
        }

        Describe "$DSCResourceName\Remove-Service" {
            # Mocks that should be called
            Mock -CommandName 'sc.exe' -Verifiable
            Mock -CommandName Test-ServiceExist -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-ServiceExist -Exactly 1
                    Assert-MockCalled -CommandName Start-Sleep -Exactly 0
                }
            }

            Mock -CommandName Test-ServiceExist -MockWith { $true } -Verifiable

            Context 'Service can not be deleted (will take 5 seconds)' {
                Mock -CommandName Start-Sleep -Verifiable

                $errorRecord = Get-InvalidArgumentError `
                    -ErrorId "ErrorDeletingService" `
                    -ErrorMessage ($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 "$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 'Called 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 "$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 'Called 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 "$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 "$DSCResourceName\New-InvalidArgumentError" {
            Context 'Throws exception' {
                $errorRecord = Get-InvalidArgumentError `
                    -ErrorId "ErrorId" `
                    -ErrorMessage "ErrorMessage"

                It 'Throws exception' {
                    { New-InvalidArgumentError `
                        -ErrorId "ErrorId" `
                        -ErrorMessage "ErrorMessage"
                    } | Should Throw $errorRecord
                }
            }
        }

        Describe "$DSCResourceName\Test-ServiceExist" {
            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-ServiceExist -Name $script:testServiceName -Verbose
                    } | Should Not Throw
                }

                It 'Result is 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-ServiceExist -Name $script:testServiceName -Verbose
                    } | Should Not Throw
                }

                It 'Result is 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 "$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 `
                        -Verbose } | Should Not Throw
                }

                It 'Result is 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' `
                        -Verbose } | Should Not Throw
                }

                It 'Result is 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' `
                        -Verbose } | Should Not Throw
                }

                It 'Result is false' {
                    $script:Result | Should Be $false
                }

                It 'Should call expected Mocks' {
                    Assert-VerifiableMocks
                    Assert-MockCalled `
                        -CommandName Get-CimInstance `
                        -Exactly 1
                }
            }
        }

        Describe "$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 -Verbose
                    } | 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-InvalidArgumentError `
                    -ErrorId "ServiceNotFound" `
                    -ErrorMessage ($LocalizedData.ServiceNotFound -f $script:testServiceName)

                It 'Should throw a ServiceNotFound exception' {
                    {
                        $script:service = Get-ServiceResource -Name $script:testServiceName -Verbose
                    } | Should Throw $errorRecord
                }

                It 'Should call expected Mocks' {
                    Assert-VerifiableMocks
                    Assert-MockCalled `
                        -CommandName Get-Service `
                        -ParameterFilter { $Name -eq $script:testServiceName } `
                        -Exactly 1
                }
            }
        }
    }
}
finally
{
    #region FOOTER
    Restore-TestEnvironment -TestEnvironment $TestEnvironment
    #endregion
}