Tests/Unit/xActiveDirectory.Common.Tests.ps1

# Import the xActiveDirectory.Common module to test
$script:resourceModulePath = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent
$script:modulesFolderPath = Join-Path -Path $script:resourceModulePath -ChildPath 'Modules\xActiveDirectory.Common'

Import-Module -Name (Join-Path -Path $script:modulesFolderPath -ChildPath 'xActiveDirectory.Common.psm1') -Force

# If one type does not exist, it's assumed the other ones does not exist either.
if (-not ('Microsoft.DirectoryServices.Deployment.Types.ForestMode' -as [Type]))
{
    Add-Type -Path (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -ChildPath 'Unit\Stubs\Microsoft.DirectoryServices.Deployment.Types.cs')
}

# If one type does not exist, it's assumed the other ones does not exist either.
if (-not ('Microsoft.ActiveDirectory.Management.ADForestMode' -as [Type]))
{
    Add-Type -Path (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -ChildPath 'Unit\Stubs\Microsoft.ActiveDirectory.Management.cs')
}

InModuleScope 'xActiveDirectory.Common' {
    Describe 'xActiveDirectory.Common\Test-DscParameterState' -Tag TestDscParameterState {
        Context 'When passing values' {
            It 'Should return true for two identical tables' {
                $mockDesiredValues = @{ Example = 'test' }

                $testParameters = @{
                    CurrentValues = $mockDesiredValues
                    DesiredValues = $mockDesiredValues
                }

                Test-DscParameterState @testParameters | Should -Be $true
            }

            It 'Should return false when a value is different for [System.String]' {
                $mockCurrentValues = @{ Example = [System.String] 'something' }
                $mockDesiredValues = @{ Example = [System.String] 'test' }

                $testParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                }

                Test-DscParameterState @testParameters | Should -Be $false
            }

            It 'Should return false when a value is different for [System.Int32]' {
                $mockCurrentValues = @{ Example = [System.Int32] 1 }
                $mockDesiredValues = @{ Example = [System.Int32] 2 }

                $testParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                }

                Test-DscParameterState @testParameters | Should -Be $false
            }

            It 'Should return false when a value is different for [System.Int16]' {
                $mockCurrentValues = @{ Example = [System.Int16] 1 }
                $mockDesiredValues = @{ Example = [System.Int16] 2 }

                $testParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                }

                Test-DscParameterState @testParameters | Should -Be $false
            }

            It 'Should return false when a value is different for [System.UInt16]' {
                $mockCurrentValues = @{ Example = [System.UInt16] 1 }
                $mockDesiredValues = @{ Example = [System.UInt16] 2 }

                $testParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                }

                Test-DscParameterState @testParameters | Should -Be $false
            }

            It 'Should return false when a value is different for [System.Boolean]' {
                $mockCurrentValues = @{ Example = [System.Boolean] $true }
                $mockDesiredValues = @{ Example = [System.Boolean] $false }

                $testParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                }

                Test-DscParameterState @testParameters | Should -Be $false
            }

            It 'Should return false when a value is missing' {
                $mockCurrentValues = @{ }
                $mockDesiredValues = @{ Example = 'test' }

                $testParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                }

                Test-DscParameterState @testParameters | Should -Be $false
            }

            It 'Should return true when only a specified value matches, but other non-listed values do not' {
                $mockCurrentValues = @{
                    Example = 'test'
                    SecondExample = 'true'
                }

                $mockDesiredValues = @{
                    Example = 'test'
                    SecondExample = 'false'
                }

                $testParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                    ValuesToCheck = @('Example')
                }

                Test-DscParameterState @testParameters | Should -Be $true
            }

            It 'Should return false when only specified values do not match, but other non-listed values do ' {
                $mockCurrentValues = @{
                    Example = 'test'
                    SecondExample = 'true'
                }

                $mockDesiredValues = @{
                    Example = 'test'
                    SecondExample = 'false'
                }

                $testParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                    ValuesToCheck = @('SecondExample')
                }

                Test-DscParameterState @testParameters | Should -Be $false
            }

            It 'Should return false when an empty hash table is used in the current values' {
                $mockCurrentValues = @{ }
                $mockDesiredValues = @{
                    Example = 'test'
                    SecondExample = 'false'
                }

                $testParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                }

                Test-DscParameterState @testParameters | Should -Be $false
            }

            It 'Should return true when evaluating a table against a CimInstance' {
                $mockCurrentValues = @{
                    Handle = '0'
                    ProcessId = '1000'
                }

                $mockWin32ProcessProperties = @{
                    Handle    = 0
                    ProcessId = 1000
                }

                $mockNewCimInstanceParameters = @{
                    ClassName  = 'Win32_Process'
                    Property   = $mockWin32ProcessProperties
                    Key        = 'Handle'
                    ClientOnly = $true
                }

                $mockDesiredValues = New-CimInstance @mockNewCimInstanceParameters

                $testParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                    ValuesToCheck = @('Handle', 'ProcessId')
                }

                Test-DscParameterState @testParameters | Should -Be $true
            }

            It 'Should return false when evaluating a table against a CimInstance and a value is wrong' {
                $mockCurrentValues = @{
                    Handle = '1'
                    ProcessId = '1000'
                }

                $mockWin32ProcessProperties = @{
                    Handle    = 0
                    ProcessId = 1000
                }

                $mockNewCimInstanceParameters = @{
                    ClassName  = 'Win32_Process'
                    Property   = $mockWin32ProcessProperties
                    Key        = 'Handle'
                    ClientOnly = $true
                }

                $mockDesiredValues = New-CimInstance @mockNewCimInstanceParameters

                $testParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                    ValuesToCheck = @('Handle', 'ProcessId')
                }

                Test-DscParameterState @testParameters | Should -Be $false
            }

            It 'Should return true when evaluating a hash table containing an array' {
                $mockCurrentValues = @{
                    Example = 'test'
                    SecondExample = @('1', '2')
                }

                $mockDesiredValues = @{
                    Example = 'test'
                    SecondExample = @('1', '2')
                }

                $testParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                }

                Test-DscParameterState @testParameters | Should -Be $true
            }

            It 'Should return false when evaluating a hash table containing an array with wrong values' {
                $mockCurrentValues = @{
                    Example = 'test'
                    SecondExample = @('A', 'B')
                }

                $mockDesiredValues = @{
                    Example = 'test'
                    SecondExample = @('1', '2')
                }

                $testParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                }

                Test-DscParameterState @testParameters | Should -Be $false
            }

            It 'Should return false when evaluating a hash table containing an array, but the CurrentValues are missing an array' {
                $mockCurrentValues = @{
                    Example = 'test'
                }

                $mockDesiredValues = @{
                    Example = 'test'
                    SecondExample = @('1', '2')
                }

                $testParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                }

                Test-DscParameterState @testParameters | Should -Be $false
            }

            It 'Should return false when evaluating a hash table containing an array, but the property i CurrentValues is $null' {
                $mockCurrentValues = @{
                    Example = 'test'
                    SecondExample = $null
                }

                $mockDesiredValues = @{
                    Example = 'test'
                    SecondExample = @('1', '2')
                }

                $testParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                }

                Test-DscParameterState @testParameters | Should -Be $false
            }
        }

        Context 'When passing invalid types for DesiredValues' {
            It 'Should throw the correct error when DesiredValues is of wrong type' {
                $mockCurrentValues = @{ Example = 'something' }
                $mockDesiredValues = 'NotHashTable'

                $testParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                }

                $mockCorrectErrorMessage = ($script:localizedData.PropertyTypeInvalidForDesiredValues -f $testParameters.DesiredValues.GetType().Name)
                { Test-DscParameterState @testParameters } | Should -Throw $mockCorrectErrorMessage
            }

            It 'Should write a warning when DesiredValues contain an unsupported type' {
                Mock -CommandName Write-Warning -Verifiable

                # This is a dummy type to test with a type that could never be a correct one.
                class MockUnknownType
                {
                    [ValidateNotNullOrEmpty()]
                    [System.String]
                    $Property1

                    [ValidateNotNullOrEmpty()]
                    [System.String]
                    $Property2

                    MockUnknownType()
                    {
                    }
                }

                $mockCurrentValues = @{ Example = New-Object -TypeName 'MockUnknownType' }
                $mockDesiredValues = @{ Example = New-Object -TypeName 'MockUnknownType' }

                $testParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                }

                Test-DscParameterState @testParameters | Should -Be $false

                Assert-MockCalled -CommandName Write-Warning -Exactly -Times 1
            }
        }

        Context 'When passing an CimInstance as DesiredValue and ValuesToCheck is $null' {
            It 'Should throw the correct error' {
                $mockCurrentValues = @{ Example = 'something' }

                $mockWin32ProcessProperties = @{
                    Handle    = 0
                    ProcessId = 1000
                }

                $mockNewCimInstanceParameters = @{
                    ClassName  = 'Win32_Process'
                    Property   = $mockWin32ProcessProperties
                    Key        = 'Handle'
                    ClientOnly = $true
                }

                $mockDesiredValues = New-CimInstance @mockNewCimInstanceParameters

                $testParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                    ValuesToCheck = $null
                }

                $mockCorrectErrorMessage = $script:localizedData.PropertyTypeInvalidForValuesToCheck
                { Test-DscParameterState @testParameters } | Should -Throw $mockCorrectErrorMessage
            }
        }

        Assert-VerifiableMock
    }
    Describe 'xActiveDirectory.Common\Get-LocalizedData' {
        $mockTestPath = {
            return $mockTestPathReturnValue
        }

        $mockImportLocalizedData = {
            $BaseDirectory | Should -Be $mockExpectedLanguagePath
        }

        BeforeEach {
            Mock -CommandName Test-Path -MockWith $mockTestPath -Verifiable
            Mock -CommandName Import-LocalizedData -MockWith $mockImportLocalizedData -Verifiable
        }

        Context 'When loading localized data for Swedish' {
            $mockExpectedLanguagePath = 'sv-SE'
            $mockTestPathReturnValue = $true

            It 'Should call Import-LocalizedData with sv-SE language' {
                Mock -CommandName Join-Path -MockWith {
                    return 'sv-SE'
                } -Verifiable

                { Get-LocalizedData -ResourceName 'DummyResource' } | Should -Not -Throw

                Assert-MockCalled -CommandName Join-Path -Exactly -Times 3 -Scope It
                Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It
                Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It
            }

            $mockExpectedLanguagePath = 'en-US'
            $mockTestPathReturnValue = $false

            It 'Should call Import-LocalizedData and fallback to en-US if sv-SE language does not exist' {
                Mock -CommandName Join-Path -MockWith {
                    return $ChildPath
                } -Verifiable

                { Get-LocalizedData -ResourceName 'DummyResource' } | Should -Not -Throw

                Assert-MockCalled -CommandName Join-Path -Exactly -Times 4 -Scope It
                Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It
                Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It
            }

            Context 'When $ScriptRoot is set to a path' {
                $mockExpectedLanguagePath = 'sv-SE'
                $mockTestPathReturnValue = $true

                It 'Should call Import-LocalizedData with sv-SE language' {
                    Mock -CommandName Join-Path -MockWith {
                        return 'sv-SE'
                    } -Verifiable

                    { Get-LocalizedData -ResourceName 'DummyResource' -ScriptRoot '.' } | Should -Not -Throw

                    Assert-MockCalled -CommandName Join-Path -Exactly -Times 1 -Scope It
                    Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It
                    Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It
                }

                $mockExpectedLanguagePath = 'en-US'
                $mockTestPathReturnValue = $false

                It 'Should call Import-LocalizedData and fallback to en-US if sv-SE language does not exist' {
                    Mock -CommandName Join-Path -MockWith {
                        return $ChildPath
                    } -Verifiable

                    { Get-LocalizedData -ResourceName 'DummyResource' -ScriptRoot '.' } | Should -Not -Throw

                    Assert-MockCalled -CommandName Join-Path -Exactly -Times 2 -Scope It
                    Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It
                    Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It
                }
            }
        }

        Context 'When loading localized data for English' {
            Mock -CommandName Join-Path -MockWith {
                return 'en-US'
            } -Verifiable

            $mockExpectedLanguagePath = 'en-US'
            $mockTestPathReturnValue = $true

            It 'Should call Import-LocalizedData with en-US language' {
                { Get-LocalizedData -ResourceName 'DummyResource' } | Should -Not -Throw
            }
        }

        Assert-VerifiableMock
    }

    Describe 'xActiveDirectory.Common\New-InvalidResultException' {
        Context 'When calling with Message parameter only' {
            It 'Should throw the correct error' {
                $mockErrorMessage = 'Mocked error'

                { New-InvalidResultException -Message $mockErrorMessage } | Should -Throw $mockErrorMessage
            }
        }

        Context 'When calling with both the Message and ErrorRecord parameter' {
            It 'Should throw the correct error' {
                $mockErrorMessage = 'Mocked error'
                $mockExceptionErrorMessage = 'Mocked exception error message'

                $mockException = New-Object -TypeName 'System.Exception' -ArgumentList $mockExceptionErrorMessage
                $mockErrorRecord = New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList @($mockException, $null, 'InvalidResult', $null)

                { New-InvalidResultException -Message $mockErrorMessage -ErrorRecord $mockErrorRecord } | Should -Throw ('System.Exception: {0} ---> System.Exception: {1}' -f $mockErrorMessage, $mockExceptionErrorMessage)
            }
        }

        Assert-VerifiableMock
    }

    Describe 'xActiveDirectory.Common\New-ObjectNotFoundException' {
        Context 'When calling with Message parameter only' {
            It 'Should throw the correct error' {
                $mockErrorMessage = 'Mocked error'

                { New-ObjectNotFoundException -Message $mockErrorMessage } | Should -Throw $mockErrorMessage
            }
        }

        Context 'When calling with both the Message and ErrorRecord parameter' {
            It 'Should throw the correct error' {
                $mockErrorMessage = 'Mocked error'
                $mockExceptionErrorMessage = 'Mocked exception error message'

                $mockException = New-Object -TypeName 'System.Exception' -ArgumentList $mockExceptionErrorMessage
                $mockErrorRecord = New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList @($mockException, $null, 'InvalidResult', $null)

                { New-ObjectNotFoundException -Message $mockErrorMessage -ErrorRecord $mockErrorRecord } | Should -Throw ('System.Exception: {0} ---> System.Exception: {1}' -f $mockErrorMessage, $mockExceptionErrorMessage)
            }
        }

        Assert-VerifiableMock
    }

    Describe 'xActiveDirectory.Common\New-InvalidOperationException' {
        Context 'When calling with Message parameter only' {
            It 'Should throw the correct error' {
                $mockErrorMessage = 'Mocked error'

                { New-InvalidOperationException -Message $mockErrorMessage } | Should -Throw $mockErrorMessage
            }
        }

        Context 'When calling with both the Message and ErrorRecord parameter' {
            It 'Should throw the correct error' {
                $mockErrorMessage = 'Mocked error'
                $mockExceptionErrorMessage = 'Mocked exception error message'

                $mockException = New-Object -TypeName 'System.Exception' -ArgumentList $mockExceptionErrorMessage
                $mockErrorRecord = New-Object -TypeName 'System.Management.Automation.ErrorRecord' -ArgumentList @($mockException, $null, 'InvalidResult', $null)

                { New-InvalidOperationException -Message $mockErrorMessage -ErrorRecord $mockErrorRecord } | Should -Throw ('System.InvalidOperationException: {0} ---> System.Exception: {1}' -f $mockErrorMessage, $mockExceptionErrorMessage)
            }
        }

        Assert-VerifiableMock
    }

    Describe 'xActiveDirectory.Common\New-InvalidArgumentException' {
        Context 'When calling with both the Message and ArgumentName parameter' {
            It 'Should throw the correct error' {
                $mockErrorMessage = 'Mocked error'
                $mockArgumentName = 'MockArgument'

                { New-InvalidArgumentException -Message $mockErrorMessage -ArgumentName $mockArgumentName } | Should -Throw ('Parameter name: {0}' -f $mockArgumentName)
            }
        }

        Assert-VerifiableMock
    }

    Describe 'DscResource.Common\Start-ProcessWithTimeout' {
        Context 'When starting a process successfully' {
            It 'Should return exit code 0' {
                $startProcessWithTimeoutParameters = @{
                    FilePath = 'powershell.exe'
                    ArgumentList = '-Command &{Start-Sleep -Seconds 2}'
                    Timeout = 300
                }

                $processExitCode = Start-ProcessWithTimeout @startProcessWithTimeoutParameters
                $processExitCode | Should -BeExactly 0
            }
        }

        Context 'When starting a process and the process does not finish before the timeout period' {
            It 'Should throw an error message' {
                $startProcessWithTimeoutParameters = @{
                    FilePath = 'powershell.exe'
                    ArgumentList = '-Command &{Start-Sleep -Seconds 4}'
                    Timeout = 2
                }

                { Start-ProcessWithTimeout @startProcessWithTimeoutParameters } | Should -Throw -ErrorId 'ProcessNotTerminated,Microsoft.PowerShell.Commands.WaitProcessCommand'
            }
        }
    }

    Describe 'xActiveDirectory.Common\Resolve-DomainFQDN' {
        It 'Returns "DomainName" when "ParentDomainName" not supplied' {
            $testDomainName = 'contoso.com'
            $testParentDomainName = $null

            $result = Resolve-DomainFQDN -DomainName $testDomainName -ParentDomainName $testParentDomainName

            $result | Should -Be $testDomainName
        }

        It 'Returns compound "DomainName.ParentDomainName" when "ParentDomainName" supplied' {
            $testDomainName = 'subdomain'
            $testParentDomainName = 'contoso.com'

            $result = Resolve-DomainFQDN -DomainName $testDomainName -ParentDomainName $testParentDomainName

            $result | Should -Be ('{0}.{1}' -f $testDomainName, $testParentDomainName)
        }
    }

    Describe 'xActiveDirectory.Common\Test-DomainMember' {
        It 'Returns "True" when domain member' {
            Mock -CommandName Get-CimInstance -MockWith {
                return @{
                    Name = $env:COMPUTERNAME
                    PartOfDomain = $true
                }
            }

            Test-DomainMember | Should -Be $true
        }

        It 'Returns "False" when workgroup member' {
            Mock -CommandName Get-CimInstance -MockWith {
                return @{
                    Name = $env:COMPUTERNAME
                }
            }

            Test-DomainMember | Should -Be $false
        }
    }

    Describe 'xActiveDirectory.Common\Get-DomainName' {
        It 'Returns expected domain name' {
            Mock -CommandName Get-CimInstance -MockWith {
                return @{
                    Name = $env:COMPUTERNAME
                    Domain = 'contoso.com'
                }
            }

            Get-DomainName | Should -Be 'contoso.com'
        }
    }

    Describe 'xActiveDirectory.Common\Assert-Module' {
        BeforeAll {
            $testModuleName = 'TestModule'
        }

        Context 'When module is not installed' {
            BeforeAll {
                Mock -CommandName Get-Module
            }

            It 'Should throw the correct error' {
                { Assert-Module -ModuleName $testModuleName } | Should -Throw ($script:localizedData.RoleNotFoundError -f $testModuleName)
            }
        }

        Context 'When module is available' {
            BeforeAll {
                Mock -CommandName Import-Module
                Mock -CommandName Get-Module -MockWith {
                    return @{
                        Name = $testModuleName
                    }
                }
            }

            Context 'When module should not be imported' {
                It 'Should not throw an error' {
                    { Assert-Module -ModuleName $testModuleName } | Should -Not -Throw

                    Assert-MockCalled -CommandName Import-Module -Exactly -Times 0 -Scope It
                }
            }

            Context 'When module should be imported' {
                It 'Should not throw an error' {
                    { Assert-Module -ModuleName $testModuleName -ImportModule } | Should -Not -Throw

                    Assert-MockCalled -CommandName Import-Module -Exactly -Times 1 -Scope It
                }
            }
        }
    }

    Describe 'xActiveDirectory.Common\Get-ADObjectParentDN' {
        It 'Returns CN object parent path' {
            Get-ADObjectParentDN -DN 'CN=Administrator,CN=Users,DC=contoso,DC=com' | Should -Be 'CN=Users,DC=contoso,DC=com'
        }

        It 'Returns OU object parent path' {
            Get-ADObjectParentDN -DN 'CN=Administrator,OU=Custom Organizational Unit,DC=contoso,DC=com' | Should -Be 'OU=Custom Organizational Unit,DC=contoso,DC=com'
        }
    }

    Describe 'xActiveDirectory.Common\Remove-DuplicateMembers' {
        It 'Removes one duplicate' {
            $members = Remove-DuplicateMembers -Members 'User1','User2','USER1'

            $members.Count | Should -Be 2
            $members -contains 'User1' | Should -Be $true
            $members -contains 'User2' | Should -Be $true
        }

        It 'Removes two duplicates' {
            $members = Remove-DuplicateMembers -Members 'User1','User2','USER1','USER2'

            $members.Count | Should -Be 2
            $members -contains 'User1' | Should -Be $true
            $members -contains 'User2' | Should -Be $true
        }

        It 'Removes double duplicates' {
            $members = Remove-DuplicateMembers -Members 'User1','User2','USER1','user1'

            $members.Count | Should -Be 2
            $members -contains 'User1' | Should -Be $true
            $members -contains 'User2' | Should -Be $true
        }
    }

    Describe 'xActiveDirectory.Common\Test-Members' {
        It 'Passes when nothing is passed' {
            Test-Members -ExistingMembers $null | Should -Be $true
        }

        It 'Passes when there are existing members but members are required' {
            $testExistingMembers = @('USER1', 'USER2')

            Test-Members -ExistingMembers $testExistingMembers | Should -Be $true
        }

        It 'Passes when existing members match required members' {
            $testExistingMembers = @('USER1', 'USER2')
            $testMembers = @('USER2', 'USER1')

            Test-Members -ExistingMembers $testExistingMembers -Members $testMembers | Should -Be $true
        }

        It 'Fails when there are no existing members and members are required' {
            $testExistingMembers = @('USER1', 'USER2')
            $testMembers = @('USER1', 'USER3')

            Test-Members -ExistingMembers $null -Members $testMembers | Should -Be $false
        }

        It 'Fails when there are more existing members than the members required' {
            $testExistingMembers = @('USER1', 'USER2', 'USER3')
            $testMembers = @('USER1', 'USER3')

            Test-Members -ExistingMembers $null -Members $testMembers | Should -Be $false
        }

        It 'Fails when there are more existing members than the members required' {
            $testExistingMembers = @('USER1', 'USER2')
            $testMembers = @('USER1', 'USER3', 'USER2')

            Test-Members -ExistingMembers $null -Members $testMembers | Should -Be $false
        }

        It 'Fails when existing members do not match required members' {
            $testExistingMembers = @('USER1', 'USER2')
            $testMembers = @('USER1', 'USER3')

            Test-Members -ExistingMembers $testExistingMembers -Members $testMembers | Should -BeFalse
        }

        It 'Passes when existing members include required member' {
            $testExistingMembers = @('USER1', 'USER2')
            $testMembersToInclude = @('USER2')

            Test-Members -ExistingMembers $testExistingMembers -MembersToInclude $testMembersToInclude | Should -BeTrue
        }

        It 'Passes when existing members include required members' {
            $testExistingMembers = @('USER1', 'USER2')
            $testMembersToInclude = @('USER2', 'USER1')

            Test-Members -ExistingMembers $testExistingMembers -MembersToInclude $testMembersToInclude | Should -BeTrue
        }

        It 'Fails when existing members is missing a required member' {
            $testExistingMembers = @('USER1')
            $testMembersToInclude = @('USER2')

            Test-Members -ExistingMembers $testExistingMembers -MembersToInclude $testMembersToInclude | Should -BeFalse
        }

        It 'Fails when existing members is missing a required member' {
            $testExistingMembers = @('USER1', 'USER3')
            $testMembersToInclude = @('USER2')

            Test-Members -ExistingMembers $testExistingMembers -MembersToInclude $testMembersToInclude | Should -BeFalse
        }

        It 'Fails when existing members is missing a required members' {
            $testExistingMembers = @('USER3')
            $testMembersToInclude = @('USER1', 'USER2')

            Test-Members -ExistingMembers $testExistingMembers -MembersToInclude $testMembersToInclude | Should -BeFalse
        }

        It 'Passes when existing member does not include excluded member' {
            $testExistingMembers = @('USER1')
            $testMembersToExclude = @('USER2')

            Test-Members -ExistingMembers $testExistingMembers -MembersToExclude $testMembersToExclude | Should -BeTrue
        }

        It 'Passes when existing member does not include excluded members' {
            $testExistingMembers = @('USER1')
            $testMembersToExclude = @('USER2', 'USER3')

            Test-Members -ExistingMembers $testExistingMembers -MembersToExclude $testMembersToExclude | Should -BeTrue
        }

        It 'Passes when existing members does not include excluded member' {
            $testExistingMembers = @('USER1', 'USER2')
            $testMembersToExclude = @('USER3')

            Test-Members -ExistingMembers $testExistingMembers -MembersToExclude $testMembersToExclude | Should -BeTrue
        }

        It 'Should fail when an existing members is include as an excluded member' {
            $testExistingMembers = @('USER1', 'USER2')
            $testMembersToExclude = @('USER2')

            Test-Members -ExistingMembers $testExistingMembers -MembersToExclude $testMembersToExclude | Should -BeFalse
        }

        It 'Should pass when MembersToExclude is set to $null' {
            $testExistingMembers = @('USER1', 'USER2')
            $testMembersToExclude = $null

            Test-Members -ExistingMembers $testExistingMembers -MembersToExclude $testMembersToExclude | Should -BeTrue
        }

        It 'Should pass when MembersToInclude is set to $null' {
            $testExistingMembers = @('USER1', 'USER2')
            $testMembersToInclude = $null

            Test-Members -ExistingMembers $testExistingMembers -MembersToInclude $testMembersToInclude | Should -BeTrue
        }

        It 'Should fail when Members is set to $null' {
            $testExistingMembers = @('USER1', 'USER2')
            $testMembers = $null

            Test-Members -ExistingMembers $testExistingMembers -Members $testMembers -Verbose | Should -BeFalse
        }

        It 'Should fail when multiple Members are the wrong members' {
            $testExistingMembers = @('USER1', 'USER2')
            $testMembers = @('USER3', 'USER4')

            Test-Members -ExistingMembers $testExistingMembers -Members $testMembers -Verbose | Should -BeFalse
        }

        It 'Should fail when multiple MembersToInclude are not present in existing members' {
            $testExistingMembers = @('USER1', 'USER2')
            $testMembersToInclude = @('USER3', 'USER4')

            Test-Members -ExistingMembers $testExistingMembers -MembersToInclude $testMembersToInclude -Verbose | Should -BeFalse
        }

        It 'Should fail when multiple MembersToExclude are present in existing members' {
            $testExistingMembers = @('USER1', 'USER2')
            $testMembersToExclude = @('USER1', 'USER2')

            Test-Members -ExistingMembers $testExistingMembers -MembersToExclude $testMembersToExclude -Verbose | Should -BeFalse
        }
    }

    Describe 'xActiveDirectory.Common\Assert-MemberParameters' {
        It "Throws if 'Members' is specified but is empty" {
            { Assert-MemberParameters -Members @() } | Should -Throw ($script:localizedData.MembersIsNullError -f 'Members', 'MembersToInclude', 'MembersToExclude')
        }

        It "Throws if 'Members' and 'MembersToInclude' are specified" {
            { Assert-MemberParameters -Members @('User1') -MembersToInclude @('User1') } | Should -Throw 'parameters conflict'
        }

        It "Throws if 'Members' and 'MembersToExclude' are specified" {
            { Assert-MemberParameters -Members @('User1') -MembersToExclude @('User2') } | Should -Throw 'parameters conflict'
        }

        It "Throws if 'MembersToInclude' and 'MembersToExclude' contain the same member" {
            { Assert-MemberParameters -MembersToExclude @('user1') -MembersToInclude @('USER1') } | Should -Throw 'member must not be included in both'
        }

        It "Throws if 'MembersToInclude' and 'MembersToExclude' are empty" {
            { Assert-MemberParameters -MembersToExclude @() -MembersToInclude @() } | Should -Throw 'At least one member must be specified'
        }
    }

    Describe 'xActiveDirectory.Common\ConvertTo-Timespan' {
        It "Returns 'System.TimeSpan' object type" {
            $testIntTimeSpan = 60

            $result = ConvertTo-TimeSpan -TimeSpan $testIntTimeSpan -TimeSpanType Minutes

            $result -is [System.TimeSpan] | Should -Be $true
        }

        It 'Creates TimeSpan from seconds' {
            $testIntTimeSpan = 60

            $result = ConvertTo-TimeSpan -TimeSpan $testIntTimeSpan -TimeSpanType Seconds

            $result.TotalSeconds | Should -Be $testIntTimeSpan
        }

        It 'Creates TimeSpan from minutes' {
            $testIntTimeSpan = 60

            $result = ConvertTo-TimeSpan -TimeSpan $testIntTimeSpan -TimeSpanType Minutes

            $result.TotalMinutes | Should -Be $testIntTimeSpan
        }

        It 'Creates TimeSpan from hours' {
            $testIntTimeSpan = 60

            $result = ConvertTo-TimeSpan -TimeSpan $testIntTimeSpan -TimeSpanType Hours

            $result.TotalHours | Should -Be $testIntTimeSpan
        }

        It 'Creates TimeSpan from days' {
            $testIntTimeSpan = 60

            $result = ConvertTo-TimeSpan -TimeSpan $testIntTimeSpan -TimeSpanType Days

            $result.TotalDays | Should -Be $testIntTimeSpan
        }
    }

    Describe 'xActiveDirectory.Common\ConvertFrom-Timespan' {
        It "Returns 'System.UInt32' object type" {
            $testIntTimeSpan = 60
            $testTimeSpan = New-TimeSpan -Seconds $testIntTimeSpan

            $result = ConvertFrom-TimeSpan -TimeSpan $testTimeSpan -TimeSpanType Seconds

            $result -is [System.UInt32] | Should -Be $true
        }

        It 'Converts TimeSpan to total seconds' {
            $testIntTimeSpan = 60
            $testTimeSpan = New-TimeSpan -Seconds $testIntTimeSpan

            $result = ConvertFrom-TimeSpan -TimeSpan $testTimeSpan -TimeSpanType Seconds

            $result | Should -Be $testTimeSpan.TotalSeconds
        }

        It 'Converts TimeSpan to total minutes' {
            $testIntTimeSpan = 60
            $testTimeSpan = New-TimeSpan -Minutes $testIntTimeSpan

            $result = ConvertFrom-TimeSpan -TimeSpan $testTimeSpan -TimeSpanType Minutes

            $result | Should -Be $testTimeSpan.TotalMinutes
        }

        It 'Converts TimeSpan to total hours' {
            $testIntTimeSpan = 60
            $testTimeSpan = New-TimeSpan -Hours $testIntTimeSpan

            $result = ConvertFrom-TimeSpan -TimeSpan $testTimeSpan -TimeSpanType Hours

            $result | Should -Be $testTimeSpan.TotalHours
        }

        It 'Converts TimeSpan to total days' {
            $testIntTimeSpan = 60
            $testTimeSpan = New-TimeSpan -Days $testIntTimeSpan

            $result = ConvertFrom-TimeSpan -TimeSpan $testTimeSpan -TimeSpanType Days

            $result | Should -Be $testTimeSpan.TotalDays
        }
    }

    Describe 'xActiveDirectory.Common\Get-ADCommonParameters' {
        It "Returns 'System.Collections.Hashtable' object type" {
            $testIdentity = 'contoso.com'

            $result = Get-ADCommonParameters -Identity $testIdentity

            $result -is [System.Collections.Hashtable] | Should -Be $true
        }

        It "Returns 'Identity' key by default" {
            $testIdentity = 'contoso.com'

            $result = Get-ADCommonParameters -Identity $testIdentity

            $result['Identity'] | Should -Be $testIdentity
        }

        It "Returns 'Name' key when 'UseNameParameter' is specified" {
            $testIdentity = 'contoso.com'

            $result = Get-ADCommonParameters -Identity $testIdentity -UseNameParameter

            $result['Name'] | Should -Be $testIdentity
        }

        foreach ($identityParam in @('UserName','GroupName','ComputerName'))
        {
            It "Returns 'Identity' key when '$identityParam' alias is specified" {
                $testIdentity = 'contoso.com'
                $getADCommonParameters = @{
                    $identityParam = $testIdentity
                }

                $result = Get-ADCommonParameters @getADCommonParameters

                $result['Identity'] | Should -Be $testIdentity
            }
        }

        It "Returns 'Identity' key by default when 'Identity' and 'CommonName' are specified" {
            $testIdentity = 'contoso.com'
            $testCommonName = 'Test Common Name'

            $result = Get-ADCommonParameters -Identity $testIdentity -CommonName $testCommonName

            $result['Identity'] | Should -Be $testIdentity
        }

        It "Returns 'Identity' key with 'CommonName' when 'Identity', 'CommonName' and 'PreferCommonName' are specified" {
            $testIdentity = 'contoso.com'
            $testCommonName = 'Test Common Name'

            $result = Get-ADCommonParameters -Identity $testIdentity -CommonName $testCommonName -PreferCommonName

            $result['Identity'] | Should -Be $testCommonName
        }

        It "Returns 'Identity' key with 'Identity' when 'Identity' and 'PreferCommonName' are specified" {
            $testIdentity = 'contoso.com'

            $result = Get-ADCommonParameters -Identity $testIdentity -PreferCommonName

            $result['Identity'] | Should -Be $testIdentity
        }

        it "Returns 'Name' key when 'UseNameParameter' and 'PreferCommonName' are supplied" {
            $testIdentity = 'contoso.com'
            $testCommonName = 'Test Common Name'

            $result = Get-ADCommonParameters -Identity $testIdentity -UseNameParameter -CommonName $testCommonName -PreferCommonName

            $result['Name'] | Should -Be $testCommonName
        }

        It "Does not return 'Credential' key by default" {
            $testIdentity = 'contoso.com'

            $result = Get-ADCommonParameters -Identity $testIdentity

            $result.ContainsKey('Credential') | Should -Be $false
        }

        It "Returns 'Credential' key when specified" {
            $testIdentity = 'contoso.com'
            $testCredential = [System.Management.Automation.PSCredential]::Empty

            $result = Get-ADCommonParameters -Identity $testIdentity -Credential $testCredential

            $result['Credential'] | Should -Be $testCredential
        }

        It "Does not return 'Server' key by default" {
            $testIdentity = 'contoso.com'

            $result = Get-ADCommonParameters -Identity $testIdentity

            $result.ContainsKey('Server') | Should -Be $false
        }

        It "Returns 'Server' key when specified" {
            $testIdentity = 'contoso.com'
            $testServer = 'testserver.contoso.com'

            $result = Get-ADCommonParameters -Identity $testIdentity -Server $testServer

            $result['Server'] | Should -Be $testServer
        }

        It "Converts 'DomainAdministratorCredential' parameter to 'Credential' key" {
            $testIdentity = 'contoso.com'
            $testCredential = [System.Management.Automation.PSCredential]::Empty

            $result = Get-ADCommonParameters -Identity $testIdentity -DomainAdministratorCredential $testCredential

            $result['Credential'] | Should -Be $testCredential
        }

        It "Converts 'DomainController' parameter to 'Server' key" {
            $testIdentity = 'contoso.com'
            $testServer = 'testserver.contoso.com'

            $result = Get-ADCommonParameters -Identity $testIdentity -DomainController $testServer

            $result['Server'] | Should -Be $testServer
        }

        It 'Accepts remaining arguments' {
            $testIdentity = 'contoso.com'

            $result = Get-ADCommonParameters -Identity $testIdentity -UnexpectedParameter 42

            $result['Identity'] | Should -Be $testIdentity
        }
    }

    Describe 'xActiveDirectory.Common\ConvertTo-DeploymentForestMode' {
        It 'Converts an Microsoft.ActiveDirectory.Management.ForestMode to Microsoft.DirectoryServices.Deployment.Types.ForestMode' {
            ConvertTo-DeploymentForestMode -Mode Windows2012Forest | Should -BeOfType [Microsoft.DirectoryServices.Deployment.Types.ForestMode]
        }

        It 'Converts an Microsoft.ActiveDirectory.Management.ForestMode to the correct Microsoft.DirectoryServices.Deployment.Types.ForestMode' {
            ConvertTo-DeploymentForestMode -Mode Windows2012Forest | Should -Be ([Microsoft.DirectoryServices.Deployment.Types.ForestMode]::Win2012)
        }

        It 'Converts valid integer to Microsoft.DirectoryServices.Deployment.Types.ForestMode' {
            ConvertTo-DeploymentForestMode -ModeId 5 | Should -BeOfType [Microsoft.DirectoryServices.Deployment.Types.ForestMode]
        }

        It 'Converts a valid integer to the correct Microsoft.DirectoryServices.Deployment.Types.ForestMode' {
            ConvertTo-DeploymentForestMode -ModeId 5 | Should -Be ([Microsoft.DirectoryServices.Deployment.Types.ForestMode]::Win2012)
        }

        It 'Throws an exception when an invalid forest mode is selected' {
            { ConvertTo-DeploymentForestMode -Mode Nonexistant } | Should -Throw
        }

        It 'Throws no exception when a null value is passed' {
            { ConvertTo-DeploymentForestMode -Mode $null } | Should -Not -Throw
        }

        It 'Throws no exception when an invalid mode id is selected' {
            { ConvertTo-DeploymentForestMode -ModeId 666 } | Should -Not -Throw
        }

        It 'Returns $null when a null value is passed' {
            ConvertTo-DeploymentForestMode -Mode $null | Should -Be $null
        }

        It 'Returns $null when an invalid mode id is selected' {
            ConvertTo-DeploymentForestMode -ModeId 666 | Should -Be $null
        }
    }

    Describe 'xActiveDirectory.Common\ConvertTo-DeploymentDomainMode' {
        It 'Converts an Microsoft.ActiveDirectory.Management.DomainMode to Microsoft.DirectoryServices.Deployment.Types.DomainMode' {
            ConvertTo-DeploymentDomainMode -Mode Windows2012Domain | Should -BeOfType [Microsoft.DirectoryServices.Deployment.Types.DomainMode]
        }

        It 'Converts an Microsoft.ActiveDirectory.Management.DomainMode to the correct Microsoft.DirectoryServices.Deployment.Types.DomainMode' {
            ConvertTo-DeploymentDomainMode -Mode Windows2012Domain | Should -Be ([Microsoft.DirectoryServices.Deployment.Types.DomainMode]::Win2012)
        }

        It 'Converts valid integer to Microsoft.DirectoryServices.Deployment.Types.DomainMode' {
            ConvertTo-DeploymentDomainMode -ModeId 5 | Should -BeOfType [Microsoft.DirectoryServices.Deployment.Types.DomainMode]
        }

        It 'Converts a valid integer to the correct Microsoft.DirectoryServices.Deployment.Types.DomainMode' {
            ConvertTo-DeploymentDomainMode -ModeId 5 | Should -Be ([Microsoft.DirectoryServices.Deployment.Types.DomainMode]::Win2012)
        }

        It 'Throws an exception when an invalid domain mode is selected' {
            { ConvertTo-DeploymentDomainMode -Mode Nonexistant } | Should -Throw
        }

        It 'Throws no exception when a null value is passed' {
            { ConvertTo-DeploymentDomainMode -Mode $null } | Should -Not -Throw
        }

        It 'Throws no exception when an invalid mode id is selected' {
            { ConvertTo-DeploymentDomainMode -ModeId 666 } | Should -Not -Throw
        }

        It 'Returns $null when a null value is passed' {
            ConvertTo-DeploymentDomainMode -Mode $null | Should -Be $null
        }

        It 'Returns $null when an invalid mode id is selected' {
            ConvertTo-DeploymentDomainMode -ModeId 666 | Should -Be $null
        }
    }

    Describe 'xActiveDirectory.Common\Restore-ADCommonObject' {
        $getAdObjectReturnValue = @(
            [PSCustomObject] @{
                Deleted           = $true
                DistinguishedName = 'CN=a375347\0ADEL:f0e3f4fe-212b-43e7-83dd-c8f3b47ebb9c,CN=Deleted Objects,DC=contoso,DC=com'
                Name              = 'a375347'
                ObjectClass       = 'user'
                ObjectGUID        = 'f0e3f4fe-212b-43e7-83dd-c8f3b47ebb9c'
                # Make this one day older.
                whenChanged       = (Get-Date).AddDays(-1)
            },
            [PSCustomObject] @{
                Deleted           = $true
                DistinguishedName = 'CN=a375347\0ADEL:d3c8b8c1-c42b-4533-af7d-3aa73ecd2216,CN=Deleted Objects,DC=contoso,DC=com'
                Name              = 'a375347'
                ObjectClass       = 'user'
                ObjectGUID        = 'd3c8b8c1-c42b-4533-af7d-3aa73ecd2216'
                whenChanged       = Get-Date
            }
        )

        $restoreAdObjectReturnValue = [PSCustomObject]@{
            DistinguishedName = 'CN=a375347,CN=Accounts,DC=contoso,DC=com'
            Name              = 'a375347'
            ObjectClass       = 'user'
            ObjectGUID        = 'd3c8b8c1-c42b-4533-af7d-3aa73ecd2216'
        }

        function Restore-ADObject
        {
        }

        $getAdCommonParameterReturnValue = @{Identity = 'something'}
        $restoreIdentity = 'SomeObjectName'
        $restoreObjectClass = 'user'
        $restoreObjectWrongClass = 'wrong'

        Context 'When there are objects in the recycle bin' {
            Mock -CommandName Get-ADObject -MockWith { return $getAdObjectReturnValue } -Verifiable
            Mock -CommandName Get-ADCommonParameters -MockWith { return $getAdCommonParameterReturnValue }
            Mock -CommandName Restore-ADObject -Verifiable

            It 'Should not throw when called with the correct parameters' {
                {Restore-ADCommonObject -Identity $restoreIdentity -ObjectClass $restoreObjectClass} | Should -Not -Throw
            }

            It 'Should return the correct restored object' {
                Mock -CommandName Restore-ADObject -MockWith { return $restoreAdObjectReturnValue}
                (Restore-ADCommonObject -Identity $restoreIdentity -ObjectClass $restoreObjectClass).ObjectClass | Should -Be 'user'
            }

            It 'Should throw the correct error when invalid parameters are used' {
                {Restore-ADCommonObject -Identity $restoreIdentity -ObjectClass $restoreObjectWrongClass} | Should -Throw "Cannot validate argument on parameter 'ObjectClass'"
            }

            It 'Should call Get-ADObject as well as Restore-ADObject' {
                Assert-VerifiableMock
            }

            It 'Should throw an InvalidOperationException when object parent does not exist' {
                Mock -CommandName Restore-ADObject -MockWith { throw (New-Object -TypeName Microsoft.ActiveDirectory.Management.ADException)}

                {Restore-ADCommonObject -Identity $restoreIdentity -ObjectClass $restoreObjectClass} | Should -Throw -ExceptionType ([System.InvalidOperationException])
            }
        }

        Context 'When there are no objects in the recycle bin' {
            Mock -CommandName Get-ADObject
            Mock -CommandName Get-ADCommonParameters -MockWith { return $getAdCommonParameterReturnValue}
            Mock -CommandName Restore-ADObject

            It 'Should return $null' {
                Restore-ADCommonObject -Identity $restoreIdentity -ObjectClass $restoreObjectClass | Should -Be $null
            }

            It 'Should not call Restore-ADObject' {
                Restore-ADCommonObject -Identity $restoreIdentity -ObjectClass $restoreObjectClass
                Assert-MockCalled -CommandName Restore-ADObject -Exactly -Times 0 -Scope It
            }
        }
    }

    Describe 'xActiveDirectory.Common\Get-ADDomainNameFromDistinguishedName' {
        $validDistinguishedNames = @(
            @{
                DN     = 'CN=group1,OU=Group,OU=Wacken,DC=contoso,DC=com'
                Domain = 'contoso.com'
            }
            @{
                DN     = 'CN=group1,OU=Group,OU=Wacken,DC=sub,DC=contoso,DC=com'
                Domain = 'sub.contoso.com'
            }
            @{
                DN     = 'CN=group1,OU=Group,OU=Wacken,DC=child,DC=sub,DC=contoso,DC=com'
                Domain = 'child.sub.contoso.com'
            }
        )

        $invalidDistinguishedNames = @(
            'Group1'
            'contoso\group1'
            'user1@contoso.com'
        )

        Context 'The distinguished name is valid' {
            foreach ($name in $validDistinguishedNames)
            {
                It "Should match domain $($name.Domain)" {
                    Get-ADDomainNameFromDistinguishedName -DistinguishedName $name.Dn | Should -Be $name.Domain
                }
            }
        }

        Context 'The distinguished name is invalid' {
            foreach ($name in $invalidDistinguishedNames)
            {
                It "Should return `$null for $name" {
                    Get-ADDomainNameFromDistinguishedName -DistinguishedName $name | Should -Be $null
                }
            }
        }
    }

    Describe 'xActiveDirectory.Common\Add-ADCommonGroupMember' {
        Mock -CommandName Assert-Module -ParameterFilter { $ModuleName -eq 'ActiveDirectory' }

        $memberData = @(
            [pscustomobject]@{
                Name = 'CN=Account1,DC=contoso,DC=com'
                Domain = 'contoso.com'
            }
            [pscustomobject]@{
                Name = 'CN=Group1,DC=contoso,DC=com'
                Domain = 'contoso.com'
            }
            [pscustomobject]@{
                Name = 'CN=Computer1,DC=contoso,DC=com'
                Domain = 'contoso.com'
            }
            [pscustomobject]@{
                Name = 'CN=Account1,DC=a,DC=contoso,DC=com'
                Domain = 'a.contoso.com'
            }
            [pscustomobject]@{
                Name = 'CN=Group1,DC=a,DC=contoso,DC=com'
                Domain = 'a.contoso.com'
            }
            [pscustomobject]@{
                Name = 'CN=Computer1,DC=a,DC=contoso,DC=com'
                Domain = 'a.contoso.com'
            }
            [pscustomobject]@{
                Name = 'CN=Account1,DC=b,DC=contoso,DC=com'
                Domain = 'b.contoso.com'
            }
            [pscustomobject]@{
                Name = 'CN=Group1,DC=b,DC=contoso,DC=com'
                Domain = 'b.contoso.com'
            }
            [pscustomobject]@{
                Name = 'CN=Computer1,DC=b,DC=contoso,DC=com'
                Domain = 'b.contoso.com'
            }
        )

        $invalidMemberData = @(
            'contoso.com\group1'
            'user1@contoso.com'
            'computer1.contoso.com'
        )

        $fakeParameters = @{
            Identity = 'SomeGroup'
        }

        Context 'When all members are in the same domain' {
            Mock -CommandName Add-ADGroupMember
            $groupCount = 0
            foreach ($domainGroup in ($memberData | Group-Object -Property Domain))
            {
                $groupCount ++
                It 'Should not throw an error when calling Add-ADCommonGroupMember' {
                    Add-ADCommonGroupMember -Members $domainGroup.Group.Name -Parameters $fakeParameters
                }
            }

            It "Should have called Add-ADGroupMember $groupCount times" {
                Assert-MockCalled -CommandName Add-ADGroupMember -Exactly -Times $groupCount
            }
        }

        Context 'When members are in different domains' {
            Mock -CommandName Add-ADGroupMember
            Mock -CommandName Get-ADObject -MockWith {
                param
                (
                    [Parameter()]
                    [System.String]
                    $Identity,

                    [Parameter()]
                    [System.String]
                    $Server,

                    [Parameter()]
                    [System.String[]]
                    $Properties
                )

                $objectClass = switch ($Identity)
                {
                    {$Identity -match 'Group'} { 'group' }
                    {$Identity -match 'Account'} { 'user' }
                    {$Identity -match 'Computer'} { 'computer' }
                }

                return ([PSCustomObject]@{
                        objectClass = $objectClass
                    })
            }
            # Mocks should return something that is used with Add-ADGroupMember
            Mock -CommandName Get-ADComputer -MockWith { return 'placeholder' }
            Mock -CommandName Get-ADGroup -MockWith { return 'placeholder' }
            Mock -CommandName Get-ADUser -MockWith { return 'placeholder' }

            It 'Should not throw an error' {
                {Add-ADCommonGroupMember -Members $memberData.Name -Parameters $fakeParameters -MembersInMultipleDomains} | Should -Not -Throw
            }

            It 'Should have called all mocked cmdlets' {
                Assert-MockCalled -CommandName Get-ADComputer -Exactly -Times $memberData.Where( {$_.Name -like '*Computer*'}).Count
                Assert-MockCalled -CommandName Get-ADUser -Exactly -Times $memberData.Where( {$_.Name -like '*Account*'}).Count
                Assert-MockCalled -CommandName Get-ADGroup -Exactly -Times $memberData.Where( {$_.Name -like '*Group*'}).Count
                Assert-MockCalled -CommandName Add-ADGroupMember -Exactly -Times $memberData.Count
            }
        }

        Context 'When the domain name cannot be determined' {
            It 'Should throw an InvalidArgumentException' {
                {Add-ADCommonGroupMember -Members $invalidMemberData  -Parameters $fakeParameters -MembersInMultipleDomains} | Should -Throw -ExceptionType ([System.ArgumentException])
            }
        }
    }

    Describe 'xActiveDirectory.Common\Get-DomainControllerObject' {
        Context 'When domain name cannot be reached' {
            BeforeAll {
                Mock -CommandName Get-ADDomainController -MockWith {
                    throw New-Object -TypeName 'Microsoft.ActiveDirectory.Management.ADServerDownException'
                }
            }

            It 'Should throw the correct error' {
                { Get-DomainControllerObject -DomainName 'contoso.com' -Verbose } | Should -Throw $localizedString.FailedEvaluatingDomainController

                Assert-MockCalled -CommandName Get-ADDomainController -Exactly -Times 1 -Scope It
            }
        }

        Context 'When current node is not a domain controller' {
            BeforeAll {
                Mock -CommandName Get-ADDomainController
                Mock -CommandName Test-IsDomainController -MockWith {
                    return $false
                }
            }

            It 'Should return $null' {
                $getDomainControllerObjectResult = Get-DomainControllerObject -DomainName 'contoso.com' -Verbose
                $getDomainControllerObjectResult | Should -BeNullOrEmpty

                Assert-MockCalled -CommandName Get-ADDomainController -Exactly -Times 1 -Scope It
            }
        }

        Context 'When current node is not a domain controller, but operating system information says it should be' {
            BeforeAll {
                Mock -CommandName Get-ADDomainController
                Mock -CommandName Test-IsDomainController -MockWith {
                    return $true
                }
            }

            It 'Should throw the correct error' {
                { Get-DomainControllerObject -DomainName 'contoso.com' -Verbose } | Should -Throw $script:localizedData.WasExpectingDomainController

                Assert-MockCalled -CommandName Get-ADDomainController -Exactly -Times 1 -Scope It
            }
        }

        Context 'When current node is a domain controller' {
            BeforeAll {
                Mock -CommandName Get-ADDomainController -MockWith {
                    return @{
                        Site            = 'MySite'
                        Domain          = 'contoso.com'
                        IsGlobalCatalog = $true
                    }
                }
            }

            It 'Should return the correct values for each property' {
                $getDomainControllerObjectResult = Get-DomainControllerObject -DomainName 'contoso.com' -Verbose

                $getDomainControllerObjectResult.Site | Should -Be 'MySite'
                $getDomainControllerObjectResult.Domain | Should -Be 'contoso.com'
                $getDomainControllerObjectResult.IsGlobalCatalog | Should -BeTrue

                Assert-MockCalled -CommandName Get-ADDomainController -Exactly -Times 1 -Scope It
            }
        }

        Context 'When current node is a domain controller, and using specific credential' {
            BeforeAll {
                Mock -CommandName Get-ADDomainController -MockWith {
                    return @{
                        Site            = 'MySite'
                        Domain          = 'contoso.com'
                        IsGlobalCatalog = $true
                    }
                }

                $mockAdministratorUser = 'admin@contoso.com'
                $mockAdministratorPassword = 'P@ssw0rd-12P@ssw0rd-12' | ConvertTo-SecureString -AsPlainText -Force
                $mockAdministratorCredential = New-Object -TypeName 'System.Management.Automation.PSCredential' -ArgumentList @($mockAdministratorUser, $mockAdministratorPassword)
            }

            It 'Should return the correct values for each property' {
                $getDomainControllerObjectResult = Get-DomainControllerObject -DomainName 'contoso.com' -Credential $mockAdministratorCredential -Verbose

                $getDomainControllerObjectResult.Site | Should -Be 'MySite'
                $getDomainControllerObjectResult.Domain | Should -Be 'contoso.com'
                $getDomainControllerObjectResult.IsGlobalCatalog | Should -BeTrue

                Assert-MockCalled -CommandName Get-ADDomainController -ParameterFilter {
                    $PSBoundParameters.ContainsKey('Credential') -eq $true
                } -Exactly -Times 1 -Scope It
            }
        }
    }

    Describe 'xActiveDirectory.Common\Test-IsDomainController' {
        Context 'When operating system information says the node is a domain controller' {
            BeforeAll {
                Mock -CommandName Get-CimInstance -MockWith {
                    return @{
                        ProductType = 2
                    }
                }
            }

            It 'Should return $true' {
                $testIsDomainControllerResult = Test-IsDomainController
                $testIsDomainControllerResult | Should -BeTrue

                Assert-MockCalled -CommandName Get-CimInstance -Exactly -Times 1 -Scope It
            }
        }

        Context 'When operating system information says the node is not a domain controller' {
            BeforeAll {
                Mock -CommandName Get-CimInstance -MockWith {
                    return @{
                        ProductType = 3
                    }
                }
            }

            It 'Should return $false' {
                $testIsDomainControllerResult = Test-IsDomainController
                $testIsDomainControllerResult | Should -BeFalse

                Assert-MockCalled -CommandName Get-CimInstance -Exactly -Times 1 -Scope It
            }
        }
    }

    Describe 'xActiveDirectory.Common\Convert-PropertyMapToObjectProperties' {
        Context 'When a property map should be converted to object properties' {
            BeforeAll {
                $propertyMapValue = @(
                    @{
                        ParameterName = 'ComputerName'
                        PropertyName = 'cn'
                    },
                    @{
                        ParameterName = 'Location'
                    }
                )
            }

            It 'Should return the correct values' {
                $convertPropertyMapToObjectPropertiesResult = Convert-PropertyMapToObjectProperties $propertyMapValue
                $convertPropertyMapToObjectPropertiesResult | Should -HaveCount 2
                $convertPropertyMapToObjectPropertiesResult[0] | Should -Be 'cn'
                $convertPropertyMapToObjectPropertiesResult[1] | Should -Be 'Location'
            }
        }

        Context 'When a property map contains a wrong type' {
            BeforeAll {
                $propertyMapValue = @(
                    @{
                        ParameterName = 'ComputerName'
                        PropertyName = 'cn'
                    },
                    'Location'
                )
            }

            It 'Should throw the correct error' {
                {
                    Convert-PropertyMapToObjectProperties $propertyMapValue
                } | Should -Throw $localizedString.PropertyMapArrayIsWrongType
            }
        }
    }

    Describe 'DscResource.Common\Test-DscPropertyState' -Tag 'TestDscPropertyState' {
        Context 'When comparing tables' {
            It 'Should return true for two identical tables' {
                $mockValues = @{
                    CurrentValue = 'Test'
                    DesiredValue = 'Test'
                }

                Test-DscPropertyState -Values $mockValues | Should -Be $true
            }
        }

        Context 'When comparing strings' {
            It 'Should return false when a value is different for [System.String]' {
                $mockValues = @{
                    CurrentValue = [System.String] 'something'
                    DesiredValue = [System.String] 'test'
                }

                Test-DscPropertyState -Values $mockValues | Should -Be $false
            }

            It 'Should return false when a String value is missing' {
                $mockValues = @{
                    CurrentValue = $null
                    DesiredValue = [System.String] 'Something'
                }

                Test-DscPropertyState -Values $mockValues | Should -Be $false
            }
        }

        Context 'When comparing integers' {
            It 'Should return false when a value is different for [System.Int32]' {
                $mockValues = @{
                    CurrentValue = [System.Int32] 1
                    DesiredValue = [System.Int32] 2
                }

                Test-DscPropertyState -Values $mockValues | Should -Be $false
            }

            It 'Should return false when a value is different for [System.Int16]' {
                $mockValues = @{
                    CurrentValue = [System.Int16] 1
                    DesiredValue = [System.Int16] 2
                }

                Test-DscPropertyState -Values $mockValues | Should -Be $false
            }

            It 'Should return false when a value is different for [System.UInt16]' {
                $mockValues = @{
                    CurrentValue = [System.UInt16] 1
                    DesiredValue = [System.UInt16] 2
                }

                Test-DscPropertyState -Values $mockValues | Should -Be $false
            }

            It 'Should return false when a Integer value is missing' {
                $mockValues = @{
                    CurrentValue = $null
                    DesiredValue = [System.Int32] 1
                }

                Test-DscPropertyState -Values $mockValues | Should -Be $false
            }
        }

        Context 'When comparing booleans' {
            It 'Should return false when a value is different for [System.Boolean]' {
                $mockValues = @{
                    CurrentValue = [System.Boolean] $true
                    DesiredValue = [System.Boolean] $false
                }

                Test-DscPropertyState -Values $mockValues | Should -Be $false
            }

            It 'Should return false when a Boolean value is missing' {
                $mockValues = @{
                    CurrentValue = $null
                    DesiredValue = [System.Boolean] $true
                }

                Test-DscPropertyState -Values $mockValues | Should -Be $false
            }
        }

        Context 'When comparing arrays' {
            It 'Should return true when evaluating an array' {
                $mockValues = @{
                    CurrentValue = @('1','2')
                    DesiredValue = @('1','2')
                }

                Test-DscPropertyState -Values $mockValues | Should -Be $true
            }

            It 'Should return false when evaluating an array with wrong values' {
                $mockValues = @{
                    CurrentValue = @('CurrentValueA','CurrentValueB')
                    DesiredValue = @('DesiredValue1','DesiredValue2')
                }

                Test-DscPropertyState -Values $mockValues | Should -Be $false
            }

            It 'Should return false when evaluating an array, but the current value is $null' {
                $mockValues = @{
                    CurrentValue = $null
                    DesiredValue = @('1','2')
                }

                Test-DscPropertyState -Values $mockValues | Should -Be $false
            }
        }

        Context -Name 'When passing invalid types for DesiredValue' {
            It 'Should write a warning when DesiredValue contain an unsupported type' {
                Mock -CommandName Write-Warning -Verifiable

                # This is a dummy type to test with a type that could never be a correct one.
                class MockUnknownType
                {
                    [ValidateNotNullOrEmpty()]
                    [System.String]
                    $Property1

                    [ValidateNotNullOrEmpty()]
                    [System.String]
                    $Property2

                    MockUnknownType()
                    {
                    }
                }

                $mockValues = @{
                    CurrentValue = New-Object -TypeName 'MockUnknownType'
                    DesiredValue = New-Object -TypeName 'MockUnknownType'
                }

                Test-DscPropertyState -Values $mockValues | Should -Be $false

                Assert-MockCalled -CommandName Write-Warning -Exactly -Times 1 -Scope It
            }
        }

        Assert-VerifiableMock
    }

    Describe 'xActiveDirectory.Common\Compare-ResourcePropertyState' {
        Context 'When one property is in desired state' {
            BeforeAll {
                $mockCurrentValues = @{
                    ComputerName = 'DC01'
                }

                $mockDesiredValues = @{
                    ComputerName = 'DC01'
                }
            }

            It 'Should return the correct values' {
                $compareTargetResourceStateParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                }

                $compareTargetResourceStateResult = Compare-ResourcePropertyState @compareTargetResourceStateParameters
                $compareTargetResourceStateResult | Should -HaveCount 1
                $compareTargetResourceStateResult.ParameterName | Should -Be 'ComputerName'
                $compareTargetResourceStateResult.Expected | Should -Be 'DC01'
                $compareTargetResourceStateResult.Actual | Should -Be 'DC01'
                $compareTargetResourceStateResult.InDesiredState | Should -BeTrue
            }
        }

        Context 'When two properties are in desired state' {
            BeforeAll {
                $mockCurrentValues = @{
                    ComputerName = 'DC01'
                    Location = 'Sweden'
                }

                $mockDesiredValues = @{
                    ComputerName = 'DC01'
                    Location = 'Sweden'
                }
            }

            It 'Should return the correct values' {
                $compareTargetResourceStateParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                }

                $compareTargetResourceStateResult = Compare-ResourcePropertyState @compareTargetResourceStateParameters
                $compareTargetResourceStateResult | Should -HaveCount 2
                $compareTargetResourceStateResult[0].ParameterName | Should -Be 'ComputerName'
                $compareTargetResourceStateResult[0].Expected | Should -Be 'DC01'
                $compareTargetResourceStateResult[0].Actual | Should -Be 'DC01'
                $compareTargetResourceStateResult[0].InDesiredState | Should -BeTrue
                $compareTargetResourceStateResult[1].ParameterName | Should -Be 'Location'
                $compareTargetResourceStateResult[1].Expected | Should -Be 'Sweden'
                $compareTargetResourceStateResult[1].Actual | Should -Be 'Sweden'
                $compareTargetResourceStateResult[1].InDesiredState | Should -BeTrue
            }
        }

        Context 'When passing just one property and that property is not in desired state' {
            BeforeAll {
                $mockCurrentValues = @{
                    ComputerName = 'DC01'
                }

                $mockDesiredValues = @{
                    ComputerName = 'APP01'
                }
            }

            It 'Should return the correct values' {
                $compareTargetResourceStateParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                }

                $compareTargetResourceStateResult = Compare-ResourcePropertyState @compareTargetResourceStateParameters
                $compareTargetResourceStateResult | Should -HaveCount 1
                $compareTargetResourceStateResult.ParameterName | Should -Be 'ComputerName'
                $compareTargetResourceStateResult.Expected | Should -Be 'APP01'
                $compareTargetResourceStateResult.Actual | Should -Be 'DC01'
                $compareTargetResourceStateResult.InDesiredState | Should -BeFalse
            }
        }

        Context 'When passing two properties and one property is not in desired state' {
            BeforeAll {
                $mockCurrentValues = @{
                    ComputerName = 'DC01'
                    Location = 'Sweden'
                }

                $mockDesiredValues = @{
                    ComputerName = 'DC01'
                    Location = 'Europe'
                }
            }

            It 'Should return the correct values' {
                $compareTargetResourceStateParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                }

                $compareTargetResourceStateResult = Compare-ResourcePropertyState @compareTargetResourceStateParameters
                $compareTargetResourceStateResult | Should -HaveCount 2
                $compareTargetResourceStateResult[0].ParameterName | Should -Be 'ComputerName'
                $compareTargetResourceStateResult[0].Expected | Should -Be 'DC01'
                $compareTargetResourceStateResult[0].Actual | Should -Be 'DC01'
                $compareTargetResourceStateResult[0].InDesiredState | Should -BeTrue
                $compareTargetResourceStateResult[1].ParameterName | Should -Be 'Location'
                $compareTargetResourceStateResult[1].Expected | Should -Be 'Europe'
                $compareTargetResourceStateResult[1].Actual | Should -Be 'Sweden'
                $compareTargetResourceStateResult[1].InDesiredState | Should -BeFalse
            }
        }

        Context 'When passing a common parameter set to desired value' {
            BeforeAll {
                $mockCurrentValues = @{
                    ComputerName = 'DC01'
                }

                $mockDesiredValues = @{
                    ComputerName = 'DC01'
                    Verbose = $true
                }
            }

            It 'Should return the correct values' {
                $compareTargetResourceStateParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                }

                $compareTargetResourceStateResult = Compare-ResourcePropertyState @compareTargetResourceStateParameters
                $compareTargetResourceStateResult | Should -HaveCount 1
                $compareTargetResourceStateResult.ParameterName | Should -Be 'ComputerName'
                $compareTargetResourceStateResult.Expected | Should -Be 'DC01'
                $compareTargetResourceStateResult.Actual | Should -Be 'DC01'
                $compareTargetResourceStateResult.InDesiredState | Should -BeTrue
            }
        }

        Context 'When using parameter Properties to compare desired values' {
            BeforeAll {
                $mockCurrentValues = @{
                    ComputerName = 'DC01'
                    Location = 'Sweden'
                }

                $mockDesiredValues = @{
                    ComputerName = 'DC01'
                    Location = 'Europe'
                }
            }

            It 'Should return the correct values' {
                $compareTargetResourceStateParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                    Properties = @(
                        'ComputerName'
                    )
                }

                $compareTargetResourceStateResult = Compare-ResourcePropertyState @compareTargetResourceStateParameters
                $compareTargetResourceStateResult | Should -HaveCount 1
                $compareTargetResourceStateResult.ParameterName | Should -Be 'ComputerName'
                $compareTargetResourceStateResult.Expected | Should -Be 'DC01'
                $compareTargetResourceStateResult.Actual | Should -Be 'DC01'
                $compareTargetResourceStateResult.InDesiredState | Should -BeTrue
            }
        }

        Context 'When using parameter Properties and IgnoreProperties to compare desired values' {
            BeforeAll {
                $mockCurrentValues = @{
                    ComputerName = 'DC01'
                    Location = 'Sweden'
                    Ensure = 'Present'
                }

                $mockDesiredValues = @{
                    ComputerName = 'DC01'
                    Location = 'Europe'
                    Ensure = 'Absent'
                }
            }

            It 'Should return the correct values' {
                $compareTargetResourceStateParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                    IgnoreProperties = @(
                        'Ensure'
                    )
                }

                $compareTargetResourceStateResult = Compare-ResourcePropertyState @compareTargetResourceStateParameters
                $compareTargetResourceStateResult | Should -HaveCount 2
                $compareTargetResourceStateResult[0].ParameterName | Should -Be 'ComputerName'
                $compareTargetResourceStateResult[0].Expected | Should -Be 'DC01'
                $compareTargetResourceStateResult[0].Actual | Should -Be 'DC01'
                $compareTargetResourceStateResult[0].InDesiredState | Should -BeTrue
                $compareTargetResourceStateResult[1].ParameterName | Should -Be 'Location'
                $compareTargetResourceStateResult[1].Expected | Should -Be 'Europe'
                $compareTargetResourceStateResult[1].Actual | Should -Be 'Sweden'
                $compareTargetResourceStateResult[1].InDesiredState | Should -BeFalse
            }
        }

        Context 'When using parameter Properties and IgnoreProperties to compare desired values' {
            BeforeAll {
                $mockCurrentValues = @{
                    ComputerName = 'DC01'
                    Location = 'Sweden'
                    Ensure = 'Present'
                }

                $mockDesiredValues = @{
                    ComputerName = 'DC01'
                    Location = 'Europe'
                    Ensure = 'Absent'
                }
            }

            It 'Should return and empty array' {
                $compareTargetResourceStateParameters = @{
                    CurrentValues = $mockCurrentValues
                    DesiredValues = $mockDesiredValues
                    Properties = @(
                        'ComputerName'
                    )
                    IgnoreProperties = @(
                        'ComputerName'
                    )
                }

                $compareTargetResourceStateResult = Compare-ResourcePropertyState @compareTargetResourceStateParameters
                $compareTargetResourceStateResult | Should -BeNullOrEmpty
            }
        }
    }

    Describe 'xActiveDirectory.Common\Assert-ADPSDrive' {
        Mock -CommandName Assert-Module

        Context 'When the AD PS Drive does not exist and the New-PSDrive function is successful' {
            Mock -CommandName Get-PSDrive -MockWith { $null }
            Mock -CommandName New-PSDrive

            It 'Should not throw' {
                { Assert-ADPSDrive } | Should -Not -Throw
            }

            It 'Should have called Assert-Module' {
                Assert-MockCalled -CommandName Assert-Module -Exactly -Times 1 -Scope Context
            }

            It 'Should have called Get-PSDrive only once' {
                Assert-MockCalled -CommandName Get-PSDrive -Exactly -Times 1 -Scope Context
            }

            It 'Should have called New-PSDrive only once' {
                Assert-MockCalled -CommandName New-PSDrive -Exactly -Times 1 -Scope Context
            }
        }

        Context 'When the AD PS Drive does not exist and the New-PSDrive function is not successful' {
            Mock -CommandName Get-PSDrive -MockWith { $null }
            Mock -CommandName New-PSDrive -MockWith { throw }

            It 'Should throw the correct error' {
                { Assert-ADPSDrive } | Should -Throw $script:localizedString.CreatingNewADPSDriveError
            }

            It 'Should call Assert-Module' {
                Assert-MockCalled -CommandName Assert-Module -Exactly -Times 1 -Scope Context
            }

            It 'Should call Get-PSDrive once' {
                Assert-MockCalled -CommandName Get-PSDrive -Exactly -Times 1 -Scope Context
            }

            It 'Should call New-PSDrive once' {
                Assert-MockCalled -CommandName New-PSDrive -Exactly -Times 1 -Scope Context
            }
        }

        Context 'When the AD PS Drive already exists' {
            Mock -CommandName Get-PSDrive -MockWith { New-MockObject -Type System.Management.Automation.PSDriveInfo }
            Mock -CommandName New-PSDrive

            It 'Should not throw' {
              { Assert-ADPSDrive } | Should -Not -Throw
            }

            It 'Should call Assert-Module only once' {
                Assert-MockCalled -CommandName Assert-Module -Exactly -Times 1 -Scope Context
            }

            It 'Should call Get-PSDrive only once' {
                Assert-MockCalled -CommandName Get-PSDrive -Exactly -Times 1 -Scope Context
            }

            It 'Should not call New-PSDrive' {
                Assert-MockCalled -CommandName New-PSDrive -Exactly -Times 0 -Scope Context
            }
        }

    }

    Describe 'xActiveDirectory.Common\Test-ADReplicationSite' {
        BeforeAll {
            function Get-ADDomainController
            {
            }

            function Get-ADReplicationSite
            {
            }

            Mock -CommandName Get-ADDomainController -MockWith {
                return $env:COMPUTERNAME
            }
        }

        Context 'When a replication site does not exist' {
            BeforeAll {
                Mock -CommandName Get-ADReplicationSite -MockWith {
                    throw New-Object -TypeName 'Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException'
                }
            }

            It 'Should return $false' {
                $testADReplicationSiteResult = Test-ADReplicationSite -SiteName 'TestSite' -DomainName 'contoso.com'
                $testADReplicationSiteResult | Should -BeFalse

                Assert-MockCalled -CommandName Get-ADDomainController -Exactly -Times 1 -Scope It
                Assert-MockCalled -CommandName Get-ADReplicationSite -Exactly -Times 1 -Scope It
            }
        }

        Context 'When a replication site exist' {
            BeforeAll {
                Mock -CommandName Get-ADReplicationSite -MockWith {
                    return 'site object'
                }
            }

            It 'Should return $true' {
                $testADReplicationSiteResult = Test-ADReplicationSite -SiteName 'TestSite' -DomainName 'contoso.com'
                $testADReplicationSiteResult | Should -BeTrue

                Assert-MockCalled -CommandName Get-ADDomainController -Exactly -Times 1 -Scope It
                Assert-MockCalled -CommandName Get-ADReplicationSite -Exactly -Times 1 -Scope It
            }
        }
    }
}