Tests/Unit/MSFT_xCertReq.Tests.ps1

[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')]
param ()

$script:DSCModuleName      = 'xCertificate'
$script:DSCResourceName    = 'MSFT_xCertReq'

#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_xCertReq'
        function Get-InvalidOperationError
        {
            [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-InvalidOperationError

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

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

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

        $validThumbprint = (
            [System.AppDomain]::CurrentDomain.GetAssemblies().GetTypes() | Where-Object {
                $_.BaseType.BaseType -eq [System.Security.Cryptography.HashAlgorithm] -and
                ($_.Name -cmatch 'Managed$' -or $_.Name -cmatch 'Provider$')
            } | Select-Object -First 1 | ForEach-Object {
                (New-Object $_).ComputeHash([String]::Empty) | ForEach-Object {
                    '{0:x2}' -f $_
                }
            }
        ) -join ''
        $CAServerFQDN          = 'rootca.contoso.com'
        $CARootName            = 'contoso-CA'
        $validSubject          = 'Test Subject'
        $validIssuer           = "CN=$CARootName, DC=contoso, DC=com"
        $KeyLength             = '1024'
        $Exportable            = $true
        $ProviderName          = '"Microsoft RSA SChannel Cryptographic Provider"'
        $OID                   = '1.3.6.1.5.5.7.3.1'
        $KeyUsage              = '0xa0'
        $CertificateTemplate   = 'WebServer'
        $SubjectAltUrl         = 'contoso.com'
        $SubjectAltName        = "dns=$SubjectAltUrl"

        $validCert      = New-Object -TypeName PSObject -Property @{
            Thumbprint  = $validThumbprint
            Subject     = "CN=$validSubject"
            Issuer      = $validIssuer
            NotBefore   = (Get-Date).AddDays(-30) # Issued on
            NotAfter    = (Get-Date).AddDays(31) # Expires after
        }
        Add-Member -InputObject $validCert -MemberType ScriptMethod -Name Verify -Value {
            return $true
        }
        $expiringCert   = New-Object -TypeName PSObject -Property @{
            Thumbprint  = $validThumbprint
            Subject     = "CN=$validSubject"
            Issuer      = $validIssuer
            NotBefore   = (Get-Date).AddDays(-30) # Issued on
            NotAfter    = (Get-Date).AddDays(30) # Expires after
        }
        Add-Member -InputObject $expiringCert -MemberType ScriptMethod -Name Verify -Value {
            return $true
        }
        $expiredCert    = New-Object -TypeName PSObject -Property @{
            Thumbprint  = $validThumbprint
            Subject     = "CN=$validSubject"
            Issuer      = $validIssuer
            NotBefore   = (Get-Date).AddDays(-30) # Issued on
            NotAfter    = (Get-Date).AddDays(-1) # Expires after
        }
        Add-Member -InputObject $expiredCert -MemberType ScriptMethod -Name Verify -Value {
            return $true
        }
        $sanOid = New-Object -TypeName System.Security.Cryptography.Oid -Property @{FriendlyName = 'Subject Alternative Name'}
        $sanExt = @{
            oid = $(,$sanOid)    
            Critical = $false
        }
        Add-Member -InputObject $sanExt -MemberType ScriptMethod -Name Format -Force -Value {
            return "DNS Name=$SubjectAltUrl"
        }
        $validSANCert      = New-Object -TypeName PSObject -Property @{
            Thumbprint     = $validThumbprint
            Subject        = "CN=$validSubject"
            Issuer         = $validIssuer
            NotBefore      = (Get-Date).AddDays(-30) # Issued on
            NotAfter       = (Get-Date).AddDays(31) # Expires after
            Extensions     = $sanExt
        }
        Add-Member -InputObject $validSANCert -MemberType ScriptMethod -Name Verify -Value {
            return $true
        }
        $incorrectSanExt = @{
            oid = $(,$sanOid)    
            Critical = $false
        }
        Add-Member -InputObject $incorrectSanExt -MemberType ScriptMethod -Name Format -Force -Value {
            return "DNS Name=incorrect.com"
        }
        $incorrectSANCert  = New-Object -TypeName PSObject -Property @{
            Thumbprint     = $validThumbprint
            Subject        = "CN=$validSubject"
            Issuer         = $validIssuer
            NotBefore      = (Get-Date).AddDays(-30) # Issued on
            NotAfter       = (Get-Date).AddDays(31) # Expires after
            Extensions     = $incorrectSanExt
        }
        Add-Member -InputObject $incorrectSANCert -MemberType ScriptMethod -Name Verify -Value {
            return $true
        }
        $emptySANCert      = New-Object -TypeName PSObject -Property @{
            Thumbprint     = $validThumbprint
            Subject        = "CN=$validSubject"
            Issuer         = $validIssuer
            NotBefore      = (Get-Date).AddDays(-30) # Issued on
            NotAfter       = (Get-Date).AddDays(31) # Expires after
            Extensions     = @()
        }
        Add-Member -InputObject $emptySANCert -MemberType ScriptMethod -Name Verify -Value {
            return $true
        }

        $CAType         = 'Enterprise'
        $CepURL         = 'DummyURL'
        $CesURL         = 'DummyURL'

        $testUsername   = 'DummyUsername'
        $testPassword   = 'DummyPassword'
        $testCredential = New-Object System.Management.Automation.PSCredential $testUsername, (ConvertTo-SecureString $testPassword -AsPlainText -Force)
        $Params = @{
            Subject               = $validSubject
            CAServerFQDN          = $CAServerFQDN
            CARootName            = $CARootName
            KeyLength             = $KeyLength
            Exportable            = $Exportable
            ProviderName          = $ProviderName
            OID                   = $OID
            KeyUsage              = $KeyUsage
            CertificateTemplate   = $CertificateTemplate
            Credential            = $testCredential
            AutoRenew             = $False
        }
        $ParamsAutoDiscovery = @{
            Subject               = $validSubject
            KeyLength             = $KeyLength
            Exportable            = $Exportable
            ProviderName          = $ProviderName
            OID                   = $OID
            KeyUsage              = $KeyUsage
            CertificateTemplate   = $CertificateTemplate
            Credential            = $testCredential
            AutoRenew             = $False
        }
        $ParamsAutoRenew = @{
            Subject               = $validSubject
            CAServerFQDN          = $CAServerFQDN
            CARootName            = $CARootName
            KeyLength             = $KeyLength
            Exportable            = $Exportable
            ProviderName          = $ProviderName
            OID                   = $OID
            KeyUsage              = $KeyUsage
            CertificateTemplate   = $CertificateTemplate
            Credential            = $testCredential
            AutoRenew             = $True
        }
        $ParamsNoCred = @{
            Subject               = $validSubject
            CAServerFQDN          = $CAServerFQDN
            CARootName            = $CARootName
            KeyLength             = $KeyLength
            Exportable            = $Exportable
            ProviderName          = $ProviderName
            OID                   = $OID
            KeyUsage              = $KeyUsage
            CertificateTemplate   = $CertificateTemplate
            Credential            = $null
            AutoRenew             = $False
        }
        $ParamsAutoRenewNoCred = @{
            Subject               = $validSubject
            CAServerFQDN          = $CAServerFQDN
            CARootName            = $CARootName
            KeyLength             = $KeyLength
            Exportable            = $Exportable
            ProviderName          = $ProviderName
            OID                   = $OID
            KeyUsage              = $KeyUsage
            CertificateTemplate   = $CertificateTemplate
            Credential            = $null
            AutoRenew             = $True
        }
        $ParamsKeyLength4096NoCred = @{
            Subject               = $validSubject
            CAServerFQDN          = $CAServerFQDN
            CARootName            = $CARootName
            KeyLength             = '4096'
            Exportable            = $Exportable
            ProviderName          = $ProviderName
            OID                   = $OID
            KeyUsage              = $KeyUsage
            CertificateTemplate   = $CertificateTemplate
            Credential            = $null
            AutoRenew             = $False
        }
        $ParamsKeyLength4096AutoRenewNoCred = @{
            Subject               = $validSubject
            CAServerFQDN          = $CAServerFQDN
            CARootName            = $CARootName
            KeyLength             = '4096'
            Exportable            = $Exportable
            ProviderName          = $ProviderName
            OID                   = $OID
            KeyUsage              = $KeyUsage
            CertificateTemplate   = $CertificateTemplate
            Credential            = $null
            AutoRenew             = $True
        }
        $ParamsSubjectAltName = @{
            Subject               = $validSubject
            CAServerFQDN          = $CAServerFQDN
            CARootName            = $CARootName
            KeyLength             = $KeyLength
            Exportable            = $Exportable
            ProviderName          = $ProviderName
            OID                   = $OID
            KeyUsage              = $KeyUsage
            CertificateTemplate   = $CertificateTemplate
            Credential            = $testCredential
            SubjectAltName        = $SubjectAltName
            AutoRenew             = $False
        }
        $ParamsSubjectAltNameNoCred = @{
            Subject               = $validSubject
            CAServerFQDN          = $CAServerFQDN
            CARootName            = $CARootName
            KeyLength             = $KeyLength
            Exportable            = $Exportable
            ProviderName          = $ProviderName
            OID                   = $OID
            KeyUsage              = $KeyUsage
            CertificateTemplate   = $CertificateTemplate
            Credential            = $null
            SubjectAltName        = $SubjectAltName
            AutoRenew             = $False
        }
        $ParamsStandaloneWebEnrollment = @{
            Subject               = $validSubject
            CAServerFQDN          = $CAServerFQDN
            CARootName            = $CARootName
            KeyLength             = $KeyLength
            Exportable            = $Exportable
            ProviderName          = $ProviderName
            OID                   = $OID
            KeyUsage              = $KeyUsage
            CertificateTemplate   = $CertificateTemplate
            Credential            = $testCredential
            AutoRenew             = $False
            CAType                = 'Standalone'
            CepURL                = $CepURL
            CesURL                = $CesURL
        }
        $ParamsEnterpriseWebEnrollment = @{
            Subject               = $validSubject
            CAServerFQDN          = $CAServerFQDN
            CARootName            = $CARootName
            KeyLength             = $KeyLength
            Exportable            = $Exportable
            ProviderName          = $ProviderName
            OID                   = $OID
            KeyUsage              = $KeyUsage
            CertificateTemplate   = $CertificateTemplate
            Credential            = $testCredential
            AutoRenew             = $False
            CAType                = $CAType
            CepURL                = $CepURL
            CesURL                = $CesURL
        }

        $CertInf = @"
[NewRequest]
Subject = "CN=$validSubject"
KeySpec = 1
KeyLength = $KeyLength
Exportable = $($Exportable.ToString().ToUpper())
MachineKeySet = TRUE
SMIME = FALSE
PrivateKeyArchive = FALSE
UserProtected = FALSE
UseExistingKeySet = FALSE
ProviderName = $ProviderName
ProviderType = 12
RequestType = CMC
KeyUsage = $KeyUsage
[RequestAttributes]
CertificateTemplate = $CertificateTemplate
[EnhancedKeyUsageExtension]
OID = $OID
"@


        $CertInfNoTemplate = $CertInf.Replace(@"
[RequestAttributes]
CertificateTemplate = $CertificateTemplate
[EnhancedKeyUsageExtension]
"@
, '[EnhancedKeyUsageExtension]')
    

        $CertInfKey = $CertInf -Replace 'KeyLength = ([0-z]*)', 'KeyLength = 4096'
        $CertInfRenew = $Certinf
        $CertInfRenew += @"
 
RenewalCert = $validThumbprint
"@

        $CertInfKeyRenew = $CertInfRenew -Replace 'KeyLength = ([0-z]*)', 'KeyLength = 4096'
        $CertInfSubjectAltName = $Certinf
        $CertInfSubjectAltName += @"
 
[Extensions]
2.5.29.17 = "{text}$SubjectAltName"
"@


        Describe "$DSCResourceName\Get-TargetResource" {
            Mock Get-ChildItem -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' } `
                -Mockwith { $validCert }
            Mock Get-CertificateTemplateName -MockWith { $CertificateTemplate }
            Mock Get-CertificateSan -MockWith { $SubjectAltName }
            Mock -CommandName Find-CertificateAuthority -MockWith {
                    return New-Object -TypeName psobject -Property @{
                        CAServerFQDN = 'rootca.contoso.com'
                        CARootName = 'contoso-CA'
                    }
                }

            $result = Get-TargetResource @Params
            $resultAutoDiscovery = Get-TargetResource @ParamsAutoDiscovery
            It 'should return a hashtable' {
                ($result -is [hashtable]) | Should Be $true
            }
            It 'should contain the input values' {
                $result.Subject              | Should BeExactly $validSubject
                $result.CAServerFQDN         | Should BeNullOrEmpty
                $result.CARootName           | Should BeExactly $CARootName
                $result.KeyLength            | Should BeNullOrEmpty
                $result.Exportable           | Should BeNullOrEmpty
                $result.ProviderName         | Should BeNullOrEmpty
                $result.OID                  | Should BeNullOrEmpty
                $result.KeyUsage             | Should BeNullOrEmpty
                $result.CertificateTemplate  | Should BeExactly $CertificateTemplate
                $result.SubjectAltName       | Should BeNullOrEmpty
            }            
            It 'should return a hashtable' {
                ($resultAutoDiscovery -is [hashtable]) | Should Be $true
            }
            It 'should contain the input values and the CA should be auto-discovered' {
                $resultAutoDiscovery.Subject              | Should BeExactly $validSubject
                $resultAutoDiscovery.CAServerFQDN         | Should BeExactly $CAServerFQDN
                $resultAutoDiscovery.CARootName           | Should BeExactly $CARootName
                $resultAutoDiscovery.KeyLength            | Should BeNullOrEmpty
                $resultAutoDiscovery.Exportable           | Should BeNullOrEmpty
                $resultAutoDiscovery.ProviderName         | Should BeNullOrEmpty
                $resultAutoDiscovery.OID                  | Should BeNullOrEmpty
                $resultAutoDiscovery.KeyUsage             | Should BeNullOrEmpty
                $resultAutoDiscovery.CertificateTemplate  | Should BeExactly $CertificateTemplate
                $resultAutoDiscovery.SubjectAltName       | Should BeNullOrEmpty
            }
            It 'Should call the mocked function Find-CertificateAuthority once' {
                Assert-MockCalled -CommandName Find-CertificateAuthority -Exactly -Times 1
            }
        }
        #endregion

        #region Set-TargetResource
        Describe "$DSCResourceName\Set-TargetResource" {
            Mock -CommandName Join-Path -MockWith { 'xCertReq-Test' } `
                -ParameterFilter { $Path -eq $env:Temp }
            Mock -CommandName Test-Path -MockWith { $true } `
                -ParameterFilter { $Path -eq 'xCertReq-Test.req' }
            Mock -CommandName Test-Path -MockWith { $true } `
                -ParameterFilter { $Path -eq 'xCertReq-Test.cer' }
            Mock -CommandName CertReq.exe
            Mock -CommandName Set-Content `
                -ParameterFilter {
                    $Path -eq 'xCertReq-Test.inf' -and `
                    $Value -eq $CertInf
                }

            Context 'autorenew is false, credentials not passed' {
                Mock -CommandName Get-ChildItem -Mockwith { } `
                    -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' }

                It 'should not throw' {
                    { Set-TargetResource @ParamsNoCred } | Should Not Throw
                }

                It 'should call expected mocks' {
                    Assert-MockCalled -CommandName Join-Path -Exactly 1
                    Assert-MockCalled -CommandName Test-Path -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.req' }
                    Assert-MockCalled -CommandName Test-Path  -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.cer' }
                    Assert-MockCalled -CommandName Set-Content -Exactly 1 `
                        -ParameterFilter {
                            $Path -eq 'xCertReq-Test.inf' -and `
                            $Value -eq $CertInf
                        }
                    Assert-MockCalled -CommandName CertReq.exe -Exactly 3
                }
            }

            Context 'autorenew is true, credentials not passed and certificate does not exist' {
                Mock -CommandName Get-ChildItem -Mockwith { } `
                    -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' }

                It 'should not throw' {
                    { Set-TargetResource @ParamsAutoRenewNoCred } | Should Not Throw
                }

                It 'should call expected mocks' {
                    Assert-MockCalled -CommandName Join-Path -Exactly 1
                    Assert-MockCalled -CommandName Test-Path -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.req' }
                    Assert-MockCalled -CommandName Test-Path  -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.cer' }
                    Assert-MockCalled -CommandName Get-ChildItem -Exactly 1 `
                        -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' }
                    Assert-MockCalled -CommandName Set-Content -Exactly 1 `
                        -ParameterFilter {
                            $Path -eq 'xCertReq-Test.inf' -and `
                            $Value -eq $CertInf
                        }
                    Assert-MockCalled -CommandName CertReq.exe -Exactly 3
                }
            }

            Context 'autorenew is true, credentials not passed and valid certificate exists' {
                Mock -CommandName Get-ChildItem -Mockwith { $validCert } `
                    -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' }

                It 'should not throw' {
                    { Set-TargetResource @ParamsAutoRenewNoCred } | Should Not Throw
                }
                It 'should call expected mocks' {
                    Assert-MockCalled -CommandName Join-Path -Exactly 1
                    Assert-MockCalled -CommandName Test-Path -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.req' }
                    Assert-MockCalled -CommandName Test-Path  -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.cer' }
                    Assert-MockCalled -CommandName Get-ChildItem -Exactly 1 `
                        -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' }
                    Assert-MockCalled -CommandName Set-Content -Exactly 1 `
                        -ParameterFilter {
                            $Path -eq 'xCertReq-Test.inf' -and `
                            $Value -eq $CertInf
                        }
                    Assert-MockCalled -CommandName CertReq.exe -Exactly 3
                }
            }

            Mock -CommandName Set-Content `
                -ParameterFilter {
                    $Path -eq 'xCertReq-Test.inf' -and `
                    $Value -eq $CertInfRenew
                }
            Context 'autorenew is true, credentials not passed and expiring certificate exists' {
                Mock -CommandName Get-ChildItem -Mockwith { $expiringCert } `
                    -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' }

                It 'should not throw' {
                    { Set-TargetResource @ParamsAutoRenewNoCred } | Should Not Throw
                }
                It 'should call expected mocks' {
                    Assert-MockCalled -CommandName Join-Path -Exactly 1
                    Assert-MockCalled -CommandName Test-Path -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.req' }
                    Assert-MockCalled -CommandName Test-Path  -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.cer' }
                    Assert-MockCalled -CommandName Get-ChildItem -Exactly 1 `
                        -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' }
                    Assert-MockCalled -CommandName Set-Content -Exactly 1 `
                        -ParameterFilter {
                            $Path -eq 'xCertReq-Test.inf' -and `
                            $Value -eq $CertInfRenew
                        }
                    Assert-MockCalled -CommandName CertReq.exe -Exactly 3
                }
            }

            Context 'autorenew is true, credentials not passed and expired certificate exists' {
                Mock -CommandName Get-ChildItem -Mockwith { $expiredCert } `
                    -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' }

                It 'should not throw' {
                    { Set-TargetResource @ParamsAutoRenewNoCred } | Should Not Throw
                }
                It 'should call expected mocks' {
                    Assert-MockCalled -CommandName Join-Path -Exactly 1
                    Assert-MockCalled -CommandName Test-Path -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.req' }
                    Assert-MockCalled -CommandName Test-Path  -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.cer' }
                    Assert-MockCalled -CommandName Get-ChildItem -Exactly 1 `
                        -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' }
                    Assert-MockCalled -CommandName Set-Content -Exactly 1 `
                        -ParameterFilter {
                            $Path -eq 'xCertReq-Test.inf' -and `
                            $Value -eq $CertInfRenew
                        }
                    Assert-MockCalled -CommandName CertReq.exe -Exactly 3
                }
            }

            Mock -CommandName Set-Content `
                -ParameterFilter {
                    $Path -eq 'xCertReq-Test.inf' -and `
                    $Value -eq $CertInfKeyRenew
                }

            Context 'autorenew is true, credentials not passed, keylength passed and expired certificate exists' {
                Mock -CommandName Get-ChildItem -Mockwith { $expiredCert } `
                    -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' }

                It 'should not throw' {
                    { Set-TargetResource @ParamsKeyLength4096AutoRenewNoCred } | Should Not Throw
                }
                It 'should call expected mocks' {
                    Assert-MockCalled -CommandName Join-Path -Exactly 1
                    Assert-MockCalled -CommandName Test-Path -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.req' }
                    Assert-MockCalled -CommandName Test-Path  -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.cer' }
                    Assert-MockCalled -CommandName Get-ChildItem -Exactly 1 `
                        -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' }
                    Assert-MockCalled -CommandName Set-Content -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.inf' }
                    Assert-MockCalled -CommandName CertReq.exe -Exactly 3
                    Assert-MockCalled -CommandName Set-Content -Exactly 1 `
                        -ParameterFilter {
                            $Path -eq 'xCertReq-Test.inf' -and `
                            $Value -eq $CertInfKeyRenew
                        }
                }
            }

            Mock -CommandName Test-Path -MockWith { $false } `
                -ParameterFilter { $Path -eq 'xCertReq-Test.req' }
            Mock -CommandName Set-Content `
                -ParameterFilter {
                    $Path -eq 'xCertReq-Test.inf' -and `
                    $Value -eq $CertInf
                }

            Context 'autorenew is false, credentials not passed, certificate request creation failed' {
                Mock -CommandName Get-ChildItem -Mockwith { } `
                    -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' }

                $errorRecord = Get-InvalidArgumentError `
                    -ErrorId 'CertificateReqNotFoundError' `
                    -ErrorMessage ($LocalizedData.CertificateReqNotFoundError -f 'xCertReq-Test.req')

                It 'should throw CertificateReqNotFoundError exception' {
                    { Set-TargetResource @ParamsNoCred } | Should Throw $errorRecord
                }

                It 'should call expected mocks' {
                    Assert-MockCalled -CommandName Join-Path -Exactly 1
                    Assert-MockCalled -CommandName Test-Path -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.req' }
                    Assert-MockCalled -CommandName Test-Path -Exactly 0 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.cer' }
                    Assert-MockCalled -CommandName Set-Content -Exactly 1 `
                        -ParameterFilter {
                            $Path -eq 'xCertReq-Test.inf' -and `
                            $Value -eq $CertInf
                        }
                    Assert-MockCalled -CommandName CertReq.exe -Exactly 1
                }
            }

            Mock -CommandName Test-Path -MockWith { $true } `
                -ParameterFilter { $Path -eq 'xCertReq-Test.req' }
            Mock -CommandName Test-Path -MockWith { $false } `
                -ParameterFilter { $Path -eq 'xCertReq-Test.cer' }

            Context 'autorenew is false, credentials not passed, certificate creation failed' {
                Mock -CommandName Get-ChildItem -Mockwith { } `
                    -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' }

                $errorRecord = Get-InvalidArgumentError `
                    -ErrorId 'CertificateCerNotFoundError' `
                    -ErrorMessage ($LocalizedData.CertificateCerNotFoundError -f 'xCertReq-Test.cer')

                It 'should throw CertificateCerNotFoundError exception' {
                    { Set-TargetResource @ParamsNoCred } | Should Throw $errorRecord
                }

                It 'should call expected mocks' {
                    Assert-MockCalled -CommandName Join-Path -Exactly 1
                    Assert-MockCalled -CommandName Test-Path -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.req' }
                    Assert-MockCalled -CommandName Test-Path -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.cer' }
                    Assert-MockCalled -CommandName Set-Content -Exactly 1 `
                        -ParameterFilter {
                            $Path -eq 'xCertReq-Test.inf' -and `
                            $Value -eq $CertInf
                        }
                    Assert-MockCalled -CommandName CertReq.exe -Exactly 2
                }
            }

            Mock -CommandName Test-Path -MockWith { $true } `
                -ParameterFilter { $Path -eq 'xCertReq-Test.req' }
            Mock -CommandName Test-Path -MockWith { $true } `
                -ParameterFilter { $Path -eq 'xCertReq-Test.cer' }
            Mock -CommandName Test-Path -MockWith { $true } `
                -ParameterFilter { $Path -eq 'xCertReq-Test.out' }

            Context 'autorenew is false, credentials passed' {
                Mock -CommandName Get-ChildItem -Mockwith { } `
                    -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' }
                Mock -CommandName Get-Content -Mockwith { 'Output' } `
                    -ParameterFilter { $Path -eq 'xCertReq-Test.out' }
                Mock -CommandName Remove-Item `
                    -ParameterFilter { $Path -eq 'xCertReq-Test.out' }
                Mock -CommandName Import-Module

                function Start-Win32Process { param ( $Path,$Arguments,$Credential ) }
                function Wait-Win32ProcessStop { param ( $Path,$Arguments,$Credential ) }

                Mock -CommandName Start-Win32Process -ModuleName MSFT_xCertReq
                Mock -CommandName Wait-Win32ProcessStop -ModuleName MSFT_xCertReq

                It 'should not throw' {
                    { Set-TargetResource @Params } | Should Not Throw
                }

                It 'should call expected mocks' {
                    Assert-MockCalled -CommandName Join-Path -Exactly 1
                    Assert-MockCalled -CommandName Test-Path -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.req' }
                    Assert-MockCalled -CommandName Test-Path  -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.cer' }
                    Assert-MockCalled -CommandName Set-Content -Exactly 1 `
                        -ParameterFilter {
                            $Path -eq 'xCertReq-Test.inf' -and `
                            $Value -eq $CertInf
                        }
                    Assert-MockCalled -CommandName CertReq.exe -Exactly 2
                    Assert-MockCalled -CommandName Start-Win32Process -ModuleName MSFT_xCertReq -Exactly 1
                    Assert-MockCalled -CommandName Wait-Win32ProcessStop -ModuleName MSFT_xCertReq -Exactly 1
                    Assert-MockCalled -CommandName Test-Path  -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.out' }
                    Assert-MockCalled -CommandName Get-Content -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.out' }
                    Assert-MockCalled -CommandName Remove-Item -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.out' }
                }
            }

            Mock -CommandName Set-Content `
                -ParameterFilter {
                    $Path -eq 'xCertReq-Test.inf' -and `
                    $Value -eq $CertInfSubjectAltName
                }

            Context 'autorenew is false, subject alt name passed, credentials not passed' {
                Mock -CommandName Get-ChildItem -Mockwith { } `
                    -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' }

                It 'should not throw' {
                    { Set-TargetResource @ParamsSubjectAltNameNoCred } | Should Not Throw
                }

                It 'should call expected mocks' {
                    Assert-MockCalled -CommandName Join-Path -Exactly 1
                    Assert-MockCalled -CommandName Test-Path -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.req' }
                    Assert-MockCalled -CommandName Test-Path  -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.cer' }
                    Assert-MockCalled -CommandName Set-Content -Exactly 1 `
                        -ParameterFilter {
                            $Path -eq 'xCertReq-Test.inf' -and `
                            $Value -eq $CertInfSubjectAltName
                        }
                    Assert-MockCalled -CommandName CertReq.exe -Exactly 3
                }
            }

            Context 'standalone CA, URL for CEP and CES passed, credentials passed, inf not containing template' {
                Mock -CommandName Set-Content -ParameterFilter {
                    $Path -eq 'xCertReq-Test.inf' -and `
                    $Value -eq $CertInfNoTemplate
                }
                Mock -CommandName Get-ChildItem -Mockwith { } `
                    -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' }

                It 'should not throw' {
                    { Set-TargetResource @ParamsStandaloneWebEnrollment } | Should Not Throw
                }

                It 'should call expected mocks' {
                    Assert-MockCalled -CommandName Join-Path -Exactly 1
                    Assert-MockCalled -CommandName Test-Path -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.req' }
                    Assert-MockCalled -CommandName Test-Path  -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.cer' }
                    Assert-MockCalled -CommandName Set-Content -Exactly 1 `
                        -ParameterFilter {
                            $Path -eq 'xCertReq-Test.inf' -and `
                            $Value -eq $CertInfNoTemplate
                        }
                    Assert-MockCalled -CommandName CertReq.exe -Exactly 3
                }
            }

            Context 'enterprise CA, URL for CEP and CES passed, credentials passed' {
                Mock -CommandName Set-Content -ParameterFilter {
                    $Path -eq 'xCertReq-Test.inf' -and `
                    $Value -eq $CertInf
                }
                Mock -CommandName Get-ChildItem -Mockwith { } `
                    -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' }

                It 'should not throw' {
                    { Set-TargetResource @ParamsEnterpriseWebEnrollment } | Should Not Throw
                }

                It 'should call expected mocks' {
                    Assert-MockCalled -CommandName Join-Path -Exactly 1
                    Assert-MockCalled -CommandName Test-Path -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.req' }
                    Assert-MockCalled -CommandName Test-Path  -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.cer' }
                    Assert-MockCalled -CommandName Set-Content -Exactly 1 `
                        -ParameterFilter {
                            $Path -eq 'xCertReq-Test.inf' -and `
                            $Value -eq $CertInf
                        }
                    Assert-MockCalled -CommandName CertReq.exe -Exactly 3
                }
            }

            Context 'Auto-discovered CA, autorenew is false, credentials passed' {
                Mock -CommandName Get-ChildItem -Mockwith { } `
                    -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' }
                Mock -CommandName Get-Content -Mockwith { 'Output' } `
                    -ParameterFilter { $Path -eq 'xCertReq-Test.out' }
                Mock -CommandName Remove-Item `
                    -ParameterFilter { $Path -eq 'xCertReq-Test.out' }
                Mock -CommandName Import-Module
                Mock -CommandName Start-Win32Process
                Mock -CommandName Wait-Win32ProcessStop
                Mock -CommandName Find-CertificateAuthority -MockWith {
                    return New-Object -TypeName psobject -Property @{
                        CARootName = "ContosoCA"
                        CAServerFQDN = "ContosoVm.contoso.com"
                    }
                }

                It 'should not throw' {
                    { Set-TargetResource @ParamsAutoDiscovery } | Should Not Throw
                }

                It 'should call expected mocks' {
                    Assert-MockCalled -CommandName Join-Path -Exactly 1
                    Assert-MockCalled -CommandName Test-Path -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.req' }
                    Assert-MockCalled -CommandName Test-Path  -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.cer' }
                    Assert-MockCalled -CommandName Set-Content -Exactly 1 `
                        -ParameterFilter {
                            $Path -eq 'xCertReq-Test.inf' -and `
                            $Value -eq $CertInf
                        }
                    Assert-MockCalled -CommandName CertReq.exe -Exactly 2
                    Assert-MockCalled -CommandName Start-Win32Process -ModuleName MSFT_xCertReq -Exactly 1
                    Assert-MockCalled -CommandName Wait-Win32ProcessStop -ModuleName MSFT_xCertReq -Exactly 1
                    Assert-MockCalled -CommandName Test-Path  -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.out' }
                    Assert-MockCalled -CommandName Get-Content -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.out' }
                    Assert-MockCalled -CommandName Remove-Item -Exactly 1 `
                        -ParameterFilter { $Path -eq 'xCertReq-Test.out' }
                    Assert-MockCalled -CommandName Find-CertificateAuthority -Exactly -Times 1
                }
            }
        }
        #endregion

        Describe "$DSCResourceName\Test-TargetResource" {
            Mock -CommandName Find-CertificateAuthority -MockWith {
                    return New-Object -TypeName psobject -Property @{
                        CARootName = "ContosoCA"
                        CAServerFQDN = "ContosoVm.contoso.com"
                    }
                }

            It 'should return a bool' {
                Test-TargetResource @Params | Should BeOfType Boolean
            }
            It 'should return false when a valid certificate does not exist' {
                Mock Get-ChildItem -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' } `
                    -Mockwith { }
                Test-TargetResource @Params | Should Be $false
            }
            It 'should return true when a valid certificate already exists and is not about to expire' {
                Mock Get-ChildItem -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' } `
                    -Mockwith { $validCert }
                Mock Get-CertificateTemplateName -MockWith { $CertificateTemplate }
                Mock Get-CertificateSan -MockWith { $SubjectAltName }
                Test-TargetResource @Params | Should Be $true
            }
            It 'should return true when a valid certificate already exists and is about to expire and autorenew set' {
                Mock Get-ChildItem -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' } `
                    -Mockwith { $expiringCert }
                Mock Get-CertificateTemplateName -MockWith { $CertificateTemplate }
                Mock Get-CertificateSan -MockWith { $SubjectAltName }
                Test-TargetResource @ParamsAutoRenew | Should Be $true
            }
                  It 'should return true when a valid certificate already exists and DNS SANs match' {
                Mock Get-ChildItem -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' } `
                    -Mockwith { $validSANCert }
                Test-TargetResource @ParamsSubjectAltName | Should Be $true
            }
                  It 'should return false when a certificate exists but contains incorrect DNS SANs' {
                Mock Get-ChildItem -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' } `
                    -Mockwith { $incorrectSANCert }
                Test-TargetResource @ParamsSubjectAltName | Should Be $false
            }
                  It 'should return false when a certificate exists but does not contain specified DNS SANs' {
                Mock Get-ChildItem -ParameterFilter { $Path -eq 'Cert:\LocalMachine\My' } `
                    -Mockwith { $emptySANCert }
                Test-TargetResource @ParamsSubjectAltName | Should Be $false
            }
            It 'Should auto-discover the CA and return false' {
                Test-TargetResource @ParamsAutoDiscovery | Should Be $false
            }
            It 'Should execute the auto-discovery function' {
                Assert-MockCalled -CommandName Find-CertificateAuthority -Exactly -Times 1
            }
        }
    }
}
finally
{
    #region FOOTER
    Restore-TestEnvironment -TestEnvironment $TestEnvironment
    #endregion
}