diff --git a/CHANGELOG.md b/CHANGELOG.md index b63b84773d..e4841dca32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added integration tests for `Remove-SqlDscAudit` command to ensure it functions correctly in real environments [issue #2241](https://github.com/dsccommunity/SqlServerDsc/issues/2241). +- Added integration tests for `Get-SqlDscStartupParameter` command to ensure it + functions correctly in real environments + [issue #2217](https://github.com/dsccommunity/SqlServerDsc/issues/2217). - Added integration tests for `Get-SqlDscTraceFlag` command to ensure it functions correctly in real environments [issue #2216](https://github.com/dsccommunity/SqlServerDsc/issues/2216). diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1c032b87ca..ab1021bfed 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -316,6 +316,7 @@ stages: 'tests/Integration/Commands/Test-SqlDscIsLoginEnabled.Integration.Tests.ps1' 'tests/Integration/Commands/New-SqlDscRole.Integration.Tests.ps1' 'tests/Integration/Commands/Get-SqlDscRole.Integration.Tests.ps1' + 'tests/Integration/Commands/Get-SqlDscStartupParameter.Integration.Tests.ps1' 'tests/Integration/Commands/Test-SqlDscIsRole.Integration.Tests.ps1' 'tests/Integration/Commands/Test-SqlDscIsDatabasePrincipal.Integration.Tests.ps1' 'tests/Integration/Commands/Grant-SqlDscServerPermission.Integration.Tests.ps1' diff --git a/source/Public/Get-SqlDscStartupParameter.ps1 b/source/Public/Get-SqlDscStartupParameter.ps1 index a5d0b76e31..1890e63b66 100644 --- a/source/Public/Get-SqlDscStartupParameter.ps1 +++ b/source/Public/Get-SqlDscStartupParameter.ps1 @@ -6,13 +6,24 @@ Get current startup parameters on a Database Engine instance. .PARAMETER ServiceObject - Specifies the Service object to return the trace flags from. + Specifies the Service object to return the startup parameters from. .PARAMETER ServerName - Specifies the server name to return the trace flags from. + Specifies the server name to return the startup parameters from. .PARAMETER InstanceName - Specifies the instance name to return the trace flags for. + Specifies the instance name to return the startup parameters for. + + .INPUTS + Microsoft.SqlServer.Management.Smo.Wmi.Service + + A service object representing the Database Engine service. + + .OUTPUTS + StartupParameters + + Returns a StartupParameters object containing the startup parameters + for the specified Database Engine instance. .EXAMPLE Get-SqlDscStartupParameter @@ -39,9 +50,6 @@ Get the startup parameters from the Database Engine instance 'SQL2022' on the server where the command in run. - - .OUTPUTS - `[StartupParameters]` #> function Get-SqlDscStartupParameter { @@ -50,7 +58,7 @@ function Get-SqlDscStartupParameter [CmdletBinding(DefaultParameterSetName = 'ByServerName')] param ( - [Parameter(ParameterSetName = 'ByServiceObject', Mandatory = $true)] + [Parameter(ParameterSetName = 'ByServiceObject', Mandatory = $true, ValueFromPipeline = $true)] [Microsoft.SqlServer.Management.Smo.Wmi.Service] $ServiceObject, @@ -65,56 +73,62 @@ function Get-SqlDscStartupParameter $InstanceName = 'MSSQLSERVER' ) - $originalErrorActionPreference = $ErrorActionPreference - - $ErrorActionPreference = 'Stop' + begin + { + $originalErrorActionPreference = $ErrorActionPreference - Assert-ElevatedUser -ErrorAction 'Stop' + $ErrorActionPreference = 'Stop' - $ErrorActionPreference = $originalErrorActionPreference + Assert-ElevatedUser -ErrorAction 'Stop' - if ($PSCmdlet.ParameterSetName -eq 'ByServiceObject') - { - $ServiceObject | Assert-ManagedServiceType -ServiceType 'DatabaseEngine' + $ErrorActionPreference = $originalErrorActionPreference } - if ($PSCmdlet.ParameterSetName -eq 'ByServerName') + process { - $getSqlDscManagedComputerServiceParameters = @{ - ServerName = $ServerName - InstanceName = $InstanceName - ServiceType = 'DatabaseEngine' + if ($PSCmdlet.ParameterSetName -eq 'ByServiceObject') + { + $ServiceObject | Assert-ManagedServiceType -ServiceType 'DatabaseEngine' } - $ServiceObject = Get-SqlDscManagedComputerService @getSqlDscManagedComputerServiceParameters - - if (-not $ServiceObject) + if ($PSCmdlet.ParameterSetName -eq 'ByServerName') { - $writeErrorParameters = @{ - Message = $script:localizedData.StartupParameter_Get_FailedToFindServiceObject - Category = 'InvalidOperation' - ErrorId = 'GSDSP0001' # CSpell: disable-line - TargetObject = $ServiceObject + $getSqlDscManagedComputerServiceParameters = @{ + ServerName = $ServerName + InstanceName = $InstanceName + ServiceType = 'DatabaseEngine' } - Write-Error @writeErrorParameters + $ServiceObject = Get-SqlDscManagedComputerService @getSqlDscManagedComputerServiceParameters + + if (-not $ServiceObject) + { + $writeErrorParameters = @{ + Message = $script:localizedData.StartupParameter_Get_FailedToFindServiceObject + Category = 'InvalidOperation' + ErrorId = 'GSDSP0001' # CSpell: disable-line + TargetObject = $ServiceObject + } + + Write-Error @writeErrorParameters + } } - } - Write-Verbose -Message ( - $script:localizedData.StartupParameter_Get_ReturnStartupParameters -f $InstanceName, $ServerName - ) + Write-Verbose -Message ( + $script:localizedData.StartupParameter_Get_ReturnStartupParameters -f $InstanceName, $ServerName + ) - $startupParameters = $null + $startupParameters = $null - if ($ServiceObject.StartupParameters) - { - $startupParameters = [StartupParameters]::Parse($ServiceObject.StartupParameters) - } - else - { - Write-Debug -Message ($script:localizedData.StartupParameter_Get_FailedToFindStartupParameters -f $MyInvocation.MyCommand) - } + if ($ServiceObject.StartupParameters) + { + $startupParameters = [StartupParameters]::Parse($ServiceObject.StartupParameters) + } + else + { + Write-Debug -Message ($script:localizedData.StartupParameter_Get_FailedToFindStartupParameters -f $MyInvocation.MyCommand) + } - return $startupParameters + return $startupParameters + } } diff --git a/tests/Integration/Commands/Get-SqlDscStartupParameter.Integration.Tests.ps1 b/tests/Integration/Commands/Get-SqlDscStartupParameter.Integration.Tests.ps1 new file mode 100644 index 0000000000..5b424a3500 --- /dev/null +++ b/tests/Integration/Commands/Get-SqlDscStartupParameter.Integration.Tests.ps1 @@ -0,0 +1,188 @@ +[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 noop" first.' + } +} + +BeforeAll { + $script:moduleName = 'SqlServerDsc' + + Import-Module -Name $script:moduleName -Force -ErrorAction 'Stop' +} + +# cSpell: ignore DSCSQLTEST +Describe 'Get-SqlDscStartupParameter' -Tag @('Integration_SQL2017', 'Integration_SQL2019', 'Integration_SQL2022') { + BeforeAll { + Write-Verbose -Message ('Running integration test as user ''{0}''.' -f $env:UserName) -Verbose + + $script:mockInstanceName = 'DSCSQLTEST' + $script:mockServerName = Get-ComputerName + } + + Context 'When using parameter set ByServerName' { + Context 'When getting startup parameters with default parameters' { + It 'Should return a StartupParameters object for the test instance' { + $result = Get-SqlDscStartupParameter -InstanceName $script:mockInstanceName -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result | Should -BeOfType (InModuleScope -ModuleName $script:moduleName -ScriptBlock { [StartupParameters] }) + $result.DataFilePath | Should -Not -BeNullOrEmpty + $result.LogFilePath | Should -Not -BeNullOrEmpty + $result.ErrorLogPath | Should -Not -BeNullOrEmpty + } + } + + Context 'When getting startup parameters for a specific instance' { + It 'Should return a StartupParameters object for the specified instance' { + $result = Get-SqlDscStartupParameter -ServerName $script:mockServerName -InstanceName $script:mockInstanceName -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result | Should -BeOfType (InModuleScope -ModuleName $script:moduleName -ScriptBlock { [StartupParameters] }) + $result.DataFilePath | Should -Not -BeNullOrEmpty + $result.LogFilePath | Should -Not -BeNullOrEmpty + $result.ErrorLogPath | Should -Not -BeNullOrEmpty + } + } + + Context 'When getting startup parameters for a specific server name' { + It 'Should return a StartupParameters object for the specified server' { + $result = Get-SqlDscStartupParameter -ServerName $script:mockServerName -InstanceName $script:mockInstanceName -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result | Should -BeOfType (InModuleScope -ModuleName $script:moduleName -ScriptBlock { [StartupParameters] }) + $result.DataFilePath | Should -Not -BeNullOrEmpty + $result.LogFilePath | Should -Not -BeNullOrEmpty + $result.ErrorLogPath | Should -Not -BeNullOrEmpty + } + } + + Context 'When getting startup parameters for a non-existent instance' { + It 'Should throw an error when the instance does not exist' { + { + Get-SqlDscStartupParameter -ServerName $script:mockServerName -InstanceName 'NonExistentInstance' -ErrorAction 'Stop' + } | Should -Throw -ErrorId 'GSDSP0001,Get-SqlDscStartupParameter' + } + } + } + + Context 'When using parameter set ByServiceObject' { + BeforeAll { + $script:serviceObject = Get-SqlDscManagedComputerService -ServerName $script:mockServerName -InstanceName $script:mockInstanceName -ServiceType 'DatabaseEngine' -ErrorAction 'Stop' + } + + Context 'When getting startup parameters using a service object' { + It 'Should return a StartupParameters object for the service object' { + $result = Get-SqlDscStartupParameter -ServiceObject $script:serviceObject -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result | Should -BeOfType (InModuleScope -ModuleName $script:moduleName -ScriptBlock { [StartupParameters] }) + $result.DataFilePath | Should -Not -BeNullOrEmpty + $result.LogFilePath | Should -Not -BeNullOrEmpty + $result.ErrorLogPath | Should -Not -BeNullOrEmpty + } + } + + Context 'When getting startup parameters using pipeline input' { + It 'Should accept ServiceObject from pipeline and return StartupParameters' { + $result = $script:serviceObject | Get-SqlDscStartupParameter -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result | Should -BeOfType (InModuleScope -ModuleName $script:moduleName -ScriptBlock { [StartupParameters] }) + $result.DataFilePath | Should -Not -BeNullOrEmpty + $result.LogFilePath | Should -Not -BeNullOrEmpty + $result.ErrorLogPath | Should -Not -BeNullOrEmpty + } + } + + Context 'When passing wrong service type' { + BeforeAll { + # Get a non-DatabaseEngine service for testing + $script:wrongServiceObject = Get-SqlDscManagedComputerService -ServerName $script:mockServerName -InstanceName $script:mockInstanceName -ServiceType 'SqlServerAgent' -ErrorAction 'Stop' + } + + It 'Should throw an error when the service type is not DatabaseEngine' { + { + Get-SqlDscStartupParameter -ServiceObject $script:wrongServiceObject -ErrorAction 'Stop' + } | Should -Throw + } + } + } + + Context 'When validating output properties' { + BeforeAll { + $script:result = Get-SqlDscStartupParameter -ServerName $script:mockServerName -InstanceName $script:mockInstanceName -ErrorAction 'Stop' + } + + It 'Should return an object with expected DataFilePath property' { + $script:result.DataFilePath | Should -Not -BeNullOrEmpty + $script:result.DataFilePath | Should -BeOfType ([System.String]) + $script:result.DataFilePath | Should -Match '\.mdf$' + } + + It 'Should return an object with expected LogFilePath property' { + $script:result.LogFilePath | Should -Not -BeNullOrEmpty + $script:result.LogFilePath | Should -BeOfType ([System.String]) + $script:result.LogFilePath | Should -Match '\.ldf$' + } + + It 'Should return an object with expected ErrorLogPath property' { + $script:result.ErrorLogPath | Should -Not -BeNullOrEmpty + $script:result.ErrorLogPath | Should -BeOfType ([System.String]) + $script:result.ErrorLogPath | Should -Match 'ERRORLOG$' + } + + It 'Should return TraceFlag property as expected type' { + # TraceFlag can be null, a single UInt32, or an array + if ($null -ne $script:result.TraceFlag) { + # Check if it's either a single UInt32 or UInt32 array + ($script:result.TraceFlag -is [System.UInt32]) -or ($script:result.TraceFlag -is [System.UInt32[]]) | Should -BeTrue -Because 'TraceFlag can be a single value or array depending on how many flags are set' + } else { + $script:result.TraceFlag | Should -BeNullOrEmpty -Because 'TraceFlag can be empty/null if no trace flags are set' + } + } + + It 'Should return InternalTraceFlag property as expected type' { + # InternalTraceFlag can be null, a single UInt32, or an array + if ($null -ne $script:result.InternalTraceFlag) { + # Check if it's either a single UInt32 or UInt32 array + ($script:result.InternalTraceFlag -is [System.UInt32]) -or ($script:result.InternalTraceFlag -is [System.UInt32[]]) | Should -BeTrue -Because 'InternalTraceFlag can be a single value or array depending on how many flags are set' + } else { + $script:result.InternalTraceFlag | Should -BeNullOrEmpty -Because 'InternalTraceFlag can be empty/null if no internal trace flags are set' + } + } + } + + Context 'When comparing results from different parameter sets' { + It 'Should return the same results for ByServerName and ByServiceObject parameter sets' { + $resultByServerName = Get-SqlDscStartupParameter -ServerName $script:mockServerName -InstanceName $script:mockInstanceName -ErrorAction 'Stop' + + $serviceObject = Get-SqlDscManagedComputerService -ServerName $script:mockServerName -InstanceName $script:mockInstanceName -ServiceType 'DatabaseEngine' -ErrorAction 'Stop' + $resultByServiceObject = Get-SqlDscStartupParameter -ServiceObject $serviceObject -ErrorAction 'Stop' + + $resultByServerName.DataFilePath | Should -Be $resultByServiceObject.DataFilePath + $resultByServerName.LogFilePath | Should -Be $resultByServiceObject.LogFilePath + $resultByServerName.ErrorLogPath | Should -Be $resultByServiceObject.ErrorLogPath + $resultByServerName.TraceFlag | Should -Be $resultByServiceObject.TraceFlag + $resultByServerName.InternalTraceFlag | Should -Be $resultByServiceObject.InternalTraceFlag + } + } +} diff --git a/tests/Integration/Commands/README.md b/tests/Integration/Commands/README.md index 14f3c07239..d597e21b70 100644 --- a/tests/Integration/Commands/README.md +++ b/tests/Integration/Commands/README.md @@ -70,6 +70,7 @@ Test-SqlDscIsLogin | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTES Test-SqlDscIsLoginEnabled | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | - New-SqlDscRole | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | SqlDscIntegrationTestRole_Persistent role Get-SqlDscRole | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | - +Get-SqlDscStartupParameter | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | - Test-SqlDscIsRole | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | - Test-SqlDscIsDatabasePrincipal | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | Test database and database principals Grant-SqlDscServerPermission | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | Grants CreateEndpoint permission to role diff --git a/tests/Unit/Public/Get-SqlDscStartupParameter.Tests.ps1 b/tests/Unit/Public/Get-SqlDscStartupParameter.Tests.ps1 index 11d59440f6..fa61756aba 100644 --- a/tests/Unit/Public/Get-SqlDscStartupParameter.Tests.ps1 +++ b/tests/Unit/Public/Get-SqlDscStartupParameter.Tests.ps1 @@ -251,6 +251,26 @@ Describe 'Get-SqlDscStartupParameter' -Tag 'Public' { Should -Invoke -CommandName Get-SqlDscManagedComputerService -Exactly -Times 0 -Scope It } } + + Context 'When passing a service object via pipeline' { + It 'Should accept ServiceObject from pipeline and return correct results' { + $result = $mockServiceObject | Get-SqlDscStartupParameter + + Should -ActualValue $result -BeOfType (InModuleScope -ScriptBlock { [StartupParameters] }) + + Should -ActualValue $result.TraceFlag -BeOfType 'System.UInt32[]' + Should -ActualValue $result.DataFilePath -BeOfType 'System.String[]' + Should -ActualValue $result.LogFilePath -BeOfType 'System.String[]' + Should -ActualValue $result.ErrorLogPath -BeOfType 'System.String[]' + + $result.DataFilePath | Should -Be 'C:\Program Files\Microsoft SQL Server\MSSQL16.SQL2022\MSSQL\DATA\master.mdf' + $result.LogFilePath | Should -Be 'C:\Program Files\Microsoft SQL Server\MSSQL16.SQL2022\MSSQL\DATA\log.ldf' + $result.ErrorLogPath | Should -Be 'C:\Program Files\Microsoft SQL Server\MSSQL16.SQL2022\MSSQL\Log\ERRORLOG' + $result.TraceFlag | Should -BeNullOrEmpty + + Should -Invoke -CommandName Get-SqlDscManagedComputerService -Exactly -Times 0 -Scope It + } + } } Context 'When one trace flag exist' {