diff --git a/.github/instructions/dsc-community-style-guidelines-powershell.instructions.md b/.github/instructions/dsc-community-style-guidelines-powershell.instructions.md index aadddb45ba..62db458d43 100644 --- a/.github/instructions/dsc-community-style-guidelines-powershell.instructions.md +++ b/.github/instructions/dsc-community-style-guidelines-powershell.instructions.md @@ -82,7 +82,7 @@ applyTo: "**/*.ps?(m|d)1" - `$PSCmdlet.ShouldProcess` must use required pattern - 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 previous value after +- Set `$ErrorActionPreference = 'Stop'` before commands using `-ErrorAction 'Stop'`; restore previous value directly after invocation (do not use try-catch-finally) ## Output streams diff --git a/CHANGELOG.md b/CHANGELOG.md index 3755977620..adf537753e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -118,6 +118,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added private function `Get-CommandParameter` to filter command parameters by excluding specified parameter names and common parameters, providing a reusable way to determine settable properties on objects. +- `Get-SqlDscServerProtocolName` + - New public command for SQL Server protocol name mappings with support + for protocol name, display name, and short name parameter sets. +- `Get-SqlDscManagedComputerInstance` + - New public command for retrieving SQL Server managed computer instance + information with pipeline support. +- `Get-SqlDscServerProtocol` + - Enhanced to support multiple parameter sets including pipeline input + from managed computer and instance objects. + - Enhanced to optionally return all protocols when ProtocolName parameter + is not specified. ### Changed diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ee79bd15d0..b998996ba3 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -296,6 +296,9 @@ stages: 'tests/Integration/Commands/Get-SqlDscConfigurationOption.Integration.Tests.ps1' 'tests/Integration/Commands/Set-SqlDscConfigurationOption.Integration.Tests.ps1' 'tests/Integration/Commands/Test-SqlDscConfigurationOption.Integration.Tests.ps1' + 'tests/Integration/Commands/Get-SqlDscManagedComputerInstance.Integration.Tests.ps1' + 'tests/Integration/Commands/Get-SqlDscServerProtocolName.Integration.Tests.ps1' + 'tests/Integration/Commands/Get-SqlDscServerProtocol.Integration.Tests.ps1' 'tests/Integration/Commands/Disable-SqlDscLogin.Integration.Tests.ps1' 'tests/Integration/Commands/Enable-SqlDscLogin.Integration.Tests.ps1' 'tests/Integration/Commands/Test-SqlDscIsLoginEnabled.Integration.Tests.ps1' diff --git a/source/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 b/source/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 index accb5c0c89..cbea18321b 100644 --- a/source/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 +++ b/source/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 @@ -1980,6 +1980,9 @@ function Find-ExceptionByNumber ServerProtocol. The property DisplayName could potentially be localized while the property Name must be exactly like it is returned by the class ServerProtocol, with the correct casing. + + The Get-ProtocolNameProperties function is deprecated and should be removed + in the future when existing code has moved to new public commands. #> function Get-ProtocolNameProperties { diff --git a/source/Public/Get-SqlDscManagedComputerInstance.ps1 b/source/Public/Get-SqlDscManagedComputerInstance.ps1 new file mode 100644 index 0000000000..05b75739e0 --- /dev/null +++ b/source/Public/Get-SqlDscManagedComputerInstance.ps1 @@ -0,0 +1,127 @@ +<# + .SYNOPSIS + Returns managed computer server instance information. + + .DESCRIPTION + Returns managed computer server instance information for a SQL Server instance + using SMO (SQL Server Management Objects). The command can retrieve a specific + instance or all instances on a managed computer. + + .PARAMETER ServerName + Specifies the name of the server where the SQL Server instance is running. + Defaults to the local computer name. + + .PARAMETER InstanceName + Specifies the name of the SQL Server instance to retrieve. + If not specified, all instances are returned. + + .PARAMETER ManagedComputerObject + Specifies a managed computer object from which to retrieve server instances. + This parameter accepts pipeline input from Get-SqlDscManagedComputer. + + .EXAMPLE + Get-SqlDscManagedComputerInstance -InstanceName 'MSSQLSERVER' + + Returns the default SQL Server instance information from the local computer. + + .EXAMPLE + Get-SqlDscManagedComputerInstance -ServerName 'MyServer' -InstanceName 'MyInstance' + + Returns the MyInstance SQL Server instance information from the MyServer computer. + + .EXAMPLE + Get-SqlDscManagedComputerInstance -ServerName 'MyServer' + + Returns all SQL Server instances information from the MyServer computer. + + .EXAMPLE + Get-SqlDscManagedComputer -ServerName 'MyServer' | Get-SqlDscManagedComputerInstance -InstanceName 'MyInstance' + + Uses pipeline input to retrieve a specific instance from a managed computer object. + + .INPUTS + Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer + + A managed computer object can be piped to this command. + + .OUTPUTS + Microsoft.SqlServer.Management.Smo.Wmi.ServerInstance + + Returns server instance objects from SMO (SQL Server Management Objects). + + .NOTES + This command uses SMO (SQL Server Management Objects) to retrieve server + instance information from the specified managed computer. +#> +function Get-SqlDscManagedComputerInstance +{ + [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 = 'ByServerName')] + [OutputType([Microsoft.SqlServer.Management.Smo.Wmi.ServerInstance])] + param + ( + [Parameter(ParameterSetName = 'ByServerName')] + [ValidateNotNullOrEmpty()] + [System.String] + $ServerName = (Get-ComputerName), + + [Parameter(ParameterSetName = 'ByServerName')] + [Parameter(ParameterSetName = 'ByManagedComputerObject')] + [ValidateNotNullOrEmpty()] + [System.String] + $InstanceName, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByManagedComputerObject')] + [Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer] + $ManagedComputerObject + ) + + process + { + if ($PSCmdlet.ParameterSetName -eq 'ByServerName') + { + Write-Verbose -Message ( + $script:localizedData.ManagedComputerInstance_GetFromServer -f $ServerName + ) + + $ManagedComputerObject = Get-SqlDscManagedComputer -ServerName $ServerName + } + else + { + Write-Verbose -Message ( + $script:localizedData.ManagedComputerInstance_GetFromObject + ) + } + + if ($PSBoundParameters.ContainsKey('InstanceName')) + { + Write-Verbose -Message ( + $script:localizedData.ManagedComputerInstance_GetSpecificInstance -f $InstanceName + ) + + $serverInstance = $ManagedComputerObject.ServerInstances[$InstanceName] + + if (-not $serverInstance) + { + $errorMessage = $script:localizedData.ManagedComputerInstance_InstanceNotFound -f $InstanceName, $ManagedComputerObject.Name + $errorRecord = [System.Management.Automation.ErrorRecord]::new( + [System.InvalidOperationException]::new($errorMessage), + 'SqlServerInstanceNotFound', + [System.Management.Automation.ErrorCategory]::ObjectNotFound, + $InstanceName + ) + $PSCmdlet.ThrowTerminatingError($errorRecord) + } + + return $serverInstance + } + else + { + Write-Verbose -Message ( + $script:localizedData.ManagedComputerInstance_GetAllInstances + ) + + return $ManagedComputerObject.ServerInstances + } + } +} diff --git a/source/Public/Get-SqlDscServerProtocol.ps1 b/source/Public/Get-SqlDscServerProtocol.ps1 new file mode 100644 index 0000000000..faa2825d50 --- /dev/null +++ b/source/Public/Get-SqlDscServerProtocol.ps1 @@ -0,0 +1,202 @@ +<# + .SYNOPSIS + Returns server protocol information for a SQL Server instance. + + .DESCRIPTION + Returns server protocol information for a SQL Server instance using + SMO (SQL Server Management Objects). The command supports getting + information for TcpIp, NamedPipes, and SharedMemory protocols. + If no protocol is specified, all protocols are returned. + + .PARAMETER ServerName + Specifies the name of the server where the SQL Server instance is running. + Defaults to the local computer name. + + .PARAMETER InstanceName + Specifies the name of the SQL Server instance for which to return protocol + information. + + .PARAMETER ProtocolName + Specifies the name of the network protocol to return information for. + Valid values are 'TcpIp', 'NamedPipes', and 'SharedMemory'. + If not specified, all protocols are returned. + + .PARAMETER ManagedComputerObject + Specifies a managed computer object from which to retrieve server protocol + information. This parameter accepts pipeline input from Get-SqlDscManagedComputer. + + .PARAMETER ManagedComputerInstanceObject + Specifies a managed computer instance object from which to retrieve server + protocol information. This parameter accepts pipeline input from + Get-SqlDscManagedComputerInstance. + + .EXAMPLE + Get-SqlDscServerProtocol -InstanceName 'MSSQLSERVER' -ProtocolName 'TcpIp' + + Returns TcpIp protocol information for the default SQL Server instance + on the local computer. + + .EXAMPLE + Get-SqlDscServerProtocol -ServerName 'MyServer' -InstanceName 'MyInstance' -ProtocolName 'NamedPipes' + + Returns NamedPipes protocol information for the MyInstance SQL Server + instance on the MyServer computer. + + .EXAMPLE + Get-SqlDscServerProtocol -InstanceName 'MSSQLSERVER' + + Returns all protocol information for the default SQL Server instance + on the local computer. + + .EXAMPLE + Get-SqlDscManagedComputer -ServerName 'MyServer' | Get-SqlDscServerProtocol -InstanceName 'MyInstance' + + Uses pipeline input from Get-SqlDscManagedComputer to retrieve all protocols + for the specified instance. + + .EXAMPLE + Get-SqlDscManagedComputerInstance -InstanceName 'MyInstance' | Get-SqlDscServerProtocol -ProtocolName 'TcpIp' + + Uses pipeline input from Get-SqlDscManagedComputerInstance to retrieve TcpIp + protocol information. + + .INPUTS + Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer + + A managed computer object can be piped to this command. + + Microsoft.SqlServer.Management.Smo.Wmi.ServerInstance + + A server instance object can be piped to this command. + + .OUTPUTS + Microsoft.SqlServer.Management.Smo.Wmi.ServerProtocol + + Returns server protocol objects from SMO (SQL Server Management Objects). +#> +function Get-SqlDscServerProtocol +{ + [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 = 'ByServerName')] + [OutputType([Microsoft.SqlServer.Management.Smo.Wmi.ServerProtocol])] + param + ( + [Parameter(ParameterSetName = 'ByServerName')] + [ValidateNotNullOrEmpty()] + [System.String] + $ServerName, + + [Parameter(Mandatory = $true, ParameterSetName = 'ByServerName')] + [Parameter(Mandatory = $true, ParameterSetName = 'ByManagedComputerObject')] + [ValidateNotNullOrEmpty()] + [System.String] + $InstanceName, + + [Parameter(ParameterSetName = 'ByServerName')] + [Parameter(ParameterSetName = 'ByManagedComputerObject')] + [Parameter(ParameterSetName = 'ByManagedComputerInstanceObject')] + [ValidateSet('TcpIp', 'NamedPipes', 'SharedMemory')] + [System.String] + $ProtocolName, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByManagedComputerObject')] + [Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer] + $ManagedComputerObject, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByManagedComputerInstanceObject')] + [Microsoft.SqlServer.Management.Smo.Wmi.ServerInstance] + $ManagedComputerInstanceObject + ) + + process + { + $previousErrorActionPreference = $ErrorActionPreference + $ErrorActionPreference = 'Stop' + + # Set default value for ServerName if not provided + if (-not $PSBoundParameters.ContainsKey('ServerName')) + { + $ServerName = Get-ComputerName -ErrorAction 'Stop' + } + + if ($PSBoundParameters.ContainsKey('ProtocolName')) + { + Write-Verbose -Message ( + $script:localizedData.ServerProtocol_GetState -f $ProtocolName, $InstanceName, $ServerName + ) + } + else + { + Write-Verbose -Message ( + $script:localizedData.ServerProtocol_GetAllProtocols -f $InstanceName, $ServerName + ) + } + + switch ($PSCmdlet.ParameterSetName) + { + 'ByServerName' + { + $serverInstance = Get-SqlDscManagedComputerInstance -ServerName $ServerName -InstanceName $InstanceName -ErrorAction 'Stop' + } + + 'ByManagedComputerObject' + { + $serverInstance = $ManagedComputerObject | Get-SqlDscManagedComputerInstance -InstanceName $InstanceName -ErrorAction 'Stop' + } + + 'ByManagedComputerInstanceObject' + { + $serverInstance = $ManagedComputerInstanceObject + } + } + + if ($PSBoundParameters.ContainsKey('ProtocolName')) + { + # Get specific protocol + $protocolMapping = Get-SqlDscServerProtocolName -ProtocolName $ProtocolName -ErrorAction 'Stop' + + $serverProtocolObject = $serverInstance.ServerProtocols[$protocolMapping.ShortName] + + $ErrorActionPreference = $previousErrorActionPreference + + if (-not $serverProtocolObject) + { + $errorMessage = $script:localizedData.ServerProtocol_ProtocolNotFound -f $protocolMapping.DisplayName, $InstanceName, $serverInstance.Parent.Name + $errorRecord = [System.Management.Automation.ErrorRecord]::new( + [System.InvalidOperationException]::new($errorMessage), + 'SqlServerProtocolNotFound', + [System.Management.Automation.ErrorCategory]::ObjectNotFound, + $ProtocolName + ) + $PSCmdlet.ThrowTerminatingError($errorRecord) + } + + return $serverProtocolObject + } + else + { + # Get all protocols + $allProtocolMappings = Get-SqlDscServerProtocolName -All -ErrorAction 'Stop' + $allServerProtocols = @() + + foreach ($protocolMapping in $allProtocolMappings) + { + $serverProtocolObject = $serverInstance.ServerProtocols[$protocolMapping.ShortName] + + if ($serverProtocolObject) + { + $allServerProtocols += $serverProtocolObject + } + } + + $ErrorActionPreference = $previousErrorActionPreference + + if ($allServerProtocols.Count -eq 0) + { + return $null + } + + return $allServerProtocols + } + } +} diff --git a/source/Public/Get-SqlDscServerProtocolName.ps1 b/source/Public/Get-SqlDscServerProtocolName.ps1 new file mode 100644 index 0000000000..a1a5ed9d8d --- /dev/null +++ b/source/Public/Get-SqlDscServerProtocolName.ps1 @@ -0,0 +1,135 @@ +<# + .SYNOPSIS + Returns SQL Server protocol name mappings. + + .DESCRIPTION + Returns SQL Server protocol name mappings including the protocol name, + display name, and short name. This command provides standardized + protocol naming across different SQL Server management interfaces. + + .PARAMETER ProtocolName + Specifies the protocol name using the ServerProtocols enumeration. + Valid values are 'TcpIp', 'NamedPipes', and 'SharedMemory'. + + .PARAMETER DisplayName + Specifies the protocol display name as shown in SQL Server Configuration Manager. + Valid values are 'TCP/IP', 'Named Pipes', and 'Shared Memory'. + + .PARAMETER ShortName + Specifies the short protocol name as used by SMO. + Valid values are 'Tcp', 'Np', and 'Sm'. + + .PARAMETER All + Returns all available protocol name mappings. + + .EXAMPLE + Get-SqlDscServerProtocolName -ProtocolName 'TcpIp' + + Returns the protocol name mapping for TCP/IP protocol. + + .EXAMPLE + Get-SqlDscServerProtocolName -DisplayName 'Named Pipes' + + Returns the protocol name mapping for Named Pipes protocol using its display name. + + .EXAMPLE + Get-SqlDscServerProtocolName -ShortName 'Sm' + + Returns the protocol name mapping for Shared Memory protocol using its short name. + + .EXAMPLE + Get-SqlDscServerProtocolName -All + + Returns all available protocol name mappings. + + .INPUTS + None + + .OUTPUTS + System.Management.Automation.PSCustomObject + + Returns a PSCustomObject with the properties Name, DisplayName, and ShortName. + + .NOTES + This command replaces the deprecated Get-ProtocolNameProperties function. +#> +function Get-SqlDscServerProtocolName +{ + [CmdletBinding(DefaultParameterSetName = 'All')] + [OutputType([System.Management.Automation.PSCustomObject])] + param + ( + [Parameter(Mandatory = $true, ParameterSetName = 'ByProtocolName')] + [ValidateSet('TcpIp', 'NamedPipes', 'SharedMemory')] + [System.String] + $ProtocolName, + + [Parameter(Mandatory = $true, ParameterSetName = 'ByDisplayName')] + [ValidateSet('TCP/IP', 'Named Pipes', 'Shared Memory')] + [System.String] + $DisplayName, + + [Parameter(Mandatory = $true, ParameterSetName = 'ByShortName')] + [ValidateSet('Tcp', 'Np', 'Sm')] + [System.String] + $ShortName, + + [Parameter(ParameterSetName = 'All')] + [System.Management.Automation.SwitchParameter] + $All + ) + + Write-Verbose -Message ( + $script:localizedData.ServerProtocolName_GetProtocolMappings + ) + + # Define all protocol mappings + $protocolMappings = @( + [PSCustomObject]@{ + Name = 'TcpIp' + DisplayName = 'TCP/IP' + ShortName = 'Tcp' + }, + [PSCustomObject]@{ + Name = 'NamedPipes' + DisplayName = 'Named Pipes' + ShortName = 'Np' + }, + [PSCustomObject]@{ + Name = 'SharedMemory' + DisplayName = 'Shared Memory' + ShortName = 'Sm' + } + ) + + # Check if All parameter is present or if no parameters specified (default case) + if ($All.IsPresent -or $PSCmdlet.ParameterSetName -eq 'All') + { + $result = $protocolMappings + } + else + { + switch ($PSCmdlet.ParameterSetName) + { + 'ByProtocolName' + { + $result = $protocolMappings | Where-Object -FilterScript { $_.Name -eq $ProtocolName } + break + } + + 'ByDisplayName' + { + $result = $protocolMappings | Where-Object -FilterScript { $_.DisplayName -eq $DisplayName } + break + } + + 'ByShortName' + { + $result = $protocolMappings | Where-Object -FilterScript { $_.ShortName -eq $ShortName } + break + } + } + } + + return $result +} diff --git a/source/en-US/SqlServerDsc.strings.psd1 b/source/en-US/SqlServerDsc.strings.psd1 index 629b73d040..aa66f40964 100644 --- a/source/en-US/SqlServerDsc.strings.psd1 +++ b/source/en-US/SqlServerDsc.strings.psd1 @@ -165,6 +165,13 @@ ConvertFrom-StringData @' ## Get-SqlDscManagedComputerService ManagedComputerService_GetState = Returning the managed computer service object(s) for server {0}. + ## Get-SqlDscManagedComputerInstance + ManagedComputerInstance_GetFromServer = Getting managed computer instance information from server '{0}'. + ManagedComputerInstance_GetFromObject = Getting managed computer instance information from managed computer object. + ManagedComputerInstance_GetSpecificInstance = Getting specific server instance '{0}'. + ManagedComputerInstance_GetAllInstances = Getting all server instances. + ManagedComputerInstance_InstanceNotFound = Could not find SQL Server instance '{0}' on server '{1}'. + ## StartupParameters StartupParameters_DebugFoundTraceFlags = {0}: Found the trace flags: {1} StartupParameters_DebugParsingStartupParameters = {0}: Parsing the startup parameters: {1} @@ -288,6 +295,14 @@ ConvertFrom-StringData @' ConvertTo_EditionName_ConvertingEditionId = Converting EditionId '{0}' to Edition name. ConvertTo_EditionName_UnknownEditionId = The EditionId '{0}' is unknown and could not be converted. + ## Get-SqlDscServerProtocol + ServerProtocol_GetState = Getting server protocol '{0}' information for instance '{1}' on server '{2}'. + ServerProtocol_GetAllProtocols = Getting all server protocols for instance '{0}' on server '{1}'. + ServerProtocol_ProtocolNotFound = Could not find server protocol '{0}' for instance '{1}' on server '{2}'. + + ## Get-SqlDscServerProtocolName + ServerProtocolName_GetProtocolMappings = Getting SQL Server protocol name mappings. + ## Assert-SqlDscLogin Assert_Login_CheckingLogin = Checking if the principal '{0}' exists as a login on the instance '{1}'. Assert_Login_LoginMissing = The principal '{0}' does not exist as a login on the instance '{1}'. diff --git a/tests/Integration/Commands/Get-SqlDscManagedComputerInstance.Integration.Tests.ps1 b/tests/Integration/Commands/Get-SqlDscManagedComputerInstance.Integration.Tests.ps1 new file mode 100644 index 0000000000..cbb80fdb15 --- /dev/null +++ b/tests/Integration/Commands/Get-SqlDscManagedComputerInstance.Integration.Tests.ps1 @@ -0,0 +1,144 @@ +[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' +} + +# cSpell: ignore DSCSQLTEST +Describe 'Get-SqlDscManagedComputerInstance' -Tag @('Integration_SQL2017', 'Integration_SQL2019', 'Integration_SQL2022') { + BeforeAll { + Write-Verbose -Message ('Running integration test as user ''{0}''.' -f $env:UserName) -Verbose + + # Starting the named instance SQL Server service prior to running tests. + Start-Service -Name 'MSSQL$DSCSQLTEST' -Verbose -ErrorAction 'Stop' + + $script:mockInstanceName = 'DSCSQLTEST' + $script:mockServerName = Get-ComputerName + } + + AfterAll { + # Stop the named instance SQL Server service to save memory on the build worker. + Stop-Service -Name 'MSSQL$DSCSQLTEST' -Verbose -ErrorAction 'Stop' + } + + Context 'When using parameter set ByServerName' { + Context 'When getting a specific instance' { + It 'Should return the correct server instance' { + $result = Get-SqlDscManagedComputerInstance -ServerName $script:mockServerName -InstanceName $script:mockInstanceName -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be $script:mockInstanceName + $result.Parent.Name | Should -Be $script:mockServerName + } + + It 'Should throw when the instance does not exist' { + { + Get-SqlDscManagedComputerInstance -ServerName $script:mockServerName -InstanceName 'NonExistentInstance' -ErrorAction 'Stop' + } | Should -Throw -ErrorId 'SqlServerInstanceNotFound,Get-SqlDscManagedComputerInstance' + } + } + + Context 'When getting all instances' { + It 'Should return all available instances' { + $result = Get-SqlDscManagedComputerInstance -ServerName $script:mockServerName -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result | Should -BeOfType ([Microsoft.SqlServer.Management.Smo.Wmi.ServerInstance]) + + # Should contain the test instance + $testInstance = $result | Where-Object -FilterScript { $_.Name -eq $script:mockInstanceName } + $testInstance | Should -Not -BeNullOrEmpty + $testInstance.Name | Should -Be $script:mockInstanceName + } + } + + Context 'When using default server name' { + It 'Should use the local computer name when ServerName is not specified' { + $result = Get-SqlDscManagedComputerInstance -InstanceName $script:mockInstanceName -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be $script:mockInstanceName + $result.Parent.Name | Should -Be $script:mockServerName + } + } + } + + Context 'When using parameter set ByManagedComputerObject' { + BeforeAll { + $script:managedComputerObject = Get-SqlDscManagedComputer -ServerName $script:mockServerName -ErrorAction 'Stop' + } + + Context 'When getting a specific instance from managed computer object' { + It 'Should return the correct server instance' { + $result = $script:managedComputerObject | Get-SqlDscManagedComputerInstance -InstanceName $script:mockInstanceName -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be $script:mockInstanceName + $result.Parent.Name | Should -Be $script:mockServerName + } + + It 'Should throw when the instance does not exist' { + { + $script:managedComputerObject | Get-SqlDscManagedComputerInstance -InstanceName 'NonExistentInstance' -ErrorAction 'Stop' + } | Should -Throw -ErrorId 'SqlServerInstanceNotFound,Get-SqlDscManagedComputerInstance' + } + } + + Context 'When getting all instances from managed computer object' { + It 'Should return all available instances' { + $result = $script:managedComputerObject | Get-SqlDscManagedComputerInstance -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result | Should -BeOfType ([Microsoft.SqlServer.Management.Smo.Wmi.ServerInstance]) + + # Should contain the test instance + $testInstance = $result | Where-Object -FilterScript { $_.Name -eq $script:mockInstanceName } + $testInstance | Should -Not -BeNullOrEmpty + $testInstance.Name | Should -Be $script:mockInstanceName + } + } + } + + Context 'When validating SMO object properties' { + It 'Should return objects with correct SMO properties' { + $result = Get-SqlDscManagedComputerInstance -ServerName $script:mockServerName -InstanceName $script:mockInstanceName -ErrorAction 'Stop' + + # Verify it's a proper SMO ServerInstance object + $result | Should -BeOfType ([Microsoft.SqlServer.Management.Smo.Wmi.ServerInstance]) + + # Verify key properties exist + $result.Name | Should -Not -BeNullOrEmpty + $result.Parent | Should -Not -BeNullOrEmpty + $result.Parent.Name | Should -Be $script:mockServerName + + # Verify ServerProtocols collection is accessible + $result.ServerProtocols | Should -Not -BeNullOrEmpty + $result.ServerProtocols.Count | Should -BeGreaterThan 0 + } + } +} diff --git a/tests/Integration/Commands/Get-SqlDscServerProtocol.Integration.Tests.ps1 b/tests/Integration/Commands/Get-SqlDscServerProtocol.Integration.Tests.ps1 new file mode 100644 index 0000000000..b74c45d1ff --- /dev/null +++ b/tests/Integration/Commands/Get-SqlDscServerProtocol.Integration.Tests.ps1 @@ -0,0 +1,235 @@ +[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' +} + +# cSpell: ignore DSCSQLTEST +Describe 'Get-SqlDscServerProtocol' -Tag @('Integration_SQL2017', 'Integration_SQL2019', 'Integration_SQL2022') { + BeforeAll { + Write-Verbose -Message ('Running integration test as user ''{0}''.' -f $env:UserName) -Verbose + + # Starting the named instance SQL Server service prior to running tests. + Start-Service -Name 'MSSQL$DSCSQLTEST' -Verbose -ErrorAction 'Stop' + + $script:mockInstanceName = 'DSCSQLTEST' + $script:mockServerName = Get-ComputerName + } + + AfterAll { + # Stop the named instance SQL Server service to save memory on the build worker. + Stop-Service -Name 'MSSQL$DSCSQLTEST' -Verbose -ErrorAction 'Stop' + } + + Context 'When using parameter set ByServerName' { + Context 'When getting a specific protocol' { + It 'Should return TcpIp protocol information' { + $result = Get-SqlDscServerProtocol -ServerName $script:mockServerName -InstanceName $script:mockInstanceName -ProtocolName 'TcpIp' -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result | Should -BeOfType ([Microsoft.SqlServer.Management.Smo.Wmi.ServerProtocol]) + $result.Name | Should -Be 'Tcp' + $result.DisplayName | Should -Be 'TCP/IP' + $result.Parent.Name | Should -Be $script:mockInstanceName + } + + It 'Should return NamedPipes protocol information' { + $result = Get-SqlDscServerProtocol -ServerName $script:mockServerName -InstanceName $script:mockInstanceName -ProtocolName 'NamedPipes' -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result | Should -BeOfType ([Microsoft.SqlServer.Management.Smo.Wmi.ServerProtocol]) + $result.Name | Should -Be 'Np' + $result.DisplayName | Should -Be 'Named Pipes' + $result.Parent.Name | Should -Be $script:mockInstanceName + } + + It 'Should return SharedMemory protocol information' { + $result = Get-SqlDscServerProtocol -ServerName $script:mockServerName -InstanceName $script:mockInstanceName -ProtocolName 'SharedMemory' -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result | Should -BeOfType ([Microsoft.SqlServer.Management.Smo.Wmi.ServerProtocol]) + $result.Name | Should -Be 'Sm' + $result.DisplayName | Should -Be 'Shared Memory' + $result.Parent.Name | Should -Be $script:mockInstanceName + } + } + + Context 'When getting all protocols' { + It 'Should return all available protocols' { + $result = Get-SqlDscServerProtocol -ServerName $script:mockServerName -InstanceName $script:mockInstanceName -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result | Should -BeOfType ([Microsoft.SqlServer.Management.Smo.Wmi.ServerProtocol]) + $result.Count | Should -BeGreaterThan 0 + + # Should contain the standard protocols + $protocolNames = $result | ForEach-Object -Process { $_.Name } + $protocolNames | Should -Contain 'Tcp' + $protocolNames | Should -Contain 'Np' + $protocolNames | Should -Contain 'Sm' + } + } + + Context 'When using default server name' { + It 'Should use local computer name when ServerName is not specified' { + $result = Get-SqlDscServerProtocol -InstanceName $script:mockInstanceName -ProtocolName 'TcpIp' -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result.Parent.Name | Should -Be $script:mockInstanceName + $result.Parent.Parent.Name | Should -Be $script:mockServerName + } + } + } + + Context 'When using parameter set ByManagedComputerObject' { + BeforeAll { + $script:managedComputerObject = Get-SqlDscManagedComputer -ServerName $script:mockServerName -ErrorAction 'Stop' + } + + Context 'When getting a specific protocol from managed computer object' { + It 'Should return TcpIp protocol information' { + $result = $script:managedComputerObject | Get-SqlDscServerProtocol -InstanceName $script:mockInstanceName -ProtocolName 'TcpIp' -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result | Should -BeOfType ([Microsoft.SqlServer.Management.Smo.Wmi.ServerProtocol]) + $result.Name | Should -Be 'Tcp' + $result.DisplayName | Should -Be 'TCP/IP' + $result.Parent.Name | Should -Be $script:mockInstanceName + } + } + + Context 'When getting all protocols from managed computer object' { + It 'Should return all available protocols' { + $result = $script:managedComputerObject | Get-SqlDscServerProtocol -InstanceName $script:mockInstanceName -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result | Should -BeOfType ([Microsoft.SqlServer.Management.Smo.Wmi.ServerProtocol]) + $result.Count | Should -BeGreaterThan 0 + + # Should contain the standard protocols + $protocolNames = $result | ForEach-Object -Process { $_.Name } + $protocolNames | Should -Contain 'Tcp' + $protocolNames | Should -Contain 'Np' + $protocolNames | Should -Contain 'Sm' + } + } + } + + Context 'When using parameter set ByManagedComputerInstanceObject' { + BeforeAll { + $script:managedComputerInstanceObject = Get-SqlDscManagedComputerInstance -ServerName $script:mockServerName -InstanceName $script:mockInstanceName -ErrorAction 'Stop' + } + + Context 'When getting a specific protocol from managed computer instance object' { + It 'Should return TcpIp protocol information' { + $result = $script:managedComputerInstanceObject | Get-SqlDscServerProtocol -ProtocolName 'TcpIp' -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result | Should -BeOfType ([Microsoft.SqlServer.Management.Smo.Wmi.ServerProtocol]) + $result.Name | Should -Be 'Tcp' + $result.DisplayName | Should -Be 'TCP/IP' + $result.Parent.Name | Should -Be $script:mockInstanceName + } + } + + Context 'When getting all protocols from managed computer instance object' { + It 'Should return all available protocols' { + $result = $script:managedComputerInstanceObject | Get-SqlDscServerProtocol -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result | Should -BeOfType ([Microsoft.SqlServer.Management.Smo.Wmi.ServerProtocol]) + $result.Count | Should -BeGreaterThan 0 + + # Should contain the standard protocols + $protocolNames = $result | ForEach-Object -Process { $_.Name } + $protocolNames | Should -Contain 'Tcp' + $protocolNames | Should -Contain 'Np' + $protocolNames | Should -Contain 'Sm' + } + } + } + + Context 'When validating SMO object properties' { + It 'Should return protocol objects with correct SMO properties' { + $result = Get-SqlDscServerProtocol -ServerName $script:mockServerName -InstanceName $script:mockInstanceName -ProtocolName 'TcpIp' -ErrorAction 'Stop' + + # Verify it's a proper SMO ServerProtocol object + $result | Should -BeOfType ([Microsoft.SqlServer.Management.Smo.Wmi.ServerProtocol]) + + # Verify key properties exist + $result.Name | Should -Not -BeNullOrEmpty + $result.DisplayName | Should -Not -BeNullOrEmpty + $result.Parent | Should -Not -BeNullOrEmpty + $result.Parent.Name | Should -Be $script:mockInstanceName + + # Verify protocol-specific properties are accessible + $result.IsEnabled | Should -Not -BeNullOrEmpty + $result.ProtocolProperties | Should -Not -BeNullOrEmpty + + # Verify IP addresses collection for TCP/IP protocol + if ($result.Name -eq 'Tcp') + { + $result.IPAddresses | Should -Not -BeNullOrEmpty + } + } + + It 'Should return multiple protocol objects when getting all protocols' { + $result = Get-SqlDscServerProtocol -ServerName $script:mockServerName -InstanceName $script:mockInstanceName -ErrorAction 'Stop' + + $result | Should -BeOfType ([Microsoft.SqlServer.Management.Smo.Wmi.ServerProtocol]) + $result.Count | Should -BeGreaterOrEqual 3 + + # Verify each protocol has required properties + foreach ($protocol in $result) + { + $protocol.Name | Should -Not -BeNullOrEmpty + $protocol.DisplayName | Should -Not -BeNullOrEmpty + $protocol.Parent.Name | Should -Be $script:mockInstanceName + $protocol.IsEnabled | Should -Not -BeNullOrEmpty + } + } + } + + Context 'When validating pipeline integration' { + It 'Should work in a pipeline from Get-SqlDscManagedComputer to Get-SqlDscServerProtocol' { + $result = Get-SqlDscManagedComputer -ServerName $script:mockServerName | Get-SqlDscServerProtocol -InstanceName $script:mockInstanceName -ProtocolName 'TcpIp' -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be 'Tcp' + $result.DisplayName | Should -Be 'TCP/IP' + } + + It 'Should work in a pipeline from Get-SqlDscManagedComputerInstance to Get-SqlDscServerProtocol' { + $result = Get-SqlDscManagedComputerInstance -ServerName $script:mockServerName -InstanceName $script:mockInstanceName | Get-SqlDscServerProtocol -ProtocolName 'NamedPipes' -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be 'Np' + $result.DisplayName | Should -Be 'Named Pipes' + } + } +} diff --git a/tests/Integration/Commands/Get-SqlDscServerProtocolName.Integration.Tests.ps1 b/tests/Integration/Commands/Get-SqlDscServerProtocolName.Integration.Tests.ps1 new file mode 100644 index 0000000000..0938a2e153 --- /dev/null +++ b/tests/Integration/Commands/Get-SqlDscServerProtocolName.Integration.Tests.ps1 @@ -0,0 +1,204 @@ +[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 'Get-SqlDscServerProtocolName' -Tag @('Integration_SQL2017', 'Integration_SQL2019', 'Integration_SQL2022') { + BeforeAll { + Write-Verbose -Message ('Running integration test as user ''{0}''.' -f $env:UserName) -Verbose + + # Define expected protocol mappings for validation + $script:expectedProtocolMappings = @{ + 'TcpIp' = @{ + Name = 'TcpIp' + DisplayName = 'TCP/IP' + ShortName = 'Tcp' + } + 'NamedPipes' = @{ + Name = 'NamedPipes' + DisplayName = 'Named Pipes' + ShortName = 'Np' + } + 'SharedMemory' = @{ + Name = 'SharedMemory' + DisplayName = 'Shared Memory' + ShortName = 'Sm' + } + } + } + + Context 'When using parameter set ByProtocolName' { + It 'Should return correct mapping for TcpIp protocol' { + $result = Get-SqlDscServerProtocolName -ProtocolName 'TcpIp' -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be $script:expectedProtocolMappings['TcpIp'].Name + $result.DisplayName | Should -Be $script:expectedProtocolMappings['TcpIp'].DisplayName + $result.ShortName | Should -Be $script:expectedProtocolMappings['TcpIp'].ShortName + } + + It 'Should return correct mapping for NamedPipes protocol' { + $result = Get-SqlDscServerProtocolName -ProtocolName 'NamedPipes' -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be $script:expectedProtocolMappings['NamedPipes'].Name + $result.DisplayName | Should -Be $script:expectedProtocolMappings['NamedPipes'].DisplayName + $result.ShortName | Should -Be $script:expectedProtocolMappings['NamedPipes'].ShortName + } + + It 'Should return correct mapping for SharedMemory protocol' { + $result = Get-SqlDscServerProtocolName -ProtocolName 'SharedMemory' -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be $script:expectedProtocolMappings['SharedMemory'].Name + $result.DisplayName | Should -Be $script:expectedProtocolMappings['SharedMemory'].DisplayName + $result.ShortName | Should -Be $script:expectedProtocolMappings['SharedMemory'].ShortName + } + } + + Context 'When using parameter set ByDisplayName' { + It 'Should return correct mapping for TCP/IP display name' { + $result = Get-SqlDscServerProtocolName -DisplayName 'TCP/IP' -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be $script:expectedProtocolMappings['TcpIp'].Name + $result.DisplayName | Should -Be $script:expectedProtocolMappings['TcpIp'].DisplayName + $result.ShortName | Should -Be $script:expectedProtocolMappings['TcpIp'].ShortName + } + + It 'Should return correct mapping for Named Pipes display name' { + $result = Get-SqlDscServerProtocolName -DisplayName 'Named Pipes' -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be $script:expectedProtocolMappings['NamedPipes'].Name + $result.DisplayName | Should -Be $script:expectedProtocolMappings['NamedPipes'].DisplayName + $result.ShortName | Should -Be $script:expectedProtocolMappings['NamedPipes'].ShortName + } + + It 'Should return correct mapping for Shared Memory display name' { + $result = Get-SqlDscServerProtocolName -DisplayName 'Shared Memory' -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be $script:expectedProtocolMappings['SharedMemory'].Name + $result.DisplayName | Should -Be $script:expectedProtocolMappings['SharedMemory'].DisplayName + $result.ShortName | Should -Be $script:expectedProtocolMappings['SharedMemory'].ShortName + } + } + + Context 'When using parameter set ByShortName' { + It 'Should return correct mapping for Tcp short name' { + $result = Get-SqlDscServerProtocolName -ShortName 'Tcp' -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be $script:expectedProtocolMappings['TcpIp'].Name + $result.DisplayName | Should -Be $script:expectedProtocolMappings['TcpIp'].DisplayName + $result.ShortName | Should -Be $script:expectedProtocolMappings['TcpIp'].ShortName + } + + It 'Should return correct mapping for Np short name' { + $result = Get-SqlDscServerProtocolName -ShortName 'Np' -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be $script:expectedProtocolMappings['NamedPipes'].Name + $result.DisplayName | Should -Be $script:expectedProtocolMappings['NamedPipes'].DisplayName + $result.ShortName | Should -Be $script:expectedProtocolMappings['NamedPipes'].ShortName + } + + It 'Should return correct mapping for Sm short name' { + $result = Get-SqlDscServerProtocolName -ShortName 'Sm' -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be $script:expectedProtocolMappings['SharedMemory'].Name + $result.DisplayName | Should -Be $script:expectedProtocolMappings['SharedMemory'].DisplayName + $result.ShortName | Should -Be $script:expectedProtocolMappings['SharedMemory'].ShortName + } + } + + Context 'When using parameter set All' { + It 'Should return all protocol mappings when -All is specified' { + $result = Get-SqlDscServerProtocolName -All -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result | Should -HaveCount 3 + + # Verify all expected protocols are present + $tcpProtocol = $result | Where-Object -FilterScript { $_.Name -eq 'TcpIp' } + $tcpProtocol | Should -Not -BeNullOrEmpty + $tcpProtocol.DisplayName | Should -Be 'TCP/IP' + $tcpProtocol.ShortName | Should -Be 'Tcp' + + $namedPipesProtocol = $result | Where-Object -FilterScript { $_.Name -eq 'NamedPipes' } + $namedPipesProtocol | Should -Not -BeNullOrEmpty + $namedPipesProtocol.DisplayName | Should -Be 'Named Pipes' + $namedPipesProtocol.ShortName | Should -Be 'Np' + + $sharedMemoryProtocol = $result | Where-Object -FilterScript { $_.Name -eq 'SharedMemory' } + $sharedMemoryProtocol | Should -Not -BeNullOrEmpty + $sharedMemoryProtocol.DisplayName | Should -Be 'Shared Memory' + $sharedMemoryProtocol.ShortName | Should -Be 'Sm' + } + + It 'Should return all protocol mappings when no parameters are specified (default)' { + $result = Get-SqlDscServerProtocolName -ErrorAction 'Stop' + + $result | Should -Not -BeNullOrEmpty + $result | Should -HaveCount 3 + + # Verify all expected protocols are present + $protocolNames = $result | ForEach-Object -Process { $_.Name } + $protocolNames | Should -Contain 'TcpIp' + $protocolNames | Should -Contain 'NamedPipes' + $protocolNames | Should -Contain 'SharedMemory' + } + } + + Context 'When validating output object properties' { + It 'Should return PSCustomObject with correct properties' { + $result = Get-SqlDscServerProtocolName -ProtocolName 'TcpIp' -ErrorAction 'Stop' + + $result | Should -BeOfType ([System.Management.Automation.PSCustomObject]) + $result.PSObject.Properties.Name | Should -Contain 'Name' + $result.PSObject.Properties.Name | Should -Contain 'DisplayName' + $result.PSObject.Properties.Name | Should -Contain 'ShortName' + } + + It 'Should return consistent object types for all parameter sets' { + $resultByProtocolName = Get-SqlDscServerProtocolName -ProtocolName 'TcpIp' -ErrorAction 'Stop' + $resultByDisplayName = Get-SqlDscServerProtocolName -DisplayName 'TCP/IP' -ErrorAction 'Stop' + $resultByShortName = Get-SqlDscServerProtocolName -ShortName 'Tcp' -ErrorAction 'Stop' + + $resultByProtocolName.GetType() | Should -Be $resultByDisplayName.GetType() + $resultByDisplayName.GetType() | Should -Be $resultByShortName.GetType() + + # Verify all return the same data + $resultByProtocolName.Name | Should -Be $resultByDisplayName.Name + $resultByDisplayName.Name | Should -Be $resultByShortName.Name + } + } +} diff --git a/tests/Integration/Commands/README.md b/tests/Integration/Commands/README.md index 4c56a43123..5b480faed5 100644 --- a/tests/Integration/Commands/README.md +++ b/tests/Integration/Commands/README.md @@ -47,6 +47,9 @@ Assert-SqlDscLogin | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTES New-SqlDscLogin | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | IntegrationTestSqlLogin, SqlIntegrationTestGroup login Get-SqlDscLogin | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | - Get-SqlDscConfigurationOption | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | - +Get-SqlDscManagedComputerInstance | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | - +Get-SqlDscServerProtocolName | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | - +Get-SqlDscServerProtocol | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | - Set-SqlDscConfigurationOption | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | - Disable-SqlDscLogin | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | - Enable-SqlDscLogin | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | - diff --git a/tests/Unit/Public/Get-SqlDscManagedComputerInstance.Tests.ps1 b/tests/Unit/Public/Get-SqlDscManagedComputerInstance.Tests.ps1 new file mode 100644 index 0000000000..4522591bb5 --- /dev/null +++ b/tests/Unit/Public/Get-SqlDscManagedComputerInstance.Tests.ps1 @@ -0,0 +1,227 @@ +[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' + + $env:SqlServerDscCI = $true + + Import-Module -Name $script:moduleName -Force -ErrorAction 'Stop' + + # Loading mocked classes + Add-Type -Path (Join-Path -Path (Join-Path -Path $PSScriptRoot -ChildPath '../Stubs') -ChildPath 'SMO.cs') + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:moduleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:moduleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:moduleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $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:moduleName -All | Remove-Module -Force + + Remove-Item -Path 'env:SqlServerDscCI' +} + +Describe 'Get-SqlDscManagedComputerInstance' -Tag 'Public' { + Context 'When testing localized strings' { + It 'Should have localized string for getting instance from server' { + InModuleScope -ScriptBlock { + $script:localizedData.ManagedComputerInstance_GetFromServer | Should -Not -BeNullOrEmpty + } + } + + It 'Should have localized string for getting instance from object' { + InModuleScope -ScriptBlock { + $script:localizedData.ManagedComputerInstance_GetFromObject | Should -Not -BeNullOrEmpty + } + } + + It 'Should have localized string for getting specific instance' { + InModuleScope -ScriptBlock { + $script:localizedData.ManagedComputerInstance_GetSpecificInstance | Should -Not -BeNullOrEmpty + } + } + + It 'Should have localized string for getting all instances' { + InModuleScope -ScriptBlock { + $script:localizedData.ManagedComputerInstance_GetAllInstances | Should -Not -BeNullOrEmpty + } + } + + It 'Should have localized string for instance not found error' { + InModuleScope -ScriptBlock { + $script:localizedData.ManagedComputerInstance_InstanceNotFound | Should -Not -BeNullOrEmpty + } + } + } + + Context 'When getting server instance by server name' { + BeforeAll { + Mock -CommandName Get-ComputerName -MockWith { + return 'LocalServer' + } + + Mock -CommandName Get-SqlDscManagedComputer -MockWith { + $mockServerInstance1 = [Microsoft.SqlServer.Management.Smo.Wmi.ServerInstance]::CreateTypeInstance() + $mockServerInstance1.Name = 'MSSQLSERVER' + + $mockServerInstance2 = [Microsoft.SqlServer.Management.Smo.Wmi.ServerInstance]::CreateTypeInstance() + $mockServerInstance2.Name = 'SQL2019' + + $mockServerInstanceCollection = [Microsoft.SqlServer.Management.Smo.Wmi.ServerInstanceCollection]::CreateTypeInstance() + $mockServerInstanceCollection['MSSQLSERVER'] = $mockServerInstance1 + $mockServerInstanceCollection['SQL2019'] = $mockServerInstance2 + + $mockManagedComputerObject = [Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer]::new() + $mockManagedComputerObject.Name = 'TestServer' + $mockManagedComputerObject.ServerInstances = $mockServerInstanceCollection + + return $mockManagedComputerObject + } + } + + It 'Should use local computer name when ServerName is not provided' { + $result = Get-SqlDscManagedComputerInstance -InstanceName 'MSSQLSERVER' + + $result | Should -Not -BeNullOrEmpty + $result | Should -BeOfType 'Microsoft.SqlServer.Management.Smo.Wmi.ServerInstance' + Should -Invoke -CommandName Get-ComputerName -Exactly -Times 1 + Should -Invoke -CommandName Get-SqlDscManagedComputer -ParameterFilter { + $ServerName -eq 'LocalServer' + } -Exactly -Times 1 + } + + It 'Should return specific instance when InstanceName is provided' { + $result = Get-SqlDscManagedComputerInstance -ServerName 'TestServer' -InstanceName 'MSSQLSERVER' + + $result | Should -Not -BeNullOrEmpty + $result | Should -BeOfType 'Microsoft.SqlServer.Management.Smo.Wmi.ServerInstance' + Should -Invoke -CommandName Get-SqlDscManagedComputer -Exactly -Times 1 + } + + It 'Should return all instances when InstanceName is not provided' { + $result = Get-SqlDscManagedComputerInstance -ServerName 'TestServer' + + $result | Should -Not -BeNullOrEmpty + $result | Should -HaveCount 2 + Should -Invoke -CommandName Get-SqlDscManagedComputer -Exactly -Times 1 + } + + It 'Should throw terminating error when instance does not exist' { + { Get-SqlDscManagedComputerInstance -ServerName 'TestServer' -InstanceName 'NonExistent' } | Should -Throw -ExpectedMessage '*Could not find SQL Server instance*NonExistent*TestServer*' + } + + It 'Should throw terminating error with correct error record properties when instance does not exist' { + { Get-SqlDscManagedComputerInstance -ServerName 'TestServer' -InstanceName 'NonExistent' } | Should -Throw -ErrorId 'SqlServerInstanceNotFound,Get-SqlDscManagedComputerInstance' + } + } + + Context 'When getting server instance by managed computer object' { + BeforeAll { + $mockServerInstance1 = [Microsoft.SqlServer.Management.Smo.Wmi.ServerInstance]::CreateTypeInstance() + $mockServerInstance1.Name = 'MSSQLSERVER' + + $mockServerInstance2 = [Microsoft.SqlServer.Management.Smo.Wmi.ServerInstance]::CreateTypeInstance() + $mockServerInstance2.Name = 'SQL2019' + + $mockServerInstanceCollection = [Microsoft.SqlServer.Management.Smo.Wmi.ServerInstanceCollection]::CreateTypeInstance() + $mockServerInstanceCollection['MSSQLSERVER'] = $mockServerInstance1 + $mockServerInstanceCollection['SQL2019'] = $mockServerInstance2 + + $mockManagedComputerObject = [Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer]::new() + $mockManagedComputerObject.Name = 'TestServer' + $mockManagedComputerObject.ServerInstances = $mockServerInstanceCollection + } + + It 'Should return specific instance from managed computer object' { + $result = $mockManagedComputerObject | Get-SqlDscManagedComputerInstance -InstanceName 'MSSQLSERVER' + + $result | Should -Not -BeNullOrEmpty + $result | Should -BeOfType 'Microsoft.SqlServer.Management.Smo.Wmi.ServerInstance' + } + + It 'Should return all instances from managed computer object' { + $result = $mockManagedComputerObject | Get-SqlDscManagedComputerInstance + + $result | Should -Not -BeNullOrEmpty + } + + It 'Should throw terminating error when instance does not exist using pipeline' { + { $mockManagedComputerObject | Get-SqlDscManagedComputerInstance -InstanceName 'NonExistent' } | Should -Throw -ExpectedMessage '*Could not find SQL Server instance*NonExistent*TestServer*' + } + } + + Context 'When parameter sets are used correctly' { + It 'Should have the correct parameters in parameter set ByServerName' -ForEach @( + @{ + ExpectedParameterSetName = 'ByServerName' + ExpectedParameters = '[-ServerName ] [-InstanceName ] []' + } + ) { + $result = (Get-Command -Name 'Get-SqlDscManagedComputerInstance').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 + } + + It 'Should have the correct parameters in parameter set ByManagedComputerObject' -ForEach @( + @{ + ExpectedParameterSetName = 'ByManagedComputerObject' + ExpectedParameters = '-ManagedComputerObject [-InstanceName ] []' + } + ) { + $result = (Get-Command -Name 'Get-SqlDscManagedComputerInstance').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 parameter properties are validated' { + It 'Should have ManagedComputerObject as a pipeline parameter' { + $parameterInfo = (Get-Command -Name 'Get-SqlDscManagedComputerInstance').Parameters['ManagedComputerObject'] + $parameterInfo.Attributes.ValueFromPipeline | Should -BeTrue + } + + It 'Should have ManagedComputerObject as a mandatory parameter in ByManagedComputerObject parameter set' { + $parameterInfo = (Get-Command -Name 'Get-SqlDscManagedComputerInstance').Parameters['ManagedComputerObject'] + $parameterSetAttribute = $parameterInfo.Attributes | Where-Object -FilterScript { $_.ParameterSetName -eq 'ByManagedComputerObject' } + $parameterSetAttribute.Mandatory | Should -BeTrue + } + } +} diff --git a/tests/Unit/Public/Get-SqlDscServerProtocol.Tests.ps1 b/tests/Unit/Public/Get-SqlDscServerProtocol.Tests.ps1 new file mode 100644 index 0000000000..ba6b4bd996 --- /dev/null +++ b/tests/Unit/Public/Get-SqlDscServerProtocol.Tests.ps1 @@ -0,0 +1,426 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +param () + +BeforeDiscovery { + try + { + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies has 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 has 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' + + $env:SqlServerDscCI = $true + + Import-Module -Name $script:moduleName + + # Loading mocked classes + Add-Type -Path (Join-Path -Path (Join-Path -Path $PSScriptRoot -ChildPath '../Stubs') -ChildPath 'SMO.cs') + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:moduleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:moduleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:moduleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $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:moduleName -All | Remove-Module -Force + + Remove-Item -Path 'env:SqlServerDscCI' +} + +Describe 'Get-SqlDscServerProtocol' -Tag 'Public' { + Context 'When testing localized strings' { + It 'Should have localized string for getting specific protocol state' { + InModuleScope -ScriptBlock { + $script:localizedData.ServerProtocol_GetState | Should -Not -BeNullOrEmpty + } + } + + It 'Should have localized string for getting all protocols' { + InModuleScope -ScriptBlock { + $script:localizedData.ServerProtocol_GetAllProtocols | Should -Not -BeNullOrEmpty + } + } + + It 'Should have localized string for protocol not found error' { + InModuleScope -ScriptBlock { + $script:localizedData.ServerProtocol_ProtocolNotFound | Should -Not -BeNullOrEmpty + } + } + } + + Context 'When getting server protocol information' { + BeforeAll { + # Mock the Get-SqlDscManagedComputerInstance command + Mock -CommandName Get-SqlDscManagedComputerInstance -MockWith { + $mockServerInstance = [PSCustomObject]@{ + ServerProtocols = @{ + 'Tcp' = [PSCustomObject]@{ + Name = 'Tcp' + DisplayName = 'TCP/IP' + IsEnabled = $true + ProtocolProperties = @{ + ListenOnAllIPs = $true + KeepAlive = 30000 + } + } + 'Np' = [PSCustomObject]@{ + Name = 'Np' + DisplayName = 'Named Pipes' + IsEnabled = $false + } + 'Sm' = [PSCustomObject]@{ + Name = 'Sm' + DisplayName = 'Shared Memory' + IsEnabled = $true + } + } + Parent = [PSCustomObject]@{ + Name = 'TestServer' + } + } + + return $mockServerInstance + } + + # Mock Get-SqlDscServerProtocolName since it's the new command + Mock -CommandName Get-SqlDscServerProtocolName -MockWith { + param($ProtocolName, $All) + + if ($All) + { + return @( + [PSCustomObject]@{ Name = 'TcpIp'; DisplayName = 'TCP/IP'; ShortName = 'Tcp' }, + [PSCustomObject]@{ Name = 'NamedPipes'; DisplayName = 'Named Pipes'; ShortName = 'Np' }, + [PSCustomObject]@{ Name = 'SharedMemory'; DisplayName = 'Shared Memory'; ShortName = 'Sm' } + ) + } + else + { + switch ($ProtocolName) + { + 'TcpIp' { return [PSCustomObject]@{ Name = 'TcpIp'; DisplayName = 'TCP/IP'; ShortName = 'Tcp' } } + 'NamedPipes' { return [PSCustomObject]@{ Name = 'NamedPipes'; DisplayName = 'Named Pipes'; ShortName = 'Np' } } + 'SharedMemory' { return [PSCustomObject]@{ Name = 'SharedMemory'; DisplayName = 'Shared Memory'; ShortName = 'Sm' } } + } + } + } + } + + It 'Should return TcpIp protocol information' { + $result = Get-SqlDscServerProtocol -InstanceName 'MSSQLSERVER' -ProtocolName 'TcpIp' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be 'Tcp' + $result.DisplayName | Should -Be 'TCP/IP' + $result.IsEnabled | Should -Be $true + + Should -Invoke -CommandName Get-SqlDscManagedComputerInstance -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-SqlDscServerProtocolName -ParameterFilter { + $ProtocolName -eq 'TcpIp' + } -Exactly -Times 1 -Scope It + } + + It 'Should return NamedPipes protocol information' { + $result = Get-SqlDscServerProtocol -InstanceName 'MSSQLSERVER' -ProtocolName 'NamedPipes' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be 'Np' + $result.DisplayName | Should -Be 'Named Pipes' + $result.IsEnabled | Should -Be $false + + Should -Invoke -CommandName Get-SqlDscManagedComputerInstance -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-SqlDscServerProtocolName -ParameterFilter { + $ProtocolName -eq 'NamedPipes' + } -Exactly -Times 1 -Scope It + } + + It 'Should return SharedMemory protocol information' { + $result = Get-SqlDscServerProtocol -InstanceName 'MSSQLSERVER' -ProtocolName 'SharedMemory' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be 'Sm' + $result.DisplayName | Should -Be 'Shared Memory' + $result.IsEnabled | Should -Be $true + + Should -Invoke -CommandName Get-SqlDscManagedComputerInstance -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-SqlDscServerProtocolName -ParameterFilter { + $ProtocolName -eq 'SharedMemory' + } -Exactly -Times 1 -Scope It + } + + It 'Should return all protocols when ProtocolName is not specified' { + $result = Get-SqlDscServerProtocol -InstanceName 'MSSQLSERVER' + + $result | Should -Not -BeNullOrEmpty + $result | Should -HaveCount 3 + + $tcpProtocol = $result | Where-Object -FilterScript { $_.Name -eq 'Tcp' } + $tcpProtocol | Should -Not -BeNullOrEmpty + $tcpProtocol.DisplayName | Should -Be 'TCP/IP' + + $npProtocol = $result | Where-Object -FilterScript { $_.Name -eq 'Np' } + $npProtocol | Should -Not -BeNullOrEmpty + $npProtocol.DisplayName | Should -Be 'Named Pipes' + + $smProtocol = $result | Where-Object -FilterScript { $_.Name -eq 'Sm' } + $smProtocol | Should -Not -BeNullOrEmpty + $smProtocol.DisplayName | Should -Be 'Shared Memory' + + Should -Invoke -CommandName Get-SqlDscManagedComputerInstance -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-SqlDscServerProtocolName -ParameterFilter { + $All -eq $true + } -Exactly -Times 1 -Scope It + } + + It 'Should use specified server name' { + Mock -CommandName Get-ComputerName -MockWith { + return 'LocalComputer' + } + + $null = Get-SqlDscServerProtocol -ServerName 'TestServer' -InstanceName 'MSSQLSERVER' -ProtocolName 'TcpIp' + + Should -Invoke -CommandName Get-SqlDscManagedComputerInstance -ParameterFilter { + $ServerName -eq 'TestServer' + } -Exactly -Times 1 -Scope It + } + + It 'Should use local computer name when ServerName is not provided' { + Mock -CommandName Get-ComputerName -MockWith { + return 'LocalComputer' + } + + $null = Get-SqlDscServerProtocol -InstanceName 'MSSQLSERVER' -ProtocolName 'TcpIp' + + Should -Invoke -CommandName Get-SqlDscManagedComputerInstance -ParameterFilter { + $ServerName -eq 'LocalComputer' + } -Exactly -Times 1 -Scope It + } + + It 'Should work with named instances' { + $result = Get-SqlDscServerProtocol -InstanceName 'SQL2019' -ProtocolName 'TcpIp' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be 'Tcp' + + Should -Invoke -CommandName Get-SqlDscManagedComputerInstance -ParameterFilter { + $InstanceName -eq 'SQL2019' + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using pipeline with managed computer object' { + BeforeAll { + Mock -CommandName Get-SqlDscManagedComputerInstance -MockWith { + $mockServerInstance = [PSCustomObject]@{ + ServerProtocols = @{ + 'Tcp' = [PSCustomObject]@{ + Name = 'Tcp' + DisplayName = 'TCP/IP' + IsEnabled = $true + } + } + Parent = [PSCustomObject]@{ + Name = 'TestServer' + } + } + return $mockServerInstance + } + + Mock -CommandName Get-SqlDscServerProtocolName -MockWith { + return [PSCustomObject]@{ Name = 'TcpIp'; DisplayName = 'TCP/IP'; ShortName = 'Tcp' } + } + + $mockManagedComputerObject = [Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer]::new() + $mockManagedComputerObject.Name = 'TestServer' + } + + It 'Should work with pipeline input from managed computer object' { + $result = $mockManagedComputerObject | Get-SqlDscServerProtocol -InstanceName 'MSSQLSERVER' -ProtocolName 'TcpIp' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be 'Tcp' + + Should -Invoke -CommandName Get-SqlDscManagedComputerInstance -ParameterFilter { + $ManagedComputerObject -eq $mockManagedComputerObject -and $InstanceName -eq 'MSSQLSERVER' + } -Exactly -Times 1 -Scope It + } + } + + Context 'When using pipeline with managed computer instance object' { + BeforeAll { + # Mock Get-SqlDscManagedComputerInstance - it should not be called in this scenario + Mock -CommandName Get-SqlDscManagedComputerInstance + + Mock -CommandName Get-SqlDscServerProtocolName -MockWith { + return [PSCustomObject]@{ Name = 'TcpIp'; DisplayName = 'TCP/IP'; ShortName = 'Tcp' } + } + + $mockManagedComputerInstanceObject = [Microsoft.SqlServer.Management.Smo.Wmi.ServerInstance]::CreateTypeInstance() + + # Create a real ServerProtocol object + $mockServerProtocol = [Microsoft.SqlServer.Management.Smo.Wmi.ServerProtocol]::CreateTypeInstance() + $mockServerProtocol.Name = 'Tcp' + $mockServerProtocol.DisplayName = 'TCP/IP' + $mockServerProtocol.IsEnabled = $true + + # Create a ServerProtocolCollection with indexer support + $mockServerProtocolCollection = [Microsoft.SqlServer.Management.Smo.Wmi.ServerProtocolCollection]::CreateTypeInstance() + $mockServerProtocolCollection['Tcp'] = $mockServerProtocol + + $mockManagedComputerInstanceObject.ServerProtocols = $mockServerProtocolCollection + $mockManagedComputerInstanceObject.Parent = [PSCustomObject]@{ + Name = 'TestServer' + } + } + + It 'Should work with pipeline input from managed computer instance object' { + $result = $mockManagedComputerInstanceObject | Get-SqlDscServerProtocol -ProtocolName 'TcpIp' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be 'Tcp' + + # Should not call Get-SqlDscManagedComputerInstance when using instance object + Should -Invoke -CommandName Get-SqlDscManagedComputerInstance -Exactly -Times 0 -Scope It + } + } + + Context 'When the SQL Server instance is not found' { + BeforeAll { + Mock -CommandName Get-SqlDscManagedComputerInstance -MockWith { + # Mock Get-SqlDscManagedComputerInstance to throw when instance not found + throw "Could not find SQL Server instance 'NONEXISTENT' on server 'TestServer'" + } + } + + It 'Should throw an error when instance is not found' { + { Get-SqlDscServerProtocol -InstanceName 'NONEXISTENT' -ProtocolName 'TcpIp' } | Should -Throw '*Could not find SQL Server instance*' + } + } + + Context 'When the protocol is not found' { + BeforeAll { + Mock -CommandName Get-SqlDscManagedComputerInstance -MockWith { + $mockServerInstance = [PSCustomObject]@{ + ServerProtocols = @{ + # Missing the Tcp protocol + } + Parent = [PSCustomObject]@{ + Name = 'TestServer' + } + } + + return $mockServerInstance + } + + Mock -CommandName Get-SqlDscServerProtocolName -MockWith { + return [PSCustomObject]@{ Name = 'TcpIp'; DisplayName = 'TCP/IP'; ShortName = 'Tcp' } + } + } + + It 'Should throw an error when protocol is not found' { + { Get-SqlDscServerProtocol -InstanceName 'MSSQLSERVER' -ProtocolName 'TcpIp' } | Should -Throw '*Could not find server protocol*' + } + + It 'Should throw terminating error with correct error record properties when protocol is not found' { + { Get-SqlDscServerProtocol -InstanceName 'MSSQLSERVER' -ProtocolName 'TcpIp' } | Should -Throw -ErrorId 'SqlServerProtocolNotFound,Get-SqlDscServerProtocol' + } + } + + Context 'When testing parameter sets' { + It 'Should have the correct parameters in parameter set ByServerName' -ForEach @( + @{ + ExpectedParameterSetName = 'ByServerName' + ExpectedParameters = '-InstanceName [-ServerName ] [-ProtocolName ] []' + } + ) { + $result = (Get-Command -Name 'Get-SqlDscServerProtocol').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 + } + + It 'Should have the correct parameters in parameter set ByManagedComputerObject' -ForEach @( + @{ + ExpectedParameterSetName = 'ByManagedComputerObject' + ExpectedParameters = '-InstanceName -ManagedComputerObject [-ProtocolName ] []' + } + ) { + $result = (Get-Command -Name 'Get-SqlDscServerProtocol').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 + } + + It 'Should have the correct parameters in parameter set ByManagedComputerInstanceObject' -ForEach @( + @{ + ExpectedParameterSetName = 'ByManagedComputerInstanceObject' + ExpectedParameters = '-ManagedComputerInstanceObject [-ProtocolName ] []' + } + ) { + $result = (Get-Command -Name 'Get-SqlDscServerProtocol').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 + } + + It 'Should have InstanceName as a mandatory parameter' { + $parameterInfo = (Get-Command -Name 'Get-SqlDscServerProtocol').Parameters['InstanceName'] + $parameterInfo.Attributes.Mandatory | Should -BeTrue + } + + It 'Should have ProtocolName as an optional parameter' { + $parameterInfo = (Get-Command -Name 'Get-SqlDscServerProtocol').Parameters['ProtocolName'] + $parameterInfo.Attributes.Mandatory | Should -BeFalse + } + + It 'Should have ServerName as an optional parameter in ByServerName parameter set' { + $parameterInfo = (Get-Command -Name 'Get-SqlDscServerProtocol').Parameters['ServerName'] + $parameterSetAttribute = $parameterInfo.Attributes | Where-Object -FilterScript { $_.ParameterSetName -eq 'ByServerName' } + $parameterSetAttribute.Mandatory | Should -BeFalse + } + + It 'Should have ManagedComputerObject as a pipeline parameter' { + $parameterInfo = (Get-Command -Name 'Get-SqlDscServerProtocol').Parameters['ManagedComputerObject'] + $parameterInfo.Attributes.ValueFromPipeline | Should -Not -Contain $false + } + + It 'Should have ManagedComputerInstanceObject as a pipeline parameter' { + $parameterInfo = (Get-Command -Name 'Get-SqlDscServerProtocol').Parameters['ManagedComputerInstanceObject'] + $parameterInfo.Attributes.ValueFromPipeline | Should -Not -Contain $false + } + } +} diff --git a/tests/Unit/Public/Get-SqlDscServerProtocolName.Tests.ps1 b/tests/Unit/Public/Get-SqlDscServerProtocolName.Tests.ps1 new file mode 100644 index 0000000000..b76db65e42 --- /dev/null +++ b/tests/Unit/Public/Get-SqlDscServerProtocolName.Tests.ps1 @@ -0,0 +1,233 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +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' + + $env:SqlServerDscCI = $true + + Import-Module -Name $script:moduleName -Force -ErrorAction 'Stop' + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:moduleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:moduleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:moduleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $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:moduleName -All | Remove-Module -Force + + Remove-Item -Path 'env:SqlServerDscCI' +} + +Describe 'Get-SqlDscServerProtocolName' -Tag 'Public' { + Context 'When testing localized strings' { + It 'Should have localized string for getting protocol mappings' { + InModuleScope -ScriptBlock { + $script:localizedData.ServerProtocolName_GetProtocolMappings | Should -Not -BeNullOrEmpty + } + } + } + + Context 'When getting protocol mappings by protocol name' { + It 'Should return correct mapping for TcpIp protocol' { + $result = Get-SqlDscServerProtocolName -ProtocolName 'TcpIp' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be 'TcpIp' + $result.DisplayName | Should -Be 'TCP/IP' + $result.ShortName | Should -Be 'Tcp' + } + + It 'Should return correct mapping for NamedPipes protocol' { + $result = Get-SqlDscServerProtocolName -ProtocolName 'NamedPipes' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be 'NamedPipes' + $result.DisplayName | Should -Be 'Named Pipes' + $result.ShortName | Should -Be 'Np' + } + + It 'Should return correct mapping for SharedMemory protocol' { + $result = Get-SqlDscServerProtocolName -ProtocolName 'SharedMemory' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be 'SharedMemory' + $result.DisplayName | Should -Be 'Shared Memory' + $result.ShortName | Should -Be 'Sm' + } + } + + Context 'When getting protocol mappings by display name' { + It 'Should return correct mapping for TCP/IP display name' { + $result = Get-SqlDscServerProtocolName -DisplayName 'TCP/IP' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be 'TcpIp' + $result.DisplayName | Should -Be 'TCP/IP' + $result.ShortName | Should -Be 'Tcp' + } + + It 'Should return correct mapping for Named Pipes display name' { + $result = Get-SqlDscServerProtocolName -DisplayName 'Named Pipes' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be 'NamedPipes' + $result.DisplayName | Should -Be 'Named Pipes' + $result.ShortName | Should -Be 'Np' + } + + It 'Should return correct mapping for Shared Memory display name' { + $result = Get-SqlDscServerProtocolName -DisplayName 'Shared Memory' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be 'SharedMemory' + $result.DisplayName | Should -Be 'Shared Memory' + $result.ShortName | Should -Be 'Sm' + } + } + + Context 'When getting protocol mappings by short name' { + It 'Should return correct mapping for Tcp short name' { + $result = Get-SqlDscServerProtocolName -ShortName 'Tcp' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be 'TcpIp' + $result.DisplayName | Should -Be 'TCP/IP' + $result.ShortName | Should -Be 'Tcp' + } + + It 'Should return correct mapping for Np short name' { + $result = Get-SqlDscServerProtocolName -ShortName 'Np' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be 'NamedPipes' + $result.DisplayName | Should -Be 'Named Pipes' + $result.ShortName | Should -Be 'Np' + } + + It 'Should return correct mapping for Sm short name' { + $result = Get-SqlDscServerProtocolName -ShortName 'Sm' + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be 'SharedMemory' + $result.DisplayName | Should -Be 'Shared Memory' + $result.ShortName | Should -Be 'Sm' + } + } + + Context 'When getting all protocol mappings' { + It 'Should return all three protocol mappings' { + $result = Get-SqlDscServerProtocolName -All + + $result | Should -Not -BeNullOrEmpty + $result | Should -HaveCount 3 + + $tcpMapping = $result | Where-Object -FilterScript { $_.Name -eq 'TcpIp' } + $tcpMapping | Should -Not -BeNullOrEmpty + $tcpMapping.DisplayName | Should -Be 'TCP/IP' + $tcpMapping.ShortName | Should -Be 'Tcp' + + $npMapping = $result | Where-Object -FilterScript { $_.Name -eq 'NamedPipes' } + $npMapping | Should -Not -BeNullOrEmpty + $npMapping.DisplayName | Should -Be 'Named Pipes' + $npMapping.ShortName | Should -Be 'Np' + + $smMapping = $result | Where-Object -FilterScript { $_.Name -eq 'SharedMemory' } + $smMapping | Should -Not -BeNullOrEmpty + $smMapping.DisplayName | Should -Be 'Shared Memory' + $smMapping.ShortName | Should -Be 'Sm' + } + + It 'Should return all protocol mappings when no parameters are specified' { + $result = Get-SqlDscServerProtocolName + + $result | Should -Not -BeNullOrEmpty + $result | Should -HaveCount 3 + } + } + + Context 'When testing parameter sets' { + It 'Should have the correct parameters in parameter set ' -ForEach @( + @{ + ExpectedParameterSetName = 'ByProtocolName' + ExpectedParameters = '-ProtocolName []' + }, + @{ + ExpectedParameterSetName = 'ByDisplayName' + ExpectedParameters = '-DisplayName []' + }, + @{ + ExpectedParameterSetName = 'ByShortName' + ExpectedParameters = '-ShortName []' + }, + @{ + ExpectedParameterSetName = 'All' + ExpectedParameters = '[-All] []' + } + ) { + $result = (Get-Command -Name 'Get-SqlDscServerProtocolName').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 + } + + It 'Should have ProtocolName as a mandatory parameter in ByProtocolName parameter set' { + $parameterSets = (Get-Command -Name 'Get-SqlDscServerProtocolName').ParameterSets + $byProtocolNameSet = $parameterSets | Where-Object -FilterScript { $_.Name -eq 'ByProtocolName' } + $protocolNameParam = $byProtocolNameSet.Parameters | Where-Object -FilterScript { $_.Name -eq 'ProtocolName' } + $protocolNameParam.IsMandatory | Should -BeTrue + } + + It 'Should have DisplayName as a mandatory parameter in ByDisplayName parameter set' { + $parameterSets = (Get-Command -Name 'Get-SqlDscServerProtocolName').ParameterSets + $byDisplayNameSet = $parameterSets | Where-Object -FilterScript { $_.Name -eq 'ByDisplayName' } + $displayNameParam = $byDisplayNameSet.Parameters | Where-Object -FilterScript { $_.Name -eq 'DisplayName' } + $displayNameParam.IsMandatory | Should -BeTrue + } + + It 'Should have ShortName as a mandatory parameter in ByShortName parameter set' { + $parameterSets = (Get-Command -Name 'Get-SqlDscServerProtocolName').ParameterSets + $byShortNameSet = $parameterSets | Where-Object -FilterScript { $_.Name -eq 'ByShortName' } + $shortNameParam = $byShortNameSet.Parameters | Where-Object -FilterScript { $_.Name -eq 'ShortName' } + $shortNameParam.IsMandatory | Should -BeTrue + } + + It 'Should have All as an optional parameter in All parameter set' { + $parameterSets = (Get-Command -Name 'Get-SqlDscServerProtocolName').ParameterSets + $allSet = $parameterSets | Where-Object -FilterScript { $_.Name -eq 'All' } + $allParam = $allSet.Parameters | Where-Object -FilterScript { $_.Name -eq 'All' } + $allParam.IsMandatory | Should -BeFalse + } + } +} diff --git a/tests/Unit/Stubs/SMO.cs b/tests/Unit/Stubs/SMO.cs index 4ed0acb68b..6390ea18ae 100644 --- a/tests/Unit/Stubs/SMO.cs +++ b/tests/Unit/Stubs/SMO.cs @@ -1458,14 +1458,47 @@ public static ServerProtocol CreateTypeInstance() } } - public class ServerProtocolCollection + public class ServerProtocolCollection : System.Collections.IEnumerable { - // Property - public Microsoft.SqlServer.Management.Smo.Wmi.ServerProtocol Item { get; set; } - public System.Int32 Count { get; set; } + // Properties + public System.Int32 Count { get { return protocols.Count; } } public System.Boolean IsSynchronized { get; set; } public System.Object SyncRoot { get; set; } + // Collection of protocols + private readonly System.Collections.Generic.Dictionary protocols = + new System.Collections.Generic.Dictionary(System.StringComparer.OrdinalIgnoreCase); + + // Indexer + public Microsoft.SqlServer.Management.Smo.Wmi.ServerProtocol this[string name] + { + get + { + if (protocols.ContainsKey(name)) + { + return protocols[name]; + } + return null; + } + set + { + if (value == null) + { + protocols.Remove(name); + } + else + { + protocols[name] = value; + } + } + } + + // IEnumerable implementation + public System.Collections.IEnumerator GetEnumerator() + { + return protocols.Values.GetEnumerator(); + } + // Fabricated constructor private ServerProtocolCollection() { } public static ServerProtocolCollection CreateTypeInstance() @@ -1493,14 +1526,46 @@ public static ServerInstance CreateTypeInstance() } } - public class ServerInstanceCollection + public class ServerInstanceCollection : System.Collections.IEnumerable { - // Property - public Microsoft.SqlServer.Management.Smo.Wmi.ServerInstance Item { get; set; } - public System.Int32 Count { get; set; } + // Properties + public System.Int32 Count { get { return instances.Count; } } public System.Boolean IsSynchronized { get; set; } public System.Object SyncRoot { get; set; } + // Collection of instances + private System.Collections.Generic.Dictionary instances = new System.Collections.Generic.Dictionary(System.StringComparer.OrdinalIgnoreCase); + + // Indexer + public Microsoft.SqlServer.Management.Smo.Wmi.ServerInstance this[string name] + { + get + { + if (instances.ContainsKey(name)) + { + return instances[name]; + } + return null; + } + set + { + if (value == null) + { + instances.Remove(name); + } + else + { + instances[name] = value; + } + } + } + + // IEnumerable implementation + public System.Collections.IEnumerator GetEnumerator() + { + return instances.Values.GetEnumerator(); + } + // Fabricated constructor private ServerInstanceCollection() { } public static ServerInstanceCollection CreateTypeInstance()