diff --git a/CHANGELOG.md b/CHANGELOG.md index 20733854d..9b2e37d3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,19 @@ collation ([issue #767](https://github.com/PowerShell/xSQLServer/issues/767)). - Fixed unit tests for Get-TargetResource to ensure correctly testing return values ([issue #849](https://github.com/PowerShell/xSQLServer/issues/849)) +- Changes to xSQLServerAlwaysOnAvailabilityGroup + - Refactored the unit tests to allow them to be more user friendly and to test + additional SQLServer variations. + - Each test will utilize the Import-SQLModuleStub to ensure the correct + module is loaded ([issue #784](https://github.com/PowerShell/xSQLServer/issues/784)). + - Fixed an issue when setting the SQLServer parameter to a Fully Qualified + Domain Name (FQDN) ([issue #468](https://github.com/PowerShell/xSQLServer/issues/468)). + - Fixed the logic so that if a parameter is not supplied to the resource, the + resource will not attempt to apply the defaults on subsequent checks + ([issue #517](https://github.com/PowerShell/xSQLServer/issues/517)). +- Added the CommonTestHelper.psm1 to store common testing functions. + - Added the Import-SQLModuleStub function to ensure the correct version of the + module stubs are loaded ([issue #784](https://github.com/PowerShell/xSQLServer/issues/784)). ## 8.2.0.0 @@ -68,12 +81,12 @@ Group is created. - Use the new helper function "Test-ClusterPermissions". - Refactored the unit tests to allow them to be more user friendly. - Added the following read-only properties to the schema ([issue #476](https://github.com/PowerShell/xSQLServer/issues/476)) + - Added the following read-only properties to the schema ([issue #476](https://github.com/PowerShell/xSQLServer/issues/476)) - EndpointPort - EndpointURL - SQLServerNetName - Version - - Use the Get-PrimaryReplicaServerObject helper function + - Use the Get-PrimaryReplicaServerObject helper function. - Changes to xSQLServerAlwaysOnAvailabilityGroupReplica - Fixed the formatting for the AvailabilityGroupNotFound error. - Added the following read-only properties to the schema ([issue #477](https://github.com/PowerShell/xSQLServer/issues/477)) diff --git a/DSCResources/MSFT_xSQLServerAlwaysOnAvailabilityGroup/MSFT_xSQLServerAlwaysOnAvailabilityGroup.psm1 b/DSCResources/MSFT_xSQLServerAlwaysOnAvailabilityGroup/MSFT_xSQLServerAlwaysOnAvailabilityGroup.psm1 index c5ef58f91..f239a9dd3 100644 --- a/DSCResources/MSFT_xSQLServerAlwaysOnAvailabilityGroup/MSFT_xSQLServerAlwaysOnAvailabilityGroup.psm1 +++ b/DSCResources/MSFT_xSQLServerAlwaysOnAvailabilityGroup/MSFT_xSQLServerAlwaysOnAvailabilityGroup.psm1 @@ -59,14 +59,14 @@ function Get-TargetResource SQLInstanceName = $SQLInstanceName Ensure = 'Present' AutomatedBackupPreference = $availabilityGroup.AutomatedBackupPreference - AvailabilityMode = $availabilityGroup.AvailabilityReplicas[$serverObject.Name].AvailabilityMode - BackupPriority = $availabilityGroup.AvailabilityReplicas[$serverObject.Name].BackupPriority - ConnectionModeInPrimaryRole = $availabilityGroup.AvailabilityReplicas[$serverObject.Name].ConnectionModeInPrimaryRole - ConnectionModeInSecondaryRole = $availabilityGroup.AvailabilityReplicas[$serverObject.Name].ConnectionModeInSecondaryRole + AvailabilityMode = $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].AvailabilityMode + BackupPriority = $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].BackupPriority + ConnectionModeInPrimaryRole = $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].ConnectionModeInPrimaryRole + ConnectionModeInSecondaryRole = $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].ConnectionModeInSecondaryRole FailureConditionLevel = $availabilityGroup.FailureConditionLevel - FailoverMode = $availabilityGroup.AvailabilityReplicas[$serverObject.Name].FailoverMode + FailoverMode = $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].FailoverMode HealthCheckTimeout = $availabilityGroup.HealthCheckTimeout - EndpointURL = $availabilityGroup.AvailabilityReplicas[$serverObject.Name].EndpointUrl + EndpointURL = $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].EndpointUrl EndpointPort = $endpointPort SQLServerNetName = $serverObject.NetName Version = $sqlMajorVersion @@ -252,7 +252,7 @@ function Set-TargetResource if ( $availabilityGroup ) { # If the primary replica is currently on this instance - if ( $availabilityGroup.PrimaryReplicaServerName -eq $serverObject.Name ) + if ( $availabilityGroup.PrimaryReplicaServerName -eq $serverObject.DomainInstanceName ) { try { @@ -292,7 +292,7 @@ function Set-TargetResource { # Set up the parameters to create the AG Replica $newReplicaParams = @{ - Name = $serverObject.Name + Name = $serverObject.DomainInstanceName Version = $sqlMajorVersion AsTemplate = $true AvailabilityMode = $AvailabilityMode @@ -367,92 +367,95 @@ function Set-TargetResource # Otherwise let's check each of the parameters passed and update the Availability Group accordingly else { + # Get the parameters that were submitted to the function + [System.Array] $submittedParameters = $PSBoundParameters.Keys + # Make sure we're communicating with the primary replica $primaryServerObject = Get-PrimaryReplicaServerObject -ServerObject $serverObject -AvailabilityGroup $availabilityGroup $availabilityGroup = $primaryServerObject.AvailabilityGroups[$Name] - if ( $AutomatedBackupPreference -ne $availabilityGroup.AutomatedBackupPreference ) + if ( ( $submittedParameters -contains 'AutomatedBackupPreference' ) -and ( $AutomatedBackupPreference -ne $availabilityGroup.AutomatedBackupPreference ) ) { $availabilityGroup.AutomatedBackupPreference = $AutomatedBackupPreference Update-AvailabilityGroup -AvailabilityGroup $availabilityGroup } - if ( $AvailabilityMode -ne $availabilityGroup.AvailabilityReplicas[$serverObject.Name].AvailabilityMode ) + if ( ( $submittedParameters -contains 'AvailabilityMode' ) -and ( $AvailabilityMode -ne $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].AvailabilityMode ) ) { - $availabilityGroup.AvailabilityReplicas[$serverObject.Name].AvailabilityMode = $AvailabilityMode - Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroup.AvailabilityReplicas[$serverObject.Name] + $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].AvailabilityMode = $AvailabilityMode + Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName] } - if ( $BackupPriority -ne $availabilityGroup.AvailabilityReplicas[$serverObject.Name].BackupPriority ) + if ( ( $submittedParameters -contains 'BackupPriority' ) -and ( $BackupPriority -ne $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].BackupPriority ) ) { - $availabilityGroup.AvailabilityReplicas[$serverObject.Name].BackupPriority = $BackupPriority - Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroup.AvailabilityReplicas[$serverObject.Name] + $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].BackupPriority = $BackupPriority + Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName] } - if ( ( $sqlMajorVersion -ge 13 ) -and ( $BasicAvailabilityGroup -ne $availabilityGroup.BasicAvailabilityGroup ) ) + if ( ( $submittedParameters -contains 'BasicAvailabilityGroup' ) -and ( $sqlMajorVersion -ge 13 ) -and ( $BasicAvailabilityGroup -ne $availabilityGroup.BasicAvailabilityGroup ) ) { $availabilityGroup.BasicAvailabilityGroup = $BasicAvailabilityGroup Update-AvailabilityGroup -AvailabilityGroup $availabilityGroup } - if ( ( $sqlMajorVersion -ge 13 ) -and ( $DatabaseHealthTrigger -ne $availabilityGroup.DatabaseHealthTrigger ) ) + if ( ( $submittedParameters -contains 'DatabaseHealthTrigger' ) -and ( $sqlMajorVersion -ge 13 ) -and ( $DatabaseHealthTrigger -ne $availabilityGroup.DatabaseHealthTrigger ) ) { $availabilityGroup.DatabaseHealthTrigger = $DatabaseHealthTrigger Update-AvailabilityGroup -AvailabilityGroup $availabilityGroup } # Make sure ConnectionModeInPrimaryRole has a value in order to avoid false positive matches when the parameter is not defined - if ( ( -not [string]::IsNullOrEmpty($ConnectionModeInPrimaryRole) ) -and ( $ConnectionModeInPrimaryRole -ne $availabilityGroup.AvailabilityReplicas[$serverObject.Name].ConnectionModeInPrimaryRole ) ) + if ( ( $submittedParameters -contains 'ConnectionModeInPrimaryRole' ) -and ( -not [string]::IsNullOrEmpty($ConnectionModeInPrimaryRole) ) -and ( $ConnectionModeInPrimaryRole -ne $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].ConnectionModeInPrimaryRole ) ) { - $availabilityGroup.AvailabilityReplicas[$serverObject.Name].ConnectionModeInPrimaryRole = $ConnectionModeInPrimaryRole - Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroup.AvailabilityReplicas[$serverObject.Name] + $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].ConnectionModeInPrimaryRole = $ConnectionModeInPrimaryRole + Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName] } # Make sure ConnectionModeInSecondaryRole has a value in order to avoid false positive matches when the parameter is not defined - if ( ( -not [string]::IsNullOrEmpty($ConnectionModeInSecondaryRole) ) -and ( $ConnectionModeInSecondaryRole -ne $availabilityGroup.AvailabilityReplicas[$serverObject.Name].ConnectionModeInSecondaryRole ) ) + if ( ( $submittedParameters -contains 'ConnectionModeInSecondaryRole' ) -and ( -not [string]::IsNullOrEmpty($ConnectionModeInSecondaryRole) ) -and ( $ConnectionModeInSecondaryRole -ne $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].ConnectionModeInSecondaryRole ) ) { - $availabilityGroup.AvailabilityReplicas[$serverObject.Name].ConnectionModeInSecondaryRole = $ConnectionModeInSecondaryRole - Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroup.AvailabilityReplicas[$serverObject.Name] + $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].ConnectionModeInSecondaryRole = $ConnectionModeInSecondaryRole + Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName] } # Break out the EndpointUrl properties - $currentEndpointProtocol, $currentEndpointHostName, $currentEndpointPort = $availabilityGroup.AvailabilityReplicas[$serverObject.Name].EndpointUrl.Replace('//', '').Split(':') + $currentEndpointProtocol, $currentEndpointHostName, $currentEndpointPort = $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].EndpointUrl.Replace('//', '').Split(':') if ( $endpoint.Protocol.Tcp.ListenerPort -ne $currentEndpointPort ) { - $newEndpointUrl = $availabilityGroup.AvailabilityReplicas[$serverObject.Name].EndpointUrl.Replace($currentEndpointPort, $endpoint.Protocol.Tcp.ListenerPort) - $availabilityGroup.AvailabilityReplicas[$serverObject.Name].EndpointUrl = $newEndpointUrl - Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroup.AvailabilityReplicas[$serverObject.Name] + $newEndpointUrl = $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].EndpointUrl.Replace($currentEndpointPort, $endpoint.Protocol.Tcp.ListenerPort) + $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].EndpointUrl = $newEndpointUrl + Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName] } - if ( $EndpointHostName -ne $currentEndpointHostName ) + if ( ( $submittedParameters -contains 'EndpointHostName' ) -and ( $EndpointHostName -ne $currentEndpointHostName ) ) { - $newEndpointUrl = $availabilityGroup.AvailabilityReplicas[$serverObject.Name].EndpointUrl.Replace($currentEndpointHostName, $EndpointHostName) - $availabilityGroup.AvailabilityReplicas[$serverObject.Name].EndpointUrl = $newEndpointUrl - Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroup.AvailabilityReplicas[$serverObject.Name] + $newEndpointUrl = $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].EndpointUrl.Replace($currentEndpointHostName, $EndpointHostName) + $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].EndpointUrl = $newEndpointUrl + Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName] } if ( $currentEndpointProtocol -ne 'TCP' ) { - $newEndpointUrl = $availabilityGroup.AvailabilityReplicas[$serverObject.Name].EndpointUrl.Replace($currentEndpointProtocol, 'TCP') - $availabilityGroup.AvailabilityReplicas[$serverObject.Name].EndpointUrl = $newEndpointUrl - Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroup.AvailabilityReplicas[$serverObject.Name] + $newEndpointUrl = $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].EndpointUrl.Replace($currentEndpointProtocol, 'TCP') + $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].EndpointUrl = $newEndpointUrl + Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName] } # Make sure FailureConditionLevel has a value in order to avoid false positive matches when the parameter is not defined - if ( ( -not [string]::IsNullOrEmpty($FailureConditionLevel) ) -and ( $FailureConditionLevel -ne $availabilityGroup.FailureConditionLevel ) ) + if ( ( $submittedParameters -contains 'FailureConditionLevel' ) -and ( -not [string]::IsNullOrEmpty($FailureConditionLevel) ) -and ( $FailureConditionLevel -ne $availabilityGroup.FailureConditionLevel ) ) { $availabilityGroup.FailureConditionLevel = $FailureConditionLevel Update-AvailabilityGroup -AvailabilityGroup $availabilityGroup } - if ( $FailoverMode -ne $availabilityGroup.AvailabilityReplicas[$serverObject.Name].FailoverMode ) + if ( ( $submittedParameters -contains 'FailoverMode' ) -and ( $FailoverMode -ne $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].FailoverMode ) ) { - $availabilityGroup.AvailabilityReplicas[$serverObject.Name].FailoverMode = $FailoverMode - Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroup.AvailabilityReplicas[$serverObject.Name] + $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName].FailoverMode = $FailoverMode + Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroup.AvailabilityReplicas[$serverObject.DomainInstanceName] } - if ( $HealthCheckTimeout -ne $availabilityGroup.HealthCheckTimeout ) + if ( ( $submittedParameters -contains 'HealthCheckTimeout' ) -and ( $HealthCheckTimeout -ne $availabilityGroup.HealthCheckTimeout ) ) { $availabilityGroup.HealthCheckTimeout = $HealthCheckTimeout Update-AvailabilityGroup -AvailabilityGroup $availabilityGroup @@ -647,8 +650,11 @@ function Test-TargetResource if ( $getTargetResourceResult.Ensure -eq 'Present' ) { - # PsBoundParameters won't work here because it doesn't account for default values - foreach ( $parameter in $MyInvocation.MyCommand.Parameters.GetEnumerator() ) + # Use $PSBoundParameters rather than $MyInvocation.MyCommand.Parameters.GetEnumerator() + # This allows us to only validate the supplied parameters + # If the parameter is not defined by the configuration, we don't care what + # it gets set to. + foreach ( $parameter in $PSBoundParameters.GetEnumerator() ) { $parameterName = $parameter.Key $parameterValue = Get-Variable -Name $parameterName -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Value diff --git a/Tests/TestHelpers/CommonTestHelper.psm1 b/Tests/TestHelpers/CommonTestHelper.psm1 new file mode 100644 index 000000000..9a23339c0 --- /dev/null +++ b/Tests/TestHelpers/CommonTestHelper.psm1 @@ -0,0 +1,64 @@ +<# + .SYNOPSIS + Ensure the correct module stubs are loaded. + + .PARAMETER SQLVersion + The major version of the SQL instance. + + .PARAMETER ModuleName + The name of the module to load the stubs for. Default is 'SQLServer'. +#> +function Import-SQLModuleStub +{ + [CmdletBinding(DefaultParameterSetName = 'Module')] + param + ( + [Parameter(Mandatory = $true, ParameterSetName = 'Version')] + [System.UInt32] + $SQLVersion, + + [Parameter(ParameterSetName = 'Module')] + [ValidateSet('SQLPS','SQLServer')] + [System.String] + $ModuleName = 'SQLServer' + ) + + # Translate the module names to their appropriate stub name + $modulesAndStubs = @{ + SQLPS = 'SQLPSStub' + SQLServer = 'SQLServerStub' + } + + # Determine which module to ensure is loaded based on the parameters passed + if ( $PsCmdlet.ParameterSetName -eq 'Version' ) + { + if ( $SQLVersion -le 12 ) + { + $ModuleName = 'SQLPS' + } + elseif ( $SQLVersion -ge 13 ) + { + $ModuleName = 'SQLServer' + } + } + + # Get the stub name + $stubModuleName = $modulesAndStubs.$ModuleName + + # Ensure none of the other stub modules are loaded + [System.Array] $otherStubModules = $modulesAndStubs.Values | Where-Object -FilterScript { $_ -ne $stubModuleName } + + if ( Get-Module -Name $otherStubModules ) + { + Remove-Module -Name $otherStubModules + } + + # If the desired module is not loaded, load it now + if ( -not ( Get-Module -Name $stubModuleName ) ) + { + # Build the path to the module stub + $moduleStubPath = Join-Path -Path ( Join-Path -Path ( Join-Path -Path ( Split-Path -Path $PSScriptRoot -Parent ) -ChildPath Unit ) -ChildPath Stubs ) -ChildPath "$($stubModuleName).psm1" + + Import-Module -Name $moduleStubPath -Force -Global + } +} diff --git a/Tests/Unit/MSFT_xSQLServerAlwaysOnAvailabilityGroup.Tests.ps1 b/Tests/Unit/MSFT_xSQLServerAlwaysOnAvailabilityGroup.Tests.ps1 index 8ce8a2cb9..1bbf5c190 100644 --- a/Tests/Unit/MSFT_xSQLServerAlwaysOnAvailabilityGroup.Tests.ps1 +++ b/Tests/Unit/MSFT_xSQLServerAlwaysOnAvailabilityGroup.Tests.ps1 @@ -1,40 +1,181 @@ -$script:DSCModuleName = 'xSQLServer' -$script:DSCResourceName = 'MSFT_xSQLServerAlwaysOnAvailabilityGroup' +#region HEADER -# Unit Test Template Version: 1.1.0 -[String] $script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +# Unit Test Template Version: 1.2.1 +$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'))) ) + (-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\')) + & 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 +Import-Module -Name (Join-Path -Path $script:moduleRoot -ChildPath (Join-Path -Path 'DSCResource.Tests' -ChildPath 'TestHelper.psm1')) -Force +Import-Module -Name (Join-Path -Path $script:moduleRoot -ChildPath (Join-Path -Path 'Tests' -ChildPath (Join-Path -Path 'TestHelpers' -ChildPath 'CommonTestHelper.psm1'))) -Force -Global + $TestEnvironment = Initialize-TestEnvironment ` - -DSCModuleName $script:DSCModuleName ` - -DSCResourceName $script:DSCResourceName ` + -DSCModuleName 'xSQLServer' ` + -DSCResourceName 'MSFT_xSQLServerAlwaysOnAvailabilityGroup' ` -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 ) +#endregion HEADER + +function Invoke-TestSetup { + # Load the SMO stubs + Add-Type -Path ( Join-Path -Path ( Join-Path -Path $PSScriptRoot -ChildPath Stubs ) -ChildPath SMO.cs ) + + # Load the default SQL Module stub + Import-SQLModuleStub +} + +function Invoke-TestCleanup { + Restore-TestEnvironment -TestEnvironment $TestEnvironment +} # Begin Testing try { - InModuleScope $script:DSCResourceName { + Invoke-TestSetup + + InModuleScope 'MSFT_xSQLServerAlwaysOnAvailabilityGroup' { + + #region parameter mocks + + # Define the values that could be passed into the Name parameter + $mockNameParameters = @{ + AbsentAvailabilityGroup = 'AvailabilityGroup2' + CreateAvailabilityGroupFailed = 'AvailabilityGroup5' + CreateAvailabilityGroupReplicaFailed = 'AvailabilityGroup4' + PresentAvailabilityGroup = 'AvailabilityGroup1' + RemoveAvailabilityGroupFailed = 'AvailabilityGroup3' + } + + # Define the values that could be passed into the SQLServer parameter + $mockSqlServerParameters = @{ + Server1 = @{ + FQDN = 'Server1.contoso.com' + #IP = '192.168.1.1' + #NetBIOS = 'Server1' + } + + Server2 = @{ + FQDN = 'Server2.contoso.com' + #IP = '192.168.1.2' + #NetBIOS = 'Server2' + } + } + + # Define the values that could be passed into the SQLInstanceName parameter + $mockSqlInstanceNameParameters = @( + 'MSSQLSERVER', + 'NamedInstance' + ) + + #endregion parameter mocks + + #region property mocks + + $mockIsHadrEnabled = $true + $mockIsDatabaseMirroringEndpointPresent = $true + + $mockServerObjectProperies = @{ + Server1 = @{ + NetName = 'Server1' + } + + Server2 = @{ + NetName = 'Server2' + } + } - #region mock server object variables + $mockAvailabilityGroupProperties = @{ + AutomatedBackupPreference = 'Secondary' # Not the default parameter value + BasicAvailabilityGroup = $true # Not the default parameter value + DatabaseHealthTrigger = $true # Not the default parameter value + DtcSupportEnabled = $true # Not the default parameter value + FailureConditionLevel = 'OnCriticalServerErrors' # Not the default parameter value + HealthCheckTimeout = 20000 + Name = $mockNameParameters.PresentAvailabilityGroup + PrimaryReplicaServerName = $mockServerObjectProperies.Server1.NetName + } - $mockServer1Name = 'Server1' - $mockServer1ServiceName = 'MSSQLSERVER' + $mockAvailabilityGroupReplicaProperties = @{ + Server1 = @{ + AvailabilityMode = 'SynchronousCommit' # Not the default parameter value + BackupPriority = 49 # Not the default parameter value + ConnectionModeInPrimaryRole = 'AllowAllConnections' # Not the default parameter value + ConnectionModeInSecondaryRole = 'AllowNoConnections' # Not the default parameter value + EndpointHostName = $mockServerObjectProperies.Server1.NetName + EndpointProtocol = 'TCP' + EndpointPort = 5022 + FailoverMode = 'Automatic' # Not the default parameter value + Name = $mockServerObjectProperies.Server1.NetName + Role = 'Primary' + } - $mockServer2Name = 'Server2' - $mockServer2ServiceName = 'MSSQLSERVER' + Server2 = @{ + AvailabilityMode = 'SynchronousCommit' # Not the default parameter value + BackupPriority = 49 # Not the default parameter value + ConnectionModeInPrimaryRole = 'AllowAllConnections' # Not the default parameter value + ConnectionModeInSecondaryRole = 'AllowNoConnections' # Not the default parameter value + EndpointHostName = $mockServerObjectProperies.Server2.NetName + EndpointProtocol = 'TCP' + EndpointPort = 5022 + FailoverMode = 'Automatic' # Not the default parameter value + Name = $mockServerObjectProperies.Server2.NetName + Role = 'Primary' + } + } - #endregion mock server object variables + $mockDatabaseMirroringEndpointProperties = @{ + Protocol = 'TCP' + ListenerPort = 5022 + } - #region mock availability group variables + #endregion property mocks + + #region test cases + + $getTargetResourceAbsentTestCases = @() + $getTargetResourcePresentTestCases = @() + $setTargetResourceCreateAvailabilityGroupFailedTestCases = @() + $setTargetResourceCreateAvailabilityGroupWithParameterTestCases = @() + $setTargetResourcesEndpointUrlTestCases = @() + $setTargetResourceEndpointMissingTestCases = @() + $setTargetResourceHadrDisabledTestCases = @() + $setTargetResourcePropertyIncorrectTestCases = @() + $setTargetResourceRemoveAvailabilityGroupTestCases = @() + $setTargetResourceRemoveAvailabilityGroupErrorTestCases = @() + $testTargetResourceAbsentTestCases = @() + $testTargetResourceEndpointIncorrectTestCases = @() + $testTargetResourcePresentTestCases = @() + $testTargetResourcePropertyIncorrectTestCases = @() + + $majorVersionsToTest = @(12,13) + $ensureCasesToTest = @('Absent','Present') + $endpointUrlPropertiesToTest = @('EndpointPort','EndpointProtocol') + + $createAvailabilityGroupFailuresToTest = @{ + $mockNameParameters.CreateAvailabilityGroupFailed = 'CreateAvailabilityGroupFailed' + $mockNameParameters.CreateAvailabilityGroupReplicaFailed = 'CreateAvailabilityGroupReplicaFailed' + } + + # Get all of the parameters tied with the resource except the required parameters, Ensure, and DtcSupportEnabled + $resourceParameters = @{} + ( Get-Command -Name Test-TargetResource ).Parameters.Values | Where-Object -FilterScript { + ( + # Ignore these specific parameters. These get tested enough. + @('Ensure', 'Name', 'SQLServer', 'SQLInstanceName', 'DtcSupportEnabled') -notcontains $_.Name + ) -and ( + # Ignore the CmdletBinding parameters + $_.Attributes.TypeId.Name -notcontains 'AliasAttribute' + ) + } | ForEach-Object -Process { + $currentParameter = $_.Name + + $resourceParameters.Add( + $currentParameter, + ( @($mockAvailabilityGroupProperties.$currentParameter,$mockAvailabilityGroupReplicaProperties.Server1.$currentParameter) -join '' ) + ) + } # Define the properties that are SQL 2016 and newer $sql13AvailabilityGroupProperties = @( @@ -43,65 +184,264 @@ try 'DtcSupportEnabled' ) - # Define properties that are Availability Replica properties - $availabilityGroupReplicaProperties = @( - 'AvailabilityMode', - 'BackupPriority', - 'ConnectionModeInPrimaryRole', - 'ConnectionModeInSecondaryRole', - 'EndpointHostName', - 'FailoverMode' - ) + foreach ( $majorVersionToTest in $majorVersionsToTest ) + { + foreach ( $mockSqlServer in $mockSqlServerParameters.GetEnumerator() ) + { + # Determine with SQL Server mock will be used + $mockSqlServerToBeUsed = $mockSqlServer.Key + + foreach ( $mockSqlServerParameter in $mockSqlServer.Value.Values ) + { + foreach ( $mockSqlInstanceNameParameter in $mockSqlInstanceNameParameters ) + { + # Build the domain instance name + if ( $mockSqlInstanceNameParameter -eq 'MSSQLSERVER' ) + { + $domainInstanceNameProperty = $mockSqlServerParameter + } + else + { + $domainInstanceNameProperty = '{0}\{1}' -f $mockSqlServerParameter,$mockSqlInstanceNameParameter + } + + if ( $mockSqlServerToBeUsed -eq 'Server1' ) + { + $getTargetResourceAbsentTestCases += @{ + Name = $mockNameParameters.AbsentAvailabilityGroup + SQLServer = $mockSqlServerParameter + SQLInstanceName = $mockSqlInstanceNameParameter + Version = $majorVersionToTest + } + + $getTargetResourcePresentTestCases += @{ + Name = $mockNameParameters.PresentAvailabilityGroup + SQLServer = $mockSqlServerParameter + SQLInstanceName = $mockSqlInstanceNameParameter + Version = $majorVersionToTest + } + + foreach ( $createAvailabilityGroupFailureToTest in $createAvailabilityGroupFailuresToTest.GetEnumerator() ) + { + $setTargetResourceCreateAvailabilityGroupFailedTestCases += @{ + ErrorResult = $createAvailabilityGroupFailureToTest.Value + Ensure = 'Present' + Name = $createAvailabilityGroupFailureToTest.Key + SQLServer = $mockSqlServerParameter + SQLInstanceName = $mockSqlInstanceNameParameter + Version = $majorVersionToTest + } + } + + $setTargetResourceEndpointMissingTestCases += @{ + Ensure = 'Present' + Name = $mockNameParameters.AbsentAvailabilityGroup + Result = 'DatabaseMirroringEndpointNotFound' + SQLServer = $mockSqlServerParameter + SQLInstanceName = $mockSqlInstanceNameParameter + Version = $majorVersionToTest + } + + $setTargetResourceHadrDisabledTestCases += @{ + Ensure = 'Present' + Name = $mockNameParameters.AbsentAvailabilityGroup + Result = 'HadrNotEnabled' + SQLServer = $mockSqlServerParameter + SQLInstanceName = $mockSqlInstanceNameParameter + Version = $majorVersionToTest + } + + $setTargetResourceRemoveAvailabilityGroupTestCases += @{ + Ensure = 'Absent' + Name = $mockNameParameters.PresentAvailabilityGroup + SQLServer = $mockSqlServerParameter + SQLInstanceName = $mockSqlInstanceNameParameter + Version = $majorVersionToTest + } + } + + switch ( $mockSqlServerToBeUsed ) + { + 'Server1' + { + $setTargetResourceRemoveAvailabilityGroupErrorTestCaseErrorResult = 'RemoveAvailabilityGroupFailed' + $setTargetResourceRemoveAvailabilityGroupErrorTestCaseName = $mockNameParameters.RemoveAvailabilityGroupFailed + } + + 'Server2' + { + $setTargetResourceRemoveAvailabilityGroupErrorTestCaseErrorResult = 'InstanceNotPrimaryReplica' + $setTargetResourceRemoveAvailabilityGroupErrorTestCaseName = $mockNameParameters.PresentAvailabilityGroup + } + } + + $setTargetResourceRemoveAvailabilityGroupErrorTestCases += @{ + ErrorResult = $setTargetResourceRemoveAvailabilityGroupErrorTestCaseErrorResult + Ensure = 'Absent' + Name = $setTargetResourceRemoveAvailabilityGroupErrorTestCaseName + SQLServer = $mockSqlServerParameter + SQLInstanceName = $mockSqlInstanceNameParameter + Version = $majorVersionToTest + } + + # Create test cases for Absent/Present + foreach ( $ensureCaseToTest in $ensureCasesToTest ) + { + $testTargetResourceAbsentTestCases += @{ + Ensure = $ensureCaseToTest + Name = $mockNameParameters.AbsentAvailabilityGroup + Result = ( $ensureCaseToTest -eq 'Absent' ) + SQLServer = $mockSqlServerParameter + SQLInstanceName = $mockSqlInstanceNameParameter + Version = $majorVersionToTest + } + + $testTargetResourcePresentTestCases += @{ + Ensure = $ensureCaseToTest + Name = $mockNameParameters.PresentAvailabilityGroup + Result = ( $ensureCaseToTest -eq 'Present' ) + SQLServer = $mockSqlServerParameter + SQLInstanceName = $mockSqlInstanceNameParameter + Version = $majorVersionToTest + } + } + + # Create Present test cases for each parameter + foreach ( $resourceParameter in $resourceParameters.GetEnumerator() ) + { + # 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 $resourceParameter.Key ) -and ( $majorVersionToTest -lt 13 ) ) + { + # Move to the next parameter + continue + } + + # Get the current parameter object + $currentParameterObject = ( Get-Command Test-TargetResource ).Parameters.($resourceParameter.Key) + + switch ( $currentParameterObject.ParameterType.ToString() ) + { + 'System.Boolean' + { + # Get the opposite value of what is supplied + $testCaseParameterValue = -not $resourceParameter.Value + } + + 'System.UInt32' + { + # Change the supplied number to something else. Absolute value is to protect against zero minus 1 + $testCaseParameterValue = [System.Math]::Abs( ( $resourceParameter.Value - 1 ) ) + } - # 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 + '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 $resourceParameter.Value + } | Select-Object -First 1 + + # If the value is null or empty, set it to something + if ( [string]::IsNullOrEmpty($testCaseParameterValue) ) + { + $testCaseParameterValue = 'AnotherHostName' + } + } + + default + { + $testCaseParameterValue = $null + } + } + + if ( $mockSqlServerToBeUsed -eq 'Server1' ) + { + $setTargetResourceCreateAvailabilityGroupWithParameterTestCases += @{ + DomainInstanceName = $domainInstanceNameProperty + Ensure = 'Present' + Name = $mockNameParameters.AbsentAvailabilityGroup + ParameterName = $resourceParameter.Key + ParameterValue = $testCaseParameterValue + SQLServer = $mockSqlServerParameter + SQLInstanceName = $mockSqlInstanceNameParameter + Version = $majorVersionToTest + } + } + + $setTargetResourcePropertyIncorrectTestCases += @{ + Ensure = 'Present' + Name = $mockNameParameters.PresentAvailabilityGroup + ParameterName = $resourceParameter.Key + ParameterValue = $testCaseParameterValue + Result = $false + SQLServer = $mockSqlServerParameter + SQLInstanceName = $mockSqlInstanceNameParameter + Version = $majorVersionToTest + } + + $testTargetResourcePropertyIncorrectTestCases += @{ + Ensure = 'Present' + Name = $mockNameParameters.PresentAvailabilityGroup + ParameterName = $resourceParameter.Key + ParameterValue = $testCaseParameterValue + Result = $false + SQLServer = $mockSqlServerParameter + SQLInstanceName = $mockSqlInstanceNameParameter + Version = $majorVersionToTest + } + } + + # Create Present test cases for the endpoint components + foreach ( $endpointProperty in $endpointUrlPropertiesToTest ) + { + switch ( $mockAvailabilityGroupReplicaProperties.Server1.$endpointProperty.GetType().ToString() ) + { + 'System.Int32' + { + # Change the supplied number to something else. Absolute value is to protect against zero minus 1 + $endpointPropertyValue = [System.Math]::Abs( ( $mockAvailabilityGroupReplicaProperties.Server1.$endpointProperty - 1 ) ) + } + + 'System.String' + { + $endpointPropertyValue = 'UDP' + } + } + + $setTargetResourcesEndpointUrlTestCases += @{ + EndpointPropertyName = $endpointProperty + EndpointPropertyValue = $endpointPropertyValue + Ensure = 'Present' + Name = $mockNameParameters.PresentAvailabilityGroup + SQLServer = $mockSqlServerParameter + SQLInstanceName = $mockSqlInstanceNameParameter + Version = $majorVersionToTest + } + + $testTargetResourceEndpointIncorrectTestCases += @{ + EndpointPropertyName = $endpointProperty + EndpointPropertyValue = $endpointPropertyValue + Ensure = 'Present' + Name = $mockNameParameters.PresentAvailabilityGroup + Result = $false + SQLServer = $mockSqlServerParameter + SQLInstanceName = $mockSqlInstanceNameParameter + Version = $majorVersionToTest + } + } + } + } + } + } + + #endregion test cases + + #region cmdlet mocks + + $mockConnectSql = { + param ( [Parameter()] [string] @@ -121,65 +461,102 @@ try $ServerObject ) - # Define the server object + # If this mock function is called from the Get-PrimaryReplicaServerObject command mock + if ( [string]::IsNullOrEmpty($SQLServer) -and [string]::IsNullOrEmpty($SQLInstanceName) -and $AvailabilityGroup -and $ServerObject ) + { + $SQLServer,$SQLInstanceName = $AvailabilityGroup.PrimaryReplicaServerName.Split('\') + } + + # Determine which SQL Server mock data we will use + $mockSqlServer = ( $mockSqlServerParameters.GetEnumerator() | Where-Object -FilterScript { $_.Value.Values -contains $SQLServer } ).Name + if ( [string]::IsNullOrEmpty($mockSqlServer) ) + { + $mockSqlServer = $SQLServer + } + $mockCurrentServerObjectProperties = $mockServerObjectProperies.$mockSqlServer + + # Build the domain instance name + if ( ( $SQLInstanceName -eq 'MSSQLSERVER' ) -or [string]::IsNullOrEmpty($SQLInstanceName) ) + { + $mockDomainInstanceName = $mockCurrentServerObjectProperties.NetName + $mockPrimaryReplicaServerName = $mockAvailabilityGroupProperties.PrimaryReplicaServerName + $mockAvailabilityGroupReplica1Name = $mockAvailabilityGroupReplicaProperties.Server1.Name + $mockAvailabilityGroupReplica2Name = $mockAvailabilityGroupReplicaProperties.Server2.Name + } + else + { + $mockDomainInstanceName = '{0}\{1}' -f $mockCurrentServerObjectProperties.NetName,$SQLInstanceName + $mockPrimaryReplicaServerName = '{0}\{1}' -f $mockAvailabilityGroupProperties.PrimaryReplicaServerName,$SQLInstanceName + $mockAvailabilityGroupReplica1Name = '{0}\{1}' -f $mockAvailabilityGroupReplicaProperties.Server1.Name,$SQLInstanceName + $mockAvailabilityGroupReplica2Name = '{0}\{1}' -f $mockAvailabilityGroupReplicaProperties.Server2.Name,$SQLInstanceName + + } + $mockServerObject = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server + $mockServerObject.DomainInstanceName = $mockDomainInstanceName $mockServerObject.IsHadrEnabled = $mockIsHadrEnabled - $mockServerObject.Name = $mockServer1Name - $mockServerObject.NetName = $mockServer1Name - $mockServerObject.ServiceName = $mockServer1ServiceName + $mockServerObject.Name = $SQLServer + $mockServerObject.NetName = $mockCurrentServerObjectProperties.NetName + $mockServerObject.ServiceName = $SQLInstanceName $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 + # Define the availability group object + $mockAvailabilityGroupObject = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityGroup + $mockAvailabilityGroupObject.AutomatedBackupPreference = $mockAvailabilityGroupProperties.AutomatedBackupPreference + $mockAvailabilityGroupObject.FailureConditionLevel = $mockAvailabilityGroupProperties.FailureConditionLevel + $mockAvailabilityGroupObject.HealthCheckTimeout = $mockAvailabilityGroupProperties.HealthCheckTimeout + $mockAvailabilityGroupObject.Name = $mockAvailabilityGroupProperties.Name + $mockAvailabilityGroupObject.PrimaryReplicaServerName = $mockPrimaryReplicaServerName if ( $Version -ge 13 ) { - $mockAvailabilityGroup1Object.BasicAvailabilityGroup = $mockAvailabilityGroup1BasicAvailabilityGroup - $mockAvailabilityGroup1Object.DatabaseHealthTrigger = $mockAvailabilityGroup1DatabaseHealthTrigger - $mockAvailabilityGroup1Object.DtcSupportEnabled = $mockAvailabilityGroup1DtcSupportEnabled + $mockAvailabilityGroupObject.BasicAvailabilityGroup = $mockAvailabilityGroupProperties.BasicAvailabilityGroup + $mockAvailabilityGroupObject.DatabaseHealthTrigger = $mockAvailabilityGroupProperties.DatabaseHealthTrigger + $mockAvailabilityGroupObject.DtcSupportEnabled = $mockAvailabilityGroupProperties.DtcSupportEnabled } + # Define an availability group object to use when mocking a remove failure + $mockAvailabilityGroupRemoveFailedObject = New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityGroup + $mockAvailabilityGroupRemoveFailedObject.Name = $mockNameParameters.RemoveAvailabilityGroupFailed + $mockAvailabilityGroupRemoveFailedObject.PrimaryReplicaServerName = $mockPrimaryReplicaServerName + # 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 + $mockAvailabilityReplica1Object.Name = $mockAvailabilityGroupReplica1Name + $mockAvailabilityReplica1Object.AvailabilityMode = $mockAvailabilityGroupReplicaProperties.Server1.AvailabilityMode + $mockAvailabilityReplica1Object.BackupPriority = $mockAvailabilityGroupReplicaProperties.Server1.BackupPriority + $mockAvailabilityReplica1Object.ConnectionModeInPrimaryRole = $mockAvailabilityGroupReplicaProperties.Server1.ConnectionModeInPrimaryRole + $mockAvailabilityReplica1Object.ConnectionModeInSecondaryRole = $mockAvailabilityGroupReplicaProperties.Server1.ConnectionModeInSecondaryRole + $mockAvailabilityReplica1Object.EndpointUrl = "$($mockAvailabilityGroupReplicaProperties.Server1.EndpointProtocol)://$($mockAvailabilityGroupReplicaProperties.Server1.EndpointHostName):$($mockAvailabilityGroupReplicaProperties.Server1.EndpointPort)" + $mockAvailabilityReplica1Object.FailoverMode = $mockAvailabilityGroupReplicaProperties.Server1.FailoverMode + $mockAvailabilityReplica1Object.Role = $mockAvailabilityGroupReplicaProperties.Server1.Role # 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 + $mockAvailabilityReplica2Object.Name = $mockAvailabilityGroupReplica2Name + $mockAvailabilityReplica2Object.AvailabilityMode = $mockAvailabilityGroupReplicaProperties.Server2.AvailabilityMode + $mockAvailabilityReplica2Object.BackupPriority = $mockAvailabilityGroupReplicaProperties.Server2.BackupPriority + $mockAvailabilityReplica2Object.ConnectionModeInPrimaryRole = $mockAvailabilityGroupReplicaProperties.Server2.ConnectionModeInPrimaryRole + $mockAvailabilityReplica2Object.ConnectionModeInSecondaryRole = $mockAvailabilityGroupReplicaProperties.Server2.ConnectionModeInSecondaryRole + $mockAvailabilityReplica2Object.EndpointUrl = "$($mockAvailabilityGroupReplicaProperties.Server2.EndpointProtocol)://$($mockAvailabilityGroupReplicaProperties.Server2.EndpointHostName):$($mockAvailabilityGroupReplicaProperties.Server2.EndpointPort)" + $mockAvailabilityReplica2Object.FailoverMode = $mockAvailabilityGroupReplicaProperties.Server2.FailoverMode + $mockAvailabilityReplica2Object.Role = $mockAvailabilityGroupReplicaProperties.Server2.Role # Add the availability group to the server object - $mockAvailabilityGroup1Object.AvailabilityReplicas.Add($mockAvailabilityReplica1Object) - $mockAvailabilityGroup1Object.AvailabilityReplicas.Add($mockAvailabilityReplica2Object) - $mockServerObject.AvailabilityGroups.Add($mockAvailabilityGroup1Object) + $mockAvailabilityGroupObject.AvailabilityReplicas.Add($mockAvailabilityReplica1Object) + $mockAvailabilityGroupObject.AvailabilityReplicas.Add($mockAvailabilityReplica2Object) + $mockServerObject.AvailabilityGroups.Add($mockAvailabilityGroupObject) + $mockServerObject.AvailabilityGroups.Add($mockAvailabilityGroupRemoveFailedObject) # Define the database mirroring endpoint object - if ( $mockDatabaseMirroringEndpointPresent ) + if ( $mockIsDatabaseMirroringEndpointPresent ) { $mockDatabaseMirroringEndpoint = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Endpoint $mockDatabaseMirroringEndpoint.EndpointType = 'DatabaseMirroring' $mockDatabaseMirroringEndpoint.Protocol = @{ - $mockDatabaseMirroringEndpointProtocol = @{ - ListenerPort = $mockDatabaseMirroringEndpointPort + $mockDatabaseMirroringEndpointProperties.Protocol = @{ + ListenerPort = $mockDatabaseMirroringEndpointProperties.ListenerPort } } $mockServerObject.Endpoints.Add($mockDatabaseMirroringEndpoint) @@ -188,95 +565,20 @@ try 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 ) + $mockNewSqlAvailabilityGroup = { + if ( $ErrorResult -eq 'CreateAvailabilityGroupFailed' ) { - $mockDatabaseMirroringEndpoint = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Endpoint - $mockDatabaseMirroringEndpoint.EndpointType = 'DatabaseMirroring' - $mockDatabaseMirroringEndpoint.Protocol = @{ - TCP = @{ - ListenerPort = $mockAvailabilityReplica1EndpointPort - } - } - $mockServerObject.Endpoints.Add($mockDatabaseMirroringEndpoint) + throw 'CreateAvailabilityGroupFailed' } + } - # 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 ) + $mockNewSqlAvailabilityGroupReplica = { + if ( $ErrorResult -eq 'CreateAvailabilityGroupReplicaFailed' ) { - $mockAvailabilityGroup1Object.BasicAvailabilityGroup = $mockAvailabilityGroup1BasicAvailabilityGroup - $mockAvailabilityGroup1Object.DatabaseHealthTrigger = $mockAvailabilityGroup1DatabaseHealthTrigger - $mockAvailabilityGroup1Object.DtcSupportEnabled = $mockAvailabilityGroup1DtcSupportEnabled + throw 'CreateAvailabilityGroupReplicaFailed' } - # 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 + return New-Object -TypeName Microsoft.SqlServer.Management.Smo.AvailabilityReplica } # Mock the Update-AvailabilityGroup function to ensure the specified property was set correctly @@ -289,15 +591,19 @@ try ) # If the current value of the property that was set is not equal to the desired value - if ( $ParameterValue -ne $AvailabilityGroup.$Parameter ) + if ( $ParameterValue -ne $AvailabilityGroup.$ParameterName ) { - $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 + foreach ( $currentProperty in $resourceParameters.Keys ) + { + # Determine if the property was submitted as part of the configuration + # $submittedParameters comes from the Set-TargetResource code + if ( $submittedParameters -contains $currentProperty ) + { + Write-Verbose -Message "The property '$($currentProperty)' value is '$($AvailabilityGroup.$currentProperty)' and should be '$( ( Get-Variable -Name $currentProperty ).Value )'." -Verbose + } } - throw "Update-AvailabilityGroup should be setting the property '$($Parameter)' to '$($ParameterValue)'." + throw "Update-AvailabilityGroup should set the property '$($ParameterName)' to '$($ParameterValue)'." } } @@ -310,23 +616,18 @@ try $AvailabilityGroupReplica ) - if ( [string]::IsNullOrEmpty($Parameter) -and [string]::IsNullOrEmpty($ParameterValue) ) - { - return - } - # Some parameters don't align directly with a property - switch ( $Parameter ) + switch ( $ParameterName ) { EndpointHostName { $validatedParameter = 'EndpointUrl' - $validatedParameterValue = "$($mockAvailabilityReplicaEndpointProtocol)://$($ParameterValue):$($mockAvailabilityReplicaEndpointPort)" + $validatedParameterValue = "$($mockDatabaseMirroringEndpointProperties.Protocol)://$($ParameterValue):$($mockDatabaseMirroringEndpointProperties.ListenerPort)" } default { - $validatedParameter = $Parameter + $validatedParameter = $ParameterName $validatedParameterValue = $ParameterValue } } @@ -334,146 +635,113 @@ try # 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 = $_ + foreach ( $currentProperty in $resourceParameters.Keys ) + { + # Determine if the property was submitted as part of the configuration + # $submittedParameters comes from the Set-TargetResource code + if ( $submittedParameters -contains $currentProperty ) + { + switch ( $currentProperty ) + { + EndpointHostName + { + $validatedCurrentProperty = 'EndpointUrl' + $validatedCurrentPropertyValue = "$($mockDatabaseMirroringEndpointProperties.Protocol)://$($ParameterValue):$($mockDatabaseMirroringEndpointProperties.ListenerPort)" + } + + default + { + $validatedCurrentProperty = $currentProperty + $validatedCurrentPropertyValue =$AvailabilityGroupReplica.$currentProperty + } + } - Write-Verbose -Message "The property '$($currentProperty)' value is '$($AvailabilityGroupReplica.$validatedParameter)' but should be '$($validatedParameterValue)'." -Verbose + Write-Verbose -Message "The property '$($validatedCurrentProperty)' value is '$($AvailabilityGroupReplica.$validatedCurrentProperty)' and should be '$( ( Get-Variable -Name $currentProperty ).Value )'." -Verbose + } } - throw "Update-AvailabilityGroupReplica should be setting the property '$($validatedParameter)' to '$($validatedParameterValue)'." + throw "Update-AvailabilityGroupReplica should set the property '$($validatedParameter)' to '$($validatedParameterValue)'." } } - #region configuration parameters - $getTargetResourceParameters = @{ - SQLServer = $mockServer1Name - SQLInstanceName = $mockServer1ServiceName - } + #endregion cmdlet mocks - $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' { + Describe 'xSQLServerAlwaysOnAvailabilityGroup\Get-TargetResource' -Tag 'Get' { 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 + Mock -CommandName Connect-SQL -MockWith $mockConnectSql -Verifiable } 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 ' -TestCases $absentTestCases { + It 'Should not return an Availability Group when Name is "", SQLServer is "", SQLInstanceName is "", and the SQL version is ""' -TestCases $getTargetResourceAbsentTestCases { param ( $Name, + $SQLServer, + $SQLInstanceName, $Version ) - $result = Get-TargetResource @getTargetResourceParameters -Name $Name - $result.Ensure | Should Be 'Absent' + # Ensure the correct stubs are loaded for the SQL version + Import-SQLModuleStub -SQLVersion $Version - 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 + $getTargetResourceParameters = @{ + Name = $Name + SQLServer = $SQLServer + SQLInstanceName = $SQLInstanceName } + + $result = Get-TargetResource @getTargetResourceParameters + + $result.Ensure | Should Be 'Absent' + + Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly } } Context 'When the Availability Group is Present' { - It 'Should return the correct Availability Group properties when Ensure is set to and the SQL version is ' -TestCases $presentTestCases { + It 'Should return the correct Availability Group properties when Name is "", SQLServer is "", SQLInstanceName is "", and the SQL version is ""' -TestCases $getTargetResourcePresentTestCases { param ( - $Ensure, $Name, + $SQLServer, + $SQLInstanceName, $Version ) - $result = Get-TargetResource @getTargetResourceParameters -Name $Name + # Ensure the correct stubs are loaded for the SQL version + Import-SQLModuleStub -SQLVersion $Version - $result.Name | Should Be $mockAvailabilityGroup1Name - $result.SQLServer | Should Be $getTargetResourceParameters.SQLServer - $result.SQLInstanceName | Should Be $getTargetResourceParameters.SQLInstanceName + $getTargetResourceParameters = @{ + Name = $Name + SQLServer = $SQLServer + SQLInstanceName = $SQLInstanceName + } + + # Determine which SQL Server mock data will be used + $mockSqlServer = ( $mockSqlServerParameters.GetEnumerator() | Where-Object -FilterScript { $_.Value.Values -contains $SQLServer } ).Name + + $result = Get-TargetResource @getTargetResourceParameters + + $result.Name | Should Be $Name + $result.SQLServer | Should Be $SQLServer + $result.SQLInstanceName | Should Be $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 + $result.AutomatedBackupPreference | Should Be $mockAvailabilityGroupProperties.AutomatedBackupPreference + $result.AvailabilityMode | Should Be $mockAvailabilityGroupReplicaProperties.$mockSqlServer.AvailabilityMode + $result.BackupPriority | Should Be $mockAvailabilityGroupReplicaProperties.$mockSqlServer.BackupPriority + $result.ConnectionModeInPrimaryRole | Should Be $mockAvailabilityGroupReplicaProperties.$mockSqlServer.ConnectionModeInPrimaryRole + $result.ConnectionModeInSecondaryRole | Should Be $mockAvailabilityGroupReplicaProperties.$mockSqlServer.ConnectionModeInSecondaryRole + $result.EndpointURL | Should Be "$($mockAvailabilityGroupReplicaProperties.$mockSqlServer.EndpointProtocol)://$($mockAvailabilityGroupReplicaProperties.$mockSqlServer.EndpointHostName):$($mockAvailabilityGroupReplicaProperties.$mockSqlServer.EndpointPort)" + $result.FailureConditionLevel | Should Be $mockAvailabilityGroupProperties.FailureConditionLevel + $result.FailoverMode | Should Be $mockAvailabilityGroupReplicaProperties.$mockSqlServer.FailoverMode + $result.HealthCheckTimeout | Should Be $mockAvailabilityGroupProperties.HealthCheckTimeout if ( $Version -ge 13 ) { - $result.BasicAvailabilityGroup | Should Be $mockAvailabilityGroup1BasicAvailabilityGroup - $result.DatabaseHealthTrigger | Should Be $mockAvailabilityGroup1DatabaseHealthTrigger - $result.DtcSupportEnabled | Should Be $mockAvailabilityGroup1DtcSupportEnabled + $result.BasicAvailabilityGroup | Should Be $mockAvailabilityGroupProperties.BasicAvailabilityGroup + $result.DatabaseHealthTrigger | Should Be $mockAvailabilityGroupProperties.DatabaseHealthTrigger + $result.DtcSupportEnabled | Should Be $mockAvailabilityGroupProperties.DtcSupportEnabled } else { @@ -487,393 +755,122 @@ try } } - Describe 'xSQLServerAlwaysOnAvailabilityGroup\Set-TargetResource' { + Describe 'xSQLServerAlwaysOnAvailabilityGroup\Set-TargetResource' -Tag 'Set' { 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 Connect-SQL -MockWith $mockConnectSql -Verifiable + Mock -CommandName Get-PrimaryReplicaServerObject -MockWith $mockConnectSql -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-SqlAvailabilityGroup $mockNewSqlAvailabilityGroup -Verifiable + Mock -CommandName New-SqlAvailabilityReplica -MockWith $mockNewSqlAvailabilityGroupReplica -Verifiable Mock -CommandName New-TerminatingError -MockWith { $ErrorType } -Verifiable Mock -CommandName Remove-SqlAvailabilityGroup -MockWith {} -Verifiable -ParameterFilter { - $InputObject.Name -eq $mockAvailabilityGroup1Name + $InputObject.Name -eq $mockNameParameters.PresentAvailabilityGroup } Mock -CommandName Remove-SqlAvailabilityGroup -MockWith { throw 'RemoveAvailabilityGroupFailed' } -Verifiable -ParameterFilter { - $InputObject.Name -eq $mockAvailabilityGroupRemoveErrorName + $InputObject.Name -eq $mockNameParameters.RemoveAvailabilityGroupFailed } - Mock -CommandName Test-ClusterPermissions -MockWith { - $mockClusterPermissionsExist - } -Verifiable + Mock -CommandName Test-ClusterPermissions -MockWith {} -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 ""' -TestCases $createAvailabilityGroupTestCases { + Context 'When the Availability Group is Absent and the desired state is Present and a parameter is supplied' { + It 'Should create the availability group "" with the parameter "" set to "" when Ensure is "", SQLServer is "", SQLInstanceName is "", and the SQL version is ""' -TestCases $setTargetResourceCreateAvailabilityGroupWithParameterTestCases { param ( + $DomainInstanceName, + $Ensure, $Name, + $ParameterName, + $ParameterValue, + $SQLServer, + $SQLInstanceName, $Version ) - $currentTestParameters = $mockResourceParameters.Clone() - $currentTestParameters.Name = $Name - - { Set-TargetResource @currentTestParameters } | Should Not Throw + # Ensure the correct stubs are loaded for the SQL version + Import-SQLModuleStub -SQLVersion $Version - 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 + $setTargetResourceParameters = @{ + Ensure = $Ensure + Name = $Name + SQLServer = $SQLServer + SQLInstanceName = $SQLInstanceName + $ParameterName = $ParameterValue } - 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' + { Set-TargetResource @setTargetResourceParameters } | 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 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 ' -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 Connect-SQL -Scope It -Times 1 -Exactly + Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 0 -Exactly 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 @{Absent=0;Present=1}.$Ensure -Exactly + Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times @{Absent=0;Present=1}.$Ensure -Exactly + Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 0 -Exactly + Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { + $InputObject.Name -eq $mockNameParameters.PresentAvailabilityGroup } - Assert-MockCalled -CommandName New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { - $Name -eq $mockAvailabilityGroupCreateErrorName + Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { + $InputObject.Name -eq $mockNameParameters.RemoveAvailabilityGroupFailed } - 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 Test-ClusterPermissions -Scope It -Times @{Absent=0;Present=1}.$Ensure -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 ' -TestCases $createAvailabilityGroupTestCases { + Context 'When the Availability Group is Absent, the desired state is Present, and creating the Availability Group fails' { + It 'Should throw "" when creating the availability group "" fails, Ensure is "", SQLServer is "", SQLInstanceName is "", and the SQL version is ""' -TestCases $setTargetResourceCreateAvailabilityGroupFailedTestCases { param ( + $ErrorResult, + $Ensure, $Name, + $SQLServer, + $SQLInstanceName, $Version ) - $currentTestParameters = $mockResourceParameters.Clone() - $currentTestParameters.Name = $mockAvailabilityGroupCreateErrorName + # Ensure the correct stubs are loaded for the SQL version + Import-SQLModuleStub -SQLVersion $Version - { Set-TargetResource @currentTestParameters } | Should Throw 'CreateAvailabilityGroupFailed' + switch ( $ErrorResult ) + { + 'CreateAvailabilityGroupFailed' + { + $assertCreateAvailabilityGroup = 1 + } - 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 + 'CreateAvailabilityGroupReplicaFailed' + { + $assertCreateAvailabilityGroup = 0 + } } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name + + $setTargetResourceParameters = @{ + Ensure = $Ensure + Name = $Name + SQLServer = $SQLServer + SQLInstanceName = $SQLInstanceName } + + { Set-TargetResource @setTargetResourceParameters } | Should Throw "$($ErrorResult)" + + Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly + Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 0 -Exactly 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 New-SqlAvailabilityGroup -Scope It -Times $assertCreateAvailabilityGroup -Exactly + Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 1 -Exactly + 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 + $InputObject.Name -eq $mockNameParameters.PresentAvailabilityGroup } Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { - $InputObject.Name -eq $mockAvailabilityGroupRemoveErrorName + $InputObject.Name -eq $mockNameParameters.RemoveAvailabilityGroupFailed } Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroup -Scope It -Times 0 -Exactly @@ -881,341 +878,310 @@ try } } - 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 ' -TestCases $removeAvailabilityGroupTestCases { + Context 'When the Availability Group is Present and a value is passed to a parameter' { + It 'Should set "" to "" when Name is "", SQLServer is "", SQLInstanceName is "", and the SQL version is ""' -TestCases $setTargetResourcePropertyIncorrectTestCases { param ( + $Ensure, + $Name, + $ParameterName, + $ParameterValue, + $Result, + $SQLServer, + $SQLInstanceName, $Version ) - $currentTestParameters = $mockResourceParameters.Clone() - $currentTestParameters.Ensure = 'Absent' - - { Set-TargetResource @currentTestParameters } | Should Not Throw + # Ensure the correct stubs are loaded for the SQL version + Import-SQLModuleStub -SQLVersion $Version - Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly -ParameterFilter { - $SQLServer -eq $mockServer1Name + $setTargetResourceParameters = @{ + Ensure = $Ensure + Name = $Name + SQLServer = $SQLServer + SQLInstanceName = $SQLInstanceName + $ParameterName = $ParameterValue } - 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 + + if ( $mockAvailabilityGroupProperties.Keys -contains $ParameterName ) + { + $assertUpdateAvailbilityGroupMockCalled = 1 + $assertUpdateAvailbilityGroupReplicaMockCalled = 0 } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name + elseif ( $mockAvailabilityGroupReplicaProperties.Server1.Keys -contains $ParameterName ) + { + $assertUpdateAvailbilityGroupMockCalled = 0 + $assertUpdateAvailbilityGroupReplicaMockCalled = 1 } + + Set-TargetResource @setTargetResourceParameters + #{ Set-TargetResource @setTargetResourceParameters } | Should Not Throw + + Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly + Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 1 -Exactly 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-SqlAvailabilityGroup -Scope It -Times 0 -Exactly + Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly 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 $mockNameParameters.PresentAvailabilityGroup } Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { - $InputObject.Name -eq $mockAvailabilityGroupRemoveErrorName + $InputObject.Name -eq $mockNameParameters.RemoveAvailabilityGroupFailed } - 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 + Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly + Assert-MockCalled -CommandName Update-AvailabilityGroup -Scope It -Times $assertUpdateAvailbilityGroupMockCalled -Exactly + Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times $assertUpdateAvailbilityGroupReplicaMockCalled -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 ' -TestCases $removeAvailabilityGroupTestCases { + Context 'When the Availability Group is Present and the desired state is Absent' { + It 'Should remove the Availability Group "" when Ensure is "", SQLServer is "", SQLInstanceName is "", and the SQL version is ""' -TestCases $setTargetResourceRemoveAvailabilityGroupTestCases { param ( + $Ensure, + $Name, + $SQLServer, + $SQLInstanceName, $Version ) - $mockAvailabilityGroup1PrimaryReplicaServerName = $mockServer2Name + # Ensure the correct stubs are loaded for the SQL version + Import-SQLModuleStub -SQLVersion $Version - $currentTestParameters = $mockResourceParameters.Clone() - $currentTestParameters.Ensure = 'Absent' + $setTargetResourceParameters = @{ + Ensure = $Ensure + Name = $Name + SQLServer = $SQLServer + SQLInstanceName = $SQLInstanceName + } - { Set-TargetResource @currentTestParameters } | Should Throw 'InstanceNotPrimaryReplica' + { Set-TargetResource @setTargetResourceParameters } | 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 Connect-SQL -Scope It -Times 1 -Exactly + Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 0 -Exactly 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 New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly + Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly + Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 0 -Exactly + Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 1 -Exactly -ParameterFilter { + $InputObject.Name -eq $mockNameParameters.PresentAvailabilityGroup } Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { - $InputObject.Name -eq $mockAvailabilityGroupRemoveErrorName + $InputObject.Name -eq $mockNameParameters.RemoveAvailabilityGroupFailed } 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 ' -TestCases $removeAvailabilityGroupTestCases { + Context 'When the Availability Group is Present and throws an error when removal is attempted' { + It 'Should throw "" when Ensure is "", Name is "", SQLServer is "", SQLInstanceName is "", and the SQL version is ""' -TestCases $setTargetResourceRemoveAvailabilityGroupErrorTestCases { param ( + $ErrorResult, + $Ensure, + $Name, + $SQLServer, + $SQLInstanceName, $Version ) - $mockAvailabilityGroupName = $mockAvailabilityGroupRemoveErrorName - - $currentTestParameters = $mockResourceParameters.Clone() - $currentTestParameters.Ensure = 'Absent' - $currentTestParameters.Name = $mockAvailabilityGroupRemoveErrorName + # Ensure the correct stubs are loaded for the SQL version + Import-SQLModuleStub -SQLVersion $Version - { Set-TargetResource @currentTestParameters } | Should Throw 'RemoveAvailabilityGroupFailed' + switch ( $ErrorResult ) + { + 'RemoveAvailabilityGroupFailed' + { + $assertRemoveAvailabilityGroupFailed = 1 + } - 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 + 'InstanceNotPrimaryReplica' + { + $assertRemoveAvailabilityGroupFailed = 0 + } } - Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Time 0 -Exactly -ParameterFilter { - $AvailabilityGroup.PrimaryReplicaServerName -eq $mockServer2Name + + $setTargetResourceParameters = @{ + Ensure = $Ensure + Name = $Name + SQLServer = $SQLServer + SQLInstanceName = $SQLInstanceName } + + { Set-TargetResource @setTargetResourceParameters } | Should Throw "$($ErrorResult)" + + Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly + Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 0 -Exactly 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-SqlAvailabilityGroup -Scope It -Times 0 -Exactly + Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly 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 + $InputObject.Name -eq $mockNameParameters.PresentAvailabilityGroup } - Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 1 -Exactly -ParameterFilter { - $InputObject.Name -eq $mockAvailabilityGroupRemoveErrorName + Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times $assertRemoveAvailabilityGroupFailed -Exactly -ParameterFilter { + $InputObject.Name -eq $mockNameParameters.RemoveAvailabilityGroupFailed } 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 } + } + + Context 'When HADR is not enabled' { + AfterAll { + $mockIsHadrEnabled = $true + } + + BeforeAll { + $mockIsHadrEnabled = $false + } - It 'Should connect to the instance hosting the primary replica when the LocalReplicaRole is not Primary and the SQL version is ' -TestCases $removeAvailabilityGroupTestCases { + It 'Should throw "" when Ensure is "", Name is "", SQLServer is "", SQLInstanceName is "", and the SQL version is ""' -TestCases $setTargetResourceHadrDisabledTestCases { param ( + $Ensure, + $Name, + $Result, + $SQLServer, + $SQLInstanceName, $Version ) - $currentTestParameters = $mockResourceParameters.Clone() - $currentTestParameters.SQLServer = $mockServer2Name + # Ensure the correct stubs are loaded for the SQL version + Import-SQLModuleStub -SQLVersion $Version - { 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 + $setTargetResourceParameters = @{ + Ensure = $Ensure + Name = $Name + SQLServer = $SQLServer + SQLInstanceName = $SQLInstanceName } + + { Set-TargetResource @setTargetResourceParameters } | Should Throw $Result + + Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly + Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 0 -Exactly 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 New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly + Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly + 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 + $InputObject.Name -eq $mockNameParameters.PresentAvailabilityGroup } Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { - $InputObject.Name -eq $mockAvailabilityGroupRemoveErrorName + $InputObject.Name -eq $mockNameParameters.RemoveAvailabilityGroupFailed } - Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -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 1 -Exactly + Assert-MockCalled -CommandName Update-AvailabilityGroupReplica -Scope It -Times 0 -Exactly + } + } + + Context 'When the Database Mirroring Endpoint is missing' { + AfterAll { + $mockIsDatabaseMirroringEndpointPresent = $true } - It 'Should set the property "" to the desired state "" when the version is ""' -TestCases $presentParameterTestCases { + BeforeAll { + $mockIsDatabaseMirroringEndpointPresent = $false + } + + It 'Should throw "" when Ensure is "", Name is "", SQLServer is "", SQLInstanceName is "", and the SQL version is ""' -TestCases $setTargetResourceEndpointMissingTestCases { param ( $Ensure, - $Parameter, - $ParameterValue, + $Name, + $Result, + $SQLServer, + $SQLInstanceName, $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 - } + # Ensure the correct stubs are loaded for the SQL version + Import-SQLModuleStub -SQLVersion $Version - # 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 + $setTargetResourceParameters = @{ + Ensure = $Ensure + Name = $Name + SQLServer = $SQLServer + SQLInstanceName = $SQLInstanceName } - { Set-TargetResource @currentTestParameters } | Should Not Throw + { Set-TargetResource @setTargetResourceParameters } | Should Throw $Result - 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 Connect-SQL -Scope It -Times 1 -Exactly + Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 0 -Exactly 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 New-SqlAvailabilityGroup -Scope It -Times 0 -Exactly + Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly + Assert-MockCalled -CommandName New-TerminatingError -Scope It -Times 1 -Exactly + Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { + $InputObject.Name -eq $mockNameParameters.PresentAvailabilityGroup } Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { - $InputObject.Name -eq $mockAvailabilityGroupRemoveErrorName + $InputObject.Name -eq $mockNameParameters.RemoveAvailabilityGroupFailed } - 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 + 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 Endpoint URL is incorrect' { + AfterEach { + # Restore up the original endpoint url settings + $mockAvailabilityGroupReplicaProperties.Server1 = $mockAvailabilityGroupReplicaPropertiesServer1Original.Clone() + $mockAvailabilityGroupReplicaProperties.Server2 = $mockAvailabilityGroupReplicaPropertiesServer2Original.Clone() + } + + BeforeEach { + # Back up the original endpoint url settings + $mockAvailabilityGroupReplicaPropertiesServer1Original = $mockAvailabilityGroupReplicaProperties.Server1.Clone() + $mockAvailabilityGroupReplicaPropertiesServer2Original = $mockAvailabilityGroupReplicaProperties.Server2.Clone() } - It 'Should set the property "EndpointUrl" to the desired state when the mock "" is "" and the version is ""' -TestCases $presentEndpointUrlTestCases { + It 'Should set "" to "" when Name is "", SQLServer is "", SQLInstanceName is "", and the SQL version is ""' -TestCases $setTargetResourcesEndpointUrlTestCases { param ( + $EndpointPropertyName, + $EndpointPropertyValue, $Ensure, - $MockVariableName, - $MockVariableValue, + $Name, + $SQLServer, + $SQLInstanceName, $Version ) - Set-Variable -Name $MockVariableName -Value $MockVariableValue + # Ensure the correct stubs are loaded for the SQL version + Import-SQLModuleStub -SQLVersion $Version - { Set-TargetResource @mockResourceParameters } | Should Not Throw + $mockAvailabilityGroupReplicaProperties.Server1.$EndpointPropertyName = $EndpointPropertyValue + $mockAvailabilityGroupReplicaProperties.Server2.$EndpointPropertyName = $EndpointPropertyValue - 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 + $setTargetResourceParameters = @{ + Ensure = $Ensure + Name = $Name + SQLServer = $SQLServer + SQLInstanceName = $SQLInstanceName } + + { Set-TargetResource @setTargetResourceParameters } | Should Not Throw + + Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly + Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 1 -Exactly 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-SqlAvailabilityGroup -Scope It -Times 0 -Exactly + Assert-MockCalled -CommandName New-SqlAvailabilityReplica -Scope It -Times 0 -Exactly 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 + $InputObject.Name -eq $mockNameParameters.PresentAvailabilityGroup } Assert-MockCalled -CommandName Remove-SqlAvailabilityGroup -Scope It -Times 0 -Exactly -ParameterFilter { - $InputObject.Name -eq $mockAvailabilityGroupRemoveErrorName + $InputObject.Name -eq $mockNameParameters.RemoveAvailabilityGroupFailed } Assert-MockCalled -CommandName Test-ClusterPermissions -Scope It -Times 1 -Exactly Assert-MockCalled -CommandName Update-AvailabilityGroup -Scope It -Times 0 -Exactly @@ -1224,275 +1190,145 @@ try } } - Describe "xSQLServerAlwaysOnAvailabilityGroup\Test-TargetResource" { + Describe 'xSQLServerAlwaysOnAvailabilityGroup\Test-TargetResource' -Tag 'Test' { 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 + Mock -CommandName Connect-SQL -MockWith $mockConnectSql -Verifiable } Context 'When the Availability Group is Absent' { - - It 'Should be "" when the desired state is "" and the SQL version is ""' -TestCases $absentTestCases { + It 'Should be "" when Ensure is "", Name is "", SQLServer is "", SQLInstanceName is "", and the SQL version is ""' -TestCases $testTargetResourceAbsentTestCases { param ( $Ensure, $Name, $Result, + $SQLServer, + $SQLInstanceName, $Version ) - $currentTestParameters = $mockResourceParameters.Clone() - $currentTestParameters.Ensure = $Ensure - $currentTestParameters.Name = $Name + # Ensure the correct stubs are loaded for the SQL version + Import-SQLModuleStub -SQLVersion $Version - Test-TargetResource @currentTestParameters | Should Be $Result + $testTargetResourceParameters = @{ + Ensure = $Ensure + Name = $Name + SQLServer = $SQLServer + SQLInstanceName = $SQLInstanceName + } + + Test-TargetResource @testTargetResourceParameters | 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 } + Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly } } - Context 'When the Availability Group is Present' { - - It 'Should be "" when the desired state is "" and the SQL version is ""' -TestCases $presentTestCases { + Context 'When the Availability Group is Present and the default parameters are passed' { + It 'Should be "" when Ensure is "", Name is "", SQLServer is "", SQLInstanceName is "", and the SQL version is ""' -TestCases $testTargetResourcePresentTestCases { param ( $Ensure, $Name, $Result, + $SQLServer, + $SQLInstanceName, $Version ) - $currentTestParameters = $mockResourceParameters.Clone() - $currentTestParameters.Ensure = $Ensure - $currentTestParameters.Name = $Name - - Test-TargetResource @currentTestParameters | Should Be $Result + # Ensure the correct stubs are loaded for the SQL version + Import-SQLModuleStub -SQLVersion $Version - 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 + $testTargetResourceParameters = @{ + Ensure = $Ensure + Name = $Name + SQLServer = $SQLServer + SQLInstanceName = $SQLInstanceName } + + Test-TargetResource @testTargetResourceParameters | Should Be $Result + + Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly } + } - It 'Should be "" when the desired state is "", the parameter "" is not "", and the SQL version is ""' -TestCases $presentParameterTestCases { + Context 'When the Availability Group is Present and a value is passed to a parameter' { + It 'Should be "" when "" is "", Name is "", SQLServer is "", SQLInstanceName is "", and the SQL version is ""' -TestCases $testTargetResourcePropertyIncorrectTestCases { param ( $Ensure, - $Result, - $Parameter, + $Name, + $ParameterName, $ParameterValue, + $Result, + $SQLServer, + $SQLInstanceName, $Version ) - $currentTestParameters = $mockResourceParameters.Clone() - $currentTestParameters.$Parameter = $ParameterValue + # Ensure the correct stubs are loaded for the SQL version + Import-SQLModuleStub -SQLVersion $Version - 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 + $testTargetResourceParameters = @{ + Ensure = $Ensure + Name = $Name + SQLServer = $SQLServer + SQLInstanceName = $SQLInstanceName + $ParameterName = $ParameterValue } + + Test-TargetResource @testTargetResourceParameters | Should Be $Result + + Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly + } + } + + Context 'When the Availability Group is Present an Enpoint property is incorrect' { + AfterEach { + # Restore up the original endpoint url settings + $mockAvailabilityGroupReplicaProperties.Server1 = $mockAvailabilityGroupReplicaPropertiesServer1Original.Clone() + $mockAvailabilityGroupReplicaProperties.Server2 = $mockAvailabilityGroupReplicaPropertiesServer2Original.Clone() + } + + BeforeEach { + # Back up the original endpoint url settings + $mockAvailabilityGroupReplicaPropertiesServer1Original = $mockAvailabilityGroupReplicaProperties.Server1.Clone() + $mockAvailabilityGroupReplicaPropertiesServer2Original = $mockAvailabilityGroupReplicaProperties.Server2.Clone() } - It 'Should be "" when the desired state is "", the mock "" is "", and the SQL version is ""' -TestCases $presentEndpointUrlTestCases { + It 'Should be "" when "" is "", Name is "", SQLServer is "", SQLInstanceName is "", and the SQL version is ""' -TestCases $testTargetResourceEndpointIncorrectTestCases { param ( + $EndpointPropertyName, + $EndpointPropertyValue, $Ensure, + $Name, $Result, - $MockVariableName, - $MockVariableValue, + $SQLServer, + $SQLInstanceName, $Version ) - Set-Variable -Name $MockVariableName -Value $MockVariableValue + # Ensure the correct stubs are loaded for the SQL version + Import-SQLModuleStub -SQLVersion $Version - Test-TargetResource @mockResourceParameters | Should Be $Result + $mockAvailabilityGroupReplicaProperties.Server1.$EndpointPropertyName = $EndpointPropertyValue + $mockAvailabilityGroupReplicaProperties.Server2.$EndpointPropertyName = $EndpointPropertyValue - Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly -ParameterFilter { - $SQLServer -eq $mockServer1Name + $testTargetResourceParameters = @{ + Ensure = $Ensure + Name = $Name + SQLServer = $SQLServer + SQLInstanceName = $SQLInstanceName } - Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly -ParameterFilter { - $SQLServer -eq $mockServer2Name - } - } + Test-TargetResource @testTargetResourceParameters | Should Be $Result + + Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly + } } } - Describe "xSQLServerAlwaysOnAvailabilityGroup\Update-AvailabilityGroup" { + Describe 'xSQLServerAlwaysOnAvailabilityGroup\Update-AvailabilityGroup' -Tag 'Helper' { BeforeAll { Mock -CommandName New-TerminatingError -MockWith { $ErrorType @@ -1523,5 +1359,5 @@ try } finally { - Restore-TestEnvironment -TestEnvironment $TestEnvironment + Invoke-TestCleanup }