From 6e3f7c5677e5ad827bedb616a6685158ffbccd72 Mon Sep 17 00:00:00 2001 From: David Reynolds Date: Mon, 26 Feb 2018 21:49:57 -0600 Subject: [PATCH 1/7] Adding support for MSFT_SqlDatabaseRecoveryModel regex style matching for database name --- .../MSFT_SqlDatabaseRecoveryModel.psm1 | 62 +++++++++++++------ 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/DSCResources/MSFT_SqlDatabaseRecoveryModel/MSFT_SqlDatabaseRecoveryModel.psm1 b/DSCResources/MSFT_SqlDatabaseRecoveryModel/MSFT_SqlDatabaseRecoveryModel.psm1 index 651504f47..fdbd7c3a7 100644 --- a/DSCResources/MSFT_SqlDatabaseRecoveryModel/MSFT_SqlDatabaseRecoveryModel.psm1 +++ b/DSCResources/MSFT_SqlDatabaseRecoveryModel/MSFT_SqlDatabaseRecoveryModel.psm1 @@ -49,25 +49,38 @@ function Get-TargetResource if ($sqlServerObject) { - Write-Verbose -Message "Getting RecoveryModel of SQL database '$Name'" - $sqlDatabaseObject = $sqlServerObject.Databases[$Name] + $databases = $sqlServerObject.Databases.Where{$_.Name -like "$Name"} - if ($sqlDatabaseObject) + if(!$databases) { - $sqlDatabaseRecoveryModel = $sqlDatabaseObject.RecoveryModel - New-VerboseMessage -Message "The current recovery model used by database $Name is '$sqlDatabaseRecoveryModel'" + throw New-TerminatingError -ErrorType NoDatabase ` + -FormatArgs @($Name, $ServerName, $InstanceName) ` + -ErrorCategory InvalidResult } - else + + $sqlDatabaseRecoveryModel = "" + foreach($sqlDatabaseObject in $databases) { - throw New-TerminatingError -ErrorType NoDatabase ` - -FormatArgs @($Name, $ServerName, $InstanceName) ` - -ErrorCategory InvalidResult + + if($sqlDatabaseObject.Name -eq "tempdb") + { + New-VerboseMessage -Message "Skipping 'tempdb', recovery model for this DB cannot be changed" + + Continue + } + + New-VerboseMessage -Message "The current recovery model used by database $($sqlDatabaseObject.Name) is '$($sqlDatabaseObject.RecoveryModel)'" + + if($sqlDatabaseRecoveryModel -notlike "*$($sqlDatabaseObject.RecoveryModel)*") + { + $sqlDatabaseRecoveryModel += ",$($sqlDatabaseObject.RecoveryModel)" + } } } $returnValue = @{ Name = $Name - RecoveryModel = $sqlDatabaseRecoveryModel + RecoveryModel = $sqlDatabaseRecoveryModel.SubString(1, $sqlDatabaseRecoveryModel.Length - 1) ServerName = $ServerName InstanceName = $InstanceName } @@ -122,24 +135,33 @@ function Set-TargetResource if ($sqlServerObject) { - Write-Verbose -Message "Setting RecoveryModel of SQL database '$Name'" - $sqlDatabaseObject = $sqlServerObject.Databases[$Name] + $databases = $sqlServerObject.Databases.Where{$_.Name -like "$Name"} + + if(!$databases) + { + throw New-TerminatingError -ErrorType NoDatabase ` + -FormatArgs @($Name, $ServerName, $InstanceName) ` + -ErrorCategory InvalidResult + } - if ($sqlDatabaseObject) + foreach($sqlDatabaseObject in $databases) { + if($sqlDatabaseObject.Name -eq "tempdb") + { + New-VerboseMessage -Message "Skipping 'tempdb', recovery model for this DB cannot be changed" + + Continue + } + + Write-Verbose -Message "Setting RecoveryModel of SQL database '$($sqlDatabaseObject.Name)'" + if ($sqlDatabaseObject.RecoveryModel -ne $RecoveryModel) { $sqlDatabaseObject.RecoveryModel = $RecoveryModel $sqlDatabaseObject.Alter() - New-VerboseMessage -Message "The recovery model for the database $Name is changed to '$RecoveryModel'." + New-VerboseMessage -Message "The recovery model for the database $($sqlDatabaseObject.Name) is changed to '$RecoveryModel'." } } - else - { - throw New-TerminatingError -ErrorType NoDatabase ` - -FormatArgs @($Name, $ServerName, $InstanceName) ` - -ErrorCategory InvalidResult - } } } From 0735746d799cfd6495353d88ab4bf03be4b47703 Mon Sep 17 00:00:00 2001 From: David Reynolds Date: Tue, 27 Feb 2018 21:57:59 -0600 Subject: [PATCH 2/7] Updated docs and removed extra line --- CHANGELOG.md | 1 + .../MSFT_SqlDatabaseRecoveryModel.psm1 | 1 - README.md | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cfbc9ee2..3a7569db9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Changes to SqlServerLogin - Fix password test fails for nativ sql users ([issue #1048](https://github.com/PowerShell/SqlServerDsc/issues/1048)). +- MSFT_SqlDatabaseRecoveryModel now support regex style matching of databases. ## 11.0.0.0 diff --git a/DSCResources/MSFT_SqlDatabaseRecoveryModel/MSFT_SqlDatabaseRecoveryModel.psm1 b/DSCResources/MSFT_SqlDatabaseRecoveryModel/MSFT_SqlDatabaseRecoveryModel.psm1 index fdbd7c3a7..f4525ba35 100644 --- a/DSCResources/MSFT_SqlDatabaseRecoveryModel/MSFT_SqlDatabaseRecoveryModel.psm1 +++ b/DSCResources/MSFT_SqlDatabaseRecoveryModel/MSFT_SqlDatabaseRecoveryModel.psm1 @@ -61,7 +61,6 @@ function Get-TargetResource $sqlDatabaseRecoveryModel = "" foreach($sqlDatabaseObject in $databases) { - if($sqlDatabaseObject.Name -eq "tempdb") { New-VerboseMessage -Message "Skipping 'tempdb', recovery model for this DB cannot be changed" diff --git a/README.md b/README.md index d966afeb2..cc7a450f7 100644 --- a/README.md +++ b/README.md @@ -656,7 +656,7 @@ Read more about recovery model in this article #### Parameters -* **`[String]` Name** _(Key)_: The SQL database name. +* **`[String]` Name** _(Key)_: The SQL database name or pattern matching multiple databases. * **`[String]` ServerName** _(Key)_: The host name of the SQL Server to be configured. * **`[String]` InstanceName** _(Key)_: The name of the SQL instance to be configured. * **`[String]` RecoveryModel** _(Required)_: The recovery model to use for the database. From bf19959684cea0785e382358b9c51c46db6704f2 Mon Sep 17 00:00:00 2001 From: David Reynolds Date: Wed, 11 Jul 2018 00:02:44 -0700 Subject: [PATCH 3/7] Added RaisError usage to write change to SQL & event logs --- .../MSFT_SqlDatabaseRecoveryModel.psm1 | 20 ++++++- .../2-SetDatabaseRecoveryModel.ps1 | 56 +++++++++++++++++++ 2 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 Examples/Resources/SqlDatabaseRecoveryModel/2-SetDatabaseRecoveryModel.ps1 diff --git a/DSCResources/MSFT_SqlDatabaseRecoveryModel/MSFT_SqlDatabaseRecoveryModel.psm1 b/DSCResources/MSFT_SqlDatabaseRecoveryModel/MSFT_SqlDatabaseRecoveryModel.psm1 index f4525ba35..f8528e2a1 100644 --- a/DSCResources/MSFT_SqlDatabaseRecoveryModel/MSFT_SqlDatabaseRecoveryModel.psm1 +++ b/DSCResources/MSFT_SqlDatabaseRecoveryModel/MSFT_SqlDatabaseRecoveryModel.psm1 @@ -1,6 +1,9 @@ Import-Module -Name (Join-Path -Path (Split-Path (Split-Path $PSScriptRoot -Parent) -Parent) ` - -ChildPath 'SqlServerDscHelper.psm1') ` - -Force + -ChildPath 'SqlServerDscHelper.psm1') ` + -Force + +$logMessage = "RAISERROR('PowerShell Desired State Configuration Updated database {0} to recovery model {1} because it matched pattern {2}',1,1,1) with log" + <# .SYNOPSIS This function gets all Key properties defined in the resource schema file @@ -159,6 +162,19 @@ function Set-TargetResource $sqlDatabaseObject.RecoveryModel = $RecoveryModel $sqlDatabaseObject.Alter() New-VerboseMessage -Message "The recovery model for the database $($sqlDatabaseObject.Name) is changed to '$RecoveryModel'." + + try + { + $null = Invoke-Query -SQLServer $ServerName ` + -SQLInstanceName $InstanceName ` + -Database $sqlDatabaseObject.Name ` + -Query $($logMessage -f $sqlDatabaseObject.Name, $RecoveryModel, $Name) ` + -ErrorAction stop + } + catch + { + Write-Warning "Failed to log DSC database recovery model to SQL and event logs." + } } } } diff --git a/Examples/Resources/SqlDatabaseRecoveryModel/2-SetDatabaseRecoveryModel.ps1 b/Examples/Resources/SqlDatabaseRecoveryModel/2-SetDatabaseRecoveryModel.ps1 new file mode 100644 index 000000000..4aeeff340 --- /dev/null +++ b/Examples/Resources/SqlDatabaseRecoveryModel/2-SetDatabaseRecoveryModel.ps1 @@ -0,0 +1,56 @@ +<# +.EXAMPLE + This example shows how to set the Recovery Model to "Simple" for SQL databases that match the + given pattern. +#> + +Configuration Example +{ + param + ( + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $SqlAdministratorCredential + ) + + Import-DscResource -ModuleName SqlServerDsc + + node localhost + { + SqlDatabase Add_SqlDatabaseAdventureworks + { + Ensure = 'Present' + Name = 'Adventureworks' + ServerName = 'sqltest.company.local' + InstanceName = 'DSC' + PsDscRunAsCredential = $SqlAdministratorCredential + } + + SqlDatabase Add_SqlDatabaseAdventureWorks2012 + { + Ensure = 'Present' + Name = 'AdventureWorks2012' + ServerName = 'sqltest.company.local' + InstanceName = 'DSC' + PsDscRunAsCredential = $SqlAdministratorCredential + } + + SqlDatabase Add_SqlDatabaseAdventureWorks2014 + { + Ensure = 'Present' + Name = 'AdventureWorks2014' + ServerName = 'sqltest.company.local' + InstanceName = 'DSC' + PsDscRunAsCredential = $SqlAdministratorCredential + } + + SqlDatabaseRecoveryModel Set_SqlDatabaseRecoveryModel_AdventureWorks2012 + { + Name = 'AdventureWorks201[24]' + RecoveryModel = 'Simple' + ServerName = 'sqltest.company.local' + InstanceName = 'DSC' + PsDscRunAsCredential = $SqlAdministratorCredential + } + } +} From 7b7741c547a41b398c3511f3560a57d690852523 Mon Sep 17 00:00:00 2001 From: David Reynolds Date: Tue, 4 Sep 2018 22:38:46 -0500 Subject: [PATCH 4/7] Converted MockConnectSQL to a class to make it easier to work with Updated exist test to pass with resource changes --- .../MSFT_SqlDatabaseRecoveryModel.Tests.ps1 | 113 +++++++++--------- 1 file changed, 56 insertions(+), 57 deletions(-) diff --git a/Tests/Unit/MSFT_SqlDatabaseRecoveryModel.Tests.ps1 b/Tests/Unit/MSFT_SqlDatabaseRecoveryModel.Tests.ps1 index 62e2bb414..b98842e31 100644 --- a/Tests/Unit/MSFT_SqlDatabaseRecoveryModel.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlDatabaseRecoveryModel.Tests.ps1 @@ -38,8 +38,8 @@ try $mockInstanceName = 'MSSQLSERVER' $mockSqlDatabaseName = 'AdventureWorks' $mockSqlDatabaseRecoveryModel = 'Simple' - $mockInvalidOperationForAlterMethod = $false - $mockExpectedRecoveryModel = 'Simple' + $mockSqlDatabaseName2 = 'AdventureWorks2' + $mockSqlDatabaseRecoveryModel2 = 'Full' # Default parameters that are used for the It-blocks $mockDefaultParameters = @{ @@ -48,40 +48,58 @@ try } #region Function mocks - $mockConnectSQL = { - return @( - ( - New-Object -TypeName Object | - Add-Member -MemberType NoteProperty -Name InstanceName -Value $mockInstanceName -PassThru | - Add-Member -MemberType NoteProperty -Name ComputerNamePhysicalNetBIOS -Value $mockServerName -PassThru | - Add-Member -MemberType ScriptProperty -Name Databases -Value { - return @{ - $mockSqlDatabaseName = ( New-Object -TypeName Object | - Add-Member -MemberType NoteProperty -Name Name -Value $mockSqlDatabaseName -PassThru | - Add-Member -MemberType NoteProperty -Name RecoveryModel -Value $mockSqlDatabaseRecoveryModel -PassThru | - Add-Member -MemberType ScriptMethod -Name Alter -Value { - if ($mockInvalidOperationForAlterMethod) - { - throw 'Mock Alter Method was called with invalid operation.' - } - - if ( $this.RecoveryModel -ne $mockExpectedRecoveryModel ) - { - throw "Called Alter Drop() method without setting the right recovery model. Expected '{0}'. But was '{1}'." ` - -f $mockExpectedRecoveryModel, $this.RecoveryModel - } - } -PassThru - ) - } - } -PassThru -Force - ) - ) + Class Database + { + [string]$Name + [string]$RecoveryModel + [bool] $mockInvalidOperationForAlterMethod = $false + [string] $mockExpectedRecoveryModel = 'Simple' + + Database($Name, $RecoveryModel) + { + $this.Name = $Name + $this.RecoveryModel = $RecoveryModel + } + + Alter() + { + if ($this.mockInvalidOperationForAlterMethod) + { + throw 'Mock Alter Method was called with invalid operation.' + } + + if ( $this.RecoveryModel -ne $this.mockExpectedRecoveryModel ) + { + throw "Called Alter Drop() method without setting the right recovery model. Expected '{0}'. But was '{1}'." ` + -f $this.mockExpectedRecoveryModel, $this.RecoveryModel + } + } + } + + Class ConnectSQL + { + [string] $InstanceName + [string] $ComputerNamePhysicalNetBIOS + [Database[]] $Databases + + ConnectSQL($InstanceName, $ComputerNamePhysicalNetBIOS, $Databases) + { + $this.InstanceName = $InstanceName + $this.ComputerNamePhysicalNetBIOS = $ComputerNamePhysicalNetBIOS + $this.Databases = $Databases + } } + + $databases = @( + ([Database]::new($mockSqlDatabaseName, $mockSqlDatabaseRecoveryModel)) + ([Database]::new($mockSqlDatabaseName2, $mockSqlDatabaseRecoveryModel2)) + ) + $mockConnectSQL = [ConnectSQL]::new($mockInstanceName, $mockServerName, $databases) #endregion Describe "MSFT_SqlDatabaseRecoveryModel\Get-TargetResource" -Tag 'Get' { BeforeEach { - Mock -CommandName Connect-SQL -MockWith $mockConnectSQL -Verifiable + Mock -CommandName Connect-SQL -MockWith { return $mockConnectSQL } -Verifiable } Context 'When passing values to parameters and database does not exist' { @@ -93,9 +111,11 @@ try } $throwInvalidOperation = ("Database 'UnknownDatabase' does not exist " + ` - "on SQL server 'localhost\MSSQLSERVER'.") + "on SQL server 'localhost\MSSQLSERVER'.") - { Get-TargetResource @testParameters } | Should -Throw $throwInvalidOperation + Mock -CommandName Connect-SQL -MockWith { throw $throwInvalidOperation } + + { Get-TargetResource @testParameters } | Should Throw $throwInvalidOperation } It 'Should call the mock function Connect-SQL' { @@ -154,7 +174,7 @@ try Describe "MSFT_SqlDatabaseRecoveryModel\Test-TargetResource" -Tag 'Test' { BeforeEach { - Mock -CommandName Connect-SQL -MockWith $mockConnectSQL -Verifiable + Mock -CommandName Connect-SQL -MockWith { return $mockConnectSQL } -Verifiable } Context 'When the system is not in the desired state' { @@ -196,7 +216,7 @@ try Describe "MSFT_SqlDatabaseRecoveryModel\Set-TargetResource" -Tag 'Set' { BeforeEach { - Mock -CommandName Connect-SQL -MockWith $mockConnectSQL -Verifiable + Mock -CommandName Connect-SQL -MockWith { return $mockConnectSQL } -Verifiable } Context 'When the system is not in the desired state, and database does not exist' { @@ -220,7 +240,7 @@ try Context 'When the system is not in the desired state' { It 'Should not throw when calling the alter method when desired recovery model should be set' { - $mockExpectedRecoveryModel = 'Full' + $mockConnectSQL.Databases.Where{$_.Name -eq 'AdventureWorks'}[0].mockExpectedRecoveryModel = 'Full' $testParameters = $mockDefaultParameters $testParameters += @{ Name = 'AdventureWorks' @@ -235,27 +255,6 @@ try } } - Context 'When the system is not in the desired state' { - It 'Should throw when calling the alter method when desired recovery model should be set' { - $mockInvalidOperationForAlterMethod = $true - $mockExpectedRecoveryModel = 'Full' - $testParameters = $mockDefaultParameters - $testParameters += @{ - Name = 'AdventureWorks' - RecoveryModel = 'Full' - } - - $throwInvalidOperation = ('Exception calling "Alter" with "0" argument(s): ' + - '"Mock Alter Method was called with invalid operation."') - - { Set-TargetResource @testParameters } | Should -Throw $throwInvalidOperation - } - - It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context - } - } - Assert-VerifiableMock } } From 6a9745795972ffc03e8b0cdf92abe1801114430a Mon Sep 17 00:00:00 2001 From: David Reynolds Date: Tue, 4 Sep 2018 23:16:45 -0500 Subject: [PATCH 5/7] Added test for TempDB and multiple DB matching Fixed issue with Get when no matching DBs are found Fixed type --- .../MSFT_SqlDatabaseRecoveryModel.psm1 | 17 ++- .../MSFT_SqlDatabaseRecoveryModel.Tests.ps1 | 116 ++++++++++++++++++ 2 files changed, 127 insertions(+), 6 deletions(-) diff --git a/DSCResources/MSFT_SqlDatabaseRecoveryModel/MSFT_SqlDatabaseRecoveryModel.psm1 b/DSCResources/MSFT_SqlDatabaseRecoveryModel/MSFT_SqlDatabaseRecoveryModel.psm1 index f8528e2a1..9f5c2382c 100644 --- a/DSCResources/MSFT_SqlDatabaseRecoveryModel/MSFT_SqlDatabaseRecoveryModel.psm1 +++ b/DSCResources/MSFT_SqlDatabaseRecoveryModel/MSFT_SqlDatabaseRecoveryModel.psm1 @@ -76,13 +76,18 @@ function Get-TargetResource if($sqlDatabaseRecoveryModel -notlike "*$($sqlDatabaseObject.RecoveryModel)*") { $sqlDatabaseRecoveryModel += ",$($sqlDatabaseObject.RecoveryModel)" - } + } } } + if([string]::IsNullOrWhiteSpace($sqlDatabaseRecoveryModel) -eq $false) + { + $sqlDatabaseRecoveryModel = $sqlDatabaseRecoveryModel.SubString(1, $sqlDatabaseRecoveryModel.Length - 1) + } + $returnValue = @{ Name = $Name - RecoveryModel = $sqlDatabaseRecoveryModel.SubString(1, $sqlDatabaseRecoveryModel.Length - 1) + RecoveryModel = $sqlDatabaseRecoveryModel ServerName = $ServerName InstanceName = $InstanceName } @@ -150,7 +155,7 @@ function Set-TargetResource { if($sqlDatabaseObject.Name -eq "tempdb") { - New-VerboseMessage -Message "Skipping 'tempdb', recovery model for this DB cannot be changed" + New-VerboseMessage -Message "Skipping 'tempdb', recovery model because this DB cannot be changed" Continue } @@ -163,15 +168,15 @@ function Set-TargetResource $sqlDatabaseObject.Alter() New-VerboseMessage -Message "The recovery model for the database $($sqlDatabaseObject.Name) is changed to '$RecoveryModel'." - try + try { $null = Invoke-Query -SQLServer $ServerName ` -SQLInstanceName $InstanceName ` -Database $sqlDatabaseObject.Name ` -Query $($logMessage -f $sqlDatabaseObject.Name, $RecoveryModel, $Name) ` - -ErrorAction stop + -ErrorAction stop } - catch + catch { Write-Warning "Failed to log DSC database recovery model to SQL and event logs." } diff --git a/Tests/Unit/MSFT_SqlDatabaseRecoveryModel.Tests.ps1 b/Tests/Unit/MSFT_SqlDatabaseRecoveryModel.Tests.ps1 index b98842e31..8666b076d 100644 --- a/Tests/Unit/MSFT_SqlDatabaseRecoveryModel.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlDatabaseRecoveryModel.Tests.ps1 @@ -93,6 +93,7 @@ try $databases = @( ([Database]::new($mockSqlDatabaseName, $mockSqlDatabaseRecoveryModel)) ([Database]::new($mockSqlDatabaseName2, $mockSqlDatabaseRecoveryModel2)) + ([Database]::new('tempDB', 'Simple')) ) $mockConnectSQL = [ConnectSQL]::new($mockInstanceName, $mockServerName, $databases) #endregion @@ -169,6 +170,46 @@ try } } + Context 'When multiple databases match the name, info should be returned for all databases' { + It 'Should return data for each database' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Name = 'AdventureWorks*' + RecoveryModel = 'Simple' + } + + $result = Get-TargetResource @testParameters + $result.RecoveryModel | Should -Be "$mockSqlDatabaseRecoveryModel,$mockSqlDatabaseRecoveryModel2" + } + + It 'Should return the expected recovery models' { + $result.ServerName | Should -Be $testParameters.ServerName + $result.InstanceName | Should -Be $testParameters.InstanceName + $result.Name | Should -Be $testParameters.Name + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + } + + Context 'When the database is tempdb it should be skipped' { + It "Should not return a Recovery Model for tempDB" { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Name = 'tempDB' + RecoveryModel = 'Simple' + } + + $result = Get-TargetResource @testParameters + $result.RecoveryModel | Should -Be "" + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + } + Assert-VerifiableMock } @@ -194,6 +235,23 @@ try } } + Context 'When the system is not in the desired state' { + It "Should return false when not all matching databases are correct" { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Name = 'AdventureWorks*' + RecoveryModel = 'Full' + } + + $result = Test-TargetResource @testParameters + $result | Should -Be $false + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + } + Context 'When the system is in the desired state' { It 'Should return the state as true when desired recovery model is correct' { $testParameters = $mockDefaultParameters @@ -211,12 +269,31 @@ try } } + Context 'When the system is in the desired state' { + It 'Should return the state as true only when all desired recovery model ar correct' { + $testParameters = $mockDefaultParameters + $testParameters += @{ + Name = 'AdventureWorks*' + RecoveryModel = 'Simple' + } + $mockConnectSQL.Databases.Where{$_.Name -eq "AdventureWorks2"}[0].RecoveryModel = 'Simple' + + $result = Test-TargetResource @testParameters + $result | Should -Be $true + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + } + Assert-VerifiableMock } Describe "MSFT_SqlDatabaseRecoveryModel\Set-TargetResource" -Tag 'Set' { BeforeEach { Mock -CommandName Connect-SQL -MockWith { return $mockConnectSQL } -Verifiable + Mock -CommandName Invoke-Query -MockWith { $null } } Context 'When the system is not in the desired state, and database does not exist' { @@ -255,6 +332,45 @@ try } } + Context 'When the system is not in the desired state' { + It 'Should not call alter when matching on tempDB' { + $mockConnectSQL.Databases.Where{$_.Name -eq 'AdventureWorks'}[0].mockInvalidOperationForAlterMethod = $true + $testParameters = $mockDefaultParameters + $testParameters += @{ + Name = 'TempDB' + RecoveryModel = 'Full' + } + + { Set-TargetResource @testParameters } | Should -Not -Throw + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + } + + Context 'When the system is in the desired state' { + It "Should not call alter when all databases have correct recovery model" { + foreach($database in $mockConnectSQL.Databases) + { + $database.mockInvalidOperationForAlterMethod = $true + $database.RecoveryModel = 'Full' + } + + $testParameters = $mockDefaultParameters + $testParameters += @{ + Name = 'AdventureWorks*' + RecoveryModel = 'Full' + } + + { Set-TargetResource @testParameters } | Should -Not -Throw + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + } + Assert-VerifiableMock } } From 8fd8cf30655ccf624f26172045ecb2c581c8538f Mon Sep 17 00:00:00 2001 From: David Reynolds Date: Wed, 5 Sep 2018 01:10:04 -0500 Subject: [PATCH 6/7] Fixed ReadMe line length issue Improved code coverage --- README.md | 3 ++- .../MSFT_SqlDatabaseRecoveryModel.Tests.ps1 | 24 ++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5950d2140..ff9e4c034 100644 --- a/README.md +++ b/README.md @@ -655,7 +655,8 @@ Read more about recovery model in this article #### Parameters -* **`[String]` Name** _(Key)_: The SQL database name or pattern matching multiple databases. +* **`[String]` Name** _(Key)_: The SQL database name or pattern matching + multiple databases. * **`[String]` ServerName** _(Key)_: The host name of the SQL Server to be configured. * **`[String]` InstanceName** _(Key)_: The name of the SQL instance to be configured. * **`[String]` RecoveryModel** _(Required)_: The recovery model to use for the database. diff --git a/Tests/Unit/MSFT_SqlDatabaseRecoveryModel.Tests.ps1 b/Tests/Unit/MSFT_SqlDatabaseRecoveryModel.Tests.ps1 index 8666b076d..3accb6a88 100644 --- a/Tests/Unit/MSFT_SqlDatabaseRecoveryModel.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlDatabaseRecoveryModel.Tests.ps1 @@ -114,7 +114,7 @@ try $throwInvalidOperation = ("Database 'UnknownDatabase' does not exist " + ` "on SQL server 'localhost\MSSQLSERVER'.") - Mock -CommandName Connect-SQL -MockWith { throw $throwInvalidOperation } + # Mock -CommandName Connect-SQL -MockWith { throw $throwInvalidOperation } { Get-TargetResource @testParameters } | Should Throw $throwInvalidOperation } @@ -349,6 +349,28 @@ try } } + Context 'When the system is not in the desired state' { + It 'Should write a warning when failing to call RaiseError' { + $mockConnectSQL.Databases.Where{$_.Name -eq 'AdventureWorks'}[0].mockInvalidOperationForAlterMethod = $false + $mockConnectSQL.Databases.Where{$_.Name -eq 'AdventureWorks'}[0].mockExpectedRecoveryModel = 'Simple' + $testParameters = $mockDefaultParameters + $testParameters += @{ + Name = 'AdventureWorks' + RecoveryModel = 'Simple' + } + + Mock -CommandName Invoke-Query -MockWith { throw "Failed to write to log" } + Mock -CommandName Write-Warning -MockWith { $null } -Verifiable + + { Set-TargetResource @testParameters } | Should -Not -Throw + Assert-MockCalled -CommandName Write-Warning -Times 1 -Scope It + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + } + } + Context 'When the system is in the desired state' { It "Should not call alter when all databases have correct recovery model" { foreach($database in $mockConnectSQL.Databases) From 573aae26ad698528d7648dbc48bcf0618ed93ba6 Mon Sep 17 00:00:00 2001 From: David Reynolds Date: Wed, 5 Sep 2018 01:12:21 -0500 Subject: [PATCH 7/7] Updated Changelog.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6df008793..277633128 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ The new optional parameters are respectively SqlSvcStartupType, AgtSvcStartupType, AsSvcStartupType, IsSvcStartupType and RsSvcStartupType ([issue #1165](https://github.com/PowerShell/SqlServerDsc/issues/1165). [Maxime Daniou (@mdaniou)](https://github.com/mdaniou) +- Changes to SqlDatabaseRecoveryModel + - Now support regex style matching of databases. ## 11.4.0.0 @@ -54,8 +56,6 @@ forced to be reimported into the session. This is to support that a never version of SQL Server was installed side-by-side so that SQLPS module should be used instead. -- Changes to SqlDatabaseRecoveryModel - - Now support regex style matching of databases. ## 11.3.0.0