Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
33593d4
`Add-SqlDscTraceFlag`: Simplify trace flag addition and ensure unique…
johlju Oct 10, 2025
4a9be43
Add integration tests to ensure de-duplication of trace flags in Add-…
johlju Oct 10, 2025
dcafa11
Refactor Add-SqlDscTraceFlag to improve trace flag comparison and pro…
johlju Oct 10, 2025
1bccb7a
`Add-SqlDscTraceFlag`: Enhance de-duplication logic and add idempoten…
johlju Oct 11, 2025
4771347
Refactor integration tests for Add-SqlDscTraceFlag to improve readabi…
johlju Oct 11, 2025
ce5589c
Fix documentation for InstanceName parameter in Add-SqlDscTraceFlag t…
johlju Oct 11, 2025
ab5ef85
Refactor tests for Add-SqlDscTraceFlag to improve de-duplication chec…
johlju Oct 11, 2025
8a4e3ce
Normalize and deduplicate trace flags in Add-SqlDscTraceFlag for impr…
johlju Oct 11, 2025
3956454
Remove redundant entries for `Add-SqlDscTraceFlag` unit and integrati…
johlju Oct 11, 2025
8c19ffc
Fix parameter order in Compare-Object for accurate trace flag comparison
johlju Oct 11, 2025
ed13b19
Refactor Add-SqlDscTraceFlag for improved de-duplication and idempote…
johlju Oct 11, 2025
5270b39
Add validation for TraceFlag parameter in Add-SqlDscTraceFlag to ensu…
johlju Oct 11, 2025
db894d3
Refactor trace flag normalization in Add-SqlDscTraceFlag for improved…
johlju Oct 11, 2025
9338719
Filter out null and zero values from trace flags in Get-SqlDscTraceFl…
johlju Oct 11, 2025
2a4ebda
Update ValidateRange attribute for TraceFlag parameter to use System.…
johlju Oct 11, 2025
d4e2da4
Merge branch 'main' into fix/issue#2277
johlju Oct 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- `Add-SqlDscTraceFlag`
- Improved de-duplication logic to normalize element types to `[System.UInt32]`
before sorting and removing duplicates, ensuring proper handling of mixed
numeric types ([issue #2277](https://github.com/dsccommunity/SqlServerDsc/issues/2277)).
- Added idempotent behavior by comparing current and desired trace flags before
calling `Set-SqlDscTraceFlag`, skipping unnecessary writes when there are no
effective changes ([issue #2277](https://github.com/dsccommunity/SqlServerDsc/issues/2277)).
- Refactored integration tests to remove `finally` blocks from `It`-blocks and
use Pester `BeforeEach`/`AfterEach` blocks instead, following DSC Community
coding guidelines. This improves test cleanup reliability and maintainability
Expand Down
63 changes: 39 additions & 24 deletions source/Public/Add-SqlDscTraceFlag.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
Specifies the server name where the instance exist.

.PARAMETER InstanceName
Specifies the instance name on which to remove the trace flags.
Specifies the instance name on which to add the trace flags.

.PARAMETER TraceFlag
Specifies the trace flags to add.
Expand Down Expand Up @@ -112,35 +112,50 @@ function Add-SqlDscTraceFlag

$ErrorActionPreference = $originalErrorActionPreference

$desiredTraceFlags = [System.UInt32[]] $currentTraceFlags + @(
$TraceFlag |
ForEach-Object -Process {
# Add only when it does not already exist.
if ($_ -notin $currentTraceFlags)
{
$_
}
}
)

$verboseDescriptionMessage = $script:localizedData.TraceFlag_Add_ShouldProcessVerboseDescription -f $InstanceName, ($TraceFlag -join ', ')
$verboseWarningMessage = $script:localizedData.TraceFlag_Add_ShouldProcessVerboseWarning -f $InstanceName
$captionMessage = $script:localizedData.TraceFlag_Add_ShouldProcessCaption

if ($PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage))
# Normalize current trace flags: ensure array, remove nulls, cast to UInt32, sort uniquely
$normalizedCurrentTraceFlags = @($currentTraceFlags) |
Where-Object -FilterScript { $null -ne $_ } |
ForEach-Object -Process { [System.UInt32] $_ } |
Sort-Object -Unique

# Normalize input trace flags: ensure array, remove nulls, cast to UInt32, sort uniquely
$normalizedInputTraceFlags = @($TraceFlag) |
Where-Object -FilterScript { $null -ne $_ } |
ForEach-Object -Process { [System.UInt32] $_ } |
Sort-Object -Unique

# Combine normalized current and input trace flags to get the desired state
$desiredTraceFlags = @(
$normalizedCurrentTraceFlags
$normalizedInputTraceFlags
) |
Sort-Object -Unique
$desiredTraceFlags = [System.UInt32[]] $desiredTraceFlags

# Compare normalized current and desired trace flags to determine if there's an effective change
$compareResult = Compare-Object -ReferenceObject $normalizedCurrentTraceFlags -DifferenceObject $desiredTraceFlags

if ($compareResult)
{
# Copy $PSBoundParameters to keep it intact.
$setSqlDscTraceFlagParameters = Remove-CommonParameter -Hashtable $PSBoundParameters
$verboseDescriptionMessage = $script:localizedData.TraceFlag_Add_ShouldProcessVerboseDescription -f $InstanceName, ($desiredTraceFlags -join ', ')
$verboseWarningMessage = $script:localizedData.TraceFlag_Add_ShouldProcessVerboseWarning -f $InstanceName
$captionMessage = $script:localizedData.TraceFlag_Add_ShouldProcessCaption

$setSqlDscTraceFlagParameters.TraceFlag = $desiredTraceFlags
if ($PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage))
{
# Copy $PSBoundParameters to keep it intact.
$setSqlDscTraceFlagParameters = Remove-CommonParameter -Hashtable $PSBoundParameters

$originalErrorActionPreference = $ErrorActionPreference
$setSqlDscTraceFlagParameters.TraceFlag = $desiredTraceFlags

$ErrorActionPreference = 'Stop'
$originalErrorActionPreference = $ErrorActionPreference

Set-SqlDscTraceFlag @setSqlDscTraceFlagParameters -ErrorAction 'Stop'
$ErrorActionPreference = 'Stop'

$ErrorActionPreference = $originalErrorActionPreference
Set-SqlDscTraceFlag @setSqlDscTraceFlagParameters -ErrorAction 'Stop'

$ErrorActionPreference = $originalErrorActionPreference
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ Describe 'Add-SqlDscTraceFlag' -Tag @('Integration_SQL2017', 'Integration_SQL201
It 'Should not duplicate existing trace flags when adding them again' {
# Arrange - Ensure a trace flag is already set
Add-SqlDscTraceFlag -ServerName $script:mockComputerName -InstanceName $script:mockInstanceName -TraceFlag $script:singleTestTraceFlag -Force -ErrorAction 'Stop'

$beforeAddTraceFlags = Get-SqlDscTraceFlag -ServerName $script:mockComputerName -InstanceName $script:mockInstanceName -ErrorAction 'Stop'
$beforeCount = ($beforeAddTraceFlags | Where-Object { $_ -eq $script:singleTestTraceFlag }).Count

Expand All @@ -101,7 +101,7 @@ Describe 'Add-SqlDscTraceFlag' -Tag @('Integration_SQL2017', 'Integration_SQL201
# Assert - Verify no duplicate was created
$afterAddTraceFlags = Get-SqlDscTraceFlag -ServerName $script:mockComputerName -InstanceName $script:mockInstanceName -ErrorAction 'Stop'
$afterCount = ($afterAddTraceFlags | Where-Object { $_ -eq $script:singleTestTraceFlag }).Count

$afterCount | Should -Be $beforeCount
$afterAddTraceFlags | Should -Contain $script:singleTestTraceFlag
}
Expand All @@ -120,6 +120,16 @@ Describe 'Add-SqlDscTraceFlag' -Tag @('Integration_SQL2017', 'Integration_SQL201
$currentTraceFlags | Should -Contain $script:singleTestTraceFlag
$currentTraceFlags | Should -Contain $script:additionalTestTraceFlag
}

It 'Should de-duplicate trace flags provided in the input array' {
# Act - Add trace flags with duplicates in the input array
$null = Add-SqlDscTraceFlag -ServerName $script:mockComputerName -InstanceName $script:mockInstanceName -TraceFlag @($script:singleTestTraceFlag, $script:singleTestTraceFlag, $script:additionalTestTraceFlag, $script:additionalTestTraceFlag) -Force -ErrorAction 'Stop'

# Assert - Verify each trace flag appears only once
$currentTraceFlags = Get-SqlDscTraceFlag -ServerName $script:mockComputerName -InstanceName $script:mockInstanceName -ErrorAction 'Stop'
($currentTraceFlags | Where-Object { $_ -eq $script:singleTestTraceFlag }).Count | Should -Be 1
($currentTraceFlags | Where-Object { $_ -eq $script:additionalTestTraceFlag }).Count | Should -Be 1
}
}

Context 'When adding trace flags using ServiceObject parameter' {
Expand Down Expand Up @@ -170,5 +180,15 @@ Describe 'Add-SqlDscTraceFlag' -Tag @('Integration_SQL2017', 'Integration_SQL201
$currentTraceFlags | Should -Contain $traceFlag
}
}

It 'Should de-duplicate trace flags provided in the input array using ServiceObject parameter' {
# Act - Add trace flags with duplicates in the input array
$null = Add-SqlDscTraceFlag -ServiceObject $script:serviceObject -TraceFlag @($script:singleTestTraceFlag, $script:singleTestTraceFlag, $script:additionalTestTraceFlag, $script:additionalTestTraceFlag) -Force -ErrorAction 'Stop'

# Assert - Verify each trace flag appears only once
$currentTraceFlags = Get-SqlDscTraceFlag -ServiceObject $script:serviceObject -ErrorAction 'Stop'
($currentTraceFlags | Where-Object { $_ -eq $script:singleTestTraceFlag }).Count | Should -Be 1
($currentTraceFlags | Where-Object { $_ -eq $script:additionalTestTraceFlag }).Count | Should -Be 1
}
}
}
114 changes: 110 additions & 4 deletions tests/Unit/Public/Add-SqlDscTraceFlag.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,116 @@ Describe 'Add-SqlDscTraceFlag' -Tag 'Public' {
It 'Should not add duplicate if it already exist' {
{ Add-SqlDscTraceFlag -ServiceObject $mockServiceObject -TraceFlag 3226 -Force } | Should -Not -Throw

Should -Invoke -CommandName Set-SqlDscTraceFlag -ParameterFilter {
$TraceFlag.Count -eq 1 -and
$TraceFlag -contains 3226
} -Exactly -Times 1 -Scope It
# Should not call Set-SqlDscTraceFlag when the trace flag already exists (idempotent)
Should -Invoke -CommandName Set-SqlDscTraceFlag -Exactly -Times 0 -Scope It
}
}

Context 'When duplicate trace flags are provided in the input' {
BeforeAll {
Mock -CommandName Set-SqlDscTraceFlag
}

Context 'When there are no existing trace flags and duplicates are provided' {
BeforeAll {
Mock -CommandName Get-SqlDscTraceFlag -MockWith {
return @()
}

$mockServiceObject = [Microsoft.SqlServer.Management.Smo.Wmi.Service]::CreateTypeInstance()
$mockServiceObject.Name = 'MSSQL$SQL2022'
}

It 'Should de-duplicate trace flags when only duplicates are provided' {
$null = Add-SqlDscTraceFlag -ServiceObject $mockServiceObject -TraceFlag 4199,4199 -Force

Should -Invoke -CommandName Set-SqlDscTraceFlag -ParameterFilter {
$TraceFlag.Count -eq 1 -and
$TraceFlag -contains 4199
} -Exactly -Times 1 -Scope It
}

It 'Should de-duplicate trace flags when mix of unique and duplicates are provided' {
$null = Add-SqlDscTraceFlag -ServiceObject $mockServiceObject -TraceFlag 4199,3226,4199,3226 -Force

Should -Invoke -CommandName Set-SqlDscTraceFlag -ParameterFilter {
$TraceFlag.Count -eq 2 -and
$TraceFlag -contains 4199 -and
$TraceFlag -contains 3226
} -Exactly -Times 1 -Scope It
}

It 'Should handle multiple duplicates of multiple trace flags' {
$null = Add-SqlDscTraceFlag -ServiceObject $mockServiceObject -TraceFlag 4199,4199,3226,3226,3226,1222 -Force

Should -Invoke -CommandName Set-SqlDscTraceFlag -ParameterFilter {
$TraceFlag.Count -eq 3 -and
$TraceFlag -contains 4199 -and
$TraceFlag -contains 3226 -and
$TraceFlag -contains 1222
} -Exactly -Times 1 -Scope It
}
}

Context 'When there are existing trace flags and duplicates are provided' {
BeforeAll {
Mock -CommandName Get-SqlDscTraceFlag -MockWith {
return @(3226)
}

$mockServiceObject = [Microsoft.SqlServer.Management.Smo.Wmi.Service]::CreateTypeInstance()
$mockServiceObject.Name = 'MSSQL$SQL2022'
}

It 'Should de-duplicate when adding trace flags that include duplicates and existing flag' {
$null = Add-SqlDscTraceFlag -ServiceObject $mockServiceObject -TraceFlag 4199,3226,4199 -Force

Should -Invoke -CommandName Set-SqlDscTraceFlag -ParameterFilter {
$TraceFlag.Count -eq 2 -and
$TraceFlag -contains 4199 -and
$TraceFlag -contains 3226
} -Exactly -Times 1 -Scope It
}

It 'Should de-duplicate when all provided trace flags are duplicates of existing flag' {
$null = Add-SqlDscTraceFlag -ServiceObject $mockServiceObject -TraceFlag 3226,3226 -Force

# Should not call Set-SqlDscTraceFlag when all flags already exist (idempotent)
Should -Invoke -CommandName Set-SqlDscTraceFlag -Exactly -Times 0 -Scope It
}

It 'Should de-duplicate complex scenario with existing and new flags' {
$null = Add-SqlDscTraceFlag -ServiceObject $mockServiceObject -TraceFlag 4199,4199,3226,1222,1222 -Force

Should -Invoke -CommandName Set-SqlDscTraceFlag -ParameterFilter {
$TraceFlag.Count -eq 3 -and
$TraceFlag -contains 4199 -and
$TraceFlag -contains 3226 -and
$TraceFlag -contains 1222
} -Exactly -Times 1 -Scope It
}
}

Context 'When there are multiple existing trace flags and duplicates are provided' {
BeforeAll {
Mock -CommandName Get-SqlDscTraceFlag -MockWith {
return @(3226, 1222)
}

$mockServiceObject = [Microsoft.SqlServer.Management.Smo.Wmi.Service]::CreateTypeInstance()
$mockServiceObject.Name = 'MSSQL$SQL2022'
}

It 'Should de-duplicate and merge with multiple existing trace flags' {
$null = Add-SqlDscTraceFlag -ServiceObject $mockServiceObject -TraceFlag 4199,4199,3226,1222 -Force

Should -Invoke -CommandName Set-SqlDscTraceFlag -ParameterFilter {
$TraceFlag.Count -eq 3 -and
$TraceFlag -contains 4199 -and
$TraceFlag -contains 3226 -and
$TraceFlag -contains 1222
} -Exactly -Times 1 -Scope It
}
}
}
}
Loading