From 458ee31bfd326b3d2f9204142b405bce078d5f9b Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Mon, 8 Sep 2025 16:17:29 +0200 Subject: [PATCH 01/38] Add Test-SqlDscIsAgentAlert function to validate SQL Agent Alerts --- azure-pipelines.yml | 2 +- ...tAlert.ps1 => Test-SqlDscIsAgentAlert.ps1} | 27 ++++----- source/en-US/SqlServerDsc.strings.psd1 | 20 +++---- tests/Integration/Commands/README.md | 2 +- ...-SqlDscIsAgentAlert.Integration.Tests.ps1} | 16 +++--- ....ps1 => Test-SqlDscIsAgentAlert.Tests.ps1} | 56 +++++++++++++------ 6 files changed, 73 insertions(+), 50 deletions(-) rename source/Public/{Test-SqlDscAgentAlert.ps1 => Test-SqlDscIsAgentAlert.ps1} (85%) rename tests/Integration/Commands/{Test-SqlDscAgentAlert.Integration.Tests.ps1 => Test-SqlDscIsAgentAlert.Integration.Tests.ps1} (80%) rename tests/Unit/Public/{Test-SqlDscAgentAlert.Tests.ps1 => Test-SqlDscIsAgentAlert.Tests.ps1} (77%) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 784c393968..f6a5029e82 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -310,7 +310,7 @@ stages: 'tests/Integration/Commands/Get-SqlDscAgentAlert.Integration.Tests.ps1' 'tests/Integration/Commands/New-SqlDscAgentAlert.Integration.Tests.ps1' 'tests/Integration/Commands/Set-SqlDscAgentAlert.Integration.Tests.ps1' - 'tests/Integration/Commands/Test-SqlDscAgentAlert.Integration.Tests.ps1' + 'tests/Integration/Commands/Test-SqlDscIsAgentAlert.Integration.Tests.ps1' # Group 8 'tests/Integration/Commands/Remove-SqlDscAgentAlert.Integration.Tests.ps1' 'tests/Integration/Commands/Remove-SqlDscDatabase.Integration.Tests.ps1' diff --git a/source/Public/Test-SqlDscAgentAlert.ps1 b/source/Public/Test-SqlDscIsAgentAlert.ps1 similarity index 85% rename from source/Public/Test-SqlDscAgentAlert.ps1 rename to source/Public/Test-SqlDscIsAgentAlert.ps1 index 4301bc20c6..eb56bbc601 100644 --- a/source/Public/Test-SqlDscAgentAlert.ps1 +++ b/source/Public/Test-SqlDscIsAgentAlert.ps1 @@ -30,25 +30,26 @@ .EXAMPLE $serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance' - Test-SqlDscAgentAlert -ServerObject $serverObject -Name 'MyAlert' + Test-SqlDscIsAgentAlert -ServerObject $serverObject -Name 'MyAlert' Tests if the SQL Agent Alert named 'MyAlert' exists. .EXAMPLE $serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance' - $serverObject | Test-SqlDscAgentAlert -Name 'MyAlert' -Severity 16 + $serverObject | Test-SqlDscIsAgentAlert -Name 'MyAlert' -Severity 16 Tests if the SQL Agent Alert named 'MyAlert' exists and has severity level 16. .EXAMPLE $serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance' - $serverObject | Test-SqlDscAgentAlert -Name 'MyAlert' -MessageId 50001 + $serverObject | Test-SqlDscIsAgentAlert -Name 'MyAlert' -MessageId 50001 Tests if the SQL Agent Alert named 'MyAlert' exists and has message ID 50001. #> -function Test-SqlDscAgentAlert +function Test-SqlDscIsAgentAlert { [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('UseSyntacticallyCorrectExamples', '', Justification = 'Because the rule does not yet support parsing the code when a parameter type is not available. The ScriptAnalyzer rule UseSyntacticallyCorrectExamples will always error in the editor due to https://github.com/indented-automation/Indented.ScriptAnalyzerRules/issues/8.')] + [Alias('Test-SqlDscAgentAlert')] [CmdletBinding()] [OutputType([System.Boolean])] param @@ -79,23 +80,23 @@ function Test-SqlDscAgentAlert # Validate that both Severity and MessageId are not specified Assert-BoundParameter -BoundParameterList $PSBoundParameters -MutuallyExclusiveList1 @('Severity') -MutuallyExclusiveList2 @('MessageId') - Write-Verbose -Message ($script:localizedData.Test_SqlDscAgentAlert_TestingAlert -f $Name) + Write-Verbose -Message ($script:localizedData.Test_SqlDscIsAgentAlert_TestingAlert -f $Name) $alertObject = Get-AgentAlertObject -ServerObject $ServerObject -Name $Name if ($null -eq $alertObject) { - Write-Verbose -Message ($script:localizedData.Test_SqlDscAgentAlert_AlertNotFound -f $Name) + Write-Verbose -Message ($script:localizedData.Test_SqlDscIsAgentAlert_AlertNotFound -f $Name) return $false } - Write-Verbose -Message ($script:localizedData.Test_SqlDscAgentAlert_AlertFound -f $Name) + Write-Verbose -Message ($script:localizedData.Test_SqlDscIsAgentAlert_AlertFound -f $Name) # If no specific properties are specified, just return true (alert exists) if (-not $PSBoundParameters.ContainsKey('Severity') -and -not $PSBoundParameters.ContainsKey('MessageId')) { - Write-Verbose -Message ($script:localizedData.Test_SqlDscAgentAlert_NoPropertyTest) + Write-Verbose -Message ($script:localizedData.Test_SqlDscIsAgentAlert_NoPropertyTest) return $true } @@ -105,13 +106,13 @@ function Test-SqlDscAgentAlert { if ($alertObject.Severity -ne $Severity) { - Write-Verbose -Message ($script:localizedData.Test_SqlDscAgentAlert_SeverityMismatch -f $alertObject.Severity, $Severity) + Write-Verbose -Message ($script:localizedData.Test_SqlDscIsAgentAlert_SeverityMismatch -f $alertObject.Severity, $Severity) return $false } else { - Write-Verbose -Message ($script:localizedData.Test_SqlDscAgentAlert_SeverityMatch -f $Severity) + Write-Verbose -Message ($script:localizedData.Test_SqlDscIsAgentAlert_SeverityMatch -f $Severity) } } @@ -120,17 +121,17 @@ function Test-SqlDscAgentAlert { if ($alertObject.MessageId -ne $MessageId) { - Write-Verbose -Message ($script:localizedData.Test_SqlDscAgentAlert_MessageIdMismatch -f $alertObject.MessageId, $MessageId) + Write-Verbose -Message ($script:localizedData.Test_SqlDscIsAgentAlert_MessageIdMismatch -f $alertObject.MessageId, $MessageId) return $false } else { - Write-Verbose -Message ($script:localizedData.Test_SqlDscAgentAlert_MessageIdMatch -f $MessageId) + Write-Verbose -Message ($script:localizedData.Test_SqlDscIsAgentAlert_MessageIdMatch -f $MessageId) } } - Write-Verbose -Message ($script:localizedData.Test_SqlDscAgentAlert_AllTestsPassed -f $Name) + Write-Verbose -Message ($script:localizedData.Test_SqlDscIsAgentAlert_AllTestsPassed -f $Name) return $true } diff --git a/source/en-US/SqlServerDsc.strings.psd1 b/source/en-US/SqlServerDsc.strings.psd1 index c9af4710d5..c1ab980ead 100644 --- a/source/en-US/SqlServerDsc.strings.psd1 +++ b/source/en-US/SqlServerDsc.strings.psd1 @@ -434,14 +434,14 @@ ConvertFrom-StringData @' # This string shall not end with full stop (.) since it is used as a title of ShouldProcess messages. Remove_SqlDscAgentAlert_RemoveShouldProcessCaption = Remove SQL Agent Alert on instance - ## Test-SqlDscAgentAlert - Test_SqlDscAgentAlert_TestingAlert = Testing if the SQL Agent Alert '{0}' exists and has the desired properties. (TSAA0001) - Test_SqlDscAgentAlert_AlertNotFound = SQL Agent Alert '{0}' was not found. (TSAA0002) - Test_SqlDscAgentAlert_AlertFound = SQL Agent Alert '{0}' was found. (TSAA0003) - Test_SqlDscAgentAlert_NoPropertyTest = No specific properties to test, alert exists. (TSAA0004) - Test_SqlDscAgentAlert_SeverityMismatch = Severity mismatch: current '{0}', expected '{1}'. (TSAA0005) - Test_SqlDscAgentAlert_SeverityMatch = Severity matches expected value '{0}'. (TSAA0006) - Test_SqlDscAgentAlert_MessageIdMismatch = Message ID mismatch: current '{0}', expected '{1}'. (TSAA0007) - Test_SqlDscAgentAlert_MessageIdMatch = Message ID matches expected value '{0}'. (TSAA0008) - Test_SqlDscAgentAlert_AllTestsPassed = All tests passed for SQL Agent Alert '{0}'. (TSAA0009) + ## Test-SqlDscIsAgentAlert + Test_SqlDscIsAgentAlert_TestingAlert = Testing if the SQL Agent Alert '{0}' exists and has the desired properties. (TSIAA0001) + Test_SqlDscIsAgentAlert_AlertNotFound = SQL Agent Alert '{0}' was not found. (TSIAA0002) + Test_SqlDscIsAgentAlert_AlertFound = SQL Agent Alert '{0}' was found. (TSIAA0003) + Test_SqlDscIsAgentAlert_NoPropertyTest = No specific properties to test, alert exists. (TSIAA0004) + Test_SqlDscIsAgentAlert_SeverityMismatch = Severity mismatch: current '{0}', expected '{1}'. (TSIAA0005) + Test_SqlDscIsAgentAlert_SeverityMatch = Severity matches expected value '{0}'. (TSIAA0006) + Test_SqlDscIsAgentAlert_MessageIdMismatch = Message ID mismatch: current '{0}', expected '{1}'. (TSIAA0007) + Test_SqlDscIsAgentAlert_MessageIdMatch = Message ID matches expected value '{0}'. (TSIAA0008) + Test_SqlDscIsAgentAlert_AllTestsPassed = All tests passed for SQL Agent Alert '{0}'. (TSIAA0009) '@ diff --git a/tests/Integration/Commands/README.md b/tests/Integration/Commands/README.md index 26710b6fd2..f20b503ede 100644 --- a/tests/Integration/Commands/README.md +++ b/tests/Integration/Commands/README.md @@ -63,7 +63,7 @@ Test-SqlDscDatabase | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTE Get-SqlDscAgentAlert | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | - New-SqlDscAgentAlert | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | Test alerts Set-SqlDscAgentAlert | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | - -Test-SqlDscAgentAlert | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | - +Test-SqlDscIsAgentAlert | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | - Remove-SqlDscAgentAlert | 8 | 2 (New-SqlDscAgentAlert) | DSCSQLTEST | - Remove-SqlDscDatabase | 8 | 2 (New-SqlDscDatabase) | DSCSQLTEST | - Remove-SqlDscRole | 8 | 2 (New-SqlDscRole) | DSCSQLTEST | - diff --git a/tests/Integration/Commands/Test-SqlDscAgentAlert.Integration.Tests.ps1 b/tests/Integration/Commands/Test-SqlDscIsAgentAlert.Integration.Tests.ps1 similarity index 80% rename from tests/Integration/Commands/Test-SqlDscAgentAlert.Integration.Tests.ps1 rename to tests/Integration/Commands/Test-SqlDscIsAgentAlert.Integration.Tests.ps1 index e99a97ce3e..825a0003ed 100644 --- a/tests/Integration/Commands/Test-SqlDscAgentAlert.Integration.Tests.ps1 +++ b/tests/Integration/Commands/Test-SqlDscIsAgentAlert.Integration.Tests.ps1 @@ -29,7 +29,7 @@ BeforeAll { Import-Module -Name $script:moduleName -Force -ErrorAction 'Stop' } -Describe 'Test-SqlDscAgentAlert' -Tag @('Integration_SQL2017', 'Integration_SQL2019', 'Integration_SQL2022'){ +Describe 'Test-SqlDscIsAgentAlert' -Tag @('Integration_SQL2017', 'Integration_SQL2019', 'Integration_SQL2022'){ BeforeAll { # Integration tests are run on the DSCSQLTEST instance $script:sqlServerInstance = 'DSCSQLTEST' @@ -64,13 +64,13 @@ Describe 'Test-SqlDscAgentAlert' -Tag @('Integration_SQL2017', 'Integration_SQL2 Context 'When checking existence only' { It 'Should return true for existing alert' { - $result = $script:sqlServerObject | Test-SqlDscAgentAlert -Name 'IntegrationTest_SeverityAlert' + $result = $script:sqlServerObject | Test-SqlDscIsAgentAlert -Name 'IntegrationTest_SeverityAlert' $result | Should -BeTrue } It 'Should return false for non-existent alert' { - $result = $script:sqlServerObject | Test-SqlDscAgentAlert -Name 'NonExistentAlert' + $result = $script:sqlServerObject | Test-SqlDscIsAgentAlert -Name 'NonExistentAlert' $result | Should -BeFalse } @@ -78,13 +78,13 @@ Describe 'Test-SqlDscAgentAlert' -Tag @('Integration_SQL2017', 'Integration_SQL2 Context 'When checking severity' { It 'Should return true for matching severity' { - $result = $script:sqlServerObject | Test-SqlDscAgentAlert -Name 'IntegrationTest_SeverityAlert' -Severity 16 + $result = $script:sqlServerObject | Test-SqlDscIsAgentAlert -Name 'IntegrationTest_SeverityAlert' -Severity 16 $result | Should -BeTrue } It 'Should return false for non-matching severity' { - $result = $script:sqlServerObject | Test-SqlDscAgentAlert -Name 'IntegrationTest_SeverityAlert' -Severity 14 + $result = $script:sqlServerObject | Test-SqlDscIsAgentAlert -Name 'IntegrationTest_SeverityAlert' -Severity 14 $result | Should -BeFalse } @@ -92,13 +92,13 @@ Describe 'Test-SqlDscAgentAlert' -Tag @('Integration_SQL2017', 'Integration_SQL2 Context 'When checking message ID' { It 'Should return true for matching message ID' { - $result = $script:sqlServerObject | Test-SqlDscAgentAlert -Name 'IntegrationTest_MessageIdAlert' -MessageId 50001 + $result = $script:sqlServerObject | Test-SqlDscIsAgentAlert -Name 'IntegrationTest_MessageIdAlert' -MessageId 50001 $result | Should -BeTrue } It 'Should return false for non-matching message ID' { - $result = $script:sqlServerObject | Test-SqlDscAgentAlert -Name 'IntegrationTest_MessageIdAlert' -MessageId 50002 + $result = $script:sqlServerObject | Test-SqlDscIsAgentAlert -Name 'IntegrationTest_MessageIdAlert' -MessageId 50002 $result | Should -BeFalse } @@ -106,7 +106,7 @@ Describe 'Test-SqlDscAgentAlert' -Tag @('Integration_SQL2017', 'Integration_SQL2 Context 'When both Severity and MessageId are specified' { It 'Should throw error for both Severity and MessageId parameters' { - { $script:sqlServerObject | Test-SqlDscAgentAlert -Name 'IntegrationTest_SeverityAlert' -Severity 16 -MessageId 50001 } | + { $script:sqlServerObject | Test-SqlDscIsAgentAlert -Name 'IntegrationTest_SeverityAlert' -Severity 16 -MessageId 50001 } | Should -Throw } } diff --git a/tests/Unit/Public/Test-SqlDscAgentAlert.Tests.ps1 b/tests/Unit/Public/Test-SqlDscIsAgentAlert.Tests.ps1 similarity index 77% rename from tests/Unit/Public/Test-SqlDscAgentAlert.Tests.ps1 rename to tests/Unit/Public/Test-SqlDscIsAgentAlert.Tests.ps1 index cc1b642f62..b3e7e2526f 100644 --- a/tests/Unit/Public/Test-SqlDscAgentAlert.Tests.ps1 +++ b/tests/Unit/Public/Test-SqlDscIsAgentAlert.Tests.ps1 @@ -44,7 +44,7 @@ AfterAll { Get-Module -Name $script:dscModuleName -All | Remove-Module -Force } -Describe 'Test-SqlDscAgentAlert' -Tag 'Public' { +Describe 'Test-SqlDscIsAgentAlert' -Tag 'Public' { Context 'When command has correct parameter sets' { It 'Should have the correct parameters in parameter set ' -ForEach @( @{ @@ -52,7 +52,7 @@ Describe 'Test-SqlDscAgentAlert' -Tag 'Public' { ExpectedParameters = '[-ServerObject] [-Name] [[-Severity] ] [[-MessageId] ] []' } ) { - $result = (Get-Command -Name 'Test-SqlDscAgentAlert').ParameterSets | + $result = (Get-Command -Name 'Test-SqlDscIsAgentAlert').ParameterSets | Where-Object -FilterScript { $_.Name -eq $ExpectedParameterSetName } | Select-Object -Property @( @{ Name = 'ParameterSetName'; Expression = { $_.Name } }, @@ -65,34 +65,34 @@ Describe 'Test-SqlDscAgentAlert' -Tag 'Public' { Context 'When command has correct parameter properties' { It 'Should have ServerObject as a mandatory parameter' { - $parameterInfo = (Get-Command -Name 'Test-SqlDscAgentAlert').Parameters['ServerObject'] + $parameterInfo = (Get-Command -Name 'Test-SqlDscIsAgentAlert').Parameters['ServerObject'] $parameterInfo.Attributes.Mandatory | Should -BeTrue } It 'Should have ServerObject accept pipeline input' { - $parameterInfo = (Get-Command -Name 'Test-SqlDscAgentAlert').Parameters['ServerObject'] + $parameterInfo = (Get-Command -Name 'Test-SqlDscIsAgentAlert').Parameters['ServerObject'] $parameterInfo.Attributes.ValueFromPipeline | Should -BeTrue } It 'Should have Name as a mandatory parameter' { - $parameterInfo = (Get-Command -Name 'Test-SqlDscAgentAlert').Parameters['Name'] + $parameterInfo = (Get-Command -Name 'Test-SqlDscIsAgentAlert').Parameters['Name'] $parameterInfo.Attributes.Mandatory | Should -BeTrue } It 'Should have Severity as an optional parameter' { - $parameterInfo = (Get-Command -Name 'Test-SqlDscAgentAlert').Parameters['Severity'] + $parameterInfo = (Get-Command -Name 'Test-SqlDscIsAgentAlert').Parameters['Severity'] $parameterInfo.Attributes.Mandatory | Should -BeFalse } It 'Should have MessageId as an optional parameter' { - $parameterInfo = (Get-Command -Name 'Test-SqlDscAgentAlert').Parameters['MessageId'] + $parameterInfo = (Get-Command -Name 'Test-SqlDscIsAgentAlert').Parameters['MessageId'] $parameterInfo.Attributes.Mandatory | Should -BeFalse } } Context 'When validating parameter ranges' { It 'Should accept valid Severity values (0-25)' { - $command = Get-Command -Name 'Test-SqlDscAgentAlert' + $command = Get-Command -Name 'Test-SqlDscIsAgentAlert' $severityParam = $command.Parameters['Severity'] $validateRangeAttribute = $severityParam.Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateRangeAttribute] } @@ -101,7 +101,7 @@ Describe 'Test-SqlDscAgentAlert' -Tag 'Public' { } It 'Should accept valid MessageId values (0-2147483647)' { - $command = Get-Command -Name 'Test-SqlDscAgentAlert' + $command = Get-Command -Name 'Test-SqlDscIsAgentAlert' $messageIdParam = $command.Parameters['MessageId'] $validateRangeAttribute = $messageIdParam.Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateRangeAttribute] } @@ -126,7 +126,7 @@ Describe 'Test-SqlDscAgentAlert' -Tag 'Public' { } It 'Should return true when alert exists and no properties are specified' { - $result = Test-SqlDscAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' + $result = Test-SqlDscIsAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' $result | Should -BeTrue Should -Invoke -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName -Times 1 -Exactly @@ -150,13 +150,13 @@ Describe 'Test-SqlDscAgentAlert' -Tag 'Public' { } It 'Should return true when alert exists and severity matches' { - $result = Test-SqlDscAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -Severity 16 + $result = Test-SqlDscIsAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -Severity 16 $result | Should -BeTrue } It 'Should return false when alert exists but severity does not match' { - $result = Test-SqlDscAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -Severity 14 + $result = Test-SqlDscIsAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -Severity 14 $result | Should -BeFalse } @@ -178,13 +178,13 @@ Describe 'Test-SqlDscAgentAlert' -Tag 'Public' { } It 'Should return true when alert exists and message ID matches' { - $result = Test-SqlDscAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -MessageId 50001 + $result = Test-SqlDscIsAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -MessageId 50001 $result | Should -BeTrue } It 'Should return false when alert exists but message ID does not match' { - $result = Test-SqlDscAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -MessageId 50002 + $result = Test-SqlDscIsAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -MessageId 50002 $result | Should -BeFalse } @@ -200,7 +200,7 @@ Describe 'Test-SqlDscAgentAlert' -Tag 'Public' { } It 'Should return false when alert does not exist' { - $result = Test-SqlDscAgentAlert -ServerObject $script:mockServerObject -Name 'NonExistentAlert' + $result = Test-SqlDscIsAgentAlert -ServerObject $script:mockServerObject -Name 'NonExistentAlert' $result | Should -BeFalse Should -Invoke -CommandName 'Get-AgentAlertObject' -ModuleName $script:dscModuleName -Times 1 -Exactly @@ -217,7 +217,7 @@ Describe 'Test-SqlDscAgentAlert' -Tag 'Public' { } It 'Should call parameter validation with both severity and message ID' { - $null = Test-SqlDscAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -Severity 16 -MessageId 50001 + $null = Test-SqlDscIsAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -Severity 16 -MessageId 50001 Should -Invoke -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName -ParameterFilter { $BoundParameterList.ContainsKey('Severity') -and $BoundParameterList.ContainsKey('MessageId') @@ -225,11 +225,33 @@ Describe 'Test-SqlDscAgentAlert' -Tag 'Public' { } It 'Should call parameter validation with only severity' { - $null = Test-SqlDscAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -Severity 16 + $null = Test-SqlDscIsAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -Severity 16 Should -Invoke -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName -ParameterFilter { $BoundParameterList.ContainsKey('Severity') -and -not $BoundParameterList.ContainsKey('MessageId') } -Times 1 -Exactly } } + + Context 'When using the alias Test-SqlDscAgentAlert' { + BeforeAll { + # Mock the alert object using SMO stub types + $script:mockAlert = [Microsoft.SqlServer.Management.Smo.Agent.Alert]::CreateTypeInstance() + $script:mockAlert.Name = 'TestAlert' + + # Mock the server object using SMO stub types + $script:mockServerObject = [Microsoft.SqlServer.Management.Smo.Server]::CreateTypeInstance() + + Mock -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName + Mock -CommandName 'Get-AgentAlertObject' -ModuleName $script:dscModuleName -MockWith { return $script:mockAlert } + } + + It 'Should work with alias Test-SqlDscAgentAlert' { + $result = Test-SqlDscAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' + + $result | Should -BeTrue + Should -Invoke -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName -Times 1 -Exactly + Should -Invoke -CommandName 'Get-AgentAlertObject' -ModuleName $script:dscModuleName -Times 1 -Exactly + } + } } From 194945a33329696b5a90cbf479a3e75201d8cd91 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Mon, 8 Sep 2025 16:19:47 +0200 Subject: [PATCH 02/38] Update OUTPUTS section in guidelines for clarity and add alias usage recommendation --- .../dsc-community-style-guidelines-powershell.instructions.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/instructions/dsc-community-style-guidelines-powershell.instructions.md b/.github/instructions/dsc-community-style-guidelines-powershell.instructions.md index b2f7f13222..cc9cf54b4c 100644 --- a/.github/instructions/dsc-community-style-guidelines-powershell.instructions.md +++ b/.github/instructions/dsc-community-style-guidelines-powershell.instructions.md @@ -65,7 +65,7 @@ applyTo: "**/*.ps?(m|d)1" - Comment-based help indentation: keywords 4 spaces, text 8 spaces - Include examples for all parameter sets and combinations - INPUTS: List each pipeline‑accepted type (one per line) with a 1‑line description. -- OUTPUTS: List each return type (one per line) with a 1‑line description. Must match both [OutputType()] and actual returns. +- OUTPUTS: List each return type (one per line) with a 1‑line description. Must match both `[OutputType()]` and actual returns. - .NOTES: Include only if it conveys critical info (constraints, side effects, security, version compatibility, breaking behavior). Keep to ≤2 short sentences. ## Functions @@ -82,6 +82,7 @@ applyTo: "**/*.ps?(m|d)1" - Inside `$PSCmdlet.ShouldProcess`-block, avoid using `Write-Verbose` - Never use backtick as line continuation in production code. - Set `$ErrorActionPreference = 'Stop'` before commands using `-ErrorAction 'Stop'`; restore after +- Use `[Alias()]` attribute for function aliases, never `Set-Alias` or `New-Alias` ## Output streams From 6766bf8bb174802217bd49b271eaccc98de0a666 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Mon, 8 Sep 2025 16:19:52 +0200 Subject: [PATCH 03/38] Clarify instructions for running scripts in PowerShell by specifying `pwsh` in the Build & Test Workflow section --- .../instructions/dsc-community-style-guidelines.instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/instructions/dsc-community-style-guidelines.instructions.md b/.github/instructions/dsc-community-style-guidelines.instructions.md index 1db020e9d5..bb94249f01 100644 --- a/.github/instructions/dsc-community-style-guidelines.instructions.md +++ b/.github/instructions/dsc-community-style-guidelines.instructions.md @@ -11,7 +11,7 @@ applyTo: "**" - **Resource**: DSC class-based resource ## Build & Test Workflow -- Run in PowerShell, from repository root +- Run scripts in `pwsh`; always from repository root - Build before running tests: `.\build.ps1 -Tasks build` - Always run tests in new PowerShell session: `Invoke-Pester -Path @({test paths}) -Output Detailed` From 9da75eae3814727de22bdc6097e9cb31093ebc51 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Mon, 8 Sep 2025 16:28:20 +0200 Subject: [PATCH 04/38] Clarify changelog guidelines by specifying to skip adding entries for existing changes and prohibiting duplicates in the Unreleased section --- .../dsc-community-style-guidelines-changelog.instructions.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/instructions/dsc-community-style-guidelines-changelog.instructions.md b/.github/instructions/dsc-community-style-guidelines-changelog.instructions.md index 5041db8fa0..58c292307d 100644 --- a/.github/instructions/dsc-community-style-guidelines-changelog.instructions.md +++ b/.github/instructions/dsc-community-style-guidelines-changelog.instructions.md @@ -10,4 +10,5 @@ applyTo: "CHANGELOG.md" - Describe notable changes briefly, ≤2 items per change type - Reference issues using format [issue #](https://github.com///issues/) - No empty lines between list items in same section -- Do not add item if there are already an existing item for the same change +- Skip adding entry if same change already exists in Unreleased section +- No duplicate sections or items in Unreleased section From d7fe31e20a20526577b2c4a9dc31be2c29510c8c Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Mon, 8 Sep 2025 16:30:45 +0200 Subject: [PATCH 05/38] Refactor alert existence check in New-SqlDscAgentAlert function to use Test-SqlDscIsAgentAlert --- CHANGELOG.md | 4 ++++ source/Public/New-SqlDscAgentAlert.ps1 | 4 ++-- .../Unit/Public/New-SqlDscAgentAlert.Tests.ps1 | 18 +++++++++--------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 184f61aa19..84890d3829 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -153,6 +153,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `source/Examples/Resources/SqlSetup/5-InstallNamedInstanceInFailoverClusterSecondNode.ps1` - Removed redundant `$SqlAdministratorCredential` parameter from example configuration. +- `New-SqlDscAgentAlert` + - Updated the command to use `Test-SqlDscIsAgentAlert` instead of directly + calling `Get-AgentAlertObject` when checking if an alert already exists + (issue [#2202](https://github.com/dsccommunity/SqlServerDsc/issues/2202)). ## [17.1.0] - 2025-05-22 diff --git a/source/Public/New-SqlDscAgentAlert.ps1 b/source/Public/New-SqlDscAgentAlert.ps1 index 4269232f23..58a62a2518 100644 --- a/source/Public/New-SqlDscAgentAlert.ps1 +++ b/source/Public/New-SqlDscAgentAlert.ps1 @@ -90,9 +90,9 @@ function New-SqlDscAgentAlert Assert-BoundParameter -BoundParameterList $PSBoundParameters -MutuallyExclusiveList1 @('Severity') -MutuallyExclusiveList2 @('MessageId') # Check if alert already exists - $existingAlert = Get-AgentAlertObject -ServerObject $ServerObject -Name $Name + $alertExists = Test-SqlDscIsAgentAlert -ServerObject $ServerObject -Name $Name - if ($existingAlert) + if ($alertExists) { $errorMessage = $script:localizedData.New_SqlDscAgentAlert_AlertAlreadyExists -f $Name New-InvalidOperationException -Message $errorMessage diff --git a/tests/Unit/Public/New-SqlDscAgentAlert.Tests.ps1 b/tests/Unit/Public/New-SqlDscAgentAlert.Tests.ps1 index 0cd93b6136..69bb8ec85f 100644 --- a/tests/Unit/Public/New-SqlDscAgentAlert.Tests.ps1 +++ b/tests/Unit/Public/New-SqlDscAgentAlert.Tests.ps1 @@ -125,7 +125,7 @@ Describe 'New-SqlDscAgentAlert' -Tag 'Public' { $script:mockNewAlert.MessageID = 0 # Mock the private functions - Mock -CommandName 'Get-AgentAlertObject' + Mock -CommandName 'Test-SqlDscIsAgentAlert' -MockWith { return $false } Mock -CommandName 'Assert-BoundParameter' } @@ -134,14 +134,14 @@ Describe 'New-SqlDscAgentAlert' -Tag 'Public' { $null = New-SqlDscAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -Severity 16 Should -Invoke -CommandName 'Assert-BoundParameter' -Times 1 -Exactly - Should -Invoke -CommandName 'Get-AgentAlertObject' -Times 1 -Exactly + Should -Invoke -CommandName 'Test-SqlDscIsAgentAlert' -Times 1 -Exactly } It 'Should create alert with message ID successfully' { $null = New-SqlDscAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -MessageId 50001 Should -Invoke -CommandName 'Assert-BoundParameter' -Times 1 -Exactly - Should -Invoke -CommandName 'Get-AgentAlertObject' -Times 1 -Exactly + Should -Invoke -CommandName 'Test-SqlDscIsAgentAlert' -Times 1 -Exactly } } @@ -170,8 +170,8 @@ Describe 'New-SqlDscAgentAlert' -Tag 'Public' { # Test maximum value (25) - should complete without errors $null = New-SqlDscAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert2' -Severity 25 - # Verify that Get-AgentAlertObject was called for each alert creation to check existence - Should -Invoke -CommandName 'Get-AgentAlertObject' -Times 2 -Exactly + # Verify that Test-SqlDscIsAgentAlert was called for each alert creation to check existence + Should -Invoke -CommandName 'Test-SqlDscIsAgentAlert' -Times 2 -Exactly # Verify that Assert-BoundParameter was called for each alert creation Should -Invoke -CommandName 'Assert-BoundParameter' -Times 2 -Exactly } @@ -183,8 +183,8 @@ Describe 'New-SqlDscAgentAlert' -Tag 'Public' { # Test maximum value (2147483647) - should complete without errors $null = New-SqlDscAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert4' -MessageId 2147483647 - # Verify that Get-AgentAlertObject was called for each alert creation to check existence - Should -Invoke -CommandName 'Get-AgentAlertObject' -Times 2 -Exactly + # Verify that Test-SqlDscIsAgentAlert was called for each alert creation to check existence + Should -Invoke -CommandName 'Test-SqlDscIsAgentAlert' -Times 2 -Exactly # Verify that Assert-BoundParameter was called for each alert creation Should -Invoke -CommandName 'Assert-BoundParameter' -Times 2 -Exactly } @@ -208,7 +208,7 @@ Describe 'New-SqlDscAgentAlert' -Tag 'Public' { $script:mockServerObject = [Microsoft.SqlServer.Management.Smo.Server]::CreateTypeInstance() $script:mockServerObject.JobServer = $script:mockJobServer - Mock -CommandName 'Get-AgentAlertObject' -MockWith { return $script:mockExistingAlert } + Mock -CommandName 'Test-SqlDscIsAgentAlert' -MockWith { return $true } Mock -CommandName 'Assert-BoundParameter' } @@ -234,7 +234,7 @@ Describe 'New-SqlDscAgentAlert' -Tag 'Public' { # Mock alert object $script:mockAlert = [Microsoft.SqlServer.Management.Smo.Agent.Alert]::CreateTypeInstance() - Mock -CommandName 'Get-AgentAlertObject' + Mock -CommandName 'Test-SqlDscIsAgentAlert' -MockWith { return $false } Mock -CommandName 'Assert-BoundParameter' } From 2efd2cf24c3eb1dff21ec46c113161b3feeead00 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Mon, 8 Sep 2025 16:35:07 +0200 Subject: [PATCH 06/38] Update changelog to reflect renaming of Test-SqlDscAgentAlert to Test-SqlDscIsAgentAlert --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84890d3829..10ccc8a369 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,7 +79,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `Remove-SqlDscRole`, and `Remove-SqlDscLogin` commands for retrieving and managing SQL Server logins and roles with support for refresh, pipeline input, and ShouldProcess. - Added `Get-SqlDscAgentAlert`, `New-SqlDscAgentAlert`, - `Set-SqlDscAgentAlert`, `Remove-SqlDscAgentAlert`, and `Test-SqlDscAgentAlert` + `Set-SqlDscAgentAlert`, `Remove-SqlDscAgentAlert`, and `Test-SqlDscIsAgentAlert` to manage SQL Agent alerts on a Database Engine instance. - Added new public commands for database management: - `Get-SqlDscDatabase` - Get databases from a SQL Server Database Engine instance From e6c17785ebaaa0c91100ed64ad057697620f7a39 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 12:51:11 +0200 Subject: [PATCH 07/38] Remove unnecessary empty SuppressMessage attributes from test files --- tests/Unit/Classes/SqlAgentAlert.Tests.ps1 | 1 - tests/Unit/Private/Get-AgentAlertObject.Tests.ps1 | 1 - tests/Unit/Public/Deny-SqlDscServerPermission.Tests.ps1 | 1 - tests/Unit/Public/Get-SqlDscAgentAlert.Tests.ps1 | 1 - tests/Unit/Public/New-SqlDscAgentAlert.Tests.ps1 | 1 - tests/Unit/Public/Remove-SqlDscAgentAlert.Tests.ps1 | 1 - tests/Unit/Public/Set-SqlDscAgentAlert.Tests.ps1 | 1 - 7 files changed, 7 deletions(-) diff --git a/tests/Unit/Classes/SqlAgentAlert.Tests.ps1 b/tests/Unit/Classes/SqlAgentAlert.Tests.ps1 index 5fa1d76cbb..c3c483d171 100644 --- a/tests/Unit/Classes/SqlAgentAlert.Tests.ps1 +++ b/tests/Unit/Classes/SqlAgentAlert.Tests.ps1 @@ -1,4 +1,3 @@ -[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Suppressing this rule because Script Analyzer does not understand Pester syntax.')] param () diff --git a/tests/Unit/Private/Get-AgentAlertObject.Tests.ps1 b/tests/Unit/Private/Get-AgentAlertObject.Tests.ps1 index b790c6c157..480567b14c 100644 --- a/tests/Unit/Private/Get-AgentAlertObject.Tests.ps1 +++ b/tests/Unit/Private/Get-AgentAlertObject.Tests.ps1 @@ -1,4 +1,3 @@ -[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Suppressing this rule because Script Analyzer does not understand Pester syntax.')] param () diff --git a/tests/Unit/Public/Deny-SqlDscServerPermission.Tests.ps1 b/tests/Unit/Public/Deny-SqlDscServerPermission.Tests.ps1 index 785c317747..a210aa7e80 100644 --- a/tests/Unit/Public/Deny-SqlDscServerPermission.Tests.ps1 +++ b/tests/Unit/Public/Deny-SqlDscServerPermission.Tests.ps1 @@ -1,4 +1,3 @@ -[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Suppressing this rule because Script Analyzer does not understand Pester syntax.')] param () diff --git a/tests/Unit/Public/Get-SqlDscAgentAlert.Tests.ps1 b/tests/Unit/Public/Get-SqlDscAgentAlert.Tests.ps1 index 7d53327660..10a04400dd 100644 --- a/tests/Unit/Public/Get-SqlDscAgentAlert.Tests.ps1 +++ b/tests/Unit/Public/Get-SqlDscAgentAlert.Tests.ps1 @@ -1,4 +1,3 @@ -[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Suppressing this rule because Script Analyzer does not understand Pester syntax.')] param () diff --git a/tests/Unit/Public/New-SqlDscAgentAlert.Tests.ps1 b/tests/Unit/Public/New-SqlDscAgentAlert.Tests.ps1 index 69bb8ec85f..5c7a1ec0e1 100644 --- a/tests/Unit/Public/New-SqlDscAgentAlert.Tests.ps1 +++ b/tests/Unit/Public/New-SqlDscAgentAlert.Tests.ps1 @@ -1,4 +1,3 @@ -[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Suppressing this rule because Script Analyzer does not understand Pester syntax.')] param () diff --git a/tests/Unit/Public/Remove-SqlDscAgentAlert.Tests.ps1 b/tests/Unit/Public/Remove-SqlDscAgentAlert.Tests.ps1 index 0cffde4a2d..cdc94f1ba0 100644 --- a/tests/Unit/Public/Remove-SqlDscAgentAlert.Tests.ps1 +++ b/tests/Unit/Public/Remove-SqlDscAgentAlert.Tests.ps1 @@ -1,4 +1,3 @@ -[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Suppressing this rule because Script Analyzer does not understand Pester syntax.')] param () diff --git a/tests/Unit/Public/Set-SqlDscAgentAlert.Tests.ps1 b/tests/Unit/Public/Set-SqlDscAgentAlert.Tests.ps1 index a8cdaf1d1e..c976dc6d95 100644 --- a/tests/Unit/Public/Set-SqlDscAgentAlert.Tests.ps1 +++ b/tests/Unit/Public/Set-SqlDscAgentAlert.Tests.ps1 @@ -1,4 +1,3 @@ -[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Suppressing this rule because Script Analyzer does not understand Pester syntax.')] param () From 7c747396a9ccfe827340efda1127f1389c4c62d3 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 12:51:34 +0200 Subject: [PATCH 08/38] Enhance error handling guidelines by specifying required parameters for Write-Error and emphasizing return usage to prevent further processing. --- .../dsc-community-style-guidelines-powershell.instructions.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/instructions/dsc-community-style-guidelines-powershell.instructions.md b/.github/instructions/dsc-community-style-guidelines-powershell.instructions.md index cc9cf54b4c..ab899a601f 100644 --- a/.github/instructions/dsc-community-style-guidelines-powershell.instructions.md +++ b/.github/instructions/dsc-community-style-guidelines-powershell.instructions.md @@ -92,7 +92,9 @@ applyTo: "**/*.ps?(m|d)1" - Use `Write-Information` for: User-facing status updates; Important operational messages; Non-error state changes - Use `Write-Warning` for: Non-fatal issues requiring attention; Deprecated functionality usage; Configuration problems that don't block execution - Use `$PSCmdlet.ThrowTerminatingError()` for terminating errors (except for classes), use relevant error category, in try-catch include exception -- Use `Write-Error` for non-terminating errors, use relevant error category +- Use `Write-Error` for non-terminating errors + - Always include `-Message` (localized string), `-Category` (relevant error category), `-ErrorId` (unique ID matching localized string ID), `-TargetObject` (object causing error) + - Always use `return` after `Write-Error` to avoid further processing ## ShouldProcess Required Pattern From 5c2cfafaefa66abb09dda22a48f004a505f0a83b Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 12:51:40 +0200 Subject: [PATCH 09/38] Remove unnecessary SuppressMessage attribute from unit test guidelines --- .../dsc-community-style-guidelines-unit-tests.instructions.md | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/instructions/dsc-community-style-guidelines-unit-tests.instructions.md b/.github/instructions/dsc-community-style-guidelines-unit-tests.instructions.md index 5cebdb743a..2b893aa1aa 100644 --- a/.github/instructions/dsc-community-style-guidelines-unit-tests.instructions.md +++ b/.github/instructions/dsc-community-style-guidelines-unit-tests.instructions.md @@ -15,7 +15,6 @@ applyTo: "tests/[u]Unit/**/*.[Tt]ests.ps1" Use this exact setup block before `Describe`: ```powershell -[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Suppressing this rule because Script Analyzer does not understand Pester syntax.')] param () From 0c132b314d1899a799a38fbcd086b8345e772400 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 12:51:46 +0200 Subject: [PATCH 10/38] Update SqlServerDsc guidelines to clarify requirements and improve structure --- .../SqlServerDsc-guidelines.instructions.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/instructions/SqlServerDsc-guidelines.instructions.md b/.github/instructions/SqlServerDsc-guidelines.instructions.md index cc8ec878ae..c61a421515 100644 --- a/.github/instructions/SqlServerDsc-guidelines.instructions.md +++ b/.github/instructions/SqlServerDsc-guidelines.instructions.md @@ -3,7 +3,16 @@ description: SqlServerDsc-specific guidelines for AI development. applyTo: "**" --- -# SqlServerDsc Guidelines +# SqlServerDsc Requirements + +## Build & Test Workflow Requirements +- Run in `pwsh` +- Run scripts from project root +- Setup build environment (once per `pwsh` session): `.build.ps1 -Task noop` +- Build project: `.build.ps1 -Task build` +- Run tests without coverage (wildcards allowed): `Invoke-PesterJob -Path '{tests filepath}' -SkipCodeCoverage` +- Run QA tests: `Invoke-PesterJob -Path 'tests/QA' -SkipCodeCoverage` +- Never run integration tests locally ## Naming - Public commands: `{Verb}-SqlDsc{Noun}` format @@ -25,7 +34,7 @@ applyTo: "**" - Reporting Services: instance `SSRS` - Power BI Report Server: instance `PBIRS` -## Test Requirements +## Tests Requirements - Unit tests: Add `$env:SqlServerDscCI = $true` in `BeforeAll`, remove in `AfterAll` - Integration tests: - If requiring SQL Server DB, start the Windows service in `BeforeAll`, stop it in `AfterAll`. From 26f3f4b3b1bde2b160443e24fec3ebb2565d37fb Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 12:51:56 +0200 Subject: [PATCH 11/38] Add Viscalyx.Common module for development and testing --- RequiredModules.psd1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RequiredModules.psd1 b/RequiredModules.psd1 index 4bc2c97598..1dc6b38d44 100644 --- a/RequiredModules.psd1 +++ b/RequiredModules.psd1 @@ -66,4 +66,7 @@ } } PlatyPS = 'latest' + + # For development + 'Viscalyx.Common' = 'latest' # Invoke-PesterJob to run tests } From 051a08d2c7e01e69f378513783748d1129ddd47e Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 12:54:58 +0200 Subject: [PATCH 12/38] Add Test-SqlDscAgentAlertProperty function and associated tests for validating SQL Agent Alert properties --- .../Public/Test-SqlDscAgentAlertProperty.ps1 | 137 ++++++++ source/en-US/SqlServerDsc.strings.psd1 | 9 +- ...scAgentAlertProperty.Integration.Tests.ps1 | 143 +++++++++ .../Test-SqlDscAgentAlertProperty.Tests.ps1 | 302 ++++++++++++++++++ 4 files changed, 585 insertions(+), 6 deletions(-) create mode 100644 source/Public/Test-SqlDscAgentAlertProperty.ps1 create mode 100644 tests/Integration/Commands/Test-SqlDscAgentAlertProperty.Integration.Tests.ps1 create mode 100644 tests/Unit/Public/Test-SqlDscAgentAlertProperty.Tests.ps1 diff --git a/source/Public/Test-SqlDscAgentAlertProperty.ps1 b/source/Public/Test-SqlDscAgentAlertProperty.ps1 new file mode 100644 index 0000000000..efccc92ad1 --- /dev/null +++ b/source/Public/Test-SqlDscAgentAlertProperty.ps1 @@ -0,0 +1,137 @@ +<# + .SYNOPSIS + Tests if a SQL Agent Alert has the specified properties. + + .DESCRIPTION + This command tests if a SQL Agent Alert on a SQL Server Database Engine + instance has the specified properties. At least one property parameter + must be specified. + + .PARAMETER ServerObject + Specifies current server connection object. + + .PARAMETER Name + Specifies the name of the SQL Agent Alert to test. + + .PARAMETER AlertObject + Specifies the SQL Agent Alert object to test. + + .PARAMETER Severity + Specifies the expected severity level for the SQL Agent Alert. Valid range is 0 to 25. + If specified, the command will return $true only if the alert exists and has this severity. + + .PARAMETER MessageId + Specifies the expected message ID for the SQL Agent Alert. Valid range is 0 to 2147483647. + If specified, the command will return $true only if the alert exists and has this message ID. + + .INPUTS + Microsoft.SqlServer.Management.Smo.Server + + SQL Server Database Engine instance object. + + .INPUTS + Microsoft.SqlServer.Management.Smo.Agent.Alert + + SQL Agent Alert object. + + .OUTPUTS + [System.Boolean] + + .EXAMPLE + $serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance' + Test-SqlDscAgentAlertProperty -ServerObject $serverObject -Name 'MyAlert' -Severity 16 + + Tests if the SQL Agent Alert named 'MyAlert' exists and has severity level 16. + + .EXAMPLE + $serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance' + $serverObject | Test-SqlDscAgentAlertProperty -Name 'MyAlert' -MessageId 50001 + + Tests if the SQL Agent Alert named 'MyAlert' exists and has message ID 50001. + + .EXAMPLE + $serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance' + $alertObject = $serverObject | Get-SqlDscAgentAlert -Name 'MyAlert' + $alertObject | Test-SqlDscAgentAlertProperty -Severity 16 + + Tests if the SQL Agent Alert has severity level 16 using alert object pipeline input. +#> +function Test-SqlDscAgentAlertProperty +{ + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('UseSyntacticallyCorrectExamples', '', Justification = 'Because the rule does not yet support parsing the code when a parameter type is not available. The ScriptAnalyzer rule UseSyntacticallyCorrectExamples will always error in the editor due to https://github.com/indented-automation/Indented.ScriptAnalyzerRules/issues/8.')] + [CmdletBinding(DefaultParameterSetName = 'ByServerAndName')] + [OutputType([System.Boolean])] + param + ( + [Parameter(ParameterSetName = 'ByServerAndName', Mandatory = $true, ValueFromPipeline = $true)] + [Microsoft.SqlServer.Management.Smo.Server] + $ServerObject, + + [Parameter(ParameterSetName = 'ByServerAndName', Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Name, + + [Parameter(ParameterSetName = 'ByAlertObject', Mandatory = $true, ValueFromPipeline = $true)] + [Microsoft.SqlServer.Management.Smo.Agent.Alert] + $AlertObject, + + [Parameter()] + [ValidateRange(0, 25)] + [System.Int32] + $Severity, + + [Parameter()] + [ValidateRange(0, 2147483647)] + [System.Int32] + $MessageId + ) + + # cSpell: ignore TSAAP + process + { + # Ensure at least one property parameter is specified + Assert-BoundParameter -BoundParameterList $PSBoundParameters -AtLeastOneList @('Severity', 'MessageId') + + # Validate that both Severity and MessageId are not specified + Assert-BoundParameter -BoundParameterList $PSBoundParameters -MutuallyExclusiveList1 @('Severity') -MutuallyExclusiveList2 @('MessageId') + + if ($PSCmdlet.ParameterSetName -eq 'ByAlertObject') + { + $alertObject = $AlertObject + } + else + { + $alertObject = Get-AgentAlertObject -ServerObject $ServerObject -Name $Name + + if ($null -eq $alertObject) + { + $errorMessage = $script:localizedData.Test_SqlDscAgentAlertProperty_AlertNotFound -f $Name + + Write-Error -Message $errorMessage -Category 'ObjectNotFound' -ErrorId 'TSAAP0002' -TargetObject $Name + + return + } + } + + # Test severity if specified + if ($PSBoundParameters.ContainsKey('Severity')) + { + if ($alertObject.Severity -ne $Severity) + { + return $false + } + } + + # Test message ID if specified + if ($PSBoundParameters.ContainsKey('MessageId')) + { + if ($alertObject.MessageId -ne $MessageId) + { + return $false + } + } + + return $true + } +} diff --git a/source/en-US/SqlServerDsc.strings.psd1 b/source/en-US/SqlServerDsc.strings.psd1 index c1ab980ead..35d8c35afd 100644 --- a/source/en-US/SqlServerDsc.strings.psd1 +++ b/source/en-US/SqlServerDsc.strings.psd1 @@ -438,10 +438,7 @@ ConvertFrom-StringData @' Test_SqlDscIsAgentAlert_TestingAlert = Testing if the SQL Agent Alert '{0}' exists and has the desired properties. (TSIAA0001) Test_SqlDscIsAgentAlert_AlertNotFound = SQL Agent Alert '{0}' was not found. (TSIAA0002) Test_SqlDscIsAgentAlert_AlertFound = SQL Agent Alert '{0}' was found. (TSIAA0003) - Test_SqlDscIsAgentAlert_NoPropertyTest = No specific properties to test, alert exists. (TSIAA0004) - Test_SqlDscIsAgentAlert_SeverityMismatch = Severity mismatch: current '{0}', expected '{1}'. (TSIAA0005) - Test_SqlDscIsAgentAlert_SeverityMatch = Severity matches expected value '{0}'. (TSIAA0006) - Test_SqlDscIsAgentAlert_MessageIdMismatch = Message ID mismatch: current '{0}', expected '{1}'. (TSIAA0007) - Test_SqlDscIsAgentAlert_MessageIdMatch = Message ID matches expected value '{0}'. (TSIAA0008) - Test_SqlDscIsAgentAlert_AllTestsPassed = All tests passed for SQL Agent Alert '{0}'. (TSIAA0009) + + ## Test-SqlDscAgentAlertProperty + Test_SqlDscAgentAlertProperty_AlertNotFound = SQL Agent Alert '{0}' was not found. (TSAAP0002) '@ diff --git a/tests/Integration/Commands/Test-SqlDscAgentAlertProperty.Integration.Tests.ps1 b/tests/Integration/Commands/Test-SqlDscAgentAlertProperty.Integration.Tests.ps1 new file mode 100644 index 0000000000..735772202e --- /dev/null +++ b/tests/Integration/Commands/Test-SqlDscAgentAlertProperty.Integration.Tests.ps1 @@ -0,0 +1,143 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Suppressing this rule because Script Analyzer does not understand Pester syntax.')] +param () + +BeforeDiscovery { + try + { + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies have been resolved, so if this module is not available, run 'noop' task. + if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) + { + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../../build.ps1" -Tasks 'noop' 3>&1 4>&1 5>&1 6>&1 > $null + } + + # If the dependencies have not been resolved, this will throw an error. + Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' + } + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' + } +} + +BeforeAll { + $script:moduleName = 'SqlServerDsc' + + Import-Module -Name $script:moduleName -Force -ErrorAction 'Stop' +} + +Describe 'Test-SqlDscAgentAlertProperty' -Tag @('Integration_SQL2017', 'Integration_SQL2019', 'Integration_SQL2022'){ + BeforeAll { + # Integration tests are run on the DSCSQLTEST instance + $script:sqlServerInstance = 'DSCSQLTEST' + + # Starting the named instance SQL Server service prior to running tests. + Start-Service -Name 'MSSQL$DSCSQLTEST' -Verbose -ErrorAction 'Stop' + + $mockSqlAdministratorUserName = 'SqlAdmin' # Using computer name as NetBIOS name throw exception. + $mockSqlAdministratorPassword = ConvertTo-SecureString -String 'P@ssw0rd1' -AsPlainText -Force + + $script:mockSqlAdminCredential = [System.Management.Automation.PSCredential]::new($mockSqlAdministratorUserName, $mockSqlAdministratorPassword) + + # Connect to the SQL Server instance + $script:sqlServerObject = Connect-SqlDscDatabaseEngine -InstanceName $script:sqlServerInstance -Credential $script:mockSqlAdminCredential -ErrorAction 'Stop' + + # Create test alerts for testing + $script:sqlServerObject | New-SqlDscAgentAlert -Name 'IntegrationTest_SeverityAlert' -Severity 16 -ErrorAction Stop + $script:sqlServerObject | New-SqlDscAgentAlert -Name 'IntegrationTest_MessageIdAlert' -MessageId 50001 -ErrorAction Stop + } + + AfterAll { + # Clean up test alerts + $script:sqlServerObject | Remove-SqlDscAgentAlert -Name 'IntegrationTest_SeverityAlert' -Force -ErrorAction 'SilentlyContinue' + $script:sqlServerObject | Remove-SqlDscAgentAlert -Name 'IntegrationTest_MessageIdAlert' -Force -ErrorAction 'SilentlyContinue' + + # Disconnect from the SQL Server + Disconnect-SqlDscDatabaseEngine -ServerObject $script:sqlServerObject + + # Stop the named instance SQL Server service to save memory on the build worker. + Stop-Service -Name 'MSSQL$DSCSQLTEST' -Verbose -ErrorAction 'Stop' + } + + Context 'When checking severity' { + It 'Should return true for matching severity' { + $result = $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'IntegrationTest_SeverityAlert' -Severity 16 + + $result | Should -BeTrue + } + + It 'Should return false for non-matching severity' { + $result = $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'IntegrationTest_SeverityAlert' -Severity 14 + + $result | Should -BeFalse + } + } + + Context 'When checking message ID' { + It 'Should return true for matching message ID' { + $result = $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'IntegrationTest_MessageIdAlert' -MessageId 50001 + + $result | Should -BeTrue + } + + It 'Should return false for non-matching message ID' { + $result = $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'IntegrationTest_MessageIdAlert' -MessageId 50002 + + $result | Should -BeFalse + } + } + + Context 'When alert does not exist' { + It 'Should return false for non-existent alert with severity' { + $result = $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'NonExistentAlert' -Severity 16 + + $result | Should -BeFalse + } + + It 'Should return false for non-existent alert with message ID' { + $result = $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'NonExistentAlert' -MessageId 50001 + + $result | Should -BeFalse + } + } + + Context 'When no properties are specified' { + It 'Should throw error when no property parameters are specified' { + { $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'IntegrationTest_SeverityAlert' } | + Should -Throw + } + } + + Context 'When using AlertObject parameter' { + It 'Should return true when alert object has matching severity' { + $alertObject = $script:sqlServerObject | Get-SqlDscAgentAlert -Name 'IntegrationTest_SeverityAlert' + $result = $alertObject | Test-SqlDscAgentAlertProperty -Severity 16 + + $result | Should -BeTrue + } + + It 'Should return false when alert object has non-matching severity' { + $alertObject = $script:sqlServerObject | Get-SqlDscAgentAlert -Name 'IntegrationTest_SeverityAlert' + $result = $alertObject | Test-SqlDscAgentAlertProperty -Severity 14 + + $result | Should -BeFalse + } + + It 'Should return true when alert object has matching message ID' { + $alertObject = $script:sqlServerObject | Get-SqlDscAgentAlert -Name 'IntegrationTest_MessageIdAlert' + $result = $alertObject | Test-SqlDscAgentAlertProperty -MessageId 50001 + + $result | Should -BeTrue + } + } + + Context 'When both Severity and MessageId are specified' { + It 'Should throw error for both Severity and MessageId parameters' { + { $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'IntegrationTest_SeverityAlert' -Severity 16 -MessageId 50001 } | + Should -Throw + } + } +} diff --git a/tests/Unit/Public/Test-SqlDscAgentAlertProperty.Tests.ps1 b/tests/Unit/Public/Test-SqlDscAgentAlertProperty.Tests.ps1 new file mode 100644 index 0000000000..549e998ed1 --- /dev/null +++ b/tests/Unit/Public/Test-SqlDscAgentAlertProperty.Tests.ps1 @@ -0,0 +1,302 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Suppressing this rule because Script Analyzer does not understand Pester syntax.')] +param () + +BeforeDiscovery { + try + { + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies have been resolved, so if this module is not available, run 'noop' task. + if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) + { + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../../build.ps1" -Tasks 'noop' 3>&1 4>&1 5>&1 6>&1 > $null + } + + # If the dependencies have not been resolved, this will throw an error. + Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' + } + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' + } +} + +BeforeAll { + $script:dscModuleName = 'SqlServerDsc' + + Import-Module -Name $script:dscModuleName -Force -ErrorAction 'Stop' + + # Load SMO stub types + Add-Type -Path "$PSScriptRoot/../Stubs/SMO.cs" + + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe 'Test-SqlDscAgentAlertProperty' -Tag 'Public' { + Context 'When command has correct parameter sets' { + It 'Should have the correct parameters in parameter set ' -ForEach @( + @{ + ExpectedParameterSetName = 'ByServerAndName' + ExpectedParameters = '-ServerObject -Name [-Severity ] [-MessageId ] []' + }, + @{ + ExpectedParameterSetName = 'ByAlertObject' + ExpectedParameters = '-AlertObject [-Severity ] [-MessageId ] []' + } + ) { + $result = (Get-Command -Name 'Test-SqlDscAgentAlertProperty').ParameterSets | + Where-Object -FilterScript { $_.Name -eq $ExpectedParameterSetName } | + Select-Object -Property @( + @{ Name = 'ParameterSetName'; Expression = { $_.Name } }, + @{ Name = 'ParameterListAsString'; Expression = { $_.ToString() } } + ) + $result.ParameterSetName | Should -Be $ExpectedParameterSetName + $result.ParameterListAsString | Should -Be $ExpectedParameters + } + } + + Context 'When command has correct parameter properties' { + It 'Should have ServerObject as a mandatory parameter' { + $parameterInfo = (Get-Command -Name 'Test-SqlDscAgentAlertProperty').Parameters['ServerObject'] + $parameterInfo.Attributes.Mandatory | Should -BeTrue + } + + It 'Should have ServerObject accept pipeline input' { + $parameterInfo = (Get-Command -Name 'Test-SqlDscAgentAlertProperty').Parameters['ServerObject'] + $parameterInfo.Attributes.ValueFromPipeline | Should -BeTrue + } + + It 'Should have Name as a mandatory parameter in ByServerAndName parameter set' { + $parameterInfo = (Get-Command -Name 'Test-SqlDscAgentAlertProperty').Parameters['Name'] + $parameterInfo.Attributes.Mandatory | Should -BeTrue + } + + It 'Should have AlertObject as a mandatory parameter in ByAlertObject parameter set' { + $parameterInfo = (Get-Command -Name 'Test-SqlDscAgentAlertProperty').Parameters['AlertObject'] + $parameterInfo.Attributes.Mandatory | Should -BeTrue + } + + It 'Should have AlertObject accept pipeline input' { + $parameterInfo = (Get-Command -Name 'Test-SqlDscAgentAlertProperty').Parameters['AlertObject'] + $parameterInfo.Attributes.ValueFromPipeline | Should -BeTrue + } + + It 'Should have Severity as an optional parameter' { + $parameterInfo = (Get-Command -Name 'Test-SqlDscAgentAlertProperty').Parameters['Severity'] + $parameterInfo.Attributes.Mandatory | Should -BeFalse + } + + It 'Should have MessageId as an optional parameter' { + $parameterInfo = (Get-Command -Name 'Test-SqlDscAgentAlertProperty').Parameters['MessageId'] + $parameterInfo.Attributes.Mandatory | Should -BeFalse + } + } + + Context 'When validating parameter ranges' { + It 'Should accept valid Severity values (0-25)' { + $command = Get-Command -Name 'Test-SqlDscAgentAlertProperty' + $severityParam = $command.Parameters['Severity'] + $validateRangeAttribute = $severityParam.Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateRangeAttribute] } + + $validateRangeAttribute.MinRange | Should -Be 0 + $validateRangeAttribute.MaxRange | Should -Be 25 + } + + It 'Should accept valid MessageId values (0-2147483647)' { + $command = Get-Command -Name 'Test-SqlDscAgentAlertProperty' + $messageIdParam = $command.Parameters['MessageId'] + $validateRangeAttribute = $messageIdParam.Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateRangeAttribute] } + + $validateRangeAttribute.MinRange | Should -Be 0 + $validateRangeAttribute.MaxRange | Should -Be 2147483647 + } + } + + Context 'When no property parameters are specified' { + BeforeAll { + # Mock server object using SMO stub types + $script:mockServerObject = [Microsoft.SqlServer.Management.Smo.Server]::CreateTypeInstance() + + Mock -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName -MockWith { throw 'At least one parameter required' } + Mock -CommandName 'Get-AgentAlertObject' -ModuleName $script:dscModuleName + } + + It 'Should throw an error when no property parameters are specified' { + { Test-SqlDscAgentAlertProperty -ServerObject $script:mockServerObject -Name 'TestAlert' } | + Should -Throw + + Should -Invoke -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName -Times 1 -Exactly + } + } + + Context 'When testing alert with severity' { + BeforeAll { + # Mock the alert object with specific severity using SMO stub types + $script:mockAlert = [Microsoft.SqlServer.Management.Smo.Agent.Alert]::CreateTypeInstance() + $script:mockAlert.Name = 'TestAlert' + $script:mockAlert.Severity = 16 + $script:mockAlert.MessageId = 0 + + # Mock server object using SMO stub types + $script:mockServerObject = [Microsoft.SqlServer.Management.Smo.Server]::CreateTypeInstance() + + Mock -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName + Mock -CommandName 'Get-AgentAlertObject' -ModuleName $script:dscModuleName -MockWith { return $script:mockAlert } + } + + It 'Should return true when alert exists and severity matches' { + $result = Test-SqlDscAgentAlertProperty -ServerObject $script:mockServerObject -Name 'TestAlert' -Severity 16 + + $result | Should -BeTrue + Should -Invoke -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName -Times 2 -Exactly + Should -Invoke -CommandName 'Get-AgentAlertObject' -ModuleName $script:dscModuleName -Times 1 -Exactly + } + + It 'Should return false when alert exists but severity does not match' { + $result = Test-SqlDscAgentAlertProperty -ServerObject $script:mockServerObject -Name 'TestAlert' -Severity 14 + + $result | Should -BeFalse + } + } + + Context 'When testing alert with message ID' { + BeforeAll { + # Mock the alert object with specific message ID using SMO stub types + $script:mockAlert = [Microsoft.SqlServer.Management.Smo.Agent.Alert]::CreateTypeInstance() + $script:mockAlert.Name = 'TestAlert' + $script:mockAlert.Severity = 0 + $script:mockAlert.MessageId = 50001 + + # Mock server object using SMO stub types + $script:mockServerObject = [Microsoft.SqlServer.Management.Smo.Server]::CreateTypeInstance() + + Mock -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName + Mock -CommandName 'Get-AgentAlertObject' -ModuleName $script:dscModuleName -MockWith { return $script:mockAlert } + } + + It 'Should return true when alert exists and message ID matches' { + $result = Test-SqlDscAgentAlertProperty -ServerObject $script:mockServerObject -Name 'TestAlert' -MessageId 50001 + + $result | Should -BeTrue + } + + It 'Should return false when alert exists but message ID does not match' { + $result = Test-SqlDscAgentAlertProperty -ServerObject $script:mockServerObject -Name 'TestAlert' -MessageId 50002 + + $result | Should -BeFalse + } + } + + Context 'When alert does not exist' { + BeforeAll { + # Mock server object using SMO stub types + $script:mockServerObject = [Microsoft.SqlServer.Management.Smo.Server]::CreateTypeInstance() + + Mock -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName + Mock -CommandName 'Get-AgentAlertObject' -ModuleName $script:dscModuleName + Mock -CommandName 'Write-Error' -ModuleName $script:dscModuleName + } + + It 'Should return false when alert does not exist (with Severity)' { + $result = Test-SqlDscAgentAlertProperty -ServerObject $script:mockServerObject -Name 'NonExistentAlert' -Severity 16 + + $result | Should -BeFalse + Should -Invoke -CommandName 'Get-AgentAlertObject' -ModuleName $script:dscModuleName -Times 1 -Exactly + Should -Invoke -CommandName 'Write-Error' -ModuleName $script:dscModuleName -Times 1 -Exactly + } + + It 'Should return false when alert does not exist (with MessageId)' { + $result = Test-SqlDscAgentAlertProperty -ServerObject $script:mockServerObject -Name 'NonExistentAlert' -MessageId 50001 + + $result | Should -BeFalse + Should -Invoke -CommandName 'Get-AgentAlertObject' -ModuleName $script:dscModuleName -Times 1 -Exactly + Should -Invoke -CommandName 'Write-Error' -ModuleName $script:dscModuleName -Times 1 -Exactly + } + + It 'Should call Write-Error with correct parameters when alert does not exist' { + $result = Test-SqlDscAgentAlertProperty -ServerObject $script:mockServerObject -Name 'NonExistentAlert' -Severity 16 + + $result | Should -BeFalse + Should -Invoke -CommandName 'Write-Error' -ModuleName $script:dscModuleName -ParameterFilter { + $Category -eq 'ObjectNotFound' -and $ErrorId -eq 'TSAAP0002' -and $TargetObject -eq 'NonExistentAlert' + } -Times 1 -Exactly + } + } + + Context 'When parameter validation is called' { + BeforeAll { + # Mock server object using SMO stub types + $script:mockServerObject = [Microsoft.SqlServer.Management.Smo.Server]::CreateTypeInstance() + + Mock -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName -ParameterFilter { $MutuallyExclusiveList1 } -MockWith { throw 'Mutually exclusive parameters' } + Mock -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName -ParameterFilter { $AtLeastOneList } + Mock -CommandName 'Get-AgentAlertObject' -ModuleName $script:dscModuleName + } + + It 'Should call parameter validation with both severity and message ID and throw' { + { Test-SqlDscAgentAlertProperty -ServerObject $script:mockServerObject -Name 'TestAlert' -Severity 16 -MessageId 50001 } | + Should -Throw + + Should -Invoke -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName -Times 2 -Exactly + } + } + + Context 'When using AlertObject parameter set' { + BeforeAll { + # Mock the alert object using SMO stub types + $script:mockAlert = [Microsoft.SqlServer.Management.Smo.Agent.Alert]::CreateTypeInstance() + $script:mockAlert.Name = 'TestAlert' + $script:mockAlert.Severity = 16 + $script:mockAlert.MessageId = 0 + + Mock -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName + } + + It 'Should return true when alert object has matching severity' { + $result = $script:mockAlert | Test-SqlDscAgentAlertProperty -Severity 16 + + $result | Should -BeTrue + Should -Invoke -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName -Times 2 -Exactly + } + + It 'Should return false when alert object has non-matching severity' { + $result = $script:mockAlert | Test-SqlDscAgentAlertProperty -Severity 14 + + $result | Should -BeFalse + } + } + + Context 'When using pipeline input' { + BeforeAll { + # Mock the alert object using SMO stub types + $script:mockAlert = [Microsoft.SqlServer.Management.Smo.Agent.Alert]::CreateTypeInstance() + $script:mockAlert.Name = 'TestAlert' + $script:mockAlert.Severity = 16 + + # Mock the server object using SMO stub types + $script:mockServerObject = [Microsoft.SqlServer.Management.Smo.Server]::CreateTypeInstance() + + Mock -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName + Mock -CommandName 'Get-AgentAlertObject' -ModuleName $script:dscModuleName -MockWith { return $script:mockAlert } + } + + It 'Should work with pipeline input' { + $result = $script:mockServerObject | Test-SqlDscAgentAlertProperty -Name 'TestAlert' -Severity 16 + + $result | Should -BeTrue + Should -Invoke -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName -Times 2 -Exactly + Should -Invoke -CommandName 'Get-AgentAlertObject' -ModuleName $script:dscModuleName -Times 1 -Exactly + } + } +} From bdb4d301afcf529e45ff3d83a94b29f7a68be691 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 12:55:07 +0200 Subject: [PATCH 13/38] Refactor Test-SqlDscIsAgentAlert to simplify parameter handling and remove unnecessary severity and message ID checks --- source/Public/Test-SqlDscIsAgentAlert.ps1 | 81 +----------- ...t-SqlDscIsAgentAlert.Integration.Tests.ps1 | 35 ----- .../Public/Test-SqlDscIsAgentAlert.Tests.ps1 | 124 +----------------- 3 files changed, 9 insertions(+), 231 deletions(-) diff --git a/source/Public/Test-SqlDscIsAgentAlert.ps1 b/source/Public/Test-SqlDscIsAgentAlert.ps1 index eb56bbc601..53f2528baa 100644 --- a/source/Public/Test-SqlDscIsAgentAlert.ps1 +++ b/source/Public/Test-SqlDscIsAgentAlert.ps1 @@ -1,10 +1,10 @@ <# .SYNOPSIS - Tests if a SQL Agent Alert exists and has the desired properties. + Tests if a SQL Agent Alert exists. .DESCRIPTION This command tests if a SQL Agent Alert exists on a SQL Server Database Engine - instance and optionally validates its properties. + instance. .PARAMETER ServerObject Specifies current server connection object. @@ -12,14 +12,6 @@ .PARAMETER Name Specifies the name of the SQL Agent Alert to test. - .PARAMETER Severity - Specifies the expected severity level for the SQL Agent Alert. Valid range is 0 to 25. - If specified, the command will return $true only if the alert exists and has this severity. - - .PARAMETER MessageId - Specifies the expected message ID for the SQL Agent Alert. Valid range is 0 to 2147483647. - If specified, the command will return $true only if the alert exists and has this message ID. - .INPUTS Microsoft.SqlServer.Management.Smo.Server @@ -36,15 +28,9 @@ .EXAMPLE $serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance' - $serverObject | Test-SqlDscIsAgentAlert -Name 'MyAlert' -Severity 16 - - Tests if the SQL Agent Alert named 'MyAlert' exists and has severity level 16. - - .EXAMPLE - $serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance' - $serverObject | Test-SqlDscIsAgentAlert -Name 'MyAlert' -MessageId 50001 + $serverObject | Test-SqlDscIsAgentAlert -Name 'MyAlert' - Tests if the SQL Agent Alert named 'MyAlert' exists and has message ID 50001. + Tests if the SQL Agent Alert named 'MyAlert' exists using pipeline input. #> function Test-SqlDscIsAgentAlert { @@ -61,28 +47,15 @@ function Test-SqlDscIsAgentAlert [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] - $Name, - - [Parameter()] - [ValidateRange(0, 25)] - [System.Int32] - $Severity, - - [Parameter()] - [ValidateRange(0, 2147483647)] - [System.Int32] - $MessageId + $Name ) - # cSpell: ignore TSAA + # cSpell: ignore TSIAA process { - # Validate that both Severity and MessageId are not specified - Assert-BoundParameter -BoundParameterList $PSBoundParameters -MutuallyExclusiveList1 @('Severity') -MutuallyExclusiveList2 @('MessageId') - Write-Verbose -Message ($script:localizedData.Test_SqlDscIsAgentAlert_TestingAlert -f $Name) - $alertObject = Get-AgentAlertObject -ServerObject $ServerObject -Name $Name + $alertObject = Get-AgentAlertObject -ServerObject $ServerObject -Name $Name -ErrorAction 'SilentlyContinue' if ($null -eq $alertObject) { @@ -93,46 +66,6 @@ function Test-SqlDscIsAgentAlert Write-Verbose -Message ($script:localizedData.Test_SqlDscIsAgentAlert_AlertFound -f $Name) - # If no specific properties are specified, just return true (alert exists) - if (-not $PSBoundParameters.ContainsKey('Severity') -and -not $PSBoundParameters.ContainsKey('MessageId')) - { - Write-Verbose -Message ($script:localizedData.Test_SqlDscIsAgentAlert_NoPropertyTest) - - return $true - } - - # Test severity if specified - if ($PSBoundParameters.ContainsKey('Severity')) - { - if ($alertObject.Severity -ne $Severity) - { - Write-Verbose -Message ($script:localizedData.Test_SqlDscIsAgentAlert_SeverityMismatch -f $alertObject.Severity, $Severity) - - return $false - } - else - { - Write-Verbose -Message ($script:localizedData.Test_SqlDscIsAgentAlert_SeverityMatch -f $Severity) - } - } - - # Test message ID if specified - if ($PSBoundParameters.ContainsKey('MessageId')) - { - if ($alertObject.MessageId -ne $MessageId) - { - Write-Verbose -Message ($script:localizedData.Test_SqlDscIsAgentAlert_MessageIdMismatch -f $alertObject.MessageId, $MessageId) - - return $false - } - else - { - Write-Verbose -Message ($script:localizedData.Test_SqlDscIsAgentAlert_MessageIdMatch -f $MessageId) - } - } - - Write-Verbose -Message ($script:localizedData.Test_SqlDscIsAgentAlert_AllTestsPassed -f $Name) - return $true } } diff --git a/tests/Integration/Commands/Test-SqlDscIsAgentAlert.Integration.Tests.ps1 b/tests/Integration/Commands/Test-SqlDscIsAgentAlert.Integration.Tests.ps1 index 825a0003ed..ccaf7eba17 100644 --- a/tests/Integration/Commands/Test-SqlDscIsAgentAlert.Integration.Tests.ps1 +++ b/tests/Integration/Commands/Test-SqlDscIsAgentAlert.Integration.Tests.ps1 @@ -75,39 +75,4 @@ Describe 'Test-SqlDscIsAgentAlert' -Tag @('Integration_SQL2017', 'Integration_SQ $result | Should -BeFalse } } - - Context 'When checking severity' { - It 'Should return true for matching severity' { - $result = $script:sqlServerObject | Test-SqlDscIsAgentAlert -Name 'IntegrationTest_SeverityAlert' -Severity 16 - - $result | Should -BeTrue - } - - It 'Should return false for non-matching severity' { - $result = $script:sqlServerObject | Test-SqlDscIsAgentAlert -Name 'IntegrationTest_SeverityAlert' -Severity 14 - - $result | Should -BeFalse - } - } - - Context 'When checking message ID' { - It 'Should return true for matching message ID' { - $result = $script:sqlServerObject | Test-SqlDscIsAgentAlert -Name 'IntegrationTest_MessageIdAlert' -MessageId 50001 - - $result | Should -BeTrue - } - - It 'Should return false for non-matching message ID' { - $result = $script:sqlServerObject | Test-SqlDscIsAgentAlert -Name 'IntegrationTest_MessageIdAlert' -MessageId 50002 - - $result | Should -BeFalse - } - } - - Context 'When both Severity and MessageId are specified' { - It 'Should throw error for both Severity and MessageId parameters' { - { $script:sqlServerObject | Test-SqlDscIsAgentAlert -Name 'IntegrationTest_SeverityAlert' -Severity 16 -MessageId 50001 } | - Should -Throw - } - } } diff --git a/tests/Unit/Public/Test-SqlDscIsAgentAlert.Tests.ps1 b/tests/Unit/Public/Test-SqlDscIsAgentAlert.Tests.ps1 index b3e7e2526f..037539913a 100644 --- a/tests/Unit/Public/Test-SqlDscIsAgentAlert.Tests.ps1 +++ b/tests/Unit/Public/Test-SqlDscIsAgentAlert.Tests.ps1 @@ -1,4 +1,3 @@ -[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Suppressing this rule because Script Analyzer does not understand Pester syntax.')] param () @@ -49,7 +48,7 @@ Describe 'Test-SqlDscIsAgentAlert' -Tag 'Public' { It 'Should have the correct parameters in parameter set ' -ForEach @( @{ ExpectedParameterSetName = '__AllParameterSets' - ExpectedParameters = '[-ServerObject] [-Name] [[-Severity] ] [[-MessageId] ] []' + ExpectedParameters = '[-ServerObject] [-Name] []' } ) { $result = (Get-Command -Name 'Test-SqlDscIsAgentAlert').ParameterSets | @@ -78,36 +77,6 @@ Describe 'Test-SqlDscIsAgentAlert' -Tag 'Public' { $parameterInfo = (Get-Command -Name 'Test-SqlDscIsAgentAlert').Parameters['Name'] $parameterInfo.Attributes.Mandatory | Should -BeTrue } - - It 'Should have Severity as an optional parameter' { - $parameterInfo = (Get-Command -Name 'Test-SqlDscIsAgentAlert').Parameters['Severity'] - $parameterInfo.Attributes.Mandatory | Should -BeFalse - } - - It 'Should have MessageId as an optional parameter' { - $parameterInfo = (Get-Command -Name 'Test-SqlDscIsAgentAlert').Parameters['MessageId'] - $parameterInfo.Attributes.Mandatory | Should -BeFalse - } - } - - Context 'When validating parameter ranges' { - It 'Should accept valid Severity values (0-25)' { - $command = Get-Command -Name 'Test-SqlDscIsAgentAlert' - $severityParam = $command.Parameters['Severity'] - $validateRangeAttribute = $severityParam.Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateRangeAttribute] } - - $validateRangeAttribute.MinRange | Should -Be 0 - $validateRangeAttribute.MaxRange | Should -Be 25 - } - - It 'Should accept valid MessageId values (0-2147483647)' { - $command = Get-Command -Name 'Test-SqlDscIsAgentAlert' - $messageIdParam = $command.Parameters['MessageId'] - $validateRangeAttribute = $messageIdParam.Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateRangeAttribute] } - - $validateRangeAttribute.MinRange | Should -Be 0 - $validateRangeAttribute.MaxRange | Should -Be 2147483647 - } } Context 'When testing alert existence only' { @@ -115,87 +84,26 @@ Describe 'Test-SqlDscIsAgentAlert' -Tag 'Public' { # Mock the alert object using SMO stub types $script:mockAlert = [Microsoft.SqlServer.Management.Smo.Agent.Alert]::CreateTypeInstance() $script:mockAlert.Name = 'TestAlert' - $script:mockAlert.Severity = 16 - $script:mockAlert.MessageId = 0 # Mock server object using SMO stub types $script:mockServerObject = [Microsoft.SqlServer.Management.Smo.Server]::CreateTypeInstance() - Mock -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName Mock -CommandName 'Get-AgentAlertObject' -ModuleName $script:dscModuleName -MockWith { return $script:mockAlert } } - It 'Should return true when alert exists and no properties are specified' { + It 'Should return true when alert exists' { $result = Test-SqlDscIsAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' $result | Should -BeTrue - Should -Invoke -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName -Times 1 -Exactly Should -Invoke -CommandName 'Get-AgentAlertObject' -ModuleName $script:dscModuleName -Times 1 -Exactly } } - Context 'When testing alert with severity' { - BeforeAll { - # Mock the alert object with specific severity using SMO stub types - $script:mockAlert = [Microsoft.SqlServer.Management.Smo.Agent.Alert]::CreateTypeInstance() - $script:mockAlert.Name = 'TestAlert' - $script:mockAlert.Severity = 16 - $script:mockAlert.MessageId = 0 - - # Mock server object using SMO stub types - $script:mockServerObject = [Microsoft.SqlServer.Management.Smo.Server]::CreateTypeInstance() - - Mock -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName - Mock -CommandName 'Get-AgentAlertObject' -ModuleName $script:dscModuleName -MockWith { return $script:mockAlert } - } - - It 'Should return true when alert exists and severity matches' { - $result = Test-SqlDscIsAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -Severity 16 - - $result | Should -BeTrue - } - - It 'Should return false when alert exists but severity does not match' { - $result = Test-SqlDscIsAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -Severity 14 - - $result | Should -BeFalse - } - } - - Context 'When testing alert with message ID' { - BeforeAll { - # Mock the alert object with specific message ID using SMO stub types - $script:mockAlert = [Microsoft.SqlServer.Management.Smo.Agent.Alert]::CreateTypeInstance() - $script:mockAlert.Name = 'TestAlert' - $script:mockAlert.Severity = 0 - $script:mockAlert.MessageId = 50001 - - # Mock server object using SMO stub types - $script:mockServerObject = [Microsoft.SqlServer.Management.Smo.Server]::CreateTypeInstance() - - Mock -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName - Mock -CommandName 'Get-AgentAlertObject' -ModuleName $script:dscModuleName -MockWith { return $script:mockAlert } - } - - It 'Should return true when alert exists and message ID matches' { - $result = Test-SqlDscIsAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -MessageId 50001 - - $result | Should -BeTrue - } - - It 'Should return false when alert exists but message ID does not match' { - $result = Test-SqlDscIsAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -MessageId 50002 - - $result | Should -BeFalse - } - } - Context 'When alert does not exist' { BeforeAll { # Mock server object using SMO stub types $script:mockServerObject = [Microsoft.SqlServer.Management.Smo.Server]::CreateTypeInstance() - Mock -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName Mock -CommandName 'Get-AgentAlertObject' -ModuleName $script:dscModuleName } @@ -207,32 +115,6 @@ Describe 'Test-SqlDscIsAgentAlert' -Tag 'Public' { } } - Context 'When parameter validation is called' { - BeforeAll { - # Mock server object using SMO stub types - $script:mockServerObject = [Microsoft.SqlServer.Management.Smo.Server]::CreateTypeInstance() - - Mock -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName - Mock -CommandName 'Get-AgentAlertObject' -ModuleName $script:dscModuleName - } - - It 'Should call parameter validation with both severity and message ID' { - $null = Test-SqlDscIsAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -Severity 16 -MessageId 50001 - - Should -Invoke -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName -ParameterFilter { - $BoundParameterList.ContainsKey('Severity') -and $BoundParameterList.ContainsKey('MessageId') - } -Times 1 -Exactly - } - - It 'Should call parameter validation with only severity' { - $null = Test-SqlDscIsAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -Severity 16 - - Should -Invoke -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName -ParameterFilter { - $BoundParameterList.ContainsKey('Severity') -and -not $BoundParameterList.ContainsKey('MessageId') - } -Times 1 -Exactly - } - } - Context 'When using the alias Test-SqlDscAgentAlert' { BeforeAll { # Mock the alert object using SMO stub types @@ -242,7 +124,6 @@ Describe 'Test-SqlDscIsAgentAlert' -Tag 'Public' { # Mock the server object using SMO stub types $script:mockServerObject = [Microsoft.SqlServer.Management.Smo.Server]::CreateTypeInstance() - Mock -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName Mock -CommandName 'Get-AgentAlertObject' -ModuleName $script:dscModuleName -MockWith { return $script:mockAlert } } @@ -250,7 +131,6 @@ Describe 'Test-SqlDscIsAgentAlert' -Tag 'Public' { $result = Test-SqlDscAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' $result | Should -BeTrue - Should -Invoke -CommandName 'Assert-BoundParameter' -ModuleName $script:dscModuleName -Times 1 -Exactly Should -Invoke -CommandName 'Get-AgentAlertObject' -ModuleName $script:dscModuleName -Times 1 -Exactly } } From 09dac6d895e694acd6366c6351162f8a0a672cc6 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 12:55:13 +0200 Subject: [PATCH 14/38] Update references in SMO.cs to use Test-SqlDscIsAgentAlert for consistency in test naming --- tests/Unit/Stubs/SMO.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/Unit/Stubs/SMO.cs b/tests/Unit/Stubs/SMO.cs index fa07aee1a6..a6d6df5476 100644 --- a/tests/Unit/Stubs/SMO.cs +++ b/tests/Unit/Stubs/SMO.cs @@ -1538,7 +1538,7 @@ namespace Microsoft.SqlServer.Management.Smo.Agent // New-SqlDscAgentAlert.Tests.ps1 // Set-SqlDscAgentAlert.Tests.ps1 // Remove-SqlDscAgentAlert.Tests.ps1 - // Test-SqlDscAgentAlert.Tests.ps1 + // Test-SqlDscIsAgentAlert.Tests.ps1 // SqlAgentAlert.Tests.ps1 public enum AlertType { @@ -1636,7 +1636,7 @@ public static AlertCollection CreateTypeInstance() // New-SqlDscAgentAlert.Tests.ps1 // Set-SqlDscAgentAlert.Tests.ps1 // Remove-SqlDscAgentAlert.Tests.ps1 - // Test-SqlDscAgentAlert.Tests.ps1 + // Test-SqlDscIsAgentAlert.Tests.ps1 public class Alert { // Constructor @@ -1683,4 +1683,3 @@ public static Alert CreateTypeInstance() #endregion } - From 69ec72d638236b6ae456500708afde37bfa532c3 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 12:55:17 +0200 Subject: [PATCH 15/38] Add Test-SqlDscAgentAlertProperty command and update Test-SqlDscIsAgentAlert for property testing --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10ccc8a369..ddb8b9dc80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -91,6 +91,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 patterns - Database objects can also be used as pipeline input for Set and Remove operations - Commands include comprehensive validation, localization, and ShouldProcess support +- `Test-SqlDscAgentAlertProperty` + - New command to test specific properties of SQL Agent alerts. + - Supports testing severity and message ID properties. + - Requires at least one property parameter to be specified. + - Supports pipeline input of + `[Microsoft.SqlServer.Management.Smo.Agent.Alert]` objects. ### Changed @@ -157,6 +163,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated the command to use `Test-SqlDscIsAgentAlert` instead of directly calling `Get-AgentAlertObject` when checking if an alert already exists (issue [#2202](https://github.com/dsccommunity/SqlServerDsc/issues/2202)). +- `Test-SqlDscIsAgentAlert` + - Removed optional `Severity` and `MessageId` parameters - use + `Test-SqlDscAgentAlertProperty` instead for property testing. + - Now only tests for alert existence. + - Added support for pipeline input of + `[Microsoft.SqlServer.Management.Smo.Agent.Alert]` objects. + - Updated examples and documentation to reflect the simplified functionality. ## [17.1.0] - 2025-05-22 From bd6da11e42a89d40c3e7620ba106fc726dc3fb2d Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 13:46:39 +0200 Subject: [PATCH 16/38] Add Test-SqlDscAgentAlertProperty integration test to pipeline --- azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f6a5029e82..d7b4b0102a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -310,6 +310,7 @@ stages: 'tests/Integration/Commands/Get-SqlDscAgentAlert.Integration.Tests.ps1' 'tests/Integration/Commands/New-SqlDscAgentAlert.Integration.Tests.ps1' 'tests/Integration/Commands/Set-SqlDscAgentAlert.Integration.Tests.ps1' + 'tests/Integration/Commands/Test-SqlDscAgentAlertProperty.Integration.Tests.ps1' 'tests/Integration/Commands/Test-SqlDscIsAgentAlert.Integration.Tests.ps1' # Group 8 'tests/Integration/Commands/Remove-SqlDscAgentAlert.Integration.Tests.ps1' From 3aa0891f482b054c01427eafba3afd1ee69bb985 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 13:47:55 +0200 Subject: [PATCH 17/38] Return $false instead of just returning when the SQL Agent Alert is not found in Test-SqlDscAgentAlertProperty --- source/Public/Test-SqlDscAgentAlertProperty.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/Public/Test-SqlDscAgentAlertProperty.ps1 b/source/Public/Test-SqlDscAgentAlertProperty.ps1 index efccc92ad1..f601011a9a 100644 --- a/source/Public/Test-SqlDscAgentAlertProperty.ps1 +++ b/source/Public/Test-SqlDscAgentAlertProperty.ps1 @@ -110,7 +110,7 @@ function Test-SqlDscAgentAlertProperty Write-Error -Message $errorMessage -Category 'ObjectNotFound' -ErrorId 'TSAAP0002' -TargetObject $Name - return + return $false } } From 207d5bb29553518fd6dc3864ea6753fcc79a5982 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 13:54:06 +0200 Subject: [PATCH 18/38] Add -ErrorAction 'Stop' to Test-SqlDscAgentAlertProperty calls and handle non-existent alert exception --- ...t-SqlDscAgentAlertProperty.Integration.Tests.ps1 | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/Integration/Commands/Test-SqlDscAgentAlertProperty.Integration.Tests.ps1 b/tests/Integration/Commands/Test-SqlDscAgentAlertProperty.Integration.Tests.ps1 index 735772202e..0a3ebf1809 100644 --- a/tests/Integration/Commands/Test-SqlDscAgentAlertProperty.Integration.Tests.ps1 +++ b/tests/Integration/Commands/Test-SqlDscAgentAlertProperty.Integration.Tests.ps1 @@ -64,13 +64,13 @@ Describe 'Test-SqlDscAgentAlertProperty' -Tag @('Integration_SQL2017', 'Integrat Context 'When checking severity' { It 'Should return true for matching severity' { - $result = $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'IntegrationTest_SeverityAlert' -Severity 16 + $result = $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'IntegrationTest_SeverityAlert' -Severity 16 -ErrorAction 'Stop' $result | Should -BeTrue } It 'Should return false for non-matching severity' { - $result = $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'IntegrationTest_SeverityAlert' -Severity 14 + $result = $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'IntegrationTest_SeverityAlert' -Severity 14 -ErrorAction 'Stop' $result | Should -BeFalse } @@ -78,13 +78,13 @@ Describe 'Test-SqlDscAgentAlertProperty' -Tag @('Integration_SQL2017', 'Integrat Context 'When checking message ID' { It 'Should return true for matching message ID' { - $result = $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'IntegrationTest_MessageIdAlert' -MessageId 50001 + $result = $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'IntegrationTest_MessageIdAlert' -MessageId 50001 -ErrorAction 'Stop' $result | Should -BeTrue } It 'Should return false for non-matching message ID' { - $result = $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'IntegrationTest_MessageIdAlert' -MessageId 50002 + $result = $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'IntegrationTest_MessageIdAlert' -MessageId 50002 -ErrorAction 'Stop' $result | Should -BeFalse } @@ -102,6 +102,11 @@ Describe 'Test-SqlDscAgentAlertProperty' -Tag @('Integration_SQL2017', 'Integrat $result | Should -BeFalse } + + It 'Should throw an exception for a non-existent alert' { + { $script:sqlServerObject | Get-SqlDscAgentAlert -Name 'NonExistentAlert' -ErrorAction 'Stop' } | + Should -Throw + } } Context 'When no properties are specified' { From d19ffa5caca825d45069fa2d324a32235731a57f Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 13:55:28 +0200 Subject: [PATCH 19/38] Add -ErrorAction 'Stop' to Test-SqlDscAgentAlertProperty calls to ensure proper error handling --- .../Test-SqlDscAgentAlertProperty.Integration.Tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Integration/Commands/Test-SqlDscAgentAlertProperty.Integration.Tests.ps1 b/tests/Integration/Commands/Test-SqlDscAgentAlertProperty.Integration.Tests.ps1 index 0a3ebf1809..c6a4485c85 100644 --- a/tests/Integration/Commands/Test-SqlDscAgentAlertProperty.Integration.Tests.ps1 +++ b/tests/Integration/Commands/Test-SqlDscAgentAlertProperty.Integration.Tests.ps1 @@ -111,7 +111,7 @@ Describe 'Test-SqlDscAgentAlertProperty' -Tag @('Integration_SQL2017', 'Integrat Context 'When no properties are specified' { It 'Should throw error when no property parameters are specified' { - { $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'IntegrationTest_SeverityAlert' } | + { $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'IntegrationTest_SeverityAlert' -ErrorAction 'Stop' } | Should -Throw } } @@ -141,7 +141,7 @@ Describe 'Test-SqlDscAgentAlertProperty' -Tag @('Integration_SQL2017', 'Integrat Context 'When both Severity and MessageId are specified' { It 'Should throw error for both Severity and MessageId parameters' { - { $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'IntegrationTest_SeverityAlert' -Severity 16 -MessageId 50001 } | + { $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'IntegrationTest_SeverityAlert' -Severity 16 -MessageId 50001 -ErrorAction 'Stop' } | Should -Throw } } From e32bc7dfb46866af5e003fdb7622ce67bb9b3ebf Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 13:56:36 +0200 Subject: [PATCH 20/38] Add -ErrorAction 'Stop' to Get-SqlDscAgentAlert calls in Test-SqlDscAgentAlertProperty tests for improved error handling --- ...st-SqlDscAgentAlertProperty.Integration.Tests.ps1 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/Integration/Commands/Test-SqlDscAgentAlertProperty.Integration.Tests.ps1 b/tests/Integration/Commands/Test-SqlDscAgentAlertProperty.Integration.Tests.ps1 index c6a4485c85..075e3e69c5 100644 --- a/tests/Integration/Commands/Test-SqlDscAgentAlertProperty.Integration.Tests.ps1 +++ b/tests/Integration/Commands/Test-SqlDscAgentAlertProperty.Integration.Tests.ps1 @@ -118,22 +118,22 @@ Describe 'Test-SqlDscAgentAlertProperty' -Tag @('Integration_SQL2017', 'Integrat Context 'When using AlertObject parameter' { It 'Should return true when alert object has matching severity' { - $alertObject = $script:sqlServerObject | Get-SqlDscAgentAlert -Name 'IntegrationTest_SeverityAlert' - $result = $alertObject | Test-SqlDscAgentAlertProperty -Severity 16 + $alertObject = $script:sqlServerObject | Get-SqlDscAgentAlert -Name 'IntegrationTest_SeverityAlert' -ErrorAction 'Stop' + $result = $alertObject | Test-SqlDscAgentAlertProperty -Severity 16 -ErrorAction 'Stop' $result | Should -BeTrue } It 'Should return false when alert object has non-matching severity' { - $alertObject = $script:sqlServerObject | Get-SqlDscAgentAlert -Name 'IntegrationTest_SeverityAlert' - $result = $alertObject | Test-SqlDscAgentAlertProperty -Severity 14 + $alertObject = $script:sqlServerObject | Get-SqlDscAgentAlert -Name 'IntegrationTest_SeverityAlert' -ErrorAction 'Stop' + $result = $alertObject | Test-SqlDscAgentAlertProperty -Severity 14 -ErrorAction 'Stop' $result | Should -BeFalse } It 'Should return true when alert object has matching message ID' { - $alertObject = $script:sqlServerObject | Get-SqlDscAgentAlert -Name 'IntegrationTest_MessageIdAlert' - $result = $alertObject | Test-SqlDscAgentAlertProperty -MessageId 50001 + $alertObject = $script:sqlServerObject | Get-SqlDscAgentAlert -Name 'IntegrationTest_MessageIdAlert' -ErrorAction 'Stop' + $result = $alertObject | Test-SqlDscAgentAlertProperty -MessageId 50001 -ErrorAction 'Stop' $result | Should -BeTrue } From 463b05fcb16c796f32c45ac87bfdb9c55352f1aa Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 13:57:15 +0200 Subject: [PATCH 21/38] Add -ErrorAction 'Stop' to Test-SqlDscIsAgentAlert calls for improved error handling --- .../Commands/Test-SqlDscIsAgentAlert.Integration.Tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Integration/Commands/Test-SqlDscIsAgentAlert.Integration.Tests.ps1 b/tests/Integration/Commands/Test-SqlDscIsAgentAlert.Integration.Tests.ps1 index ccaf7eba17..1229eb23d2 100644 --- a/tests/Integration/Commands/Test-SqlDscIsAgentAlert.Integration.Tests.ps1 +++ b/tests/Integration/Commands/Test-SqlDscIsAgentAlert.Integration.Tests.ps1 @@ -64,13 +64,13 @@ Describe 'Test-SqlDscIsAgentAlert' -Tag @('Integration_SQL2017', 'Integration_SQ Context 'When checking existence only' { It 'Should return true for existing alert' { - $result = $script:sqlServerObject | Test-SqlDscIsAgentAlert -Name 'IntegrationTest_SeverityAlert' + $result = $script:sqlServerObject | Test-SqlDscIsAgentAlert -Name 'IntegrationTest_SeverityAlert' -ErrorAction 'Stop' $result | Should -BeTrue } It 'Should return false for non-existent alert' { - $result = $script:sqlServerObject | Test-SqlDscIsAgentAlert -Name 'NonExistentAlert' + $result = $script:sqlServerObject | Test-SqlDscIsAgentAlert -Name 'NonExistentAlert' -ErrorAction 'Stop' $result | Should -BeFalse } From 2fc75de624f53e6b171af799521e56f6d3045782 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 13:57:53 +0200 Subject: [PATCH 22/38] Add environment variable setup and cleanup in BeforeAll and AfterAll blocks --- tests/Unit/Public/Test-SqlDscIsAgentAlert.Tests.ps1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/Unit/Public/Test-SqlDscIsAgentAlert.Tests.ps1 b/tests/Unit/Public/Test-SqlDscIsAgentAlert.Tests.ps1 index 037539913a..275c255ad9 100644 --- a/tests/Unit/Public/Test-SqlDscIsAgentAlert.Tests.ps1 +++ b/tests/Unit/Public/Test-SqlDscIsAgentAlert.Tests.ps1 @@ -26,6 +26,8 @@ BeforeDiscovery { BeforeAll { $script:dscModuleName = 'SqlServerDsc' + $env:SqlServerDscCI = $true + Import-Module -Name $script:dscModuleName -Force -ErrorAction 'Stop' # Load SMO stub types @@ -41,6 +43,8 @@ AfterAll { # Unload the module being tested so that it doesn't impact any other tests. Get-Module -Name $script:dscModuleName -All | Remove-Module -Force + + Remove-Item -Path 'Env:SqlServerDscCI' -ErrorAction 'SilentlyContinue' } Describe 'Test-SqlDscIsAgentAlert' -Tag 'Public' { From 995818b6b657fa64ae10ebf0a5bd4770c6fea84e Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 14:00:20 +0200 Subject: [PATCH 23/38] Add Test-SqlDscAgentAlertProperty to integration tests run order --- tests/Integration/Commands/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Integration/Commands/README.md b/tests/Integration/Commands/README.md index f20b503ede..18780e964d 100644 --- a/tests/Integration/Commands/README.md +++ b/tests/Integration/Commands/README.md @@ -63,6 +63,7 @@ Test-SqlDscDatabase | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTE Get-SqlDscAgentAlert | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | - New-SqlDscAgentAlert | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | Test alerts Set-SqlDscAgentAlert | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | - +Test-SqlDscAgentAlertProperty | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | - Test-SqlDscIsAgentAlert | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | - Remove-SqlDscAgentAlert | 8 | 2 (New-SqlDscAgentAlert) | DSCSQLTEST | - Remove-SqlDscDatabase | 8 | 2 (New-SqlDscDatabase) | DSCSQLTEST | - From dad271f1585dce7cb15f06488e184233645fe968 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 14:02:41 +0200 Subject: [PATCH 24/38] Add environment variable setup and cleanup in BeforeAll and AfterAll blocks --- tests/Unit/Public/New-SqlDscAgentAlert.Tests.ps1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/Unit/Public/New-SqlDscAgentAlert.Tests.ps1 b/tests/Unit/Public/New-SqlDscAgentAlert.Tests.ps1 index 5c7a1ec0e1..a8a107ba9e 100644 --- a/tests/Unit/Public/New-SqlDscAgentAlert.Tests.ps1 +++ b/tests/Unit/Public/New-SqlDscAgentAlert.Tests.ps1 @@ -26,6 +26,8 @@ BeforeDiscovery { BeforeAll { $script:dscModuleName = 'SqlServerDsc' + $env:SqlServerDscCI = $true + Import-Module -Name $script:dscModuleName -Force -ErrorAction 'Stop' # Load SMO stub types @@ -41,6 +43,8 @@ AfterAll { # Unload the module being tested so that it doesn't impact any other tests. Get-Module -Name $script:dscModuleName -All | Remove-Module -Force + + Remove-Item -Path 'env:SqlServerDscCI' -ErrorAction 'SilentlyContinue' } Describe 'New-SqlDscAgentAlert' -Tag 'Public' { From 38132cdac600f77c6d3b0d02b25128eda8a62ed8 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 15:30:57 +0200 Subject: [PATCH 25/38] Update build environment instructions for clarity --- .github/instructions/SqlServerDsc-guidelines.instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/instructions/SqlServerDsc-guidelines.instructions.md b/.github/instructions/SqlServerDsc-guidelines.instructions.md index c61a421515..32703fb1de 100644 --- a/.github/instructions/SqlServerDsc-guidelines.instructions.md +++ b/.github/instructions/SqlServerDsc-guidelines.instructions.md @@ -8,7 +8,7 @@ applyTo: "**" ## Build & Test Workflow Requirements - Run in `pwsh` - Run scripts from project root -- Setup build environment (once per `pwsh` session): `.build.ps1 -Task noop` +- Setup build and test environment (once per `pwsh` session): `.build.ps1 -Task noop` - Build project: `.build.ps1 -Task build` - Run tests without coverage (wildcards allowed): `Invoke-PesterJob -Path '{tests filepath}' -SkipCodeCoverage` - Run QA tests: `Invoke-PesterJob -Path 'tests/QA' -SkipCodeCoverage` From 98cba99b8cfa51271d42e89a1e69adac070f89cf Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 15:31:09 +0200 Subject: [PATCH 26/38] Update Test-SqlDscIsAgentAlert message for clarity by removing redundant phrasing --- source/en-US/SqlServerDsc.strings.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/en-US/SqlServerDsc.strings.psd1 b/source/en-US/SqlServerDsc.strings.psd1 index 35d8c35afd..f6ff43b8e7 100644 --- a/source/en-US/SqlServerDsc.strings.psd1 +++ b/source/en-US/SqlServerDsc.strings.psd1 @@ -435,7 +435,7 @@ ConvertFrom-StringData @' Remove_SqlDscAgentAlert_RemoveShouldProcessCaption = Remove SQL Agent Alert on instance ## Test-SqlDscIsAgentAlert - Test_SqlDscIsAgentAlert_TestingAlert = Testing if the SQL Agent Alert '{0}' exists and has the desired properties. (TSIAA0001) + Test_SqlDscIsAgentAlert_TestingAlert = Testing if the SQL Agent Alert '{0}' exists. (TSIAA0001) Test_SqlDscIsAgentAlert_AlertNotFound = SQL Agent Alert '{0}' was not found. (TSIAA0002) Test_SqlDscIsAgentAlert_AlertFound = SQL Agent Alert '{0}' was found. (TSIAA0003) From e80d0b19b2886dd018010958ec1d8e18dc9b7353 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 15:36:30 +0200 Subject: [PATCH 27/38] Remove unnecessary comment from Test-SqlDscIsAgentAlert function --- source/Public/Test-SqlDscIsAgentAlert.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/source/Public/Test-SqlDscIsAgentAlert.ps1 b/source/Public/Test-SqlDscIsAgentAlert.ps1 index 53f2528baa..69a61597b5 100644 --- a/source/Public/Test-SqlDscIsAgentAlert.ps1 +++ b/source/Public/Test-SqlDscIsAgentAlert.ps1 @@ -50,7 +50,6 @@ function Test-SqlDscIsAgentAlert $Name ) - # cSpell: ignore TSIAA process { Write-Verbose -Message ($script:localizedData.Test_SqlDscIsAgentAlert_TestingAlert -f $Name) From 6bc9f580292998e37b3fadcfd70ad20420251c9b Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 16:39:38 +0200 Subject: [PATCH 28/38] Fix path formatting in build instructions for consistency across guidelines --- .github/instructions/SqlServerDsc-guidelines.instructions.md | 4 ++-- .../dsc-community-style-guidelines.instructions.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/instructions/SqlServerDsc-guidelines.instructions.md b/.github/instructions/SqlServerDsc-guidelines.instructions.md index 32703fb1de..192732556f 100644 --- a/.github/instructions/SqlServerDsc-guidelines.instructions.md +++ b/.github/instructions/SqlServerDsc-guidelines.instructions.md @@ -8,8 +8,8 @@ applyTo: "**" ## Build & Test Workflow Requirements - Run in `pwsh` - Run scripts from project root -- Setup build and test environment (once per `pwsh` session): `.build.ps1 -Task noop` -- Build project: `.build.ps1 -Task build` +- Setup build and test environment (once per `pwsh` session): `./build.ps1 -Task noop` +- Build project: `./build.ps1 -Task build` - Run tests without coverage (wildcards allowed): `Invoke-PesterJob -Path '{tests filepath}' -SkipCodeCoverage` - Run QA tests: `Invoke-PesterJob -Path 'tests/QA' -SkipCodeCoverage` - Never run integration tests locally diff --git a/.github/instructions/dsc-community-style-guidelines.instructions.md b/.github/instructions/dsc-community-style-guidelines.instructions.md index bb94249f01..04733c4f90 100644 --- a/.github/instructions/dsc-community-style-guidelines.instructions.md +++ b/.github/instructions/dsc-community-style-guidelines.instructions.md @@ -12,7 +12,7 @@ applyTo: "**" ## Build & Test Workflow - Run scripts in `pwsh`; always from repository root -- Build before running tests: `.\build.ps1 -Tasks build` +- Build before running tests: `./build.ps1 -Tasks build` - Always run tests in new PowerShell session: `Invoke-Pester -Path @({test paths}) -Output Detailed` ## File Organization From c91efaee92caa0cce0e95be2fe8c24b0baee5d58 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 16:44:55 +0200 Subject: [PATCH 29/38] Update error ID for Test-SqlDscAgentAlertProperty to maintain consistency in error handling --- source/Public/Test-SqlDscAgentAlertProperty.ps1 | 2 +- source/en-US/SqlServerDsc.strings.psd1 | 2 +- tests/Unit/Public/Test-SqlDscAgentAlertProperty.Tests.ps1 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/Public/Test-SqlDscAgentAlertProperty.ps1 b/source/Public/Test-SqlDscAgentAlertProperty.ps1 index f601011a9a..019b3e795b 100644 --- a/source/Public/Test-SqlDscAgentAlertProperty.ps1 +++ b/source/Public/Test-SqlDscAgentAlertProperty.ps1 @@ -108,7 +108,7 @@ function Test-SqlDscAgentAlertProperty { $errorMessage = $script:localizedData.Test_SqlDscAgentAlertProperty_AlertNotFound -f $Name - Write-Error -Message $errorMessage -Category 'ObjectNotFound' -ErrorId 'TSAAP0002' -TargetObject $Name + Write-Error -Message $errorMessage -Category 'ObjectNotFound' -ErrorId 'TSDAAP0001' -TargetObject $Name return $false } diff --git a/source/en-US/SqlServerDsc.strings.psd1 b/source/en-US/SqlServerDsc.strings.psd1 index f6ff43b8e7..9966a671bf 100644 --- a/source/en-US/SqlServerDsc.strings.psd1 +++ b/source/en-US/SqlServerDsc.strings.psd1 @@ -440,5 +440,5 @@ ConvertFrom-StringData @' Test_SqlDscIsAgentAlert_AlertFound = SQL Agent Alert '{0}' was found. (TSIAA0003) ## Test-SqlDscAgentAlertProperty - Test_SqlDscAgentAlertProperty_AlertNotFound = SQL Agent Alert '{0}' was not found. (TSAAP0002) + Test_SqlDscAgentAlertProperty_AlertNotFound = SQL Agent Alert '{0}' was not found. (TSDAAP0001) '@ diff --git a/tests/Unit/Public/Test-SqlDscAgentAlertProperty.Tests.ps1 b/tests/Unit/Public/Test-SqlDscAgentAlertProperty.Tests.ps1 index 549e998ed1..191ba05c39 100644 --- a/tests/Unit/Public/Test-SqlDscAgentAlertProperty.Tests.ps1 +++ b/tests/Unit/Public/Test-SqlDscAgentAlertProperty.Tests.ps1 @@ -229,7 +229,7 @@ Describe 'Test-SqlDscAgentAlertProperty' -Tag 'Public' { $result | Should -BeFalse Should -Invoke -CommandName 'Write-Error' -ModuleName $script:dscModuleName -ParameterFilter { - $Category -eq 'ObjectNotFound' -and $ErrorId -eq 'TSAAP0002' -and $TargetObject -eq 'NonExistentAlert' + $Category -eq 'ObjectNotFound' -and $ErrorId -eq 'TSDAAP0001' -and $TargetObject -eq 'NonExistentAlert' } -Times 1 -Exactly } } From a411af076a1840a0a9b1c817fbe4d638d025127d Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 9 Sep 2025 16:50:06 +0200 Subject: [PATCH 30/38] Update INPUTS and OUTPUTS sections for clarity and consistency in comment-based help --- ...tyle-guidelines-powershell.instructions.md | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/.github/instructions/dsc-community-style-guidelines-powershell.instructions.md b/.github/instructions/dsc-community-style-guidelines-powershell.instructions.md index ab899a601f..0c7002ac3a 100644 --- a/.github/instructions/dsc-community-style-guidelines-powershell.instructions.md +++ b/.github/instructions/dsc-community-style-guidelines-powershell.instructions.md @@ -64,8 +64,8 @@ applyTo: "**/*.ps?(m|d)1" - Comment-based help: SYNOPSIS, DESCRIPTION (40+ chars), PARAMETER, EXAMPLE sections before function/class - Comment-based help indentation: keywords 4 spaces, text 8 spaces - Include examples for all parameter sets and combinations -- INPUTS: List each pipeline‑accepted type (one per line) with a 1‑line description. -- OUTPUTS: List each return type (one per line) with a 1‑line description. Must match both `[OutputType()]` and actual returns. +- INPUTS: List each pipeline‑accepted type (one per line) with a 1‑line description. Repeat keyword for each input type. +- OUTPUTS: List each return type (one per line) with a 1‑line description. Repeat keyword for each output type. Must match both `[OutputType()]` and actual returns. - .NOTES: Include only if it conveys critical info (constraints, side effects, security, version compatibility, breaking behavior). Keep to ≤2 short sentences. ## Functions @@ -136,14 +136,25 @@ if ($Force.IsPresent -and -not $Confirm) Parameter description .INPUTS - TypeName + TypeName1 - Description + Description1 + + .INPUTS + TypeName2 + + Description2 .OUTPUTS - TypeName + TypeName1 + + Description1 + + .OUTPUTS + TypeName2 + + Description2 - Description #> function Get-Something { From 91d4e6f1e03b28fcf8210806c9d1bc85c34d8b53 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sat, 13 Sep 2025 12:33:01 +0200 Subject: [PATCH 31/38] Update test assertions to use 'Test-SqlDscIsAgentAlert' instead of 'Get-AgentAlertObject' --- tests/Unit/Public/New-SqlDscAgentAlert.Tests.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Unit/Public/New-SqlDscAgentAlert.Tests.ps1 b/tests/Unit/Public/New-SqlDscAgentAlert.Tests.ps1 index 2121bc3ffd..cfa35b8cbb 100644 --- a/tests/Unit/Public/New-SqlDscAgentAlert.Tests.ps1 +++ b/tests/Unit/Public/New-SqlDscAgentAlert.Tests.ps1 @@ -199,7 +199,7 @@ Describe 'New-SqlDscAgentAlert' -Tag 'Public' { $result | Should -BeNullOrEmpty Should -Invoke -CommandName 'Assert-BoundParameter' -Times 1 -Exactly - Should -Invoke -CommandName 'Get-AgentAlertObject' -Times 1 -Exactly + Should -Invoke -CommandName 'Test-SqlDscIsAgentAlert' -Times 1 -Exactly } It 'Should create alert with message ID using pipeline input' { @@ -207,7 +207,7 @@ Describe 'New-SqlDscAgentAlert' -Tag 'Public' { $result | Should -BeNullOrEmpty Should -Invoke -CommandName 'Assert-BoundParameter' -Times 1 -Exactly - Should -Invoke -CommandName 'Get-AgentAlertObject' -Times 1 -Exactly + Should -Invoke -CommandName 'Test-SqlDscIsAgentAlert' -Times 1 -Exactly } It 'Should create alert with PassThru using pipeline input' { @@ -224,7 +224,7 @@ Describe 'New-SqlDscAgentAlert' -Tag 'Public' { $result | Should -BeNullOrEmpty Should -Invoke -CommandName 'Assert-BoundParameter' -Times 1 -Exactly - Should -Invoke -CommandName 'Get-AgentAlertObject' -Times 1 -Exactly + Should -Invoke -CommandName 'Test-SqlDscIsAgentAlert' -Times 1 -Exactly } It 'Should create alert with Force and PassThru using pipeline input' { From ce2fd39d2e4f79fde4c08efff48efac7b8ae4c09 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sat, 13 Sep 2025 12:33:09 +0200 Subject: [PATCH 32/38] Clarify SqlServerDsc-specific guidelines in copilot instructions --- .github/copilot-instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 94d2d514e8..72b236f3da 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,2 +1,2 @@ # Requirements -- SqlServerDsc-specific guidelines override general project guidelines +- SqlServerDsc-specific guidelines and requirements override general project guidelines and requirements From b54b0f178f18c0ce8874720829270754c79c2658 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sat, 13 Sep 2025 12:33:16 +0200 Subject: [PATCH 33/38] Add guideline for passing original exception in catch blocks --- .../dsc-community-style-guidelines-powershell.instructions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/instructions/dsc-community-style-guidelines-powershell.instructions.md b/.github/instructions/dsc-community-style-guidelines-powershell.instructions.md index 3634d4bac3..1fd755cc10 100644 --- a/.github/instructions/dsc-community-style-guidelines-powershell.instructions.md +++ b/.github/instructions/dsc-community-style-guidelines-powershell.instructions.md @@ -95,6 +95,7 @@ applyTo: "**/*.ps?(m|d)1" - Use `$PSCmdlet.ThrowTerminatingError()` for terminating errors (except for classes), use relevant error category, in try-catch include exception with localized message - Use `Write-Error` for non-terminating errors - Always include `-Message` (localized string), `-Category` (relevant error category), `-ErrorId` (unique ID matching localized string ID), `-TargetObject` (object causing error) + - In catch blocks, pass original exception using `-Exception` - Always use `return` after `Write-Error` to avoid further processing ## ShouldProcess Required Pattern From 231c23a9908b72f53c2e717c7f5b059e23abafff Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sat, 13 Sep 2025 12:33:27 +0200 Subject: [PATCH 34/38] Refine Build & Test Workflow section in community guidelines for clarity and consistency --- .../dsc-community-style-guidelines.instructions.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/instructions/dsc-community-style-guidelines.instructions.md b/.github/instructions/dsc-community-style-guidelines.instructions.md index 04733c4f90..fea8f66cc3 100644 --- a/.github/instructions/dsc-community-style-guidelines.instructions.md +++ b/.github/instructions/dsc-community-style-guidelines.instructions.md @@ -10,10 +10,11 @@ applyTo: "**" - **Function**: Private function - **Resource**: DSC class-based resource -## Build & Test Workflow -- Run scripts in `pwsh`; always from repository root -- Build before running tests: `./build.ps1 -Tasks build` -- Always run tests in new PowerShell session: `Invoke-Pester -Path @({test paths}) -Output Detailed` +## Build & Test Workflow Requirements +- Run PowerShell script files from repository root +- Setup build and test environment (once per `pwsh` session): `./build.ps1 -Task noop` +- Build project before running tests: `./build.ps1 -Tasks build` +- Always run tests in new `pwsh` session: `Invoke-Pester -Path @({test paths}) -Output Detailed` ## File Organization - Public commands: `source/Public/{CommandName}.ps1` From 74b96348aed501b39aebff7108cba8179a43fb57 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sat, 13 Sep 2025 12:33:33 +0200 Subject: [PATCH 35/38] Refine Build & Test Workflow requirements for clarity and consistency --- .github/instructions/SqlServerDsc-guidelines.instructions.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/instructions/SqlServerDsc-guidelines.instructions.md b/.github/instructions/SqlServerDsc-guidelines.instructions.md index 192732556f..63b6b671cd 100644 --- a/.github/instructions/SqlServerDsc-guidelines.instructions.md +++ b/.github/instructions/SqlServerDsc-guidelines.instructions.md @@ -6,10 +6,9 @@ applyTo: "**" # SqlServerDsc Requirements ## Build & Test Workflow Requirements -- Run in `pwsh` -- Run scripts from project root +- Run PowerShell script files from repository root - Setup build and test environment (once per `pwsh` session): `./build.ps1 -Task noop` -- Build project: `./build.ps1 -Task build` +- Build project before running tests: `./build.ps1 -Tasks build` - Run tests without coverage (wildcards allowed): `Invoke-PesterJob -Path '{tests filepath}' -SkipCodeCoverage` - Run QA tests: `Invoke-PesterJob -Path 'tests/QA' -SkipCodeCoverage` - Never run integration tests locally From 7cf071c4e50a8b08631c6c9d57cba1a9da562c39 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sat, 13 Sep 2025 12:58:33 +0200 Subject: [PATCH 36/38] Fix typo in Build & Test Workflow requirements for consistency --- .github/instructions/SqlServerDsc-guidelines.instructions.md | 2 +- .../instructions/dsc-community-style-guidelines.instructions.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/instructions/SqlServerDsc-guidelines.instructions.md b/.github/instructions/SqlServerDsc-guidelines.instructions.md index 63b6b671cd..ec64ae1271 100644 --- a/.github/instructions/SqlServerDsc-guidelines.instructions.md +++ b/.github/instructions/SqlServerDsc-guidelines.instructions.md @@ -7,7 +7,7 @@ applyTo: "**" ## Build & Test Workflow Requirements - Run PowerShell script files from repository root -- Setup build and test environment (once per `pwsh` session): `./build.ps1 -Task noop` +- Setup build and test environment (once per `pwsh` session): `./build.ps1 -Tasks noop` - Build project before running tests: `./build.ps1 -Tasks build` - Run tests without coverage (wildcards allowed): `Invoke-PesterJob -Path '{tests filepath}' -SkipCodeCoverage` - Run QA tests: `Invoke-PesterJob -Path 'tests/QA' -SkipCodeCoverage` diff --git a/.github/instructions/dsc-community-style-guidelines.instructions.md b/.github/instructions/dsc-community-style-guidelines.instructions.md index fea8f66cc3..f0d5748090 100644 --- a/.github/instructions/dsc-community-style-guidelines.instructions.md +++ b/.github/instructions/dsc-community-style-guidelines.instructions.md @@ -12,7 +12,7 @@ applyTo: "**" ## Build & Test Workflow Requirements - Run PowerShell script files from repository root -- Setup build and test environment (once per `pwsh` session): `./build.ps1 -Task noop` +- Setup build and test environment (once per `pwsh` session): `./build.ps1 -Tasks noop` - Build project before running tests: `./build.ps1 -Tasks build` - Always run tests in new `pwsh` session: `Invoke-Pester -Path @({test paths}) -Output Detailed` From dd3d32607fad256e9f42e9833eda19cd00ec8d7d Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sat, 13 Sep 2025 13:41:35 +0200 Subject: [PATCH 37/38] Remove unnecessary ValidateNotNullOrEmpty attribute from Name parameter in Test-SqlDscAgentAlertProperty function --- source/Public/Test-SqlDscAgentAlertProperty.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/source/Public/Test-SqlDscAgentAlertProperty.ps1 b/source/Public/Test-SqlDscAgentAlertProperty.ps1 index 019b3e795b..475107c3ab 100644 --- a/source/Public/Test-SqlDscAgentAlertProperty.ps1 +++ b/source/Public/Test-SqlDscAgentAlertProperty.ps1 @@ -68,7 +68,6 @@ function Test-SqlDscAgentAlertProperty $ServerObject, [Parameter(ParameterSetName = 'ByServerAndName', Mandatory = $true)] - [ValidateNotNullOrEmpty()] [System.String] $Name, From e9d788c4f111b2f7552d942abd5eea2f541aca2c Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sat, 13 Sep 2025 13:41:41 +0200 Subject: [PATCH 38/38] Update tests for non-existent alerts to expect exceptions instead of false returns --- ...scAgentAlertProperty.Integration.Tests.ps1 | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/tests/Integration/Commands/Test-SqlDscAgentAlertProperty.Integration.Tests.ps1 b/tests/Integration/Commands/Test-SqlDscAgentAlertProperty.Integration.Tests.ps1 index 075e3e69c5..8590dcea1e 100644 --- a/tests/Integration/Commands/Test-SqlDscAgentAlertProperty.Integration.Tests.ps1 +++ b/tests/Integration/Commands/Test-SqlDscAgentAlertProperty.Integration.Tests.ps1 @@ -91,21 +91,18 @@ Describe 'Test-SqlDscAgentAlertProperty' -Tag @('Integration_SQL2017', 'Integrat } Context 'When alert does not exist' { - It 'Should return false for non-existent alert with severity' { - $result = $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'NonExistentAlert' -Severity 16 - - $result | Should -BeFalse - } - - It 'Should return false for non-existent alert with message ID' { - $result = $script:sqlServerObject | Test-SqlDscAgentAlertProperty -Name 'NonExistentAlert' -MessageId 50001 - - $result | Should -BeFalse + It 'Should throw an exception for non-existent alert with severity' { + { + $null = $script:sqlServerObject | + Test-SqlDscAgentAlertProperty -Name 'NonExistentAlert' -Severity 16 -ErrorAction 'Stop' + } | Should -Throw } - It 'Should throw an exception for a non-existent alert' { - { $script:sqlServerObject | Get-SqlDscAgentAlert -Name 'NonExistentAlert' -ErrorAction 'Stop' } | - Should -Throw + It 'Should throw an exception for non-existent alert with message ID' { + { + $null = $script:sqlServerObject | + Test-SqlDscAgentAlertProperty -Name 'NonExistentAlert' -MessageId 50001 -ErrorAction 'Stop' + } | Should -Throw } }