Tests/Unit/MSFT_xSQLServerAlwaysOnAvailabilityGroup.Tests.ps1
$script:DSCModuleName = 'xSQLServer' $script:DSCResourceName = 'MSFT_xSQLServerAlwaysOnAvailabilityGroup' # Unit Test Template Version: 1.1.0 [String] $script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) { & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\')) } Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force $TestEnvironment = Initialize-TestEnvironment ` -DSCModuleName $script:DSCModuleName ` -DSCResourceName $script:DSCResourceName ` -TestType Unit # Loading stub cmdlets Import-Module -Name ( Join-Path -Path ( Join-Path -Path $PSScriptRoot -ChildPath Stubs ) -ChildPath SQLPSStub.psm1 ) -Force -Global Add-Type -Path ( Join-Path -Path ( Join-Path -Path $PSScriptRoot -ChildPath Stubs ) -ChildPath SMO.cs ) # Begin Testing try { InModuleScope $script:DSCResourceName { #region mock server object variables $mockServer1Name = 'Server1' $mockServer1ServiceName = 'MSSQLSERVER' $mockServer2Name = 'Server2' $mockServer2ServiceName = 'MSSQLSERVER' #endregion mock server object variables #region mock availability group variables # Define the properties that are SQL 2016 and newer $sql13AvailabilityGroupProperties = @( 'BasicAvailabilityGroup' 'DatabaseHealthTrigger' 'DtcSupportEnabled' ) # Define properties that are Availability Replica properties $availabilityGroupReplicaProperties = @( 'AvailabilityMode', 'BackupPriority', 'ConnectionModeInPrimaryRole', 'ConnectionModeInSecondaryRole', 'EndpointHostName', 'FailoverMode' ) # The following will be set dynamically during tests $mockAvailabilityGroupName = '' $mockAvailabilityReplicaEndpointProtocol = '' $mockAvailabilityReplicaEndpointPort = 0 $mockDatabaseMirroringEndpointProtocol = '' $mockDatabaseMirroringEndpointPort = 0 $mockAvailabilityGroupAbsentName = 'AbsentAvailabilityGroup' $mockAvailabilityGroupCreateErrorName = 'ErrorCreateAvailabilityGroup' $mockAvailabilityGroupPresentName = 'NewAvailabilityGroup' $mockAvailabilityGroupRemoveErrorName = 'ErrorRemoveAvailabilityGroup' $mockAvailabilityGroupReplicaAbsentName = $mockServer2Name $mockAvailabilityGroupReplicaPresentName = $mockServer1Name $mockAvailabilityGroup1Name = 'AvailabilityGroup1' $mockAvailabilityGroup1BasicAvailabilityGroup = $false $mockAvailabilityGroup1DatabaseHealthTrigger = $false $mockAvailabilityGroup1DtcSupportEnabled = $false $mockAvailabilityGroup1AutomatedBackupPreference = 'Secondary' $mockAvailabilityGroup1FailureConditionLevel = 'OnCriticalServerErrors' $mockAvailabilityGroup1HealthCheckTimeout = 30000 $mockAvailabilityGroup1PrimaryReplicaServerName = $mockServer1Name $mockAvailabilityReplica1Name = $mockServer1Name $mockAvailabilityReplica1AvailabilityMode = 'AsynchronousCommit' $mockAvailabilityReplica1BackupPriority = 50 $mockAvailabilityReplica1ConnectionModeInPrimaryRole = 'AllowAllConnections' $mockAvailabilityReplica1ConnectionModeInSecondaryRole = 'AllowNoConnections' $mockAvailabilityReplica1EndpointHostName = $mockServer1Name $mockAvailabilityReplica1EndpointProtocol = 'TCP' $mockAvailabilityReplica1EndpointPort = 5022 $mockAvailabilityReplica1FailoverMode = 'Manual' $mockAvailabilityReplica1Role = 'Primary' $mockAvailabilityReplica2Name = $mockServer2Name $mockAvailabilityReplica2AvailabilityMode = 'AsynchronousCommit' $mockAvailabilityReplica2BackupPriority = 50 $mockAvailabilityReplica2ConnectionModeInPrimaryRole = 'AllowAllConnections' $mockAvailabilityReplica2ConnectionModeInSecondaryRole = 'AllowNoConnections' $mockAvailabilityReplica2EndpointHostName = $mockServer2Name $mockAvailabilityReplica2EndpointProtocol = 'TCP' $mockAvailabilityReplica2EndpointPort = 5022 $mockAvailabilityReplica2FailoverMode = 'Manual' $mockAvailabilityReplica2Role = 'Primary' #endregion mock availability group variables $mockConnectSqlServer1 = { Param ( [Parameter()] [string] $SQLServer, [Parameter()] [string] $SQLInstanceName, # The following two parameters are used to mock Get-PrimaryReplicaServerObject [Parameter()] [Microsoft.SqlServer.Management.Smo.AvailabilityGroup] $AvailabilityGroup, [Parameter()] [Microsoft.SqlServer.Management.Smo.Server] $ServerObject ) # Define the server object $mockServerObject = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server $mockServerObject.IsHadrEnabled = $mockIsHadrEnabled $mockServerObject.Name = $mockServer1Name $mockServerObject.NetName = $mockServer1Name $mockServerObject.ServiceName = $mockServer1ServiceName $mockServerObject.Version = @{ Major = $Version } # Define the availability group 1 object $mockAvailabilityGroup1Object = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityGroup $mockAvailabilityGroup1Object.AutomatedBackupPreference = $mockAvailabilityGroup1AutomatedBackupPreference $mockAvailabilityGroup1Object.FailureConditionLevel = $mockAvailabilityGroup1FailureConditionLevel $mockAvailabilityGroup1Object.HealthCheckTimeout = $mockAvailabilityGroup1HealthCheckTimeout $mockAvailabilityGroup1Object.Name = $mockAvailabilityGroupName $mockAvailabilityGroup1Object.PrimaryReplicaServerName = $mockAvailabilityGroup1PrimaryReplicaServerName if ( $Version -ge 13 ) { $mockAvailabilityGroup1Object.BasicAvailabilityGroup = $mockAvailabilityGroup1BasicAvailabilityGroup $mockAvailabilityGroup1Object.DatabaseHealthTrigger = $mockAvailabilityGroup1DatabaseHealthTrigger $mockAvailabilityGroup1Object.DtcSupportEnabled = $mockAvailabilityGroup1DtcSupportEnabled } # Define the availability replica 1 object $mockAvailabilityReplica1Object = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityReplica $mockAvailabilityReplica1Object.Name = $mockAvailabilityReplica1Name $mockAvailabilityReplica1Object.AvailabilityMode = $mockAvailabilityReplica1AvailabilityMode $mockAvailabilityReplica1Object.BackupPriority = $mockAvailabilityReplica2BackupPriority $mockAvailabilityReplica1Object.ConnectionModeInPrimaryRole = $mockAvailabilityReplica1ConnectionModeInPrimaryRole $mockAvailabilityReplica1Object.ConnectionModeInSecondaryRole = $mockAvailabilityReplica1ConnectionModeInSecondaryRole $mockAvailabilityReplica1Object.EndpointUrl = "$($mockAvailabilityReplicaEndpointProtocol)://$($mockAvailabilityReplica1EndpointHostName):$($mockAvailabilityReplicaEndpointPort)" $mockAvailabilityReplica1Object.FailoverMode = $mockAvailabilityReplica1FailoverMode $mockAvailabilityReplica1Object.Role = $mockAvailabilityReplica1Role # Define the availability replica 2 object $mockAvailabilityReplica2Object = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityReplica $mockAvailabilityReplica2Object.Name = $mockAvailabilityReplica2Name $mockAvailabilityReplica2Object.AvailabilityMode = $mockAvailabilityReplica2AvailabilityMode $mockAvailabilityReplica2Object.BackupPriority = $mockAvailabilityReplica1BackupPriority $mockAvailabilityReplica2Object.ConnectionModeInPrimaryRole = $mockAvailabilityReplica2ConnectionModeInPrimaryRole $mockAvailabilityReplica2Object.ConnectionModeInSecondaryRole = $mockAvailabilityReplica2ConnectionModeInSecondaryRole $mockAvailabilityReplica2Object.EndpointUrl = "$($mockAvailabilityReplica2EndpointProtocol)://$($mockAvailabilityReplica2EndpointHostName):$($mockAvailabilityReplica2EndpointPort)" $mockAvailabilityReplica2Object.FailoverMode = $mockAvailabilityReplica2FailoverMode $mockAvailabilityReplica2Object.Role = $mockAvailabilityReplica2Role # Add the availability group to the server object $mockAvailabilityGroup1Object.AvailabilityReplicas.Add($mockAvailabilityReplica1Object) $mockAvailabilityGroup1Object.AvailabilityReplicas.Add($mockAvailabilityReplica2Object) $mockServerObject.AvailabilityGroups.Add($mockAvailabilityGroup1Object) # Define the database mirroring endpoint object if ( $mockDatabaseMirroringEndpointPresent ) { $mockDatabaseMirroringEndpoint = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Endpoint $mockDatabaseMirroringEndpoint.EndpointType = 'DatabaseMirroring' $mockDatabaseMirroringEndpoint.Protocol = @{ $mockDatabaseMirroringEndpointProtocol = @{ ListenerPort = $mockDatabaseMirroringEndpointPort } } $mockServerObject.Endpoints.Add($mockDatabaseMirroringEndpoint) } return $mockServerObject } $mockConnectSqlServer2 = { Param ( [Parameter()] [string] $SQLServer, [Parameter()] [string] $SQLInstanceName, # The following two parameters are used to mock Get-PrimaryReplicaServerObject [Parameter()] [Microsoft.SqlServer.Management.Smo.AvailabilityGroup] $AvailabilityGroup, [Parameter()] [Microsoft.SqlServer.Management.Smo.Server] $ServerObject ) # Define the server object $mockServerObject = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server $mockServerObject.IsHadrEnabled = $mockIsHadrEnabled $mockServerObject.Name = $mockServer2Name $mockServerObject.NetName = $mockServer2Name $mockServerObject.ServiceName = $mockServer2ServiceName $mockServerObject.Version = @{ Major = $Version } # Define the database mirroring endpoint object if ( $mockDatabaseMirroringEndpointPresent ) { $mockDatabaseMirroringEndpoint = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Endpoint $mockDatabaseMirroringEndpoint.EndpointType = 'DatabaseMirroring' $mockDatabaseMirroringEndpoint.Protocol = @{ TCP = @{ ListenerPort = $mockAvailabilityReplica1EndpointPort } } $mockServerObject.Endpoints.Add($mockDatabaseMirroringEndpoint) } # Define the availability group 1 object $mockAvailabilityGroup1Object = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityGroup $mockAvailabilityGroup1Object.AutomatedBackupPreference = $mockAvailabilityGroup1AutomatedBackupPreference $mockAvailabilityGroup1Object.FailureConditionLevel = $mockAvailabilityGroup1FailureConditionLevel $mockAvailabilityGroup1Object.HealthCheckTimeout = $mockAvailabilityGroup1HealthCheckTimeout $mockAvailabilityGroup1Object.Name = $mockAvailabilityGroupName $mockAvailabilityGroup1Object.LocalReplicaRole = 'Secondary' $mockAvailabilityGroup1Object.PrimaryReplicaServerName = $mockAvailabilityGroup1PrimaryReplicaServerName if ( $Version -ge 13 ) { $mockAvailabilityGroup1Object.BasicAvailabilityGroup = $mockAvailabilityGroup1BasicAvailabilityGroup $mockAvailabilityGroup1Object.DatabaseHealthTrigger = $mockAvailabilityGroup1DatabaseHealthTrigger $mockAvailabilityGroup1Object.DtcSupportEnabled = $mockAvailabilityGroup1DtcSupportEnabled } # Define the availability replica 1 object $mockAvailabilityReplica1Object = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityReplica $mockAvailabilityReplica1Object.Name = $mockAvailabilityReplica1Name $mockAvailabilityReplica1Object.AvailabilityMode = $mockAvailabilityReplica1AvailabilityMode $mockAvailabilityReplica1Object.BackupPriority = $mockAvailabilityReplica1BackupPriority $mockAvailabilityReplica1Object.ConnectionModeInPrimaryRole = $mockAvailabilityReplica1ConnectionModeInPrimaryRole $mockAvailabilityReplica1Object.ConnectionModeInSecondaryRole = $mockAvailabilityReplica1ConnectionModeInSecondaryRole $mockAvailabilityReplica1Object.EndpointUrl = "$($mockAvailabilityReplicaEndpointProtocol)://$($mockAvailabilityReplica1EndpointHostName):$($mockAvailabilityReplica1EndpointPort)" $mockAvailabilityReplica1Object.FailoverMode = $mockAvailabilityReplica1FailoverMode $mockAvailabilityReplica1Object.Role = $mockAvailabilityReplica1Role # Add the availability group to the server object $mockAvailabilityGroup1Object.AvailabilityReplicas.Add($mockAvailabilityReplica1Object) $mockServerObject.AvailabilityGroups.Add($mockAvailabilityGroup1Object) return $mockServerObject } $mockNewSqlAvailabilityReplica = { $mock = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityReplica $mock.Name = $mockAvailabilityReplica1Name $mock.AvailabilityMode = $mockAvailabilityReplica1AvailabilityMode $mock.BackupPriority = $mockAvailabilityReplica1BackupPriority $mock.ConnectionModeInPrimaryRole = $mockAvailabilityReplica1ConnectionModeInPrimaryRole $mock.ConnectionModeInSecondaryRole = $mockAvailabilityReplica1ConnectionModeInSecondaryRole $mock.EndpointUrl = "$($mockAvailabilityReplicaEndpointProtocol)://$($mockAvailabilityReplica1EndpointHostName):$($mockAvailabilityReplicaEndpointPort)" $mock.FailoverMode = $mockAvailabilityReplica1FailoverMode $mock.Role = $mockAvailabilityReplica1Role return $mock } # Mock the Update-AvailabilityGroup function to ensure the specified property was set correctly $mockUpdateAvailabiltyGroup = { param ( [Parameter()] [Microsoft.SqlServer.Management.Smo.AvailabilityGroup] $AvailabilityGroup ) # If the current value of the property that was set is not equal to the desired value if ( $ParameterValue -ne $AvailabilityGroup.$Parameter ) { $AvailabilityGroup | Get-Member -MemberType Property | Select-Object -ExpandProperty Name | ForEach-Object -Process { $currentProperty = $_ Write-Verbose -Message "The property '$($currentProperty)' value is '$($AvailabilityGroup.$Parameter)' but should be '$($ParameterValue)'." -Verbose } throw "Update-AvailabilityGroup should be setting the property '$($Parameter)' to '$($ParameterValue)'." } } # Mock the Update-AvailabilityGroupReplica function to ensure the specified property was set correctly $mockUpdateAvailabiltyGroupReplica = { param ( [Parameter()] [Microsoft.SqlServer.Management.Smo.AvailabilityReplica] $AvailabilityGroupReplica ) if ( [string]::IsNullOrEmpty($Parameter) -and [string]::IsNullOrEmpty($ParameterValue) ) { return } # Some parameters don't align directly with a property switch ( $Parameter ) { EndpointHostName { $validatedParameter = 'EndpointUrl' $validatedParameterValue = "$($mockAvailabilityReplicaEndpointProtocol)://$($ParameterValue):$($mockAvailabilityReplicaEndpointPort)" } default { $validatedParameter = $Parameter $validatedParameterValue = $ParameterValue } } # If the current value of the property that was set is not equal to the desired value if ( $validatedParameterValue -ne $AvailabilityGroupReplica.$validatedParameter ) { $AvailabilityGroupReplica | Get-Member -MemberType Property | Select-Object -ExpandProperty Name | ForEach-Object -Process { $currentProperty = $_ Write-Verbose -Message "The property '$($currentProperty)' value is '$($AvailabilityGroupReplica.$validatedParameter)' but should be '$($validatedParameterValue)'." -Verbose } throw "Update-AvailabilityGroupReplica should be setting the property '$($validatedParameter)' to '$($validatedParameterValue)'." } } #region configuration parameters $getTargetResourceParameters = @{ SQLServer = $mockServer1Name SQLInstanceName = $mockServer1ServiceName } $mockResourceParameters = @{ Name = $mockAvailabilityGroup1Name SQLServer = $mockServer1Name SQLInstanceName = $mockServer1ServiceName AutomatedBackupPreference = $mockAvailabilityGroup1AutomatedBackupPreference AvailabilityMode = $mockAvailabilityReplica1AvailabilityMode BackupPriority = $mockAvailabilityReplica1BackupPriority BasicAvailabilityGroup = $mockAvailabilityGroup1BasicAvailabilityGroup DatabaseHealthTrigger = $mockAvailabilityGroup1DatabaseHealthTrigger DtcSupportEnabled = $mockAvailabilityGroup1DtcSupportEnabled EndpointHostName = $mockServer1Name Ensure = 'Present' ConnectionModeInPrimaryRole = $mockAvailabilityReplica1ConnectionModeInPrimaryRole ConnectionModeInSecondaryRole = $mockAvailabilityReplica1ConnectionModeInSecondaryRole FailureConditionLevel = $mockAvailabilityGroup1FailureConditionLevel FailoverMode = $mockAvailabilityReplica1FailoverMode HealthCheckTimeout = $mockAvailabilityGroup1HealthCheckTimeout } #endregion configuration parameters Describe 'xSQLServerAlwaysOnAvailabilityGroup\Get-TargetResource' { BeforeAll { Mock -CommandName Connect-SQL -MockWith $mockConnectSqlServer1 -Verifiable -ParameterFilter { $SQLServer -eq $mockServer1Name } Mock -CommandName Connect-SQL -MockWith $mockConnectSqlServer2 -Verifiable -ParameterFilter { $SQLServer -eq $mockServer2Name } $mockAvailabilityGroupName = $mockAvailabilityGroup1Name $mockAvailabilityReplicaEndpointPort = 5022 $mockAvailabilityReplicaEndpointProtocol = $mockAvailabilityReplica1EndpointProtocol $mockDatabaseMirroringEndpointPresent = $true $mockDatabaseMirroringEndpointProtocol = 'TCP' $mockDatabaseMirroringEndpointPort = 5022 #region test cases $absentTestCases = @( @{ Name = $mockAvailabilityGroupAbsentName Version = 12 }, @{ Name = $mockAvailabilityGroupAbsentName Version = 13 } ) $presentTestCases = @( @{ Ensure = 'Present' Name = $mockAvailabilityGroup1Name Version = 12 }, @{ Ensure = 'Present' Name = $mockAvailabilityGroup1Name Version = 13 }, @{ Ensure = 'Absent' Name = $mockAvailabilityGroup1Name Version = 12 }, @{ Ensure = 'Absent' Name = $mockAvailabilityGroup1Name Version = 13 } ) #endregion test cases } Context 'When the Availability Group is Absent' { It 'Should not return an Availability Group when Ensure is set to Present and the SQL version is <Version>' -TestCases $absentTestCases { param ( $Name, $Version ) $result = Get-TargetResource @getTargetResourceParameters -Name $Name $result.Ensure | Should Be 'Absent' Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly -ParameterFilter { $SQLServer -eq $mockServer1Name } Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly -ParameterFilter { $SQLServer -eq $mockServer2Name } } } Context 'When the Availability Group is Present' { It 'Should return the correct Availability Group properties when Ensure is set to <Ensure> and the SQL version is <Version>' -TestCases $presentTestCases { param ( $Ensure, $Name, $Version ) $result = Get-TargetResource @getTargetResourceParameters -Name $Name $result.Name | Should Be $mockAvailabilityGroup1Name $result.SQLServer | Should Be $getTargetResourceParameters.SQLServer $result.SQLInstanceName | Should Be $getTargetResourceParameters.SQLInstanceName $result.Ensure | Should Be 'Present' $result.AutomatedBackupPreference | Should Be $mockAvailabilityGroup1AutomatedBackupPreference $result.AvailabilityMode | Should Be $mockAvailabilityReplica1AvailabilityMode $result.BackupPriority | Should Be $mockAvailabilityReplica1BackupPriority $result.ConnectionModeInPrimaryRole | Should Be $mockAvailabilityReplica1ConnectionModeInPrimaryRole $result.ConnectionModeInSecondaryRole | Should Be $mockAvailabilityReplica1ConnectionModeInSecondaryRole #result.EndpointURL #### Need this as well! $result.FailureConditionLevel | Should Be $mockAvailabilityGroup1FailureConditionLevel $result.FailoverMode | Should Be $mockAvailabilityReplica1FailoverMode $result.HealthCheckTimeout | Should Be $mockAvailabilityGroup1HealthCheckTimeout if ( $Version -ge 13 ) { $result.BasicAvailabilityGroup | Should Be $mockAvailabilityGroup1BasicAvailabilityGroup $result.DatabaseHealthTrigger | Should Be $mockAvailabilityGroup1DatabaseHealthTrigger $result.DtcSupportEnabled | Should Be $mockAvailabilityGroup1DtcSupportEnabled } else { $result.BasicAvailabilityGroup | Should BeNullOrEmpty $result.DatabaseHealthTrigger | Should BeNullOrEmpty $result.DtcSupportEnabled | Should BeNullOrEmpty } Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly } } } Describe 'xSQLServerAlwaysOnAvailabilityGroup\Set-TargetResource' { BeforeAll { Mock -CommandName Connect-SQL -MockWith $mockConnectSqlServer1 -Verifiable -ParameterFilter { $SQLServer -eq $mockServer1Name } Mock -CommandName Connect-SQL -MockWith $mockConnectSqlServer2 -Verifiable -ParameterFilter { $SQLServer -eq $mockServer2Name } Mock -CommandName Get-PrimaryReplicaServerObject -MockWith $mockConnectSqlServer1 -Verifiable -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer1Name } Mock -CommandName Get-PrimaryReplicaServerObject -MockWith $mockConnectSqlServer2 -Verifiable -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name } Mock -CommandName Invoke-Query -MockWith {} -Verifiable Mock -CommandName Import-SQLPSModule -MockWith {} -Verifiable Mock -CommandName New-SqlAvailabilityGroup {} -Verifiable -ParameterFilter { $Name -eq $mockAvailabilityGroupPresentName } Mock -CommandName New-SqlAvailabilityGroup { throw 'CreateAvailabilityGroupFailed' } -Verifiable -ParameterFilter { $Name -eq $mockAvailabilityGroupCreateErrorName } Mock -CommandName New-SqlAvailabilityReplica -MockWith $mockNewSqlAvailabilityReplica -Verifiable -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaPresentName } Mock -CommandName New-SqlAvailabilityReplica -MockWith { throw 'CreateAvailabilityGroupReplicaFailed' } -Verifiable -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaAbsentName } Mock -CommandName New-TerminatingError -MockWith { $ErrorType } -Verifiable Mock -CommandName Remove-SqlAvailabilityGroup -MockWith {} -Verifiable -ParameterFilter { $InputObject.Name -eq $mockAvailabilityGroup1Name } Mock -CommandName Remove-SqlAvailabilityGroup -MockWith { throw 'RemoveAvailabilityGroupFailed' } -Verifiable -ParameterFilter { $InputObject.Name -eq $mockAvailabilityGroupRemoveErrorName } Mock -CommandName Test-ClusterPermissions -MockWith { $mockClusterPermissionsExist } -Verifiable Mock -CommandName Update-AvailabilityGroup -MockWith $mockUpdateAvailabiltyGroup -Verifiable Mock -CommandName Update-AvailabilityGroupReplica -MockWith $mockUpdateAvailabiltyGroupReplica -Verifiable #region test cases $versionsToTest = @(12, 13) $createAvailabilityGroupTestCases = $versionsToTest | ForEach-Object -Process { $versionToTest = $_ return @{ Name = $mockAvailabilityGroupPresentName Version = $versionToTest } } $removeAvailabilityGroupTestCases = $versionsToTest | ForEach-Object -Process { $versionToTest = $_ return @{ Version = $versionToTest } } [array]$presentParameterTestCases = $versionsToTest | ForEach-Object -Process { $versionToTest = $_ $mockResourceParameters.GetEnumerator() | ForEach-Object -Process { if ( @('Name', 'SQLServer', 'SQLInstanceName', 'DtcSupportEnabled') -notcontains $_.Key ) { $currentParameter = $_.Key # Move on if we're testing a version less than 13 and it's a property that was only introduced in 13 if ( ( $sql13AvailabilityGroupProperties -contains $currentParameter ) -and ( $versionToTest -lt 13 ) ) { # Move to the next parameter return } # Get the current parameter object $currentParameterObject = ( Get-Command Test-TargetResource ).Parameters.$currentParameter switch ( $currentParameterObject.ParameterType.ToString() ) { 'System.Boolean' { # Get the opposite value of what is supplied $testCaseParameterValue = -not $mockResourceParameters.$currentParameter } 'System.UInt32' { # Change the supplied number to something else. Absolute value is to protect against zero minus 1 $testCaseParameterValue = [System.Math]::Abs( ( $mockResourceParameters.$currentParameter - 1 ) ) } 'System.String' { # Get the valid values for the current parameter $currentParameterValidValues = $currentParameterObject.Attributes.ValidValues # Select a value other than what is defined in the mocks $testCaseParameterValue = $currentParameterValidValues | Where-Object -FilterScript { $_ -ne $mockResourceParameters.$currentParameter } | Select-Object -First 1 # If the value is null or empty, set it to something if ( [string]::IsNullOrEmpty($testCaseParameterValue) ) { $testCaseParameterValue = 'IncorrectValue' } } default { $testCaseParameterValue = $null } } return @{ Ensure = 'Present' Parameter = $currentParameter ParameterValue = $testCaseParameterValue Version = $versionToTest } } } # One-off test for when the endpoint host name is not specified return @{ Ensure = 'Present' Parameter = 'EndpointHostName' ParameterValue = '' Version = $versionToTest } } # Build a few test cases specifically for the EndpointUrl components [array]$presentEndpointUrlTestCases = $versionsToTest | ForEach-Object -Process { $versionToTest = $_ return @( @{ Ensure = 'Present' MockVariableName = 'mockAvailabilityReplicaEndpointProtocol' MockVariableValue = 'UDP' Version = $versionToTest }, @{ Ensure = 'Present' MockVariableName = 'mockAvailabilityReplicaEndpointPort' MockVariableValue = 1234 Version = $versionToTest } ) } #endregion test cases } BeforeEach { $mockAvailabilityReplicaEndpointPort = 5022 $mockAvailabilityReplicaEndpointProtocol = $mockAvailabilityReplica1EndpointProtocol $mockClusterPermissionsExist = $true $mockDatabaseMirroringEndpointPresent = $true $mockDatabaseMirroringEndpointProtocol = 'TCP' $mockDatabaseMirroringEndpointPort = 5022 $mockIsHadrEnabled = $true } Context 'When the Availability Group is Absent' { BeforeAll { $mockAvailabilityGroupName = $mockAvailabilityGroup1Name } It 'Should create the Availability Group when Ensure is set to Present and the SQL version is "<Version>"' -TestCases $createAvailabilityGroupTestCases { param ( $Name, $Version ) $currentTestParameters = $mockResourceParameters.Clone() $currentTestParameters.Name = $Name { Set-TargetResource @currentTestParameters } | Should Not Throw Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly -ParameterFilter { $SQLServer -eq $mockServer1Name } Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly -ParameterFilter { $SQLServer -eq $mockServer2Name } 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 Import-SQLPSModule -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaAbsentName } Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 1 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 1 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupCreateErrorName } Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroup -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly } It 'Should throw the correct error "HadrNotEnabled" when Ensure is set to Present, but HADR is not enabled' { $mockIsHadrEnabled = $false { Set-TargetResource @mockResourceParameters } | Should Throw 'HadrNotEnabled' Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly -ParameterFilter { $SQLServer -eq $mockServer1Name } Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly -ParameterFilter { $SQLServer -eq $mockServer2Name } 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 Import-SQLPSModule -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaAbsentName } Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupCreateErrorName } Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroup -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly } It 'Should throw the correct error "DatabaseMirroringEndpointNotFound" when Ensure is set to Present, but no DatabaseMirroring endpoints are present' { $mockDatabaseMirroringEndpointPresent = $false { Set-TargetResource @mockResourceParameters } | Should Throw 'DatabaseMirroringEndpointNotFound' Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly -ParameterFilter { $SQLServer -eq $mockServer1Name } Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly -ParameterFilter { $SQLServer -eq $mockServer2Name } 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 Import-SQLPSModule -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaAbsentName } Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupCreateErrorName } Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroup -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly } It 'Should throw the correct error, CreateAvailabilityGroupReplicaFailed, when Ensure is set to Present, but the Availability Group Replica failed to create and the SQL version is <Version>' -TestCases $createAvailabilityGroupTestCases { param ( $Name, $Version ) $currentTestParameters = $mockResourceParameters.Clone() $currentTestParameters.Name = $Name $currentTestParameters.SQLServer = $mockServer2Name { Set-TargetResource @currentTestParameters } | Should Throw 'CreateAvailabilityGroupReplicaFailed' Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly -ParameterFilter { $SQLServer -eq $mockServer1Name } Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly -ParameterFilter { $SQLServer -eq $mockServer2Name } 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 Import-SQLPSModule -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 1 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaAbsentName } Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupCreateErrorName } Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 1 Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroup -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly } It 'Should throw the correct error "CreateAvailabilityGroupFailed" when Ensure is set to Present, but the Availability Group failed to create and the SQL version is <Version>' -TestCases $createAvailabilityGroupTestCases { param ( $Name, $Version ) $currentTestParameters = $mockResourceParameters.Clone() $currentTestParameters.Name = $mockAvailabilityGroupCreateErrorName { Set-TargetResource @currentTestParameters } | Should Throw 'CreateAvailabilityGroupFailed' Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly -ParameterFilter { $SQLServer -eq $mockServer1Name } Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly -ParameterFilter { $SQLServer -eq $mockServer2Name } 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 Import-SQLPSModule -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaAbsentName } Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 1 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 1 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupCreateErrorName } Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 1 Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $InputObject.Name -eq $mockAvailabilityGroup1Name } Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $InputObject.Name -eq $mockAvailabilityGroupRemoveErrorName } Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroup -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly } } Context 'When the Availability Group is Present' { BeforeEach { $mockAvailabilityGroupName = $mockAvailabilityGroup1Name $mockAvailabilityGroup1PrimaryReplicaServerName = $mockServer1Name } It 'Should remove the Availability Group when Ensure is set to Absent and the SQL version is <Version>' -TestCases $removeAvailabilityGroupTestCases { param ( $Version ) $currentTestParameters = $mockResourceParameters.Clone() $currentTestParameters.Ensure = 'Absent' { Set-TargetResource @currentTestParameters } | Should Not Throw Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly -ParameterFilter { $SQLServer -eq $mockServer1Name } Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly -ParameterFilter { $SQLServer -eq $mockServer2Name } 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 Import-SQLPSModule -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaAbsentName } Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupCreateErrorName } Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 1 -Exactly -ParameterFilter { $InputObject.Name -eq $mockAvailabilityGroup1Name } Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $InputObject.Name -eq $mockAvailabilityGroupRemoveErrorName } Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroup -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly } It 'Should throw the correct error message "InstanceNotPrimaryReplica" when Ensure is "Absent", the primary replica is not on the current instance, and the SQL Version is <Version>' -TestCases $removeAvailabilityGroupTestCases { param ( $Version ) $mockAvailabilityGroup1PrimaryReplicaServerName = $mockServer2Name $currentTestParameters = $mockResourceParameters.Clone() $currentTestParameters.Ensure = 'Absent' { Set-TargetResource @currentTestParameters } | Should Throw 'InstanceNotPrimaryReplica' Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly -ParameterFilter { $SQLServer -eq $mockServer1Name } Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly -ParameterFilter { $SQLServer -eq $mockServer2Name } 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 Import-SQLPSModule -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaAbsentName } Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupCreateErrorName } Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $InputObject.Name -eq $mockAvailabilityGroup1Name } Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $InputObject.Name -eq $mockAvailabilityGroupRemoveErrorName } Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroup -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly } It 'Should throw the correct error message when Ensure is "Absent", the Availability Group remove fails, and the SQL version is <Version>' -TestCases $removeAvailabilityGroupTestCases { param ( $Version ) $mockAvailabilityGroupName = $mockAvailabilityGroupRemoveErrorName $currentTestParameters = $mockResourceParameters.Clone() $currentTestParameters.Ensure = 'Absent' $currentTestParameters.Name = $mockAvailabilityGroupRemoveErrorName { Set-TargetResource @currentTestParameters } | Should Throw 'RemoveAvailabilityGroupFailed' Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly -ParameterFilter { $SQLServer -eq $mockServer1Name } Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly -ParameterFilter { $SQLServer -eq $mockServer2Name } 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 Import-SQLPSModule -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaAbsentName } Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupCreateErrorName } Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $InputObject.Name -eq $mockAvailabilityGroup1Name } Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 1 -Exactly -ParameterFilter { $InputObject.Name -eq $mockAvailabilityGroupRemoveErrorName } Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroup -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly } It 'Should connect to the instance hosting the primary replica when the LocalReplicaRole is not Primary and the SQL version is <Version>' -TestCases $removeAvailabilityGroupTestCases { param ( $Version ) $currentTestParameters = $mockResourceParameters.Clone() $currentTestParameters.SQLServer = $mockServer2Name { Set-TargetResource @currentTestParameters } | Should Not Throw Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly -ParameterFilter { $SQLServer -eq $mockServer1Name } Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly -ParameterFilter { $SQLServer -eq $mockServer2Name } Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer1Name } Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name } Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaAbsentName } Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupCreateErrorName } Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $InputObject.Name -eq $mockAvailabilityGroup1Name } Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $InputObject.Name -eq $mockAvailabilityGroupRemoveErrorName } Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroup -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 1 -Exactly } It 'Should set the property "<Parameter>" to the desired state "<ParameterValue>" when the version is "<Version>"' -TestCases $presentParameterTestCases { param ( $Ensure, $Parameter, $ParameterValue, $Version ) $currentTestParameters = $mockResourceParameters.Clone() $currentTestParameters.$Parameter = $ParameterValue # Define how many times each mock should be called if ( $currentTestParameters.Ensure -eq 'Present' ) { $assertConnectSql = 1 $assertGetPrimaryReplicaServerObject = 1 $assertRemoveSqlAvailabilityGroup = 0 $assertTestClusterPermissions = 1 # Determine if the Availability Group or Availability Group Replica is being updated if ( $availabilityGroupReplicaProperties -contains ( Get-Variable -Name Parameter ).Value ) { $assertUpdateAvailabilityGroup = 0 $assertUpdateAvailabilityGroupReplica = 1 } else { $assertUpdateAvailabilityGroup = 1 $assertUpdateAvailabilityGroupReplica = 0 } # This validates the endpoint hostname is calculated correctly if ( ( $Parameter -eq 'EndpointHostName' ) -and ( [string]::IsNullOrEmpty($ParameterValue) ) ) { $assertUpdateAvailabilityGroup = 0 $assertUpdateAvailabilityGroupReplica = 0 } } else { $assertConnectSql = 1 $assertGetPrimaryReplicaServerObject = 0 $assertRemoveSqlAvailabilityGroup = 1 $assertTestClusterPermissions = 0 $assertUpdateAvailabilityGroup = 0 $assertUpdateAvailabilityGroupReplica = 0 } { Set-TargetResource @currentTestParameters } | Should Not Throw Assert-MockCalled -CommandName Connect-SQL -Scope It -Times $assertConnectSql -Exactly -ParameterFilter { $SQLServer -eq $mockServer1Name } Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly -ParameterFilter { $SQLServer -eq $mockServer2Name } Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time $assertGetPrimaryReplicaServerObject -Exactly -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer1Name } Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name } Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaAbsentName } Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupCreateErrorName } Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times $assertRemoveSqlAvailabilityGroup -Exactly -ParameterFilter { $InputObject.Name -eq $mockAvailabilityGroup1Name } Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $InputObject.Name -eq $mockAvailabilityGroupRemoveErrorName } Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times $assertTestClusterPermissions -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroup -Scope It -Times $assertUpdateAvailabilityGroup -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times $assertUpdateAvailabilityGroupReplica -Exactly } It 'Should set the property "EndpointUrl" to the desired state when the mock "<MockVariableName>" is "<MockVariableValue>" and the version is "<Version>"' -TestCases $presentEndpointUrlTestCases { param ( $Ensure, $MockVariableName, $MockVariableValue, $Version ) Set-Variable -Name $MockVariableName -Value $MockVariableValue { Set-TargetResource @mockResourceParameters } | Should Not Throw Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly -ParameterFilter { $SQLServer -eq $mockServer1Name } Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly -ParameterFilter { $SQLServer -eq $mockServer2Name } Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 1 -Exactly -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer1Name } Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name } Assert-MockCalled -CommandName Import-SQLPSModule -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaAbsentName } Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupReplicaPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupPresentName } Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $Name -eq $mockAvailabilityGroupCreateErrorName } Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $InputObject.Name -eq $mockAvailabilityGroup1Name } Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { $InputObject.Name -eq $mockAvailabilityGroupRemoveErrorName } Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroup -Scope It -Times 0 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 1 -Exactly } } } Describe "xSQLServerAlwaysOnAvailabilityGroup\Test-TargetResource" { BeforeAll { Mock -CommandName Connect-SQL -MockWith $mockConnectSqlServer1 -Verifiable -ParameterFilter { $SQLServer -eq $mockServer1Name } Mock -CommandName Connect-SQL -MockWith $mockConnectSqlServer2 -Verifiable -ParameterFilter { $SQLServer -eq $mockServer2Name } $mockAvailabilityGroupName = $mockAvailabilityGroup1Name $mockAvailabilityReplicaEndpointPort = 5022 $mockAvailabilityReplicaEndpointProtocol = $mockAvailabilityReplica1EndpointProtocol $mockDatabaseMirroringEndpointPresent = $true $mockDatabaseMirroringEndpointProtocol = 'TCP' $mockDatabaseMirroringEndpointPort = 5022 #region test cases $versionsToTest = @(12, 13) $absentTestCases = @( @{ Ensure = 'Present' Name = $mockAvailabilityGroupAbsentName Result = $false Version = 12 }, @{ Ensure = 'Present' Name = $mockAvailabilityGroupAbsentName Result = $false Version = 13 }, @{ Ensure = 'Absent' Name = $mockAvailabilityGroupAbsentName Result = $true Version = 12 }, @{ Ensure = 'Absent' Name = $mockAvailabilityGroupAbsentName Result = $true Version = 13 } ) $presentTestCases = @( @{ Ensure = 'Present' Name = $mockAvailabilityGroup1Name Result = $true Version = 12 }, @{ Ensure = 'Present' Name = $mockAvailabilityGroup1Name Result = $true Version = 13 }, @{ Ensure = 'Absent' Name = $mockAvailabilityGroup1Name Result = $false Version = 12 }, @{ Ensure = 'Absent' Name = $mockAvailabilityGroup1Name Result = $false Version = 13 } ) [array]$presentParameterTestCases = $versionsToTest | ForEach-Object -Process { $versionToTest = $_ $mockResourceParameters.GetEnumerator() | ForEach-Object -Process { if ( @('Name', 'SQLServer', 'SQLInstanceName', 'DtcSupportEnabled') -notcontains $_.Key ) { $currentParameter = $_.Key # Move on if we're testing a version less than 13 and it's a property that was only introduced in 13 if ( ( $sql13AvailabilityGroupProperties -contains $currentParameter ) -and ( $versionToTest -lt 13 ) ) { # Move to the next parameter return } # Get the current parameter object $currentParameterObject = ( Get-Command Test-TargetResource ).Parameters.$currentParameter switch ( $currentParameterObject.ParameterType.ToString() ) { 'System.Boolean' { # Get the opposite value of what is supplied $testCaseParameterValue = -not $mockResourceParameters.$currentParameter } 'System.UInt32' { # Change the supplied number to something else. Absolute value is to protect against zero minus 1 $testCaseParameterValue = [System.Math]::Abs( ( $mockResourceParameters.$currentParameter - 1 ) ) } 'System.String' { # Get the valid values for the current parameter $currentParameterValidValues = $currentParameterObject.Attributes.ValidValues # Select a value other than what is defined in the mocks $testCaseParameterValue = $currentParameterValidValues | Where-Object -FilterScript { $_ -ne $mockResourceParameters.$currentParameter } | Select-Object -First 1 # If the value is null or empty, set it to something if ( [string]::IsNullOrEmpty($testCaseParameterValue) ) { $testCaseParameterValue = 'IncorrectValue' } } default { $testCaseParameterValue = $null } } return @{ Ensure = 'Present' Result = $false Parameter = $currentParameter ParameterValue = $testCaseParameterValue Version = $versionToTest } } } # One-off test for when the endpoint host name is not specified return @{ Ensure = 'Present' Result = $true Parameter = 'EndpointHostName' ParameterValue = '' Version = $versionToTest } } # Build a few test cases specifically for the EndpointUrl components [array]$presentEndpointUrlTestCases = $versionsToTest | ForEach-Object -Process { $versionToTest = $_ return @( @{ Ensure = 'Present' Result = $false MockVariableName = 'mockAvailabilityReplicaEndpointProtocol' MockVariableValue = 'UDP' Version = $versionToTest }, @{ Ensure = 'Present' Result = $false MockVariableName = 'mockAvailabilityReplicaEndpointPort' MockVariableValue = 1234 Version = $versionToTest } ) } #endregion test cases } Context 'When the Availability Group is Absent' { It 'Should be "<Result>" when the desired state is "<Ensure>" and the SQL version is "<Version>"' -TestCases $absentTestCases { param ( $Ensure, $Name, $Result, $Version ) $currentTestParameters = $mockResourceParameters.Clone() $currentTestParameters.Ensure = $Ensure $currentTestParameters.Name = $Name Test-TargetResource @currentTestParameters | Should Be $Result Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly -ParameterFilter { $SQLServer -eq $mockServer1Name } Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly -ParameterFilter { $SQLServer -eq $mockServer2Name } } } Context 'When the Availability Group is Present' { It 'Should be "<Result>" when the desired state is "<Ensure>" and the SQL version is "<Version>"' -TestCases $presentTestCases { param ( $Ensure, $Name, $Result, $Version ) $currentTestParameters = $mockResourceParameters.Clone() $currentTestParameters.Ensure = $Ensure $currentTestParameters.Name = $Name Test-TargetResource @currentTestParameters | Should Be $Result Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly -ParameterFilter { $SQLServer -eq $mockServer1Name } Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly -ParameterFilter { $SQLServer -eq $mockServer2Name } } It 'Should be "<Result>" when the desired state is "<Ensure>", the parameter "<Parameter>" is not "<ParameterValue>", and the SQL version is "<Version>"' -TestCases $presentParameterTestCases { param ( $Ensure, $Result, $Parameter, $ParameterValue, $Version ) $currentTestParameters = $mockResourceParameters.Clone() $currentTestParameters.$Parameter = $ParameterValue Test-TargetResource @currentTestParameters | Should Be $Result Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly -ParameterFilter { $SQLServer -eq $mockServer1Name } Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly -ParameterFilter { $SQLServer -eq $mockServer2Name } } It 'Should be "<Result>" when the desired state is "<Ensure>", the mock "<MockVariableName>" is "<ParameterValue>", and the SQL version is "<Version>"' -TestCases $presentEndpointUrlTestCases { param ( $Ensure, $Result, $MockVariableName, $MockVariableValue, $Version ) Set-Variable -Name $MockVariableName -Value $MockVariableValue Test-TargetResource @mockResourceParameters | Should Be $Result Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly -ParameterFilter { $SQLServer -eq $mockServer1Name } Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly -ParameterFilter { $SQLServer -eq $mockServer2Name } } } } Describe "xSQLServerAlwaysOnAvailabilityGroup\Update-AvailabilityGroup" { BeforeAll { Mock -CommandName New-TerminatingError -MockWith { $ErrorType } $mockAvailabilityGroup = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityGroup } Context 'When the Availability Group is altered' { It 'Should silently alter the Availability Group' { { Update-AvailabilityGroup -AvailabilityGroup $mockAvailabilityGroup } | Should Not Throw Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 0 -Exactly } It 'Should throw the correct error, AlterAvailabilityGroupFailed, when altering the Availaiblity Group fails' { $mockAvailabilityGroup.Name = 'AlterFailed' { Update-AvailabilityGroup -AvailabilityGroup $mockAvailabilityGroup } | Should Throw 'AlterAvailabilityGroupFailed' Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 1 -Exactly } } } } } finally { Restore-TestEnvironment -TestEnvironment $TestEnvironment } |