From 92612af8de4fedca3cb1438e8456428e21a44375 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Fri, 16 Dec 2022 14:33:46 +0100 Subject: [PATCH] SqlAGReplica: Converted unit tests to Pester 5 (#1820) - SqlAgReplica - Converted unit test to Pester 5. - No longer tries to enforce EndpointHostName when it is not part of the configuration (issue #1821). - Now `Get-TargetResource` always returns values for the properties `Name` and `AvailabilityGroupName` (issue #1822). - Now `Test-TargetResource` no longer test properties that cannot be enforced (issue #1822). --- CHANGELOG.md | 9 + .../DSC_SqlAGReplica/DSC_SqlAGReplica.psm1 | 20 +- tests/Unit/DSC_SqlAGReplica.Tests.ps1 | 2411 +++++++++-------- 3 files changed, 1321 insertions(+), 1119 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38ab6014a..ffb65bb48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,12 +57,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 running in Azure DevOps. - `Install-SqlServerDsc` - No longer throws an exception when parameter `AgtSvcAccount` is not specified. +- SqlAgReplica + - Converted unit test to Pester 5. ### Fixed - SqlServerDsc - Localized strings file `en-US/SqlServerDsc.strings.psd1` no longer referencing the wrong module in a comment. +- SqlAGReplica + - No longer tries to enforce EndpointHostName when it is not part of the + configuration ([issue #1821](https://github.com/dsccommunity/SqlServerDsc/issues/1821)). + - Now `Get-TargetResource` always returns values for the properties `Name` + and `AvailabilityGroupName` ([issue #1822](https://github.com/dsccommunity/SqlServerDsc/issues/1822)). + - Now `Test-TargetResource` no longer test properties that cannot + be enforced ([issue #1822](https://github.com/dsccommunity/SqlServerDsc/issues/1822)). ## [16.0.0] - 2022-09-09 diff --git a/source/DSCResources/DSC_SqlAGReplica/DSC_SqlAGReplica.psm1 b/source/DSCResources/DSC_SqlAGReplica/DSC_SqlAGReplica.psm1 index 23eab902a..0d54488ec 100644 --- a/source/DSCResources/DSC_SqlAGReplica/DSC_SqlAGReplica.psm1 +++ b/source/DSCResources/DSC_SqlAGReplica/DSC_SqlAGReplica.psm1 @@ -65,8 +65,8 @@ function Get-TargetResource # Create the return object $alwaysOnAvailabilityGroupReplicaResource = @{ Ensure = 'Absent' - Name = '' - AvailabilityGroupName = '' + Name = $Name + AvailabilityGroupName = $AvailabilityGroupName AvailabilityMode = '' BackupPriority = '' ConnectionModeInPrimaryRole = '' @@ -87,9 +87,6 @@ function Get-TargetResource if ( $availabilityGroup ) { - # Add the Availability Group name to the results - $alwaysOnAvailabilityGroupReplicaResource.AvailabilityGroupName = $availabilityGroup.Name - # Try to find the replica $availabilityGroupReplica = $availabilityGroup.AvailabilityReplicas[$Name] @@ -97,7 +94,6 @@ function Get-TargetResource { # Add the Availability Group Replica properties to the results $alwaysOnAvailabilityGroupReplicaResource.Ensure = 'Present' - $alwaysOnAvailabilityGroupReplicaResource.Name = $availabilityGroupReplica.Name $alwaysOnAvailabilityGroupReplicaResource.AvailabilityMode = $availabilityGroupReplica.AvailabilityMode $alwaysOnAvailabilityGroupReplicaResource.BackupPriority = $availabilityGroupReplica.BackupPriority $alwaysOnAvailabilityGroupReplicaResource.ConnectionModeInPrimaryRole = $availabilityGroupReplica.ConnectionModeInPrimaryRole @@ -672,11 +668,6 @@ function Test-TargetResource 'Present' { $parametersToCheck = @( - 'Name', - 'AvailabilityGroupName', - 'ServerName', - 'InstanceName', - 'Ensure', 'AvailabilityMode', 'BackupPriority', 'ConnectionModeInPrimaryRole', @@ -721,13 +712,8 @@ function Test-TargetResource # Get the Endpoint URL properties $currentEndpointProtocol, $currentEndpointHostName, $currentEndpointPort = $getTargetResourceResult.EndpointUrl.Replace('//', '').Split(':') - if ( -not $EndpointHostName ) - { - $EndpointHostName = $getTargetResourceResult.EndpointHostName - } - # Verify the hostname in the endpoint URL is correct - if ( $EndpointHostName -ne $currentEndpointHostName ) + if ( $PSBoundParameters.ContainsKey('EndpointHostName') -and $EndpointHostName -ne $currentEndpointHostName ) { Write-Verbose -Message ( $script:localizedData.ParameterNotInDesiredState -f 'EndpointHostName', $EndpointHostName, $currentEndpointHostName diff --git a/tests/Unit/DSC_SqlAGReplica.Tests.ps1 b/tests/Unit/DSC_SqlAGReplica.Tests.ps1 index 1bf4bd2d1..53cd1ea98 100644 --- a/tests/Unit/DSC_SqlAGReplica.Tests.ps1 +++ b/tests/Unit/DSC_SqlAGReplica.Tests.ps1 @@ -1,24 +1,39 @@ <# .SYNOPSIS - Automated unit test for DSC_SqlAGReplica DSC resource. - + Unit test for DSC_SqlAGReplica DSC resource. #> -return -Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '..\TestHelpers\CommonTestHelper.psm1') -$script:dscModuleName = 'SqlServerDsc' -$script:dscResourceName = 'DSC_SqlAGReplica' +# Suppressing this rule because Script Analyzer does not understand Pester's syntax. +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +# Suppressing this rule because tests are mocking passwords in clear text. +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +param () -function Invoke-TestSetup -{ +BeforeDiscovery { try { - Import-Module -Name DscResource.Test -Force -ErrorAction 'Stop' + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. + if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) + { + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null + } + + # If the dependencies has not been resolved, this will throw an error. + Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' + } } catch [System.IO.FileNotFoundException] { - throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' } +} + +BeforeAll { + $script:dscModuleName = 'SqlServerDsc' + $script:dscResourceName = 'DSC_SqlAGReplica' $script:testEnvironment = Initialize-TestEnvironment ` -DSCModuleName $script:dscModuleName ` @@ -26,337 +41,288 @@ function Invoke-TestSetup -ResourceType 'Mof' ` -TestType 'Unit' + Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '..\TestHelpers\CommonTestHelper.psm1') + # Loading mocked classes Add-Type -Path (Join-Path -Path (Join-Path -Path $PSScriptRoot -ChildPath 'Stubs') -ChildPath 'SMO.cs') - # Load the default SQL Module stub - Import-SQLModuleStub -} + # Load the correct SQL Module stub + $script:stubModuleName = Import-SQLModuleStub -PassThru -function Invoke-TestCleanup -{ - Restore-TestEnvironment -TestEnvironment $script:testEnvironment + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscResourceName } -Invoke-TestSetup - -try -{ - InModuleScope $script:dscResourceName { - - #region parameter mocks +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') - $mockServerName = 'Server1' - $mockInstanceName = 'MSSQLSERVER' - $mockPrimaryReplicaServerName = 'Server2' - $mockPrimaryReplicaInstanceName = 'MSSQLSERVER' - $mockAvailabilityGroupName = 'AG_AllServers' - $mockAvailabilityGroupReplicaName = $mockServerName - $mockEnsure = 'Present' - $mockAvailabilityMode = 'AsynchronousCommit' - $mockBackupPriority = 50 - $mockConnectionModeInPrimaryRole = 'AllowAllConnections' - $mockConnectionModeInSecondaryRole = 'AllowNoConnections' - $mockEndpointHostName = $mockServerName - $mockFailoverMode = 'Manual' - $mockReadOnlyRoutingConnectionUrl = "TCP://$($mockServerName).domain.com:1433" - $mockReadOnlyRoutingList = @('Server1', 'Server2') - $mockProcessOnlyOnActiveNode = $false - - #endregion + Restore-TestEnvironment -TestEnvironment $script:testEnvironment - #region server mock variables + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscResourceName -All | Remove-Module -Force - $mockServer1Name = 'Server1' - $mockServer1NetName = $mockServer1Name - $mockServer1IsHadrEnabled = $true - $mockServer1ServiceName = 'MSSQLSERVER' + # Unload the stub module. + Remove-SqlModuleStub -Name $script:stubModuleName - $mockServer2Name = 'Server2' - $mockServer2NetName = $mockServer1Name - $mockServer2IsHadrEnabled = $true - $mockServer2ServiceName = $mockServer1ServiceName + # Remove module common test helper. + Get-Module -Name 'CommonTestHelper' -All | Remove-Module -Force +} - $mockServer3Name = 'Server3' - $mockServer3NetName = $mockServer3Name - $mockServer3IsHadrEnabled = $true - $mockServer3ServiceName = $mockServer1ServiceName +Describe 'SqlAGReplica\Get-TargetResource' { + BeforeAll { + $mockConnectSqlServer1 = { + # Mock the server object + $mockServerObject = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server + $mockServerObject.Name = 'Server1' + $mockServerObject.NetName = 'Server1' + $mockServerObject.IsHadrEnabled = $true + $mockServerObject.ServiceName = 'MSSQLSERVER' - #endregion + # Mock the availability group replicas + $mockAvailabilityGroupReplica1 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityReplica + $mockAvailabilityGroupReplica1.AvailabilityMode = 'AsynchronousCommit' + $mockAvailabilityGroupReplica1.BackupPriority = 50 + $mockAvailabilityGroupReplica1.ConnectionModeInPrimaryRole = 'AllowAllConnections' + $mockAvailabilityGroupReplica1.ConnectionModeInSecondaryRole = 'AllowNoConnections' + $mockAvailabilityGroupReplica1.EndpointUrl = 'TCP://Server1:5022' + $mockAvailabilityGroupReplica1.FailoverMode = 'Manual' + $mockAvailabilityGroupReplica1.Name = 'Server1' + $mockAvailabilityGroupReplica1.ReadOnlyRoutingConnectionUrl = 'TCP://Server1.domain.com:1433' + $mockAvailabilityGroupReplica1.ReadOnlyRoutingList = @('Server1', 'Server2') - #region Login mocks + $mockAvailabilityGroupReplica2 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityReplica + $mockAvailabilityGroupReplica2.AvailabilityMode = 'AsynchronousCommit' + $mockAvailabilityGroupReplica2.BackupPriority = 50 + $mockAvailabilityGroupReplica2.ConnectionModeInPrimaryRole = 'AllowAllConnections' + $mockAvailabilityGroupReplica2.ConnectionModeInSecondaryRole = 'AllowNoConnections' + $mockAvailabilityGroupReplica2.EndpointUrl = 'TCP://Server2:5022' + $mockAvailabilityGroupReplica2.FailoverMode = 'Manual' + $mockAvailabilityGroupReplica2.Name = 'Server2' + $mockAvailabilityGroupReplica2.ReadOnlyRoutingConnectionUrl = 'TCP://Server2.domain.com:1433' + $mockAvailabilityGroupReplica2.ReadOnlyRoutingList = @('Server1', 'Server2') - $mockLogins = @{} # Will be dynamically set during tests + $mockAvailabilityGroupReplica3 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityReplica + $mockAvailabilityGroupReplica3.AvailabilityMode = 'AsynchronousCommit' + $mockAvailabilityGroupReplica3.BackupPriority = 50 + $mockAvailabilityGroupReplica3.ConnectionModeInPrimaryRole = 'AllowAllConnections' + $mockAvailabilityGroupReplica3.ConnectionModeInSecondaryRole = 'AllowNoConnections' + $mockAvailabilityGroupReplica3.EndpointUrl = 'TCP://Server3:5022' + $mockAvailabilityGroupReplica3.FailoverMode = 'Manual' + $mockAvailabilityGroupReplica3.Name = 'Server3' + $mockAvailabilityGroupReplica3.ReadOnlyRoutingConnectionUrl = 'TCP://Server3.domain.com:1433' + $mockAvailabilityGroupReplica3.ReadOnlyRoutingList = @('Server1', 'Server2') - $mockNtServiceClusSvcName = 'NT SERVICE\ClusSvc' - $mockNtAuthoritySystemName = 'NT AUTHORITY\SYSTEM' + # Mock the availability groups + $mockAvailabilityGroup1 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityGroup + $mockAvailabilityGroup1.Name = 'AG_AllServers' + $mockAvailabilityGroup1.PrimaryReplicaServerName = 'Server2' + $mockAvailabilityGroup1.LocalReplicaRole = 'Secondary' + $mockAvailabilityGroup1.AvailabilityReplicas.Add($mockAvailabilityGroupReplica1) + $mockAvailabilityGroup1.AvailabilityReplicas.Add($mockAvailabilityGroupReplica2) + $mockAvailabilityGroup1.AvailabilityReplicas.Add($mockAvailabilityGroupReplica3) - $mockAllLoginsAbsent = @{} + $mockServerObject.AvailabilityGroups.Add($mockAvailabilityGroup1) - $mockNtServiceClusSvcPresent = @{ - $mockNtServiceClusSvcName = ( New-Object -TypeName Microsoft.SqlServer.Management.Smo.Login($mockServerName, $mockNtServiceClusSvcName) ) - } + $mockEndpoint = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Endpoint + $mockEndpoint.EndpointType = 'DatabaseMirroring' + $mockEndpoint.Protocol = @{ + TCP = @{ + ListenerPort = 5022 + } + } - $mockNtAuthoritySystemPresent = @{ - $mockNtAuthoritySystemName = ( New-Object -TypeName Microsoft.SqlServer.Management.Smo.Login($mockServerName, $mockNtAuthoritySystemName) ) - } + $mockServerObject.Endpoints.Add($mockEndpoint) - $mockAllLoginsPresent = @{ - $mockNtServiceClusSvcName = ( New-Object -TypeName Microsoft.SqlServer.Management.Smo.Login($mockServerName, $mockNtServiceClusSvcName) ) - $mockNtAuthoritySystemName = ( New-Object -TypeName Microsoft.SqlServer.Management.Smo.Login($mockServerName, $mockNtAuthoritySystemName) ) + return $mockServerObject } - #endregion - - #region Endpoint mock variables - - $mockEndpointPort = 5022 - - #endregion Endpoint mock variables - - #region Availability Group mock variables - - $mockAvailabilityGroup1Name = 'AG_AllServers' - $mockAvailabilityGroup1PrimaryReplicaServer = $mockServer2Name - - $mockAvailabilityGroup2Name = 'AG_PrimaryOnServer2' - $mockAvailabilityGroup2PrimaryReplicaServer = $mockServer2Name - - $mockAvailabilityGroup3Name = 'AG_PrimaryOnServer3' - $mockAvailabilityGroup3PrimaryReplicaServer = $mockServer3Name - - #endregion - - #region Availability Group Replica mock variables + Mock -CommandName Connect-SQL -MockWith $mockConnectSqlServer1 + } - $mockAlternateEndpointPort = $false - $mockAlternateEndpointProtocol = $false + Context 'When the Availability Group Replica is absent' { + It 'Should not return an Availability Group Replica' { + InModuleScope -ScriptBlock { + $getTargetResourceParameters = @{ + Name = 'Server1' + AvailabilityGroupName = 'AbsentAG' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + } - $mockAvailabilityGroupReplica1Name = $mockServer1Name - $mockAvailabilityGroupReplica1AvailabilityMode = 'AsynchronousCommit' - $mockAvailabilityGroupReplica1BackupPriority = 50 - $mockAvailabilityGroupReplica1ConnectionModeInPrimaryRole = 'AllowAllConnections' - $mockAvailabilityGroupReplica1ConnectionModeInSecondaryRole = 'AllowNoConnections' - $mockAvailabilityGroupReplica1EndpointProtocol = 'TCP' - $mockAvailabilityGroupReplica1EndpointPort = $mockEndpointPort - $mockAvailabilityGroupReplica1EndpointUrl = "$($mockAvailabilityGroupReplica1EndpointProtocol)://$($mockServer1Name):$($mockAvailabilityGroupReplica1EndpointPort)" - $mockAvailabilityGroupReplica1FailoverMode = 'Manual' - $mockAvailabilityGroupReplica1ReadOnlyRoutingConnectionUrl = "TCP://$($mockServer1Name).domain.com:1433" - $mockAvailabilityGroupReplica1ReadOnlyRoutingList = $mockReadOnlyRoutingList + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + + $getTargetResourceResult.AvailabilityGroupName | Should -Be 'AbsentAG' + $getTargetResourceResult.AvailabilityMode | Should -BeNullOrEmpty + $getTargetResourceResult.BackupPriority | Should -BeNullOrEmpty + $getTargetResourceResult.ConnectionModeInPrimaryRole | Should -BeNullOrEmpty + $getTargetResourceResult.ConnectionModeInSecondaryRole | Should -BeNullOrEmpty + $getTargetResourceResult.EndpointUrl | Should -BeNullOrEmpty + $getTargetResourceResult.EndpointPort | Should -Be 5022 + $getTargetResourceResult.Ensure | Should -Be 'Absent' + $getTargetResourceResult.FailoverMode | Should -BeNullOrEmpty + $getTargetResourceResult.Name | Should -Be 'Server1' + $getTargetResourceResult.ReadOnlyRoutingConnectionUrl | Should -BeNullOrEmpty + $getTargetResourceResult.ReadOnlyRoutingList | Should -BeNullOrEmpty + $getTargetResourceResult.ServerName | Should -Be 'Server1' + $getTargetResourceResult.InstanceName | Should -Be 'MSSQLSERVER' + $getTargetResourceResult.EndpointHostName | Should -Be 'Server1' + } - $mockAvailabilityGroupReplica2Name = $mockServer2Name - $mockAvailabilityGroupReplica2AvailabilityMode = 'AsynchronousCommit' - $mockAvailabilityGroupReplica2BackupPriority = 50 - $mockAvailabilityGroupReplica2ConnectionModeInPrimaryRole = 'AllowAllConnections' - $mockAvailabilityGroupReplica2ConnectionModeInSecondaryRole = 'AllowNoConnections' - $mockAvailabilityGroupReplica2EndpointProtocol = 'TCP' - $mockAvailabilityGroupReplica2EndpointPort = $mockEndpointPort - $mockAvailabilityGroupReplica2EndpointUrl = "$($mockAvailabilityGroupReplica2EndpointProtocol)://$($mockServer2Name):$($mockAvailabilityGroupReplica2EndpointPort)" - $mockAvailabilityGroupReplica2FailoverMode = 'Manual' - $mockAvailabilityGroupReplica2ReadOnlyRoutingConnectionUrl = "TCP://$($mockServer2Name).domain.com:1433" - $mockAvailabilityGroupReplica2ReadOnlyRoutingList = $mockReadOnlyRoutingList + Should -Invoke -CommandName Connect-SQL -Exactly -Times 1 -Scope It + } + } - $mockAvailabilityGroupReplica3Name = $mockServer3Name - $mockAvailabilityGroupReplica3AvailabilityMode = 'AsynchronousCommit' - $mockAvailabilityGroupReplica3BackupPriority = 50 - $mockAvailabilityGroupReplica3ConnectionModeInPrimaryRole = 'AllowAllConnections' - $mockAvailabilityGroupReplica3ConnectionModeInSecondaryRole = 'AllowNoConnections' - $mockAvailabilityGroupReplica3EndpointProtocol = 'TCP' - $mockAvailabilityGroupReplica3EndpointPort = $mockEndpointPort - $mockAvailabilityGroupReplica3EndpointUrl = "$($mockAvailabilityGroupReplica3EndpointProtocol)://$($mockServer3Name):$($mockAvailabilityGroupReplica3EndpointPort)" - $mockAvailabilityGroupReplica3FailoverMode = 'Manual' - $mockAvailabilityGroupReplica3ReadOnlyRoutingConnectionUrl = "TCP://$($mockServer3Name).domain.com:1433" - $mockAvailabilityGroupReplica3ReadOnlyRoutingList = $mockReadOnlyRoutingList + Context 'When the Availability Group Replica is present' { + It 'Should return an Availability Group Replica' { + InModuleScope -ScriptBlock { + $getTargetResourceParameters = @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + } - #endregion + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + + $getTargetResourceResult.AvailabilityGroupName | Should -Be 'AG_AllServers' + $getTargetResourceResult.AvailabilityMode | Should -Be 'AsynchronousCommit' + $getTargetResourceResult.BackupPriority | Should -Be 50 + $getTargetResourceResult.ConnectionModeInPrimaryRole | Should -Be 'AllowAllConnections' + $getTargetResourceResult.ConnectionModeInSecondaryRole | Should -Be 'AllowNoConnections' + $getTargetResourceResult.EndpointUrl | Should -Be 'TCP://Server1:5022' + $getTargetResourceResult.EndpointPort | Should -Be 5022 + $getTargetResourceResult.Ensure | Should -Be 'Present' + $getTargetResourceResult.FailoverMode | Should -Be 'Manual' + $getTargetResourceResult.Name | Should -Be 'Server1' + $getTargetResourceResult.ReadOnlyRoutingConnectionUrl | Should -Be 'TCP://Server1.domain.com:1433' + $getTargetResourceResult.ReadOnlyRoutingList | Should -Be @('Server1', 'Server2') + $getTargetResourceResult.ServerName | Should -Be 'Server1' + $getTargetResourceResult.InstanceName | Should -Be 'MSSQLSERVER' + $getTargetResourceResult.EndpointHostName | Should -Be 'Server1' + } - #region Function mocks + Should -Invoke -CommandName Connect-SQL -Exactly -Times 1 -Scope It + } + } +} +Describe 'SqlAGReplica\Set-TargetResource' { + BeforeAll { $mockConnectSqlServer1 = { - param - ( - [Parameter()] - [System.String] - $ServerName, - - [Parameter()] - [System.String] - $InstanceName, - - # The following two parameters are used to mock Get-PrimaryReplicaServerObject - [Parameter()] - [Microsoft.SqlServer.Management.Smo.AvailabilityGroup] - $AvailabilityGroup, - - [Parameter()] - [Microsoft.SqlServer.Management.Smo.Server] - $ServerObject - ) - # Mock the server object $mockServerObject = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server - $mockServerObject.Name = $mockServer1Name - $mockServerObject.NetName = $mockServer1NetName - $mockServerObject.IsHadrEnabled = $mockServer1IsHadrEnabled - $mockServerObject.Logins = $mockLogins - $mockServerObject.ServiceName = $mockServer1ServiceName + $mockServerObject.Name = 'Server1' + $mockServerObject.NetName = 'Server1' + $mockServerObject.IsHadrEnabled = $true + $mockServerObject.ServiceName = 'MSSQLSERVER' # Mock the availability group replicas $mockAvailabilityGroupReplica1 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityReplica - $mockAvailabilityGroupReplica1.AvailabilityMode = $mockAvailabilityGroupReplica1AvailabilityMode - $mockAvailabilityGroupReplica1.BackupPriority = $mockAvailabilityGroupReplica1BackupPriority - $mockAvailabilityGroupReplica1.ConnectionModeInPrimaryRole = $mockAvailabilityGroupReplica1ConnectionModeInPrimaryRole - $mockAvailabilityGroupReplica1.ConnectionModeInSecondaryRole = $mockAvailabilityGroupReplica1ConnectionModeInSecondaryRole - $mockAvailabilityGroupReplica1.EndpointUrl = $mockAvailabilityGroupReplica1EndpointUrl - $mockAvailabilityGroupReplica1.FailoverMode = $mockAvailabilityGroupReplica1FailoverMode - $mockAvailabilityGroupReplica1.Name = $mockAvailabilityGroupReplica1Name - $mockAvailabilityGroupReplica1.ReadOnlyRoutingConnectionUrl = $mockAvailabilityGroupReplica1ReadOnlyRoutingConnectionUrl - $mockAvailabilityGroupReplica1.ReadOnlyRoutingList = $mockAvailabilityGroupReplica1ReadOnlyRoutingList + $mockAvailabilityGroupReplica1.AvailabilityMode = 'AsynchronousCommit' + $mockAvailabilityGroupReplica1.BackupPriority = 50 + $mockAvailabilityGroupReplica1.ConnectionModeInPrimaryRole = 'AllowAllConnections' + $mockAvailabilityGroupReplica1.ConnectionModeInSecondaryRole = 'AllowNoConnections' + $mockAvailabilityGroupReplica1.EndpointUrl = 'TCP://Server1:5022' + $mockAvailabilityGroupReplica1.FailoverMode = 'Manual' + $mockAvailabilityGroupReplica1.Name = 'Server1' + $mockAvailabilityGroupReplica1.ReadOnlyRoutingConnectionUrl = 'TCP://Server1.domain.com:1433' + $mockAvailabilityGroupReplica1.ReadOnlyRoutingList = @('Server1', 'Server2') $mockAvailabilityGroupReplica2 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityReplica - $mockAvailabilityGroupReplica2.AvailabilityMode = $mockAvailabilityGroupReplica2AvailabilityMode - $mockAvailabilityGroupReplica2.BackupPriority = $mockAvailabilityGroupReplica2BackupPriority - $mockAvailabilityGroupReplica2.ConnectionModeInPrimaryRole = $mockAvailabilityGroupReplica2ConnectionModeInPrimaryRole - $mockAvailabilityGroupReplica2.ConnectionModeInSecondaryRole = $mockAvailabilityGroupReplica2ConnectionModeInSecondaryRole - $mockAvailabilityGroupReplica2.EndpointUrl = $mockAvailabilityGroupReplica2EndpointUrl - $mockAvailabilityGroupReplica2.FailoverMode = $mockAvailabilityGroupReplica2FailoverMode - $mockAvailabilityGroupReplica2.Name = $mockAvailabilityGroupReplica2Name - $mockAvailabilityGroupReplica2.ReadOnlyRoutingConnectionUrl = $mockAvailabilityGroupReplica2ReadOnlyRoutingConnectionUrl - $mockAvailabilityGroupReplica2.ReadOnlyRoutingList = $mockAvailabilityGroupReplica2ReadOnlyRoutingList + $mockAvailabilityGroupReplica2.AvailabilityMode = 'AsynchronousCommit' + $mockAvailabilityGroupReplica2.BackupPriority = 50 + $mockAvailabilityGroupReplica2.ConnectionModeInPrimaryRole = 'AllowAllConnections' + $mockAvailabilityGroupReplica2.ConnectionModeInSecondaryRole = 'AllowNoConnections' + $mockAvailabilityGroupReplica2.EndpointUrl = 'TCP://Server2:5022' + $mockAvailabilityGroupReplica2.FailoverMode = 'Manual' + $mockAvailabilityGroupReplica2.Name = 'Server2' + $mockAvailabilityGroupReplica2.ReadOnlyRoutingConnectionUrl = 'TCP://Server2.domain.com:1433' + $mockAvailabilityGroupReplica2.ReadOnlyRoutingList = @('Server1', 'Server2') $mockAvailabilityGroupReplica3 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityReplica - $mockAvailabilityGroupReplica3.AvailabilityMode = $mockAvailabilityGroupReplica3AvailabilityMode - $mockAvailabilityGroupReplica3.BackupPriority = $mockAvailabilityGroupReplica3BackupPriority - $mockAvailabilityGroupReplica3.ConnectionModeInPrimaryRole = $mockAvailabilityGroupReplica3ConnectionModeInPrimaryRole - $mockAvailabilityGroupReplica3.ConnectionModeInSecondaryRole = $mockAvailabilityGroupReplica3ConnectionModeInSecondaryRole - $mockAvailabilityGroupReplica3.EndpointUrl = $mockAvailabilityGroupReplica3EndpointUrl - $mockAvailabilityGroupReplica3.FailoverMode = $mockAvailabilityGroupReplica3FailoverMode - $mockAvailabilityGroupReplica3.Name = $mockAvailabilityGroupReplica3Name - $mockAvailabilityGroupReplica3.ReadOnlyRoutingConnectionUrl = $mockAvailabilityGroupReplica3ReadOnlyRoutingConnectionUrl - $mockAvailabilityGroupReplica3.ReadOnlyRoutingList = $mockAvailabilityGroupReplica3ReadOnlyRoutingList - - if ( $mockAlternateEndpointPort ) - { - $mockAvailabilityGroupReplica1.EndpointUrl = $mockAvailabilityGroupReplica1EndpointUrl.Replace($mockAvailabilityGroupReplica1EndpointPort, '1234') - $mockAvailabilityGroupReplica2.EndpointUrl = $mockAvailabilityGroupReplica2EndpointUrl.Replace($mockAvailabilityGroupReplica2EndpointPort, '1234') - $mockAvailabilityGroupReplica3.EndpointUrl = $mockAvailabilityGroupReplica3EndpointUrl.Replace($mockAvailabilityGroupReplica3EndpointPort, '1234') - } - - if ( $mockAlternateEndpointProtocol ) - { - $mockAvailabilityGroupReplica1.EndpointUrl = $mockAvailabilityGroupReplica1EndpointUrl.Replace($mockAvailabilityGroupReplica1EndpointProtocol, 'UDP') - $mockAvailabilityGroupReplica2.EndpointUrl = $mockAvailabilityGroupReplica2EndpointUrl.Replace($mockAvailabilityGroupReplica2EndpointProtocol, 'UDP') - $mockAvailabilityGroupReplica3.EndpointUrl = $mockAvailabilityGroupReplica3EndpointUrl.Replace($mockAvailabilityGroupReplica3EndpointProtocol, 'UDP') - } + $mockAvailabilityGroupReplica3.AvailabilityMode = 'AsynchronousCommit' + $mockAvailabilityGroupReplica3.BackupPriority = 50 + $mockAvailabilityGroupReplica3.ConnectionModeInPrimaryRole = 'AllowAllConnections' + $mockAvailabilityGroupReplica3.ConnectionModeInSecondaryRole = 'AllowNoConnections' + $mockAvailabilityGroupReplica3.EndpointUrl = 'TCP://Server3:5022' + $mockAvailabilityGroupReplica3.FailoverMode = 'Manual' + $mockAvailabilityGroupReplica3.Name = 'Server3' + $mockAvailabilityGroupReplica3.ReadOnlyRoutingConnectionUrl = 'TCP://Server3.domain.com:1433' + $mockAvailabilityGroupReplica3.ReadOnlyRoutingList = @('Server1', 'Server2') # Mock the availability groups $mockAvailabilityGroup1 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityGroup - $mockAvailabilityGroup1.Name = $mockAvailabilityGroup1Name - $mockAvailabilityGroup1.PrimaryReplicaServerName = $mockAvailabilityGroup1PrimaryReplicaServer + $mockAvailabilityGroup1.Name = 'AG_AllServers' + $mockAvailabilityGroup1.PrimaryReplicaServerName = 'Server2' $mockAvailabilityGroup1.LocalReplicaRole = 'Secondary' $mockAvailabilityGroup1.AvailabilityReplicas.Add($mockAvailabilityGroupReplica1) $mockAvailabilityGroup1.AvailabilityReplicas.Add($mockAvailabilityGroupReplica2) $mockAvailabilityGroup1.AvailabilityReplicas.Add($mockAvailabilityGroupReplica3) $mockServerObject.AvailabilityGroups.Add($mockAvailabilityGroup1) - # Mock the mirroring endpoint if required - if ( $mockDatabaseMirroringEndpoint ) - { - $mockEndpoint = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Endpoint - $mockEndpoint.EndpointType = 'DatabaseMirroring' - $mockEndpoint.Protocol = @{ - TCP = @{ - ListenerPort = $mockendpointPort - } + $mockEndpoint = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Endpoint + $mockEndpoint.EndpointType = 'DatabaseMirroring' + $mockEndpoint.Protocol = @{ + TCP = @{ + ListenerPort = 5022 } - $mockServerObject.Endpoints.Add($mockEndpoint) } + $mockServerObject.Endpoints.Add($mockEndpoint) + return $mockServerObject } $mockConnectSqlServer2 = { - param - ( - [Parameter()] - [System.String] - $ServerName, - - [Parameter()] - [System.String] - $InstanceName, - - # The following two parameters are used to mock Get-PrimaryReplicaServerObject - [Parameter()] - [Microsoft.SqlServer.Management.Smo.AvailabilityGroup] - $AvailabilityGroup, - - [Parameter()] - [Microsoft.SqlServer.Management.Smo.Server] - $ServerObject - ) - # Mock the server object $mockServerObject = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server - $mockServerObject.Name = $mockServer2Name - $mockServerObject.NetName = $mockServer2NetName - $mockServerObject.IsHadrEnabled = $mockServer2IsHadrEnabled - $mockServerObject.Logins = $mockLogins - $mockServerObject.ServiceName = $mockServer2ServiceName + $mockServerObject.Name = 'Server2' + $mockServerObject.NetName = 'Server2' + $mockServerObject.IsHadrEnabled = $true + $mockServerObject.ServiceName = 'MSSQLSERVER' #region Mock the availability group replicas $mockAvailabilityGroupReplica1 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityReplica - $mockAvailabilityGroupReplica1.AvailabilityMode = $mockAvailabilityGroupReplica1AvailabilityMode - $mockAvailabilityGroupReplica1.BackupPriority = $mockAvailabilityGroupReplica1BackupPriority - $mockAvailabilityGroupReplica1.ConnectionModeInPrimaryRole = $mockAvailabilityGroupReplica1ConnectionModeInPrimaryRole - $mockAvailabilityGroupReplica1.ConnectionModeInSecondaryRole = $mockAvailabilityGroupReplica1ConnectionModeInSecondaryRole - $mockAvailabilityGroupReplica1.EndpointUrl = $mockAvailabilityGroupReplica1EndpointUrl - $mockAvailabilityGroupReplica1.FailoverMode = $mockAvailabilityGroupReplica1FailoverMode - $mockAvailabilityGroupReplica1.Name = $mockAvailabilityGroupReplica1Name - $mockAvailabilityGroupReplica1.ReadOnlyRoutingConnectionUrl = $mockAvailabilityGroupReplica1ReadOnlyRoutingConnectionUrl - $mockAvailabilityGroupReplica1.ReadOnlyRoutingList = $mockAvailabilityGroupReplica1ReadOnlyRoutingList + $mockAvailabilityGroupReplica1.AvailabilityMode = 'AsynchronousCommit' + $mockAvailabilityGroupReplica1.BackupPriority = 50 + $mockAvailabilityGroupReplica1.ConnectionModeInPrimaryRole = 'AllowAllConnections' + $mockAvailabilityGroupReplica1.ConnectionModeInSecondaryRole = 'AllowNoConnections' + $mockAvailabilityGroupReplica1.EndpointUrl = 'TCP://Server1:5022' + $mockAvailabilityGroupReplica1.FailoverMode = 'Manual' + $mockAvailabilityGroupReplica1.Name = 'Server1' + $mockAvailabilityGroupReplica1.ReadOnlyRoutingConnectionUrl = 'TCP://Server1.domain.com:1433' + $mockAvailabilityGroupReplica1.ReadOnlyRoutingList = @('Server1', 'Server2') $mockAvailabilityGroupReplica2 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityReplica - $mockAvailabilityGroupReplica2.AvailabilityMode = $mockAvailabilityGroupReplica2AvailabilityMode - $mockAvailabilityGroupReplica2.BackupPriority = $mockAvailabilityGroupReplica2BackupPriority - $mockAvailabilityGroupReplica2.ConnectionModeInPrimaryRole = $mockAvailabilityGroupReplica2ConnectionModeInPrimaryRole - $mockAvailabilityGroupReplica2.ConnectionModeInSecondaryRole = $mockAvailabilityGroupReplica2ConnectionModeInSecondaryRole - $mockAvailabilityGroupReplica2.EndpointUrl = $mockAvailabilityGroupReplica2EndpointUrl - $mockAvailabilityGroupReplica2.FailoverMode = $mockAvailabilityGroupReplica2FailoverMode - $mockAvailabilityGroupReplica2.Name = $mockAvailabilityGroupReplica2Name - $mockAvailabilityGroupReplica2.ReadOnlyRoutingConnectionUrl = $mockAvailabilityGroupReplica2ReadOnlyRoutingConnectionUrl - $mockAvailabilityGroupReplica2.ReadOnlyRoutingList = $mockAvailabilityGroupReplica2ReadOnlyRoutingList + $mockAvailabilityGroupReplica2.AvailabilityMode = 'AsynchronousCommit' + $mockAvailabilityGroupReplica2.BackupPriority = 50 + $mockAvailabilityGroupReplica2.ConnectionModeInPrimaryRole = 'AllowAllConnections' + $mockAvailabilityGroupReplica2.ConnectionModeInSecondaryRole = 'AllowNoConnections' + $mockAvailabilityGroupReplica2.EndpointUrl = 'TCP://Server2:5022' + $mockAvailabilityGroupReplica2.FailoverMode = 'Manual' + $mockAvailabilityGroupReplica2.Name = 'Server2' + $mockAvailabilityGroupReplica2.ReadOnlyRoutingConnectionUrl = 'TCP://Server2.domain.com:1433' + $mockAvailabilityGroupReplica2.ReadOnlyRoutingList = @('Server1', 'Server2') $mockAvailabilityGroupReplica3 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityReplica - $mockAvailabilityGroupReplica3.AvailabilityMode = $mockAvailabilityGroupReplica3AvailabilityMode - $mockAvailabilityGroupReplica3.BackupPriority = $mockAvailabilityGroupReplica3BackupPriority - $mockAvailabilityGroupReplica3.ConnectionModeInPrimaryRole = $mockAvailabilityGroupReplica3ConnectionModeInPrimaryRole - $mockAvailabilityGroupReplica3.ConnectionModeInSecondaryRole = $mockAvailabilityGroupReplica3ConnectionModeInSecondaryRole - $mockAvailabilityGroupReplica3.EndpointUrl = $mockAvailabilityGroupReplica3EndpointUrl - $mockAvailabilityGroupReplica3.FailoverMode = $mockAvailabilityGroupReplica3FailoverMode - $mockAvailabilityGroupReplica3.Name = $mockAvailabilityGroupReplica3Name - $mockAvailabilityGroupReplica3.ReadOnlyRoutingConnectionUrl = $mockAvailabilityGroupReplica3ReadOnlyRoutingConnectionUrl - $mockAvailabilityGroupReplica3.ReadOnlyRoutingList = $mockAvailabilityGroupReplica3ReadOnlyRoutingList + $mockAvailabilityGroupReplica3.AvailabilityMode = 'AsynchronousCommit' + $mockAvailabilityGroupReplica3.BackupPriority = 50 + $mockAvailabilityGroupReplica3.ConnectionModeInPrimaryRole = 'AllowAllConnections' + $mockAvailabilityGroupReplica3.ConnectionModeInSecondaryRole = 'AllowNoConnections' + $mockAvailabilityGroupReplica3.EndpointUrl = 'TCP://Server3:5022' + $mockAvailabilityGroupReplica3.FailoverMode = 'Manual' + $mockAvailabilityGroupReplica3.Name = 'Server3' + $mockAvailabilityGroupReplica3.ReadOnlyRoutingConnectionUrl = 'TCP://Server3.domain.com:1433' + $mockAvailabilityGroupReplica3.ReadOnlyRoutingList = @('Server1', 'Server2') #endregion Mock the availability group replicas - if ( $mockAlternateEndpointPort ) - { - $mockAvailabilityGroupReplica1.EndpointUrl = $mockAvailabilityGroupReplica1EndpointUrl.Replace($mockAvailabilityGroupReplica1EndpointPort, '1234') - $mockAvailabilityGroupReplica2.EndpointUrl = $mockAvailabilityGroupReplica2EndpointUrl.Replace($mockAvailabilityGroupReplica2EndpointPort, '1234') - $mockAvailabilityGroupReplica3.EndpointUrl = $mockAvailabilityGroupReplica3EndpointUrl.Replace($mockAvailabilityGroupReplica3EndpointPort, '1234') - } - - if ( $mockAlternateEndpointProtocol ) - { - $mockAvailabilityGroupReplica1.EndpointUrl = $mockAvailabilityGroupReplica1EndpointUrl.Replace($mockAvailabilityGroupReplica1EndpointProtocol, 'UDP') - $mockAvailabilityGroupReplica2.EndpointUrl = $mockAvailabilityGroupReplica2EndpointUrl.Replace($mockAvailabilityGroupReplica2EndpointProtocol, 'UDP') - $mockAvailabilityGroupReplica3.EndpointUrl = $mockAvailabilityGroupReplica3EndpointUrl.Replace($mockAvailabilityGroupReplica3EndpointProtocol, 'UDP') - } - # Mock the availability groups $mockAvailabilityGroup1 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityGroup - $mockAvailabilityGroup1.Name = $mockAvailabilityGroup1Name - $mockAvailabilityGroup1.PrimaryReplicaServerName = $mockAvailabilityGroup1PrimaryReplicaServer + $mockAvailabilityGroup1.Name = 'AG_AllServers' + $mockAvailabilityGroup1.PrimaryReplicaServerName = 'Server2' $mockAvailabilityGroup1.LocalReplicaRole = 'Primary' $mockAvailabilityGroup1.AvailabilityReplicas.Add($mockAvailabilityGroupReplica1) $mockAvailabilityGroup1.AvailabilityReplicas.Add($mockAvailabilityGroupReplica2) @@ -364,119 +330,81 @@ try $mockServerObject.AvailabilityGroups.Add($mockAvailabilityGroup1) $mockAvailabilityGroup2 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityGroup - $mockAvailabilityGroup2.Name = $mockAvailabilityGroup2Name - $mockAvailabilityGroup2.PrimaryReplicaServerName = $mockAvailabilityGroup2PrimaryReplicaServer + $mockAvailabilityGroup2.Name = 'AG_PrimaryOnServer2' + $mockAvailabilityGroup2.PrimaryReplicaServerName = 'Server2' $mockAvailabilityGroup2.LocalReplicaRole = 'Primary' $mockAvailabilityGroup2.AvailabilityReplicas.Add($mockAvailabilityGroupReplica2) $mockAvailabilityGroup2.AvailabilityReplicas.Add($mockAvailabilityGroupReplica3) $mockServerObject.AvailabilityGroups.Add($mockAvailabilityGroup2) $mockAvailabilityGroup3 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityGroup - $mockAvailabilityGroup3.Name = $mockAvailabilityGroup3Name - $mockAvailabilityGroup3.PrimaryReplicaServerName = $mockAvailabilityGroup3PrimaryReplicaServer + $mockAvailabilityGroup3.Name = 'AG_PrimaryOnServer3' + $mockAvailabilityGroup3.PrimaryReplicaServerName = 'Server3' $mockAvailabilityGroup3.LocalReplicaRole = 'Secondary' $mockAvailabilityGroup3.AvailabilityReplicas.Add($mockAvailabilityGroupReplica2) $mockAvailabilityGroup3.AvailabilityReplicas.Add($mockAvailabilityGroupReplica3) $mockServerObject.AvailabilityGroups.Add($mockAvailabilityGroup3) - # Mock the mirroring endpoint if required - if ( $mockDatabaseMirroringEndpoint ) - { - $mockEndpoint = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Endpoint - $mockEndpoint.EndpointType = 'DatabaseMirroring' - $mockEndpoint.Protocol = @{ - TCP = @{ - ListenerPort = $mockendpointPort - } + $mockEndpoint = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Endpoint + $mockEndpoint.EndpointType = 'DatabaseMirroring' + $mockEndpoint.Protocol = @{ + TCP = @{ + ListenerPort = 5022 } - $mockServerObject.Endpoints.Add($mockEndpoint) } + $mockServerObject.Endpoints.Add($mockEndpoint) + return $mockServerObject } $mockConnectSqlServer3 = { - param - ( - [Parameter()] - [System.String] - $ServerName, - - [Parameter()] - [System.String] - $InstanceName, - - # The following two parameters are used to mock Get-PrimaryReplicaServerObject - [Parameter()] - [Microsoft.SqlServer.Management.Smo.AvailabilityGroup] - $AvailabilityGroup, - - [Parameter()] - [Microsoft.SqlServer.Management.Smo.Server] - $ServerObject - ) - # Mock the server object $mockServerObject = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server - $mockServerObject.Name = $mockServer3Name - $mockServerObject.NetName = $mockServer3NetName - $mockServerObject.IsHadrEnabled = $mockServer3IsHadrEnabled - $mockServerObject.Logins = $mockLogins - $mockServerObject.ServiceName = $mockServer3ServiceName + $mockServerObject.Name = 'Server3' + $mockServerObject.NetName = 'Server3' + $mockServerObject.IsHadrEnabled = $true + $mockServerObject.ServiceName = 'MSSQLSERVER' #region Mock the availability group replicas $mockAvailabilityGroupReplica1 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityReplica - $mockAvailabilityGroupReplica1.AvailabilityMode = $mockAvailabilityGroupReplica1AvailabilityMode - $mockAvailabilityGroupReplica1.BackupPriority = $mockAvailabilityGroupReplica1BackupPriority - $mockAvailabilityGroupReplica1.ConnectionModeInPrimaryRole = $mockAvailabilityGroupReplica1ConnectionModeInPrimaryRole - $mockAvailabilityGroupReplica1.ConnectionModeInSecondaryRole = $mockAvailabilityGroupReplica1ConnectionModeInSecondaryRole - $mockAvailabilityGroupReplica1.EndpointUrl = $mockAvailabilityGroupReplica1EndpointUrl - $mockAvailabilityGroupReplica1.FailoverMode = $mockAvailabilityGroupReplica1FailoverMode - $mockAvailabilityGroupReplica1.Name = $mockAvailabilityGroupReplica1Name - $mockAvailabilityGroupReplica1.ReadOnlyRoutingConnectionUrl = $mockAvailabilityGroupReplica1ReadOnlyRoutingConnectionUrl - $mockAvailabilityGroupReplica1.ReadOnlyRoutingList = $mockAvailabilityGroupReplica1ReadOnlyRoutingList + $mockAvailabilityGroupReplica1.AvailabilityMode = 'AsynchronousCommit' + $mockAvailabilityGroupReplica1.BackupPriority = 50 + $mockAvailabilityGroupReplica1.ConnectionModeInPrimaryRole = 'AllowAllConnections' + $mockAvailabilityGroupReplica1.ConnectionModeInSecondaryRole = 'AllowNoConnections' + $mockAvailabilityGroupReplica1.EndpointUrl = 'TCP://Server1:5022' + $mockAvailabilityGroupReplica1.FailoverMode = 'Manual' + $mockAvailabilityGroupReplica1.Name = 'Server1' + $mockAvailabilityGroupReplica1.ReadOnlyRoutingConnectionUrl = 'TCP://Server1.domain.com:1433' + $mockAvailabilityGroupReplica1.ReadOnlyRoutingList = @('Server1', 'Server2') $mockAvailabilityGroupReplica2 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityReplica - $mockAvailabilityGroupReplica2.AvailabilityMode = $mockAvailabilityGroupReplica2AvailabilityMode - $mockAvailabilityGroupReplica2.BackupPriority = $mockAvailabilityGroupReplica2BackupPriority - $mockAvailabilityGroupReplica2.ConnectionModeInPrimaryRole = $mockAvailabilityGroupReplica2ConnectionModeInPrimaryRole - $mockAvailabilityGroupReplica2.ConnectionModeInSecondaryRole = $mockAvailabilityGroupReplica2ConnectionModeInSecondaryRole - $mockAvailabilityGroupReplica2.EndpointUrl = $mockAvailabilityGroupReplica2EndpointUrl - $mockAvailabilityGroupReplica2.FailoverMode = $mockAvailabilityGroupReplica2FailoverMode - $mockAvailabilityGroupReplica2.Name = $mockAvailabilityGroupReplica2Name - $mockAvailabilityGroupReplica2.ReadOnlyRoutingConnectionUrl = $mockAvailabilityGroupReplica2ReadOnlyRoutingConnectionUrl - $mockAvailabilityGroupReplica2.ReadOnlyRoutingList = $mockAvailabilityGroupReplica2ReadOnlyRoutingList + $mockAvailabilityGroupReplica2.AvailabilityMode = 'AsynchronousCommit' + $mockAvailabilityGroupReplica2.BackupPriority = 50 + $mockAvailabilityGroupReplica2.ConnectionModeInPrimaryRole = 'AllowAllConnections' + $mockAvailabilityGroupReplica2.ConnectionModeInSecondaryRole = 'AllowNoConnections' + $mockAvailabilityGroupReplica2.EndpointUrl = 'TCP://Server2:5022' + $mockAvailabilityGroupReplica2.FailoverMode = 'Manual' + $mockAvailabilityGroupReplica2.Name = 'Server2' + $mockAvailabilityGroupReplica2.ReadOnlyRoutingConnectionUrl = 'TCP://Server2.domain.com:1433' + $mockAvailabilityGroupReplica2.ReadOnlyRoutingList = @('Server1', 'Server2') $mockAvailabilityGroupReplica3 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityReplica - $mockAvailabilityGroupReplica3.AvailabilityMode = $mockAvailabilityGroupReplica3AvailabilityMode - $mockAvailabilityGroupReplica3.BackupPriority = $mockAvailabilityGroupReplica3BackupPriority - $mockAvailabilityGroupReplica3.ConnectionModeInPrimaryRole = $mockAvailabilityGroupReplica3ConnectionModeInPrimaryRole - $mockAvailabilityGroupReplica3.ConnectionModeInSecondaryRole = $mockAvailabilityGroupReplica3ConnectionModeInSecondaryRole - $mockAvailabilityGroupReplica3.EndpointUrl = $mockAvailabilityGroupReplica3EndpointUrl - $mockAvailabilityGroupReplica3.FailoverMode = $mockAvailabilityGroupReplica3FailoverMode - $mockAvailabilityGroupReplica3.Name = $mockAvailabilityGroupReplica3Name - $mockAvailabilityGroupReplica3.ReadOnlyRoutingConnectionUrl = $mockAvailabilityGroupReplica3ReadOnlyRoutingConnectionUrl - $mockAvailabilityGroupReplica3.ReadOnlyRoutingList = $mockAvailabilityGroupReplica3ReadOnlyRoutingList + $mockAvailabilityGroupReplica3.AvailabilityMode = 'AsynchronousCommit' + $mockAvailabilityGroupReplica3.BackupPriority = 50 + $mockAvailabilityGroupReplica3.ConnectionModeInPrimaryRole = 'AllowAllConnections' + $mockAvailabilityGroupReplica3.ConnectionModeInSecondaryRole = 'AllowNoConnections' + $mockAvailabilityGroupReplica3.EndpointUrl = 'TCP://Server3:5022' + $mockAvailabilityGroupReplica3.FailoverMode = 'Manual' + $mockAvailabilityGroupReplica3.Name = 'Server3' + $mockAvailabilityGroupReplica3.ReadOnlyRoutingConnectionUrl = 'TCP://Server3.domain.com:1433' + $mockAvailabilityGroupReplica3.ReadOnlyRoutingList = @('Server1', 'Server2') #endregion Mock the availability group replicas - if ( $mockAlternateEndpointPort ) - { - $mockAvailabilityGroupReplica1.EndpointUrl = $mockAvailabilityGroupReplica1EndpointUrl.Replace($mockAvailabilityGroupReplica1EndpointPort, '1234') - $mockAvailabilityGroupReplica2.EndpointUrl = $mockAvailabilityGroupReplica2EndpointUrl.Replace($mockAvailabilityGroupReplica2EndpointPort, '1234') - $mockAvailabilityGroupReplica3.EndpointUrl = $mockAvailabilityGroupReplica3EndpointUrl.Replace($mockAvailabilityGroupReplica3EndpointPort, '1234') - } - - if ( $mockAlternateEndpointProtocol ) - { - $mockAvailabilityGroupReplica1.EndpointUrl = $mockAvailabilityGroupReplica1EndpointUrl.Replace($mockAvailabilityGroupReplica1EndpointProtocol, 'UDP') - $mockAvailabilityGroupReplica2.EndpointUrl = $mockAvailabilityGroupReplica2EndpointUrl.Replace($mockAvailabilityGroupReplica2EndpointProtocol, 'UDP') - $mockAvailabilityGroupReplica3.EndpointUrl = $mockAvailabilityGroupReplica3EndpointUrl.Replace($mockAvailabilityGroupReplica3EndpointProtocol, 'UDP') - } - # Mock the availability groups $mockAvailabilityGroup1 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityGroup - $mockAvailabilityGroup1.Name = $mockAvailabilityGroup1Name - $mockAvailabilityGroup1.PrimaryReplicaServerName = $mockAvailabilityGroup1PrimaryReplicaServer + $mockAvailabilityGroup1.Name = 'AG_AllServers' + $mockAvailabilityGroup1.PrimaryReplicaServerName = 'Server2' $mockAvailabilityGroup1.LocalReplicaRole = 'Secondary' $mockAvailabilityGroup1.AvailabilityReplicas.Add($mockAvailabilityGroupReplica1) $mockAvailabilityGroup1.AvailabilityReplicas.Add($mockAvailabilityGroupReplica2) @@ -484,963 +412,1242 @@ try $mockServerObject.AvailabilityGroups.Add($mockAvailabilityGroup1) $mockAvailabilityGroup2 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityGroup - $mockAvailabilityGroup2.Name = $mockAvailabilityGroup2Name - $mockAvailabilityGroup2.PrimaryReplicaServerName = $mockAvailabilityGroup2PrimaryReplicaServer + $mockAvailabilityGroup2.Name = 'AG_PrimaryOnServer2' + $mockAvailabilityGroup2.PrimaryReplicaServerName = 'Server2' $mockAvailabilityGroup2.LocalReplicaRole = 'Secondary' $mockAvailabilityGroup2.AvailabilityReplicas.Add($mockAvailabilityGroupReplica2) $mockAvailabilityGroup2.AvailabilityReplicas.Add($mockAvailabilityGroupReplica3) $mockServerObject.AvailabilityGroups.Add($mockAvailabilityGroup2) $mockAvailabilityGroup3 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityGroup - $mockAvailabilityGroup3.Name = $mockAvailabilityGroup3Name - $mockAvailabilityGroup3.PrimaryReplicaServerName = $mockAvailabilityGroup3PrimaryReplicaServer + $mockAvailabilityGroup3.Name = 'AG_PrimaryOnServer3' + $mockAvailabilityGroup3.PrimaryReplicaServerName = 'Server3' $mockAvailabilityGroup3.LocalReplicaRole = 'Primary' $mockAvailabilityGroup3.AvailabilityReplicas.Add($mockAvailabilityGroupReplica2) $mockAvailabilityGroup3.AvailabilityReplicas.Add($mockAvailabilityGroupReplica3) $mockServerObject.AvailabilityGroups.Add($mockAvailabilityGroup3) # Mock the mirroring endpoint if required - if ( $mockDatabaseMirroringEndpoint ) - { - $mockEndpoint = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Endpoint - $mockEndpoint.EndpointType = 'DatabaseMirroring' - $mockEndpoint.Protocol = @{ - TCP = @{ - ListenerPort = $mockendpointPort - } + $mockEndpoint = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Endpoint + $mockEndpoint.EndpointType = 'DatabaseMirroring' + $mockEndpoint.Protocol = @{ + TCP = @{ + ListenerPort = 5022 } - $mockServerObject.Endpoints.Add($mockEndpoint) } + $mockServerObject.Endpoints.Add($mockEndpoint) + return $mockServerObject } - $mockAvailabilityGroupReplicaPropertyName = '' # Set dynamically during runtime - $mockAvailabilityGroupReplicaPropertyValue = '' # Set dynamically during runtime - - $mockUpdateAvailabilityGroupReplica = { - param - ( - [Parameter()] - [Microsoft.SqlServer.Management.Smo.AvailabilityReplica] - $AvailabilityGroupReplica - ) - - if ( [System.String]::IsNullOrEmpty($mockAvailabilityGroupReplicaPropertyName) -and [System.String]::IsNullOrEmpty($mockAvailabilityGroupReplicaPropertyValue) ) - { - return - } + Mock -CommandName Import-SQLPSModule - if ( ( $mockAvailabilityGroupReplicaPropertyValue -join ',' ) -ne ( $AvailabilityGroupReplica.$mockAvailabilityGroupReplicaPropertyName -join ',' ) ) - { - throw - } + Mock -CommandName Connect-SQL -MockWith $mockConnectSqlServer1 -ParameterFilter { + $ServerName -eq 'Server1' } - #endregion - - Describe 'SqlAGReplica\Get-TargetResource' { - BeforeEach { - $getTargetResourceParameters = @{ - Name = $mockAvailabilityGroupReplicaName - AvailabilityGroupName = $mockAvailabilityGroupName - ServerName = $mockServerName - InstanceName = $mockInstanceName - } + Mock -CommandName Connect-SQL -MockWith $mockConnectSqlServer2 -ParameterFilter { + $ServerName -eq 'Server2' + } - $mockDatabaseMirroringEndpoint = $true + Mock -CommandName Connect-SQL -MockWith $mockConnectSqlServer3 -ParameterFilter { + $ServerName -eq 'Server3' + } - Mock -CommandName Connect-SQL -MockWith $mockConnectSqlServer1 -Verifiable - } + Mock -CommandName Get-PrimaryReplicaServerObject -MockWith $mockConnectSqlServer1 -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server1' + } - Context 'When the Availability Group Replica is absent' { + Mock -CommandName Get-PrimaryReplicaServerObject -MockWith $mockConnectSqlServer2 -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server2' + } - It 'Should not return an Availability Group Replica' { + Mock -CommandName Get-PrimaryReplicaServerObject -MockWith $mockConnectSqlServer3 -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server3' + } - $getTargetResourceParameters.Name = 'AbsentReplica' - $getTargetResourceParameters.AvailabilityGroupName = 'AbsentAG' + Mock -CommandName Join-SqlAvailabilityGroup + Mock -CommandName New-SqlAvailabilityReplica + Mock -CommandName Test-ClusterPermissions + } - $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + Context 'When the desired state is absent' { + Context 'When the availability group exist' { + BeforeAll { + Mock -CommandName Update-AvailabilityGroupReplica + Mock -CommandName Remove-SqlAvailabilityReplica + } - $getTargetResourceResult.AvailabilityGroupName | Should -BeNullOrEmpty - $getTargetResourceResult.AvailabilityMode | Should -BeNullOrEmpty - $getTargetResourceResult.BackupPriority | Should -BeNullOrEmpty - $getTargetResourceResult.ConnectionModeInPrimaryRole | Should -BeNullOrEmpty - $getTargetResourceResult.ConnectionModeInSecondaryRole | Should -BeNullOrEmpty - $getTargetResourceResult.EndpointUrl | Should -BeNullOrEmpty - $getTargetResourceResult.EndpointPort | Should -Be $mockendpointPort - $getTargetResourceResult.Ensure | Should -Be 'Absent' - $getTargetResourceResult.FailoverMode | Should -BeNullOrEmpty - $getTargetResourceResult.Name | Should -BeNullOrEmpty - $getTargetResourceResult.ReadOnlyRoutingConnectionUrl | Should -BeNullOrEmpty - $getTargetResourceResult.ReadOnlyRoutingList | Should -BeNullOrEmpty - $getTargetResourceResult.ServerName | Should -Be $mockServerName - $getTargetResourceResult.InstanceName | Should -Be $mockInstanceName - $getTargetResourceResult.EndpointHostName | Should -Be $mockServerName + It 'Should silently remove the availability group replica' { + InModuleScope -ScriptBlock { + $setTargetResourceParameters = @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + Ensure = 'Absent' + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw } - } - Context 'When the Availability Group Replica is present' { + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server1' + } -Times 1 -Exactly - $mockEnsure = 'Present' + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server2' + } -Times 0 -Exactly - It 'Should return an Availability Group Replica' { + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server3' + } -Times 0 -Exactly - $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server1' + } - $getTargetResourceResult.AvailabilityGroupName | Should -Be $mockAvailabilityGroupName - $getTargetResourceResult.AvailabilityMode | Should -Be $mockAvailabilityMode - $getTargetResourceResult.BackupPriority | Should -Be $mockBackupPriority - $getTargetResourceResult.ConnectionModeInPrimaryRole | Should -Be $mockConnectionModeInPrimaryRole - $getTargetResourceResult.ConnectionModeInSecondaryRole | Should -Be $mockConnectionModeInSecondaryRole - $getTargetResourceResult.EndpointUrl | Should -Be $mockAvailabilityGroupReplica1EndpointUrl - $getTargetResourceResult.EndpointPort | Should -Be $mockendpointPort - $getTargetResourceResult.Ensure | Should -Be $mockEnsure - $getTargetResourceResult.FailoverMode | Should -Be $mockFailoverMode - $getTargetResourceResult.Name | Should -Be $mockServerName - $getTargetResourceResult.ReadOnlyRoutingConnectionUrl | Should -Be $mockReadOnlyRoutingConnectionUrl - $getTargetResourceResult.ReadOnlyRoutingList | Should -Be $mockReadOnlyRoutingList - $getTargetResourceResult.ServerName | Should -Be $mockServerName - $getTargetResourceResult.InstanceName | Should -Be $mockInstanceName - $getTargetResourceResult.EndpointHostName | Should -Be $mockServerName + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server2' + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server3' } + + Should -Invoke -CommandName Import-SQLPSModule -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Join-SqlAvailabilityGroup -Scope It -Times 0 -Exactly + Should -Invoke -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly + Should -Invoke -CommandName Remove-SqlAvailabilityReplica -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Test-ClusterPermissions -Scope It -Times 0 -Exactly + Should -Invoke -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly } } - Describe 'SqlAGReplica\Set-TargetResource' { - + Context 'When the removal of the availability group replica fails' { BeforeAll { - Mock -CommandName Import-SQLPSModule -Verifiable + Mock -CommandName Update-AvailabilityGroupReplica + Mock -CommandName Remove-SqlAvailabilityReplica -MockWith { + throw 'RemoveAvailabilityGroupReplicaFailed' + } } - BeforeEach { - $mockDatabaseMirroringEndpoint = $true - $mockLogins = $mockNtServiceClusSvcPresent - $mockServer1IsHadrEnabled = $true + It 'Should throw the correct error (RemoveAvailabilityGroupReplicaFailed)' { + InModuleScope -ScriptBlock { + $setTargetResourceParameters = @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + Ensure = 'Absent' + } - Mock -CommandName Connect-SQL -MockWith $mockConnectSqlServer1 -Verifiable -ParameterFilter { - $ServerName -eq $mockServer1Name - } - Mock -CommandName Connect-SQL -MockWith $mockConnectSqlServer2 -Verifiable -ParameterFilter { - $ServerName -eq $mockServer2Name - } - Mock -CommandName Connect-SQL -MockWith $mockConnectSqlServer3 -Verifiable -ParameterFilter { - $ServerName -eq $mockServer3Name + $mockErrorMessage = $script:localizedData.RemoveAvailabilityGroupReplicaFailed -f $setTargetResourceParameters.Name, $setTargetResourceParameters.AvailabilityGroupName, $setTargetResourceParameters.InstanceName + + { Set-TargetResource @setTargetResourceParameters } | Should -Throw -ExpectedMessage $mockErrorMessage } - Mock -CommandName Get-PrimaryReplicaServerObject -MockWith $mockConnectSqlServer1 -Verifiable -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer1Name + + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server1' + } -Times 1 -Exactly + + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server2' + } -Times 0 -Exactly + + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server3' + } -Times 0 -Exactly + + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server1' } - Mock -CommandName Get-PrimaryReplicaServerObject -MockWith $mockConnectSqlServer2 -Verifiable -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name + + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server2' } - Mock -CommandName Get-PrimaryReplicaServerObject -MockWith $mockConnectSqlServer3 -Verifiable -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer3Name + + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server3' } - Mock -CommandName Join-SqlAvailabilityGroup -Verifiable - Mock -CommandName New-SqlAvailabilityReplica -Verifiable - Mock -CommandName Test-ClusterPermissions -MockWith { - $null - } -Verifiable + + Should -Invoke -CommandName Import-SQLPSModule -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Join-SqlAvailabilityGroup -Scope It -Times 0 -Exactly + Should -Invoke -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly + Should -Invoke -CommandName Remove-SqlAvailabilityReplica -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Test-ClusterPermissions -Scope It -Times 0 -Exactly + Should -Invoke -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly } + } + } - Context 'When the desired state is absent' { + Context 'When HADR is not enabled' { # cSpell: disable-line + BeforeAll { + $mockServerObject = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server + $mockServerObject.Name = 'ServerNotEnabled' + $mockServerObject.NetName = 'ServerNotEnabled' + $mockServerObject.IsHadrEnabled = $false + $mockServerObject.ServiceName = 'MSSQLSERVER' + + Mock -CommandName Connect-SQL -MockWith { + return $mockServerObject + } -ParameterFilter { + $ServerName -eq 'ServerNotEnabled' + } + } - BeforeAll { - Mock -CommandName Update-AvailabilityGroupReplica -Verifiable + It 'Should throw the correct error' { # cSpell: disable-line + InModuleScope -ScriptBlock { + $setTargetResourceParameters = @{ + Name = 'ServerNotEnabled' + AvailabilityGroupName = 'AG_PrimaryOnServer2' + ServerName = 'ServerNotEnabled' + InstanceName = 'MSSQLSERVER' } - BeforeEach { - $setTargetResourceParameters = @{ - Name = $mockServerName - AvailabilityGroupName = $mockAvailabilityGroupName - ServerName = $mockServerName - InstanceName = $mockInstanceName - Ensure = 'Absent' - } - } + $mockErrorRecord = Get-InvalidOperationRecord -Message ( + $script:localizedData.HadrNotEnabled # cSpell: disable-line + ) - It 'Should silently remove the availability group replica' { + { Set-TargetResource @setTargetResourceParameters } | Should -Throw -ExpectedMessage $mockErrorRecord + } - Mock -CommandName Remove-SqlAvailabilityReplica -Verifiable + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'ServerNotEnabled' + } -Times 1 -Exactly - { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + Should -Invoke -CommandName Import-SQLPSModule -Exactly -Times 1 -Scope It + } + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer1Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer2Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer3Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer1Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer3Name - } - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Join-SqlAvailabilityGroup -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly + Context 'When the database mirroring endpoint is absent' { + BeforeAll { + $mockServerObject = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server + $mockServerObject.Name = 'ServerWithoutEndpoint' + $mockServerObject.NetName = 'ServerWithoutEndpoint' + $mockServerObject.IsHadrEnabled = $true + $mockServerObject.ServiceName = 'MSSQLSERVER' + + Mock -CommandName Connect-SQL -MockWith { + return $mockServerObject + } -ParameterFilter { + $ServerName -eq 'ServerWithoutEndpoint' + } + } + + It 'Should throw the correct error' { # cSpell: disable-line + InModuleScope -ScriptBlock { + $setTargetResourceParameters = @{ + Name = 'ServerWithoutEndpoint' + AvailabilityGroupName = 'AG_PrimaryOnServer2' + ServerName = 'ServerWithoutEndpoint' + InstanceName = 'MSSQLSERVER' } - It 'Should throw the correct error (RemoveAvailabilityGroupReplicaFailed) when removing the availability group replica fails' { + $mockErrorRecord = Get-ObjectNotFoundRecord -Message ( + $script:localizedData.DatabaseMirroringEndpointNotFound -f 'ServerWithoutEndpoint\MSSQLSERVER' + ) + + { Set-TargetResource @setTargetResourceParameters } | Should -Throw -ExpectedMessage $mockErrorRecord + } - Mock -CommandName Remove-SqlAvailabilityReplica -MockWith { throw 'RemoveAvailabilityGroupReplicaFailed' } -Verifiable + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'ServerWithoutEndpoint' + } -Times 1 -Exactly - $mockErrorMessage = $script:localizedData.RemoveAvailabilityGroupReplicaFailed -f $setTargetResourceParameters.Name, $setTargetResourceParameters.AvailabilityGroupName, $setTargetResourceParameters.InstanceName - { Set-TargetResource @setTargetResourceParameters } | Should -Throw $mockErrorMessage - - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer1Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer2Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer3Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer1Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer3Name - } - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Join-SqlAvailabilityGroup -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly + Should -Invoke -CommandName Import-SQLPSModule -Exactly -Times 1 -Scope It + } + } + + Context 'When the desired state is present and the availability group is absent' { + BeforeAll { + Mock -CommandName Remove-SqlAvailabilityReplica + Mock -CommandName Update-AvailabilityGroupReplica + } + + It 'Should create the availability group replica' { + InModuleScope -ScriptBlock { + $setTargetResourceParameters = @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_PrimaryOnServer2' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + PrimaryReplicaServerName = 'Server2' + PrimaryReplicaInstanceName = 'MSSQLSERVER' + Ensure = 'Present' + AvailabilityMode = 'AsynchronousCommit' + BackupPriority = 50 + ConnectionModeInPrimaryRole = 'AllowAllConnections' + ConnectionModeInSecondaryRole = 'AllowNoConnections' + EndpointHostName = 'Server1' + FailoverMode = 'Manual' + ReadOnlyRoutingConnectionUrl = 'TCP://Server1.domain.com:1433' + ReadOnlyRoutingList = @('Server1', 'Server2') } + + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw } - Context 'When the desired state is present and the availability group is absent' { + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server1' + } -Times 1 -Exactly - BeforeAll { - Mock -CommandName Remove-SqlAvailabilityReplica -Verifiable - Mock -CommandName Update-AvailabilityGroupReplica -Verifiable - } + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server2' + } -Times 1 -Exactly - BeforeEach { - $setTargetResourceParameters = @{ - Name = $mockServerName - AvailabilityGroupName = $mockAvailabilityGroup2Name - ServerName = $mockServerName - InstanceName = $mockInstanceName - PrimaryReplicaServerName = $mockPrimaryReplicaServerName - PrimaryReplicaInstanceName = $mockPrimaryReplicaInstanceName - Ensure = $mockEnsure - AvailabilityMode = $mockAvailabilityMode - BackupPriority = $mockBackupPriority - ConnectionModeInPrimaryRole = $mockConnectionModeInPrimaryRole - ConnectionModeInSecondaryRole = $mockConnectionModeInSecondaryRole - EndpointHostName = $mockEndpointHostName - FailoverMode = $mockFailoverMode - ReadOnlyRoutingConnectionUrl = $mockReadOnlyRoutingConnectionUrl - ReadOnlyRoutingList = $mockReadOnlyRoutingList - } - } + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server3' + } -Times 0 -Exactly - It 'Should throw the correct error (HadrNotEnabled) when HADR is not enabled' { + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server1' + } - $mockServer1IsHadrEnabled = $false + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server2' + } - { Set-TargetResource @setTargetResourceParameters } | Should -Throw $script:localizedData.HadrNotEnabled + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server3' + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer1Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer2Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer3Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer1Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer3Name + Should -Invoke -CommandName Import-SQLPSModule -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Join-SqlAvailabilityGroup -Exactly -Times 1 -Scope It + Should -Invoke -CommandName New-SqlAvailabilityReplica -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 0 -Exactly + Should -Invoke -CommandName Test-ClusterPermissions -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly + } + + Context 'When the endpoint hostname is not defined' { + It 'Should create the availability group replica' { + InModuleScope -ScriptBlock { + $setTargetResourceParameters = @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_PrimaryOnServer2' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + PrimaryReplicaServerName = 'Server2' + PrimaryReplicaInstanceName = 'MSSQLSERVER' + Ensure = 'Present' + AvailabilityMode = 'AsynchronousCommit' + BackupPriority = 50 + ConnectionModeInPrimaryRole = 'AllowAllConnections' + ConnectionModeInSecondaryRole = 'AllowNoConnections' + EndpointHostName = '' + FailoverMode = 'Manual' + ReadOnlyRoutingConnectionUrl = 'TCP://Server1.domain.com:1433' + ReadOnlyRoutingList = @('Server1', 'Server2') } - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Join-SqlAvailabilityGroup -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly + + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw } - It "Should throw when the logins '$($mockNtServiceClusSvcName)' or '$($mockNtAuthoritySystemName)' are absent or do not have permissions to manage availability groups" { + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server1' + } -Times 1 -Exactly - Mock -CommandName Test-ClusterPermissions -MockWith { throw } -Verifiable + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server2' + } -Times 1 -Exactly - $mockLogins = $mockAllLoginsAbsent.Clone() + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server3' + } -Times 0 -Exactly - { Set-TargetResource @setTargetResourceParameters } | Should -Throw + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server1' + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer1Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer2Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer3Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer1Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer3Name - } - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Join-SqlAvailabilityGroup -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server2' } - It "Should create the availability group replica when '$($mockNtServiceClusSvcName)' or '$($mockNtAuthoritySystemName)' is present and has the permissions to manage availability groups" { + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server3' + } - { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + Should -Invoke -CommandName Import-SQLPSModule -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Join-SqlAvailabilityGroup -Exactly -Times 1 -Scope It + Should -Invoke -CommandName New-SqlAvailabilityReplica -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 0 -Exactly + Should -Invoke -CommandName Test-ClusterPermissions -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly + } + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer1Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer2Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer3Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer1Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer3Name + Context 'When primary replica server is incorrectly supplied and the availability group exists' { + It 'Should create the availability group replica' { + InModuleScope -ScriptBlock { + $setTargetResourceParameters = @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_PrimaryOnServer2' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + PrimaryReplicaServerName = 'Server3' + PrimaryReplicaInstanceName = 'MSSQLSERVER' + Ensure = 'Present' + AvailabilityMode = 'AsynchronousCommit' + BackupPriority = 50 + ConnectionModeInPrimaryRole = 'AllowAllConnections' + ConnectionModeInSecondaryRole = 'AllowNoConnections' + EndpointHostName = '' + FailoverMode = 'Manual' + ReadOnlyRoutingConnectionUrl = 'TCP://Server1.domain.com:1433' + ReadOnlyRoutingList = @('Server1', 'Server2') } - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Join-SqlAvailabilityGroup -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly + + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw } - It 'Should throw the correct error (DatabaseMirroringEndpointNotFound) when the database mirroring endpoint is absent' { + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server1' + } -Times 1 -Exactly - $mockDatabaseMirroringEndpoint = $false + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server2' + } -Times 0 -Exactly - $mockErrorMessage = $script:localizedData.DatabaseMirroringEndpointNotFound -f ('{0}\{1}' -f $setTargetResourceParameters.ServerName, $setTargetResourceParameters.InstanceName) + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server3' + } -Times 1 -Exactly - { Set-TargetResource @setTargetResourceParameters } | Should -Throw $mockErrorMessage + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server1' + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer1Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer2Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer3Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer1Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer3Name - } - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Join-SqlAvailabilityGroup -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server2' } - It 'Should create the availability group replica when the endpoint hostname is not defined' { + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server3' + } - $setTargetResourceParameters.EndpointHostName = '' + Should -Invoke -CommandName Import-SQLPSModule -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Join-SqlAvailabilityGroup -Exactly -Times 1 -Scope It + Should -Invoke -CommandName New-SqlAvailabilityReplica -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 0 -Exactly + Should -Invoke -CommandName Test-ClusterPermissions -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly + } + } - { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + Context 'When the availability group replica fails to create' { + BeforeAll { + Mock -CommandName New-SqlAvailabilityReplica { + throw 'Mocked error' + } + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer1Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer2Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer3Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer1Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer3Name + It 'Should throw the correct error' { + InModuleScope -ScriptBlock { + $setTargetResourceParameters = @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_PrimaryOnServer2' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + PrimaryReplicaServerName = 'Server2' + PrimaryReplicaInstanceName = 'MSSQLSERVER' + Ensure = 'Present' + AvailabilityMode = 'AsynchronousCommit' + BackupPriority = 50 + ConnectionModeInPrimaryRole = 'AllowAllConnections' + ConnectionModeInSecondaryRole = 'AllowNoConnections' + EndpointHostName = '' + FailoverMode = 'Manual' + ReadOnlyRoutingConnectionUrl = 'TCP://Server1.domain.com:1433' + ReadOnlyRoutingList = @('Server1', 'Server2') } - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Join-SqlAvailabilityGroup -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly + + $mockErrorRecord = Get-InvalidOperationRecord -Message ( + # Adding wildcard at the end of string so Pester ignores additional messages in the error message (e.g. the string 'Mocked error') + ($script:localizedData.FailedCreateAvailabilityGroupReplica -f 'Server1', 'AG_PrimaryOnServer2', 'MSSQLSERVER') + '*' + ) + + { Set-TargetResource @setTargetResourceParameters } | Should -Throw -ExpectedMessage $mockErrorRecord } - It 'Should create the availability group replica when primary replica server is incorrectly supplied and the availability group exists' { + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server1' + } -Times 1 -Exactly - $setTargetResourceParameters.PrimaryReplicaServerName = $mockServer3Name + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server2' + } -Times 1 -Exactly - { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server3' + } -Times 0 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer1Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer2Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer3Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer1Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer3Name - } - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Join-SqlAvailabilityGroup -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server1' } - It 'Should throw the correct error when the availability group replica fails to create' { + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server2' + } - Mock -CommandName New-SqlAvailabilityReplica { throw } -Verifiable + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server3' + } - $mockErrorMessage = $script:localizedData.FailedCreateAvailabilityGroupReplica -f $setTargetResourceParameters.Name, $setTargetResourceParameters.AvailabilityGroupName, $setTargetResourceParameters.InstanceName + Should -Invoke -CommandName Import-SQLPSModule -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Join-SqlAvailabilityGroup -Scope It -Times 0 -Exactly + Should -Invoke -CommandName New-SqlAvailabilityReplica -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 0 -Exactly + Should -Invoke -CommandName Test-ClusterPermissions -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly + } + } - { Set-TargetResource @setTargetResourceParameters } | Should -Throw $mockErrorMessage + Context 'When the availability group replica fails to join the availability group' { + BeforeAll { + Mock -CommandName Join-SqlAvailabilityGroup { + throw 'Mocked error' + } + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer1Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer2Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer3Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer1Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer3Name + It 'Should throw the correct error' { + InModuleScope -ScriptBlock { + $setTargetResourceParameters = @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_PrimaryOnServer2' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + PrimaryReplicaServerName = 'Server2' + PrimaryReplicaInstanceName = 'MSSQLSERVER' + Ensure = 'Present' + AvailabilityMode = 'AsynchronousCommit' + BackupPriority = 50 + ConnectionModeInPrimaryRole = 'AllowAllConnections' + ConnectionModeInSecondaryRole = 'AllowNoConnections' + EndpointHostName = '' + FailoverMode = 'Manual' + ReadOnlyRoutingConnectionUrl = 'TCP://Server1.domain.com:1433' + ReadOnlyRoutingList = @('Server1', 'Server2') } - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Join-SqlAvailabilityGroup -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly + + $mockErrorRecord = Get-InvalidOperationRecord -Message ( + # Adding wildcard at the end of string so Pester ignores additional messages in the error message (e.g. the string 'Mocked error') + ($script:localizedData.FailedJoinAvailabilityGroup -f 'Server1', 'AG_PrimaryOnServer2', 'MSSQLSERVER') + '*' + ) + + { Set-TargetResource @setTargetResourceParameters } | Should -Throw -ExpectedMessage $mockErrorRecord } - It 'Should throw the correct error (JoinAvailabilityGroupFailed) when the availability group replica fails to join the availability group' { + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server1' + } -Times 1 -Exactly + + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server2' + } -Times 1 -Exactly - Mock -CommandName Join-SqlAvailabilityGroup -MockWith { throw } -Verifiable + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server3' + } -Times 0 -Exactly - $mockErrorMessage = $script:localizedData.FailedJoinAvailabilityGroup -f $setTargetResourceParameters.Name, $setTargetResourceParameters.AvailabilityGroupName, $setTargetResourceParameters.InstanceName + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server1' + } - { Set-TargetResource @setTargetResourceParameters } | Should -Throw $mockErrorMessage + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server2' + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer1Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer2Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer3Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer1Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer3Name + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server3' + } + + Should -Invoke -CommandName Import-SQLPSModule -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Join-SqlAvailabilityGroup -Exactly -Times 1 -Scope It + Should -Invoke -CommandName New-SqlAvailabilityReplica -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 0 -Exactly + Should -Invoke -CommandName Test-ClusterPermissions -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly + } + } + + Context 'When the availability group does not exist on the primary replica' { + It 'Should throw the correct error' { + InModuleScope -ScriptBlock { + $setTargetResourceParameters = @{ + Name = 'Server1' + AvailabilityGroupName = 'DoesNotExist' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + PrimaryReplicaServerName = 'Server2' + PrimaryReplicaInstanceName = 'MSSQLSERVER' + Ensure = 'Present' + AvailabilityMode = 'AsynchronousCommit' + BackupPriority = 50 + ConnectionModeInPrimaryRole = 'AllowAllConnections' + ConnectionModeInSecondaryRole = 'AllowNoConnections' + EndpointHostName = '' + FailoverMode = 'Manual' + ReadOnlyRoutingConnectionUrl = 'TCP://Server1.domain.com:1433' + ReadOnlyRoutingList = @('Server1', 'Server2') } - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Join-SqlAvailabilityGroup -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly + + $mockErrorRecord = Get-ObjectNotFoundRecord -Message ( + # Adding wildcard at the end of string so Pester ignores additional messages in the error message (e.g. the string 'Mocked error') + ($script:localizedData.AvailabilityGroupNotFound -f 'DoesNotExist', 'MSSQLSERVER') + '*' + ) + + { Set-TargetResource @setTargetResourceParameters } | Should -Throw -ExpectedMessage $mockErrorRecord } - It 'Should throw the correct error (AvailabilityGroupNotFound) when the availability group does not exist on the primary replica' { + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server1' + } -Times 1 -Exactly - $setTargetResourceParameters.AvailabilityGroupName = 'DoesNotExist' + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server2' + } -Times 1 -Exactly - $mockErrorMessage = $script:localizedData.AvailabilityGroupNotFound -f $setTargetResourceParameters.AvailabilityGroupName, $setTargetResourceParameters.InstanceName + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server3' + } -Times 0 -Exactly - { Set-TargetResource @setTargetResourceParameters } | Should -Throw $mockErrorMessage + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server1' + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer1Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer2Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer3Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer1Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer3Name - } - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Join-SqlAvailabilityGroup -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly - } - } - - Context 'When the desired state is present and the availability group is present' { - - BeforeAll { - Mock -CommandName Remove-SqlAvailabilityReplica -Verifiable - Mock -CommandName Update-AvailabilityGroupReplica -MockWith $mockUpdateAvailabilityGroupReplica -Verifiable - - # Create a hash table to provide test properties and values for the update tests - $mockTestProperties = @{ - AvailabilityMode = 'SynchronousCommit' - BackupPriority = 75 - ConnectionModeInPrimaryRole = 'AllowReadWriteConnections' - ConnectionModeInSecondaryRole = 'AllowReadIntentConnectionsOnly' - FailoverMode = 'Automatic' - ReadOnlyRoutingConnectionUrl = 'TCP://TestHost.domain.com:1433' - ReadOnlyRoutingList = @('Server2', 'Server1') - } + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server2' } - BeforeEach { - $mockAlternateEndpointPort = $false - $mockAlternateEndpointProtocol = $false + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server3' + } + Should -Invoke -CommandName Import-SQLPSModule -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Join-SqlAvailabilityGroup -Scope It -Times 0 -Exactly + Should -Invoke -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly + Should -Invoke -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 0 -Exactly + Should -Invoke -CommandName Test-ClusterPermissions -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly + } + } + } + + Context 'When the desired state is present and the availability group is present' { + Context 'When the availability group replica does not exist' { + It 'Should throw the correct error' { + InModuleScope -ScriptBlock { $setTargetResourceParameters = @{ - Name = $mockServerName - AvailabilityGroupName = $mockAvailabilityGroupName - ServerName = $mockServerName - InstanceName = $mockInstanceName - PrimaryReplicaServerName = $mockPrimaryReplicaServerName - PrimaryReplicaInstanceName = $mockPrimaryReplicaInstanceName - Ensure = $mockEnsure - AvailabilityMode = $mockAvailabilityMode - BackupPriority = $mockBackupPriority - ConnectionModeInPrimaryRole = $mockConnectionModeInPrimaryRole - ConnectionModeInSecondaryRole = $mockConnectionModeInSecondaryRole - EndpointHostName = $mockEndpointHostName - FailoverMode = $mockFailoverMode - ReadOnlyRoutingConnectionUrl = $mockReadOnlyRoutingConnectionUrl - ReadOnlyRoutingList = $mockReadOnlyRoutingList + Name = 'ReplicaNotFound' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + PrimaryReplicaServerName = 'Server2' + PrimaryReplicaInstanceName = 'MSSQLSERVER' + Ensure = 'Present' + AvailabilityMode = 'AsynchronousCommit' + BackupPriority = 50 + ConnectionModeInPrimaryRole = 'AllowAllConnections' + ConnectionModeInSecondaryRole = 'AllowNoConnections' + EndpointHostName = 'ReplicaNotFound' + FailoverMode = 'Manual' + ReadOnlyRoutingConnectionUrl = 'TCP://Server1.domain.com:1433' + ReadOnlyRoutingList = @('Server1', 'Server2') } + + $mockErrorRecord = Get-ObjectNotFoundRecord -Message ( + # Adding wildcard at the end of string so Pester ignores additional messages in the error message (e.g. the string 'Mocked error') + ($script:localizedData.ReplicaNotFound -f 'ReplicaNotFound', 'AG_AllServers', 'MSSQLSERVER') + '*' + ) + + { Set-TargetResource @setTargetResourceParameters } | Should -Throw -ExpectedMessage $mockErrorRecord } - It 'Should throw the correct error (ReplicaNotFound) when the availability group replica does not exist' { + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server1' + } -Times 1 -Exactly - $setTargetResourceParameters.Name = 'ReplicaNotFound' + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server2' + } -Times 0 -Exactly - $mockErrorMessage = $script:localizedData.ReplicaNotFound -f $setTargetResourceParameters.Name, $setTargetResourceParameters.AvailabilityGroupName, $setTargetResourceParameters.InstanceName + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server3' + } -Times 0 -Exactly - { Set-TargetResource @setTargetResourceParameters } | Should -Throw $mockErrorMessage + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server1' + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer1Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer2Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer3Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer1Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer3Name - } - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Join-SqlAvailabilityGroup -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly - } - - foreach ( $mockTestProperty in $mockTestProperties.GetEnumerator() ) - { - It "Should set the property '$($mockTestProperty.Key)' to the desired state" { - - $mockAvailabilityGroupReplicaPropertyName = $mockTestProperty.Key - $mockAvailabilityGroupReplicaPropertyValue = $mockTestProperty.Value - $setTargetResourceParameters.$mockAvailabilityGroupReplicaPropertyName = $mockAvailabilityGroupReplicaPropertyValue - - { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw - - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer1Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer2Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer3Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer1Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer3Name - } - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Join-SqlAvailabilityGroup -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 1 #-Exactly - } + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server2' } - It "Should set the Endpoint Hostname to the desired state" { + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server3' + } - $setTargetResourceParameters.EndpointHostName = 'AnotherEndpointHostName' + Should -Invoke -CommandName Import-SQLPSModule -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Join-SqlAvailabilityGroup -Scope It -Times 0 -Exactly + Should -Invoke -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly + Should -Invoke -CommandName Test-ClusterPermissions -Exactly -Times 1 -Scope It + } + } - { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + Context 'When property is not in desired state' -ForEach @( + @{ + MockPropertyName = 'AvailabilityMode' + MockPropertyValue = 'SynchronousCommit' + } + @{ + MockPropertyName = 'BackupPriority' + MockPropertyValue = 60 + } + @{ + MockPropertyName = 'ConnectionModeInPrimaryRole' + MockPropertyValue = 'AllowReadWriteConnections' + } + @{ + MockPropertyName = 'ConnectionModeInSecondaryRole' + MockPropertyValue = 'AllowReadIntentConnectionsOnly' + } + @{ + MockPropertyName = 'FailoverMode' + MockPropertyValue = 'Automatic' + } + @{ + MockPropertyName = 'ReadOnlyRoutingConnectionUrl' + MockPropertyValue = 'TCP://TestHost.domain.com:1433' + } + @{ + MockPropertyName = 'ReadOnlyRoutingList' + MockPropertyValue = @('Server2', 'Server1') + } + @{ + MockPropertyName = 'EndpointHostName' + MockPropertyValue = 'AnotherEndpointHostName' + } + ) { + BeforeAll { + Mock -CommandName Remove-SqlAvailabilityReplica + Mock -CommandName Update-AvailabilityGroupReplica + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer1Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer2Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer3Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer1Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer3Name + It 'Should set the property to the desired state' { + InModuleScope -Parameters $_ -ScriptBlock { + $setTargetResourceParameters = @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + PrimaryReplicaServerName = 'Server2' + PrimaryReplicaInstanceName = 'MSSQLSERVER' + Ensure = 'Present' + AvailabilityMode = 'AsynchronousCommit' + BackupPriority = 50 + ConnectionModeInPrimaryRole = 'AllowAllConnections' + ConnectionModeInSecondaryRole = 'AllowNoConnections' + EndpointHostName = 'Server1' + FailoverMode = 'Manual' + ReadOnlyRoutingConnectionUrl = 'TCP://Server1.domain.com:1433' + ReadOnlyRoutingList = @('Server1', 'Server2') } - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Join-SqlAvailabilityGroup -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 1 -Exactly + + $setTargetResourceParameters.$MockPropertyName = $MockPropertyValue + + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw } - It "Should set the Endpoint Port to the desired state" { + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server1' + } -Times 1 -Exactly - $mockAvailabilityGroupReplicaPropertyName = 'EndpointUrl' - $mockAvailabilityGroupReplicaPropertyValue = $mockAvailabilityGroupReplica1EndpointUrl - $mockAlternateEndpointPort = $true + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server2' + } -Times 0 -Exactly - { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + Should -Invoke -CommandName Connect-SQL -Scope It -ParameterFilter { + $ServerName -eq 'Server3' + } -Times 0 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer1Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer2Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer3Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer1Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer3Name - } - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Join-SqlAvailabilityGroup -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 1 -Exactly + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server1' } - It "Should set the Endpoint Protocol to the desired state" { + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server2' + } - $mockAvailabilityGroupReplicaPropertyName = 'EndpointUrl' - $mockAvailabilityGroupReplicaPropertyValue = $mockAvailabilityGroupReplica1EndpointUrl - $mockAlternateEndpointProtocol = $true + Should -Invoke -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { + $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server3' + } - { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + Should -Invoke -CommandName Import-SQLPSModule -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Join-SqlAvailabilityGroup -Scope It -Times 0 -Exactly + Should -Invoke -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly + Should -Invoke -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 0 -Exactly + Should -Invoke -CommandName Test-ClusterPermissions -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Update-AvailabilityGroupReplica -ParameterFilter { + switch ($MockPropertyName) + { + # ReadOnlyRoutingList is an array, so we have to evaluate it differently. + 'ReadOnlyRoutingList' + { + # Verifies the command passes the array in the correct order. + $AvailabilityGroupReplica.$MockPropertyName.Count -eq 2 -and + $AvailabilityGroupReplica.$MockPropertyName[0] -eq 'Server2' -and + $AvailabilityGroupReplica.$MockPropertyName[1] -eq 'Server1' + + break + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer1Name - } -Times 1 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer2Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Connect-SQL -Scope It -ParameterFilter { - $ServerName -eq $mockServer3Name - } -Times 0 -Exactly - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer1Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name - } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer3Name + # EndpointHostName changes a different property name. + 'EndpointHostName' + { + # Verifies the command passes the array in the correct order. + $AvailabilityGroupReplica.EndpointUrl -eq 'TCP://AnotherEndpointHostName:5022' + + break + } + + default + { + $AvailabilityGroupReplica.$MockPropertyName -eq $MockPropertyValue + } } - Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Join-SqlAvailabilityGroup -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Remove-SqlAvailabilityReplica -Scope It -Times 0 -Exactly - Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 1 -Exactly - } + } -Exactly -Times 1 -Scope It } } - Describe 'SqlAGReplica\Test-TargetResource' { + Context 'When the endpoint port differ from the port in the replica''s endpoint URL' { + BeforeAll { + Mock -CommandName Update-AvailabilityGroupReplica + + Mock -CommandName Connect-Sql -ParameterFilter { + $ServerName -eq 'Server10' + } -MockWith { + # Mock the server object + $mockServerObject = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server + $mockServerObject.Name = 'Server10' + $mockServerObject.NetName = 'Server10' + $mockServerObject.IsHadrEnabled = $true + $mockServerObject.ServiceName = 'MSSQLSERVER' + + # Mock the availability group replicas + $mockAvailabilityGroupReplica1 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityReplica + $mockAvailabilityGroupReplica1.EndpointUrl = 'TCP://Server10:1234' + $mockAvailabilityGroupReplica1.Name = 'Server10' + + # Mock the availability groups + $mockAvailabilityGroup1 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityGroup + $mockAvailabilityGroup1.Name = 'AG_AllServers' + $mockAvailabilityGroup1.PrimaryReplicaServerName = 'Server1' + $mockAvailabilityGroup1.LocalReplicaRole = 'Primary' + $mockAvailabilityGroup1.AvailabilityReplicas.Add($mockAvailabilityGroupReplica1) + $mockServerObject.AvailabilityGroups.Add($mockAvailabilityGroup1) + + $mockEndpoint = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Endpoint + $mockEndpoint.EndpointType = 'DatabaseMirroring' + $mockEndpoint.Protocol = @{ + TCP = @{ + ListenerPort = 5022 + } + } - BeforeEach { - $mockAlternateEndpointPort = $false - $mockAlternateEndpointProtocol = $false - $mockDatabaseMirroringEndpoint = $true + $mockServerObject.Endpoints.Add($mockEndpoint) - $testTargetResourceParameters = @{ - Name = $mockServerName - AvailabilityGroupName = $mockAvailabilityGroupName - ServerName = $mockServerName - InstanceName = $mockInstanceName - PrimaryReplicaServerName = $mockPrimaryReplicaServerName - PrimaryReplicaInstanceName = $mockPrimaryReplicaInstanceName - Ensure = $mockEnsure - AvailabilityMode = $mockAvailabilityMode - BackupPriority = $mockBackupPriority - ConnectionModeInPrimaryRole = $mockConnectionModeInPrimaryRole - ConnectionModeInSecondaryRole = $mockConnectionModeInSecondaryRole - EndpointHostName = $mockEndpointHostName - FailoverMode = $mockFailoverMode - ReadOnlyRoutingConnectionUrl = $mockReadOnlyRoutingConnectionUrl - ReadOnlyRoutingList = $mockReadOnlyRoutingList - ProcessOnlyOnActiveNode = $mockProcessOnlyOnActiveNode + return $mockServerObject } - - Mock -CommandName Connect-SQL -MockWith $mockConnectSqlServer1 -Verifiable - Mock -CommandName Test-ActiveNode -MockWith { - return -not $mockProcessOnlyOnActiveNode - } -Verifiable } - Context 'When the desired state is absent' { - - It 'Should return $true when the Availability Replica is absent' { - - $testTargetResourceParameters.Name = $mockAvailabilityGroupReplica2Name - $testTargetResourceParameters.AvailabilityGroupName = $mockAvailabilityGroup2Name - $testTargetResourceParameters.Ensure = 'Absent' - - Test-TargetResource @testTargetResourceParameters | Should -Be $true + It 'Should set the replica''s endpoint URL to use the same port as the endpoint' { + InModuleScope -Parameters $_ -ScriptBlock { + $setTargetResourceParameters = @{ + Name = 'Server10' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server10' + InstanceName = 'MSSQLSERVER' + Ensure = 'Present' + EndpointHostName = 'Server10' + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw } - It 'Should return $false when the Availability Replica is present' { + Should -Invoke -CommandName Update-AvailabilityGroupReplica -Exactly -Times 1 -Scope It + } + } - $testTargetResourceParameters.Ensure = 'Absent' + Context 'When the endpoint protocol differ from the protocol in the replica''s endpoint URL' { + BeforeAll { + Mock -CommandName Update-AvailabilityGroupReplica + + Mock -CommandName Connect-Sql -ParameterFilter { + $ServerName -eq 'Server10' + } -MockWith { + # Mock the server object + $mockServerObject = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server + $mockServerObject.Name = 'Server10' + $mockServerObject.NetName = 'Server10' + $mockServerObject.IsHadrEnabled = $true + $mockServerObject.ServiceName = 'MSSQLSERVER' + + # Mock the availability group replicas + $mockAvailabilityGroupReplica1 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityReplica + $mockAvailabilityGroupReplica1.EndpointUrl = 'UDP://Server10:5022' + $mockAvailabilityGroupReplica1.Name = 'Server10' + + # Mock the availability groups + $mockAvailabilityGroup1 = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityGroup + $mockAvailabilityGroup1.Name = 'AG_AllServers' + $mockAvailabilityGroup1.PrimaryReplicaServerName = 'Server1' + $mockAvailabilityGroup1.LocalReplicaRole = 'Primary' + $mockAvailabilityGroup1.AvailabilityReplicas.Add($mockAvailabilityGroupReplica1) + $mockServerObject.AvailabilityGroups.Add($mockAvailabilityGroup1) + + $mockEndpoint = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Endpoint + $mockEndpoint.EndpointType = 'DatabaseMirroring' + $mockEndpoint.Protocol = @{ + TCP = @{ + ListenerPort = 5022 + } + } - Test-TargetResource @testTargetResourceParameters | Should -Be $false + $mockServerObject.Endpoints.Add($mockEndpoint) - Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly + return $mockServerObject } } - Context 'When the desired state is present' { - - BeforeAll { - $propertiesToCheck = @{ - AvailabilityMode = 'SynchronousCommit' - BackupPriority = 42 - ConnectionModeInPrimaryRole = 'AllowReadWriteConnections' - ConnectionModeInSecondaryRole = 'AllowReadIntentConnectionsOnly' - FailoverMode = 'Automatic' - ReadOnlyRoutingConnectionUrl = 'WrongUrl' - ReadOnlyRoutingList = @('WrongServer') + It 'Should set the replica''s endpoint URL to use the same port as the endpoint' { + InModuleScope -Parameters $_ -ScriptBlock { + $setTargetResourceParameters = @{ + Name = 'Server10' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server10' + InstanceName = 'MSSQLSERVER' + Ensure = 'Present' + EndpointHostName = 'Server10' } + + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw } - It "Should return $true when the Availability Replica is present all properties are in the desired state" { - Test-TargetResource @testTargetResourceParameters | Should -Be $true + Should -Invoke -CommandName Update-AvailabilityGroupReplica -Exactly -Times 1 -Scope It + } + } + } +} - Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Test-ActiveNode -Scope It -Times 1 -Exactly +Describe 'SqlAGReplica\Test-TargetResource' { + Context 'When the system is in the desired state' { + Context 'When the Availability Replica should be absent' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + Ensure = 'Absent' + } } + } - It 'Should return $false when the Availability Replica is absent' { + It 'Should return $true' { + InModuleScope -ScriptBlock { + $mockTestTargetResourceParameters = @{ + Ensure = 'Absent' + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + } - $testTargetResourceParameters.Name = $mockAvailabilityGroupReplica2Name - $testTargetResourceParameters.AvailabilityGroupName = $mockAvailabilityGroup2Name + Test-TargetResource @mockTestTargetResourceParameters | Should -BeTrue + } - Test-TargetResource @testTargetResourceParameters | Should -Be $false + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Test-ActiveNode -Scope It -Times 1 -Exactly + Context 'When the Availability Replica should be present' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + Ensure = 'Present' + EndpointPort = '5022' + EndpointUrl = 'TCP://Server1:5022' + } } + } - It 'Should return $true when the Availability Replica is present' { - - Test-TargetResource @testTargetResourceParameters | Should -Be $true + It 'Should return $true' { + InModuleScope -ScriptBlock { + $mockTestTargetResourceParameters = @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Test-ActiveNode -Scope It -Times 1 -Exactly + Test-TargetResource @mockTestTargetResourceParameters | Should -BeTrue } - foreach ( $propertyToCheck in $propertiesToCheck.GetEnumerator() ) - { - It "Should return $false when the Availability Replica is present and the property '$($propertyToCheck.Key)' is not in the desired state" { - $testTargetResourceParameters.($propertyToCheck.Key) = $propertyToCheck.Value - - Test-TargetResource @testTargetResourceParameters | Should -Be $false + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Test-ActiveNode -Scope It -Times 1 -Exactly + Context 'When the system is not in the desired state' { + Context 'When the Availability Replica should be absent' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + Ensure = 'Present' + EndpointPort = '5022' + EndpointUrl = 'TCP://Server1:5022' } } + } - It 'Should return $false when the Availability Replica is present and the Availabiltiy Mode is not in the desired state' { + It 'Should return $false' { + InModuleScope -ScriptBlock { + $mockTestTargetResourceParameters = @{ + Ensure = 'Absent' + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + } - $testTargetResourceParameters.AvailabilityMode = 'SynchronousCommit' + Test-TargetResource @mockTestTargetResourceParameters | Should -BeFalse + } - Test-TargetResource @testTargetResourceParameters | Should -Be $false + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Test-ActiveNode -Scope It -Times 1 -Exactly + Context 'When the Availability Replica should be present' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + Ensure = 'Absent' + } } + } - It 'Should return $true when the Availability Replica is present and the Endpoint Hostname is not specified' { + It 'Should return $false' { + InModuleScope -ScriptBlock { + $mockTestTargetResourceParameters = @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + } - $testTargetResourceParameters.EndpointHostName = '' + Test-TargetResource @mockTestTargetResourceParameters | Should -BeFalse + } - Test-TargetResource @testTargetResourceParameters | Should -Be $true + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Test-ActiveNode -Scope It -Times 1 -Exactly + Context 'When enforcing the state shall happen only when the node is the active node' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + Ensure = 'Present' + EndpointPort = '5022' + EndpointUrl = 'TCP://Server1:5022' + IsActiveNode = $false + } } + } - It 'Should return $false when the Availability Replica is present and the Endpoint Hostname is not in the desired state' { + It 'Should return $true' { + InModuleScope -ScriptBlock { + $mockTestTargetResourceParameters = @{ + Ensure = 'Absent' + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + ProcessOnlyOnActiveNode = $true + } - $testTargetResourceParameters.EndpointHostName = 'OtherHostName' + Test-TargetResource @mockTestTargetResourceParameters | Should -BeTrue + } - Test-TargetResource @testTargetResourceParameters | Should -Be $false + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Test-ActiveNode -Scope It -Times 1 -Exactly + Context 'When property is not in desired state' -ForEach @( + @{ + MockPropertyName = 'EndpointHostName' + MockPropertyValue = 'Server2' + } + @{ + MockPropertyName = 'AvailabilityMode' + MockPropertyValue = 'SynchronousCommit' + } + @{ + MockPropertyName = 'BackupPriority' + MockPropertyValue = 60 + } + @{ + MockPropertyName = 'ConnectionModeInPrimaryRole' + MockPropertyValue = 'AllowReadWriteConnections' + } + @{ + MockPropertyName = 'ConnectionModeInSecondaryRole' + MockPropertyValue = 'AllowReadIntentConnectionsOnly' + } + @{ + MockPropertyName = 'FailoverMode' + MockPropertyValue = 'Automatic' + } + @{ + MockPropertyName = 'ReadOnlyRoutingConnectionUrl' + MockPropertyValue = 'TCP://WrongHostname.domain.com:1433' + } + @{ + MockPropertyName = 'ReadOnlyRoutingList' + MockPropertyValue = @('Server2', 'Server1') + } + ) { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + Ensure = 'Present' + PrimaryReplicaServerName = 'Server2' + PrimaryReplicaInstanceName = 'MSSQLSERVER' + AvailabilityMode = 'AsynchronousCommit' + BackupPriority = 50 + ConnectionModeInPrimaryRole = 'AllowAllConnections' + ConnectionModeInSecondaryRole = 'AllowNoConnections' + EndpointHostName = 'Server1' + FailoverMode = 'Manual' + ReadOnlyRoutingConnectionUrl = 'TCP://Server1.domain.com:1433' + ReadOnlyRoutingList = @('Server1', 'Server2') + + # Read properties + EndpointPort = '5022' + EndpointUrl = 'TCP://Server1:5022' + IsActiveNode = $true + } } + } - It 'Should return $false when the Availability Replica is present and the Endpoint Protocol is not in the desired state' { - - $mockAlternateEndpointProtocol = $true + It 'Should return $false' { + InModuleScope -Parameters $_ -ScriptBlock { + $mockTestTargetResourceParameters = @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + ProcessOnlyOnActiveNode = $true + } - Test-TargetResource @testTargetResourceParameters | Should -Be $false + $mockTestTargetResourceParameters.$MockPropertyName = $MockPropertyValue - Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Test-ActiveNode -Scope It -Times 1 -Exactly + Test-TargetResource @mockTestTargetResourceParameters | Should -BeFalse } - It 'Should return $false when the Availability Replica is present and the Endpoint Port is not in the desired state' { - - $mockAlternateEndpointPort = $true + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } - Test-TargetResource @testTargetResourceParameters | Should -Be $false + Context 'When endpoint port differ from the endpoint URL port' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + Ensure = 'Present' + + # Read properties + EndpointPort = '5022' + EndpointUrl = 'TCP://Server1:1433' + IsActiveNode = $true + } + } + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Test-ActiveNode -Scope It -Times 1 -Exactly + It 'Should return $false' { + InModuleScope -ScriptBlock { + $mockTestTargetResourceParameters = @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + ProcessOnlyOnActiveNode = $true + } - $mockAlternateEndpointPort = $false + Test-TargetResource @mockTestTargetResourceParameters | Should -BeFalse } - It 'Should return $true when ProcessOnlyOnActiveNode is "$true" and the current node is not actively hosting the instance' { - $mockProcessOnlyOnActiveNode = $true + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } - $testTargetResourceParameters.ProcessOnlyOnActiveNode = $mockProcessOnlyOnActiveNode + Context 'When endpoint protocol differ from the endpoint URL protocol' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + Ensure = 'Present' + + # Read properties + EndpointPort = '5022' + EndpointUrl = 'UDP://Server1:5022' + IsActiveNode = $true + } + } + } - Test-TargetResource @testTargetResourceParameters | Should -Be $true + It 'Should return $false' { + InModuleScope -ScriptBlock { + $mockTestTargetResourceParameters = @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + ProcessOnlyOnActiveNode = $true + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly - Assert-MockCalled -CommandName Test-ActiveNode -Scope It -Times 1 -Exactly + Test-TargetResource @mockTestTargetResourceParameters | Should -BeFalse } + + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It } } } } -finally -{ - Invoke-TestCleanup -}