diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d4d96bbc..c61904162 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,12 +24,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix a path in VS Code workspace settings to correctly use Script Analyzer on Linux and macOS. - Highlighted note comments throughout markdown and generated markdown. +- SqlServerDsc.Common + - Fix unit tests so they work cross-platform. +- ScriptAnalyzer.Tests was fixed so they work cross-platform. - SqlSetup - Highlighted a note in the README.md. - SqlMemory - Highlighted an important note in the README.md. - SqlMaxDop - Highlighted an important note in the README.md. +- `Get-SqlDscPreferredModule` + - Fix unit tests so they work cross-platform. ### Fixed diff --git a/tests/QA/ScriptAnalyzer.Tests.ps1 b/tests/QA/ScriptAnalyzer.Tests.ps1 index 0590e6eb4..0b8a15f80 100644 --- a/tests/QA/ScriptAnalyzer.Tests.ps1 +++ b/tests/QA/ScriptAnalyzer.Tests.ps1 @@ -50,6 +50,21 @@ BeforeDiscovery { foreach ($moduleFile in $moduleFiles) { + # Skipping Examples on Linux and macOS as they cannot be parsed. + if (($IsLinux -or $IsMacOs) -and $moduleFile.FullName -match 'Examples') + { + continue + } + + <# + Skipping prefix.psl because it contains `using module` using a relative + path which is not available when testing the source (only when built). + #> + if ($moduleFile.FullName -match 'prefix\.ps1') + { + continue + } + $moduleFilePathNormalized = $moduleFile.FullName -replace '\\', '/' $repositoryPathNormalized = $repositoryPath -replace '\\', '/' $escapedRepositoryPath = [System.Text.RegularExpressions.RegEx]::Escape($repositoryPathNormalized) @@ -66,7 +81,7 @@ Describe 'Script Analyzer Rules' { Context 'When there are source files' { BeforeAll { $repositoryPath = Resolve-Path -Path (Join-Path -Path $PSScriptRoot -ChildPath '../..') - $scriptAnalyzerSettingsPath = Join-Path -Path $repositoryPath -ChildPath '.vscode\analyzersettings.psd1' + $scriptAnalyzerSettingsPath = Join-Path -Path $repositoryPath -ChildPath '.vscode/analyzersettings.psd1' } It 'Should pass all PS Script Analyzer rules for file ''''' -ForEach $testCases { diff --git a/tests/Unit/Private/Invoke-SetupAction.Tests.ps1 b/tests/Unit/Private/Invoke-SetupAction.Tests.ps1 index c8b9f4324..d2b329660 100644 --- a/tests/Unit/Private/Invoke-SetupAction.Tests.ps1 +++ b/tests/Unit/Private/Invoke-SetupAction.Tests.ps1 @@ -1382,11 +1382,6 @@ Describe 'Invoke-SetupAction' -Tag 'Private' { MockParameterValue = 'TESTCLU01A' # cspell: disable-line MockExpectedRegEx = '\/FAILOVERCLUSTERGROUP="TESTCLU01A"' # cspell: disable-line } - @{ - MockParameterName = 'FailoverClusterGroup' - MockParameterValue = 'TESTCLU01A' # cspell: disable-line - MockExpectedRegEx = '\/FAILOVERCLUSTERGROUP="TESTCLU01A"' # cspell: disable-line - } @{ MockParameterName = 'FailoverClusterDisks' # This is the failover cluster resource name. @@ -1963,11 +1958,6 @@ Describe 'Invoke-SetupAction' -Tag 'Private' { MockParameterValue = 'TESTCLU01A' # cspell: disable-line MockExpectedRegEx = '\/FAILOVERCLUSTERGROUP="TESTCLU01A"' # cspell: disable-line } - @{ - MockParameterName = 'FailoverClusterGroup' - MockParameterValue = 'TESTCLU01A' # cspell: disable-line - MockExpectedRegEx = '\/FAILOVERCLUSTERGROUP="TESTCLU01A"' # cspell: disable-line - } @{ MockParameterName = 'FailoverClusterDisks' # This is the failover cluster resource name. @@ -3003,169 +2993,6 @@ Describe 'Invoke-SetupAction' -Tag 'Private' { } } - Context 'When setup action is ''Upgrade''' { - BeforeAll { - Mock -CommandName Assert-SetupActionProperties - Mock -CommandName Assert-ElevatedUser - Mock -CommandName Test-Path -ParameterFilter { - $Path -match 'setup\.exe' - } -MockWith { - return $true - } - } - - Context 'When specifying only mandatory parameters' { - BeforeAll { - Mock -CommandName Start-SqlSetupProcess -MockWith { - return 0 - } -RemoveParameterValidation 'FilePath' - - InModuleScope -ScriptBlock { - $script:mockDefaultParameters = @{ - Upgrade = $true - AcceptLicensingTerms = $true - MediaPath = '\SqlMedia' - InstanceName = 'INSTANCE' - } - } - } - - Context 'When using parameter Confirm with value $false' { - It 'Should call the mock with the correct argument string' { - InModuleScope -ScriptBlock { - Invoke-SetupAction -Confirm:$false @mockDefaultParameters - - Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { - $ArgumentList | Should -MatchExactly '\/ACTION=Upgrade' - $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line - $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line - - # Return $true if none of the above throw. - $true - } -Exactly -Times 1 -Scope It - } - } - } - - Context 'When using parameter Force' { - It 'Should call the mock with the correct argument string' { - InModuleScope -ScriptBlock { - Invoke-SetupAction -Force @mockDefaultParameters - - Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { - $ArgumentList | Should -MatchExactly '\/ACTION=Upgrade' - $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line - $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line - - # Return $true if none of the above throw. - $true - } -Exactly -Times 1 -Scope It - } - } - } - - Context 'When using parameter WhatIf' { - It 'Should call the mock with the correct argument string' { - InModuleScope -ScriptBlock { - Invoke-SetupAction -WhatIf @mockDefaultParameters - - Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It - } - } - } - } - - Context 'When specifying optional parameter ' -ForEach @( - @{ - MockParameterName = 'Enu' - MockParameterValue = $true - MockExpectedRegEx = '\/ENU\s*' - } - @{ - MockParameterName = 'UpdateEnabled' - MockParameterValue = $true - MockExpectedRegEx = '\/UPDATEENABLED=True' # cspell: disable-line - } - @{ - MockParameterName = 'UpdateSource' - MockParameterValue = '\SqlMedia\Updates' - MockExpectedRegEx = '\/UPDATESOURCE="\\SqlMedia\\Updates"' # cspell: disable-line - } - @{ - MockParameterName = 'InstanceDir' - MockParameterValue = 'C:\Program Files\Microsoft SQL Server' - MockExpectedRegEx = '\/INSTANCEDIR="C:\\Program Files\\Microsoft SQL Server"' # cspell: disable-line - } - @{ - MockParameterName = 'InstanceId' - MockParameterValue = 'Instance' - MockExpectedRegEx = '\/INSTANCEID="Instance"' # cspell: disable-line - } - @{ - MockParameterName = 'ProductKey' - MockParameterValue = '22222-00000-00000-00000-00000' - MockExpectedRegEx = '\/PID="22222-00000-00000-00000-00000"' - } - @{ - MockParameterName = 'BrowserSvcStartupType' - MockParameterValue = 'Manual' - MockExpectedRegEx = '\/BROWSERSVCSTARTUPTYPE="Manual"' # cspell: disable-line - } - @{ - MockParameterName = 'ISSvcAccount' - MockParameterValue = 'DOMAIN\ServiceAccount$' - MockExpectedRegEx = '\/ISSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line - } - @{ - MockParameterName = 'ISSvcPassword' - MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line - MockExpectedRegEx = '\/ISSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line - } - @{ - MockParameterName = 'ISSvcStartupType' - MockParameterValue = 'Automatic' - MockExpectedRegEx = '\/ISSVCSTARTUPTYPE="Automatic"' # cspell: disable-line - } - ) { - BeforeAll { - Mock -CommandName Start-SqlSetupProcess -MockWith { - return 0 - } -RemoveParameterValidation 'FilePath' - - InModuleScope -ScriptBlock { - $script:mockDefaultParameters = @{ - Upgrade = $true - AcceptLicensingTerms = $true - MediaPath = '\SqlMedia' - InstanceName = 'INSTANCE' - Force = $true - } - } - } - - BeforeEach { - InModuleScope -ScriptBlock { - $script:installSqlDscServerParameters = $mockDefaultParameters.Clone() - } - } - - It 'Should call the mock with the correct argument string' { - InModuleScope -Parameters $_ -ScriptBlock { - $installSqlDscServerParameters.$MockParameterName = $MockParameterValue - - Invoke-SetupAction @installSqlDscServerParameters - - Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { - $ArgumentList | Should -MatchExactly $MockExpectedRegEx - - # Return $true if none of the above throw. - $true - } -Exactly -Times 1 -Scope It - } - } - } - } - Context 'When setup action is ''PrepareImage''' { BeforeAll { Mock -CommandName Assert-SetupActionProperties diff --git a/tests/Unit/Public/Get-SqlDscPreferredModule.Tests.ps1 b/tests/Unit/Public/Get-SqlDscPreferredModule.Tests.ps1 index 64d39f113..a8923aa1a 100644 --- a/tests/Unit/Public/Get-SqlDscPreferredModule.Tests.ps1 +++ b/tests/Unit/Public/Get-SqlDscPreferredModule.Tests.ps1 @@ -140,7 +140,7 @@ Describe 'Get-SqlDscPreferredModule' -Tag 'Public' { } It 'Should return the correct module name' { - Get-SqlDscPreferredModule | Should -Be 'C:\Program Files (x86)\Microsoft SQL Server\130\Tools\PowerShell\Modules\SQLPS' + Get-SqlDscPreferredModule | Should -Be ('C:\Program Files (x86)\Microsoft SQL Server\130\Tools\PowerShell\Modules\SQLPS' -replace '\\', [IO.Path]::DirectorySeparatorChar) } } @@ -231,7 +231,7 @@ Describe 'Get-SqlDscPreferredModule' -Tag 'Public' { } It 'Should return the correct module name' { - Get-SqlDscPreferredModule | Should -Be 'C:\Program Files (x86)\Microsoft SQL Server\160\Tools\PowerShell\Modules\SQLPS' + Get-SqlDscPreferredModule | Should -Be ('C:\Program Files (x86)\Microsoft SQL Server\160\Tools\PowerShell\Modules\SQLPS' -replace '\\', [IO.Path]::DirectorySeparatorChar) } } } @@ -302,7 +302,7 @@ Describe 'Get-SqlDscPreferredModule' -Tag 'Public' { } It 'Should return the correct module name' { - Get-SqlDscPreferredModule -Name @('SqlServer', 'SQLPS') | Should -Be 'C:\Program Files (x86)\Microsoft SQL Server\130\Tools\PowerShell\Modules\SQLPS' + Get-SqlDscPreferredModule -Name @('SqlServer', 'SQLPS') | Should -Be ('C:\Program Files (x86)\Microsoft SQL Server\130\Tools\PowerShell\Modules\SQLPS' -replace '\\', [IO.Path]::DirectorySeparatorChar) } } @@ -398,12 +398,17 @@ Describe 'Get-SqlDscPreferredModule' -Tag 'Public' { } It 'Should return the correct module name' { - Get-SqlDscPreferredModule -Name @('SqlServer', 'SQLPS') | Should -Be 'C:\Program Files (x86)\Microsoft SQL Server\160\Tools\PowerShell\Modules\SQLPS' + Get-SqlDscPreferredModule -Name @('SqlServer', 'SQLPS') | Should -Be ('C:\Program Files (x86)\Microsoft SQL Server\160\Tools\PowerShell\Modules\SQLPS' -replace '\\', [IO.Path]::DirectorySeparatorChar) } } } - Context 'When specifying the parameter Refresh' { + <# + This test cannot run on Linux or macOS as Refresh is refreshing the PSModulePath + from the environment variable target scope Machine which does not exist + on Linux and macOS. + #> + Context 'When specifying the parameter Refresh' -Skip:($IsLinux -or $IsMacOS) { BeforeAll { Mock -CommandName Set-PSModulePath Mock -CommandName Get-PSModulePath -MockWith { diff --git a/tests/Unit/Public/Install-SqlDscServer.Tests.ps1 b/tests/Unit/Public/Install-SqlDscServer.Tests.ps1 index 88bc2c39b..61fa0fc8c 100644 --- a/tests/Unit/Public/Install-SqlDscServer.Tests.ps1 +++ b/tests/Unit/Public/Install-SqlDscServer.Tests.ps1 @@ -1297,11 +1297,6 @@ Describe 'Install-SqlDscServer' -Tag 'Public' { MockParameterValue = 'TESTCLU01A' # cspell: disable-line MockExpectedRegEx = '\/FAILOVERCLUSTERGROUP="TESTCLU01A"' # cspell: disable-line } - @{ - MockParameterName = 'FailoverClusterGroup' - MockParameterValue = 'TESTCLU01A' # cspell: disable-line - MockExpectedRegEx = '\/FAILOVERCLUSTERGROUP="TESTCLU01A"' # cspell: disable-line - } @{ MockParameterName = 'FailoverClusterDisks' # This is the failover cluster resource name. @@ -1865,155 +1860,6 @@ Describe 'Install-SqlDscServer' -Tag 'Public' { } } - Context 'When setup action is ''Upgrade''' { - BeforeAll { - Mock -CommandName Assert-SetupActionProperties - Mock -CommandName Assert-ElevatedUser - Mock -CommandName Test-Path -ParameterFilter { - $Path -match 'setup\.exe' - } -MockWith { - return $true - } - } - - Context 'When specifying only mandatory parameters' { - BeforeAll { - Mock -CommandName Start-SqlSetupProcess -MockWith { - return 0 - } -RemoveParameterValidation 'FilePath' - - $mockDefaultParameters = @{ - Upgrade = $true - AcceptLicensingTerms = $true - MediaPath = '\SqlMedia' - InstanceName = 'INSTANCE' - } - } - - Context 'When using parameter Confirm with value $false' { - It 'Should call the mock with the correct argument string' { - Install-SqlDscServer -Confirm:$false @mockDefaultParameters - - Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { - $ArgumentList | Should -MatchExactly '\/ACTION=Upgrade' - $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line - $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line - - # Return $true if none of the above throw. - $true - } -Exactly -Times 1 -Scope It - } - } - - Context 'When using parameter Force' { - It 'Should call the mock with the correct argument string' { - Install-SqlDscServer -Force @mockDefaultParameters - - Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { - $ArgumentList | Should -MatchExactly '\/ACTION=Upgrade' - $ArgumentList | Should -MatchExactly '\/IACCEPTSQLSERVERLICENSETERMS' # cspell: disable-line - $ArgumentList | Should -MatchExactly '\/INSTANCENAME="INSTANCE"' # cspell: disable-line - - # Return $true if none of the above throw. - $true - } -Exactly -Times 1 -Scope It - } - } - - Context 'When using parameter WhatIf' { - It 'Should call the mock with the correct argument string' { - Install-SqlDscServer -WhatIf @mockDefaultParameters - - Should -Invoke -CommandName Start-SqlSetupProcess -Exactly -Times 0 -Scope It - } - } - } - - Context 'When specifying optional parameter ' -ForEach @( - @{ - MockParameterName = 'Enu' - MockParameterValue = $true - MockExpectedRegEx = '\/ENU\s*' - } - @{ - MockParameterName = 'UpdateEnabled' - MockParameterValue = $true - MockExpectedRegEx = '\/UPDATEENABLED=True' # cspell: disable-line - } - @{ - MockParameterName = 'UpdateSource' - MockParameterValue = '\SqlMedia\Updates' - MockExpectedRegEx = '\/UPDATESOURCE="\\SqlMedia\\Updates"' # cspell: disable-line - } - @{ - MockParameterName = 'InstanceDir' - MockParameterValue = 'C:\Program Files\Microsoft SQL Server' - MockExpectedRegEx = '\/INSTANCEDIR="C:\\Program Files\\Microsoft SQL Server"' # cspell: disable-line - } - @{ - MockParameterName = 'InstanceId' - MockParameterValue = 'Instance' - MockExpectedRegEx = '\/INSTANCEID="Instance"' # cspell: disable-line - } - @{ - MockParameterName = 'ProductKey' - MockParameterValue = '22222-00000-00000-00000-00000' - MockExpectedRegEx = '\/PID="22222-00000-00000-00000-00000"' - } - @{ - MockParameterName = 'BrowserSvcStartupType' - MockParameterValue = 'Manual' - MockExpectedRegEx = '\/BROWSERSVCSTARTUPTYPE="Manual"' # cspell: disable-line - } - @{ - MockParameterName = 'ISSvcAccount' - MockParameterValue = 'DOMAIN\ServiceAccount$' - MockExpectedRegEx = '\/ISSVCACCOUNT="DOMAIN\\ServiceAccount\$"' # cspell: disable-line - } - @{ - MockParameterName = 'ISSvcPassword' - MockParameterValue = 'jT7ELPbD2GGuvLmjABDL' | ConvertTo-SecureString -AsPlainText -Force # cspell: disable-line - MockExpectedRegEx = '\/ISSVCPASSWORD="jT7ELPbD2GGuvLmjABDL"' # cspell: disable-line - } - @{ - MockParameterName = 'ISSvcStartupType' - MockParameterValue = 'Automatic' - MockExpectedRegEx = '\/ISSVCSTARTUPTYPE="Automatic"' # cspell: disable-line - } - ) { - BeforeAll { - Mock -CommandName Start-SqlSetupProcess -MockWith { - return 0 - } -RemoveParameterValidation 'FilePath' - - $mockDefaultParameters = @{ - Upgrade = $true - AcceptLicensingTerms = $true - MediaPath = '\SqlMedia' - InstanceName = 'INSTANCE' - Force = $true - } - } - - BeforeEach { - $installSqlDscServerParameters = $mockDefaultParameters.Clone() - } - - It 'Should call the mock with the correct argument string' { - $installSqlDscServerParameters.$MockParameterName = $MockParameterValue - - Install-SqlDscServer @installSqlDscServerParameters - - Should -Invoke -CommandName Start-SqlSetupProcess -ParameterFilter { - $ArgumentList | Should -MatchExactly $MockExpectedRegEx - - # Return $true if none of the above throw. - $true - } -Exactly -Times 1 -Scope It - } - } - } - Context 'When setup action is ''PrepareImage''' { BeforeAll { Mock -CommandName Assert-SetupActionProperties diff --git a/tests/Unit/SqlServerDsc.Common.Tests.ps1 b/tests/Unit/SqlServerDsc.Common.Tests.ps1 index 5c0853eb9..960bb8fee 100644 --- a/tests/Unit/SqlServerDsc.Common.Tests.ps1 +++ b/tests/Unit/SqlServerDsc.Common.Tests.ps1 @@ -522,6 +522,10 @@ Describe 'SqlServerDsc.Common\Invoke-InstallationMediaCopy' -Tag 'InvokeInstalla Context 'When invoking installation media copy, using SourcePath containing leaf' { BeforeAll { $mockSourcePathUNCWithLeaf = '\\server\share\leaf' + + Mock -CommandName Join-Path -MockWith { + return $mockDestinationPath + '\leaf' + } } It 'Should call the correct mocks' { @@ -557,6 +561,10 @@ Describe 'SqlServerDsc.Common\Invoke-InstallationMediaCopy' -Tag 'InvokeInstalla Context 'When invoking installation media copy, using SourcePath containing a second leaf' { BeforeAll { $mockSourcePathUNCWithLeaf = '\\server\share\leaf\secondleaf' + + Mock -CommandName Join-Path -MockWith { + return $mockDestinationPath + '\secondleaf' + } } It 'Should call the correct mocks' { @@ -592,6 +600,10 @@ Describe 'SqlServerDsc.Common\Invoke-InstallationMediaCopy' -Tag 'InvokeInstalla Context 'When invoking installation media copy, using SourcePath without a leaf' { BeforeAll { $mockSourcePathUNC = '\\server\share' + + Mock -CommandName Join-Path -MockWith { + return $mockDestinationPath + '\' + $mockSourcePathGuid + } } It 'Should call the correct mocks' { @@ -642,6 +654,36 @@ Describe 'SqlServerDsc.Common\Connect-UncPath' -Tag 'ConnectUncPath' { ($mockFqdnShareCredentialPassword | ConvertTo-SecureString -AsPlainText -Force) ) + InModuleScope -ScriptBlock { + # Stubs for cross-platform testing. + function script:New-SmbMapping + { + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '', Justification = 'Suppressing this rule because parameter Password is used to mock the real command.')] + [CmdletBinding()] + param + ( + [Parameter()] + [System.String] + $RemotePath, + + [Parameter()] + [System.String] + $UserName, + + [Parameter()] + [System.String] + $Password + ) + + throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand + } + + function script:Remove-SmbMapping + { + throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand + } + } + Mock -CommandName New-SmbMapping -MockWith { return @{ RemotePath = $mockSourcePathUNC @@ -649,6 +691,13 @@ Describe 'SqlServerDsc.Common\Connect-UncPath' -Tag 'ConnectUncPath' { } } + AfterAll { + InModuleScope -ScriptBlock { + Remove-Item -Path 'function:/New-SmbMapping' + Remove-Item -Path 'function:/Remove-SmbMapping' + } + } + Context 'When connecting to a UNC path without credentials (using current credentials)' { It 'Should call the correct mocks' { { @@ -725,9 +774,23 @@ Describe 'SqlServerDsc.Common\Disconnect-UncPath' -Tag 'DisconnectUncPath' { BeforeAll { $mockSourcePathUNC = '\\server\share' + InModuleScope -ScriptBlock { + # Stubs for cross-platform testing. + function script:Remove-SmbMapping + { + throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand + } + } + Mock -CommandName Remove-SmbMapping } + AfterAll { + InModuleScope -ScriptBlock { + Remove-Item -Path 'function:/Remove-SmbMapping' + } + } + Context 'When disconnecting from an UNC path' { It 'Should call the correct mocks' { { @@ -774,10 +837,20 @@ Describe 'SqlServerDsc.Common\Test-PendingRestart' -Tag 'TestPendingRestart' { } Describe 'SqlServerDsc.Common\Start-SqlSetupProcess' -Tag 'StartSqlSetupProcess' { + BeforeAll { + $mockPowerShellExecutable = if ($IsLinux -or $IsMacOS) + { + 'pwsh' + } + else + { + 'powershell.exe' + } + } Context 'When starting a process successfully' { It 'Should return exit code 0' { $startSqlSetupProcessParameters = @{ - FilePath = 'powershell.exe' + FilePath = $mockPowerShellExecutable ArgumentList = '-NonInteractive -NoProfile -Command &{Start-Sleep -Seconds 2}' Timeout = 30 } @@ -790,7 +863,7 @@ Describe 'SqlServerDsc.Common\Start-SqlSetupProcess' -Tag 'StartSqlSetupProcess' Context 'When starting a process and the process does not finish before the timeout period' { It 'Should throw an error message' { $startSqlSetupProcessParameters = @{ - FilePath = 'powershell.exe' + FilePath = $mockPowerShellExecutable ArgumentList = '-NonInteractive -NoProfile -Command &{Start-Sleep -Seconds 4}' Timeout = 2 } @@ -1194,7 +1267,8 @@ Describe 'SqlServerDsc.Common\Restart-SqlService' -Tag 'RestartSqlService' { } } -Describe 'SqlServerDsc.Common\Restart-SqlClusterService' -Tag 'RestartSqlClusterService' { +# This test is skipped on Linux and macOS due to it is missing CIM Instance. +Describe 'SqlServerDsc.Common\Restart-SqlClusterService' -Tag 'RestartSqlClusterService' -Skip:($IsLinux -or $IsMacOS) { Context 'When not clustered instance is found' { BeforeAll { Mock -CommandName Get-CimInstance @@ -2322,7 +2396,7 @@ Describe 'SqlServerDsc.Common\Test-ImpersonatePermissions' -Tag 'TestImpersonate } Describe 'SqlServerDsc.Common\Connect-SQL' -Tag 'ConnectSql' { - BeforeAll { + BeforeEach { $mockNewObject_MicrosoftDatabaseEngine = { <# $ArgumentList[0] will contain the ServiceInstance when calling mock New-Object. @@ -2411,23 +2485,22 @@ Describe 'SqlServerDsc.Common\Connect-SQL' -Tag 'ConnectSql' { $mockWinFqdnCredentialPassword = 'StrongerOne7.' $mockWinFqdnCredentialSecurePassword = ConvertTo-SecureString -String $mockWinFqdnCredentialPassword -AsPlainText -Force $mockWinFqdnCredential = New-Object -TypeName PSCredential -ArgumentList ($mockWinFqdnCredentialUserName, $mockWinFqdnCredentialSecurePassword) - } - BeforeEach { Mock -CommandName Import-SqlDscPreferredModule } - Context 'When connecting to the default instance using integrated Windows Authentication' { - BeforeAll { + # Skipping on Linux and macOS because they do not support Windows Authentication. + Context 'When connecting to the default instance using integrated Windows Authentication' -Skip:($IsLinux -or $IsMacOS) { + BeforeEach { + $mockExpectedDatabaseEngineServer = 'TestServer' + $mockExpectedDatabaseEngineInstance = 'MSSQLSERVER' + Mock -CommandName New-Object ` -MockWith $mockNewObject_MicrosoftDatabaseEngine ` -ParameterFilter $mockNewObject_MicrosoftDatabaseEngine_ParameterFilter } It 'Should return the correct service instance' { - $mockExpectedDatabaseEngineServer = 'TestServer' - $mockExpectedDatabaseEngineInstance = 'MSSQLSERVER' - $databaseEngineServerObject = Connect-SQL -ServerName $mockExpectedDatabaseEngineServer -ErrorAction 'Stop' $databaseEngineServerObject.ConnectionContext.ServerInstance | Should -BeExactly $mockExpectedDatabaseEngineServer @@ -2437,17 +2510,17 @@ Describe 'SqlServerDsc.Common\Connect-SQL' -Tag 'ConnectSql' { } Context 'When connecting to the default instance using SQL Server Authentication' { - BeforeAll { + BeforeEach { + $mockExpectedDatabaseEngineServer = 'TestServer' + $mockExpectedDatabaseEngineInstance = 'MSSQLSERVER' + $mockExpectedDatabaseEngineLoginSecure = $false + Mock -CommandName New-Object ` -MockWith $mockNewObject_MicrosoftDatabaseEngine ` -ParameterFilter $mockNewObject_MicrosoftDatabaseEngine_ParameterFilter } It 'Should return the correct service instance' { - $mockExpectedDatabaseEngineServer = 'TestServer' - $mockExpectedDatabaseEngineInstance = 'MSSQLSERVER' - $mockExpectedDatabaseEngineLoginSecure = $false - $databaseEngineServerObject = Connect-SQL -ServerName $mockExpectedDatabaseEngineServer -SetupCredential $mockSqlCredential -LoginType 'SqlLogin' -ErrorAction 'Stop' $databaseEngineServerObject.ConnectionContext.LoginSecure | Should -Be $false $databaseEngineServerObject.ConnectionContext.Login | Should -Be $mockSqlCredentialUserName @@ -2459,17 +2532,18 @@ Describe 'SqlServerDsc.Common\Connect-SQL' -Tag 'ConnectSql' { } } - Context 'When connecting to the named instance using integrated Windows Authentication' { - BeforeAll { + # Skipping on Linux and macOS because they do not support Windows Authentication. + Context 'When connecting to the named instance using integrated Windows Authentication' -Skip:($IsLinux -or $IsMacOS) { + BeforeEach { + $mockExpectedDatabaseEngineServer = Get-ComputerName + $mockExpectedDatabaseEngineInstance = 'SqlInstance' + Mock -CommandName New-Object ` -MockWith $mockNewObject_MicrosoftDatabaseEngine ` -ParameterFilter $mockNewObject_MicrosoftDatabaseEngine_ParameterFilter } It 'Should return the correct service instance' { - $mockExpectedDatabaseEngineServer = $env:COMPUTERNAME - $mockExpectedDatabaseEngineInstance = 'SqlInstance' - $databaseEngineServerObject = Connect-SQL -InstanceName $mockExpectedDatabaseEngineInstance -ErrorAction 'Stop' $databaseEngineServerObject.ConnectionContext.ServerInstance | Should -BeExactly "$mockExpectedDatabaseEngineServer\$mockExpectedDatabaseEngineInstance" @@ -2479,17 +2553,17 @@ Describe 'SqlServerDsc.Common\Connect-SQL' -Tag 'ConnectSql' { } Context 'When connecting to the named instance using SQL Server Authentication' { - BeforeAll { + BeforeEach { + $mockExpectedDatabaseEngineServer = Get-ComputerName + $mockExpectedDatabaseEngineInstance = 'SqlInstance' + $mockExpectedDatabaseEngineLoginSecure = $false + Mock -CommandName New-Object ` -MockWith $mockNewObject_MicrosoftDatabaseEngine ` -ParameterFilter $mockNewObject_MicrosoftDatabaseEngine_ParameterFilter } It 'Should return the correct service instance' { - $mockExpectedDatabaseEngineServer = $env:COMPUTERNAME - $mockExpectedDatabaseEngineInstance = 'SqlInstance' - $mockExpectedDatabaseEngineLoginSecure = $false - $databaseEngineServerObject = Connect-SQL -InstanceName $mockExpectedDatabaseEngineInstance -SetupCredential $mockSqlCredential -LoginType 'SqlLogin' -ErrorAction 'Stop' $databaseEngineServerObject.ConnectionContext.LoginSecure | Should -Be $false $databaseEngineServerObject.ConnectionContext.Login | Should -Be $mockSqlCredentialUserName @@ -2501,17 +2575,18 @@ Describe 'SqlServerDsc.Common\Connect-SQL' -Tag 'ConnectSql' { } } - Context 'When connecting to the named instance using integrated Windows Authentication and different server name' { - BeforeAll { + # Skipping on Linux and macOS because they do not support Windows Authentication. + Context 'When connecting to the named instance using integrated Windows Authentication and different server name' -Skip:($IsLinux -or $IsMacOS) { + BeforeEach { + $mockExpectedDatabaseEngineServer = 'SERVER' + $mockExpectedDatabaseEngineInstance = 'SqlInstance' + Mock -CommandName New-Object ` -MockWith $mockNewObject_MicrosoftDatabaseEngine ` -ParameterFilter $mockNewObject_MicrosoftDatabaseEngine_ParameterFilter } It 'Should return the correct service instance' { - $mockExpectedDatabaseEngineServer = 'SERVER' - $mockExpectedDatabaseEngineInstance = 'SqlInstance' - $databaseEngineServerObject = Connect-SQL -ServerName $mockExpectedDatabaseEngineServer -InstanceName $mockExpectedDatabaseEngineInstance -ErrorAction 'Stop' $databaseEngineServerObject.ConnectionContext.ServerInstance | Should -BeExactly "$mockExpectedDatabaseEngineServer\$mockExpectedDatabaseEngineInstance" @@ -2521,17 +2596,17 @@ Describe 'SqlServerDsc.Common\Connect-SQL' -Tag 'ConnectSql' { } Context 'When connecting to the named instance using Windows Authentication impersonation' { - BeforeAll { + BeforeEach { + $mockExpectedDatabaseEngineServer = Get-ComputerName + $mockExpectedDatabaseEngineInstance = 'SqlInstance' + Mock -CommandName New-Object ` -MockWith $mockNewObject_MicrosoftDatabaseEngine ` -ParameterFilter $mockNewObject_MicrosoftDatabaseEngine_ParameterFilter - - $mockExpectedDatabaseEngineServer = $env:COMPUTERNAME - $mockExpectedDatabaseEngineInstance = 'SqlInstance' } Context 'When using the default login type' { - BeforeAll { + BeforeEach { $testParameters = @{ ServerName = $mockExpectedDatabaseEngineServer InstanceName = $mockExpectedDatabaseEngineInstance @@ -2555,7 +2630,7 @@ Describe 'SqlServerDsc.Common\Connect-SQL' -Tag 'ConnectSql' { Context 'When using the WindowsUser login type' { Context 'When authenticating using NetBIOS domain' { - BeforeAll { + BeforeEach { $testParameters = @{ ServerName = $mockExpectedDatabaseEngineServer InstanceName = $mockExpectedDatabaseEngineInstance @@ -2579,7 +2654,7 @@ Describe 'SqlServerDsc.Common\Connect-SQL' -Tag 'ConnectSql' { } Context 'When authenticating using Fully Qualified Domain Name (FQDN)' { - BeforeAll { + BeforeEach { $testParameters = @{ ServerName = $mockExpectedDatabaseEngineServer InstanceName = $mockExpectedDatabaseEngineInstance @@ -2605,16 +2680,17 @@ Describe 'SqlServerDsc.Common\Connect-SQL' -Tag 'ConnectSql' { } Context 'When using encryption' { - BeforeAll { + BeforeEach { + $mockExpectedDatabaseEngineServer = 'SERVER' + $mockExpectedDatabaseEngineInstance = 'SqlInstance' + Mock -CommandName New-Object ` -MockWith $mockNewObject_MicrosoftDatabaseEngine ` -ParameterFilter $mockNewObject_MicrosoftDatabaseEngine_ParameterFilter } - It 'Should return the correct service instance' { - $mockExpectedDatabaseEngineServer = 'SERVER' - $mockExpectedDatabaseEngineInstance = 'SqlInstance' - + # Skipping on Linux and macOS because they do not support Windows Authentication. + It 'Should return the correct service instance' -Skip:($IsLinux -or $IsMacOS) { $databaseEngineServerObject = Connect-SQL -Encrypt -ServerName $mockExpectedDatabaseEngineServer -InstanceName $mockExpectedDatabaseEngineInstance -ErrorAction 'Stop' $databaseEngineServerObject.ConnectionContext.ServerInstance | Should -BeExactly "$mockExpectedDatabaseEngineServer\$mockExpectedDatabaseEngineInstance" @@ -2703,7 +2779,7 @@ Describe 'SqlServerDsc.Common\Connect-SQL' -Tag 'ConnectSql' { } It 'Should not throw an exception' { - { Connect-SQL -ServerName 'localhost' -ErrorAction 'SilentlyContinue' } | + { Connect-SQL -ServerName 'localhost' -SetupCredential $mockSqlCredential -LoginType 'SqlLogin' -ErrorAction 'SilentlyContinue' } | Should -Not -Throw Should -Invoke -CommandName New-Object -ParameterFilter { @@ -2805,7 +2881,7 @@ Describe 'SqlServerDsc.Common\Test-ClusterPermissions' -Tag 'TestClusterPermissi } Context 'When the cluster has permissions to the instance' { - It "Should return NullOrEmpty when '$($clusterServiceName)' is present and has the permissions to manage availability groups" { + It "Should return NullOrEmpty when 'NT SERVICE\ClusSvc' is present and has the permissions to manage availability groups" { $mockClusterServicePermissionsPresent = $true Test-ClusterPermissions -ServerObject $mockServerObject | Should -Be $true @@ -2818,7 +2894,7 @@ Describe 'SqlServerDsc.Common\Test-ClusterPermissions' -Tag 'TestClusterPermissi } } - It "Should return NullOrEmpty when '$($systemAccountName)' is present and has the permissions to manage availability groups" { + It "Should return NullOrEmpty when 'NT AUTHORITY\System' is present and has the permissions to manage availability groups" { $mockSystemPermissionsPresent = $true Test-ClusterPermissions -ServerObject $mockServerObject | Should -Be $true @@ -2848,6 +2924,40 @@ Describe 'SqlServerDsc.Common\Restart-ReportingServicesService' -Tag 'RestartRep ) } } + + InModuleScope -ScriptBlock { + # Stubs for cross-platform testing. + function script:Get-Service + { + [CmdletBinding()] + param + ( + [Parameter()] + [System.String] + $Name + ) + + throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand + } + + function script:Stop-Service + { + throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand + } + + function script:Start-Service + { + throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand + } + } + } + + AfterAll { + InModuleScope -ScriptBlock { + Remove-Item -Path 'function:/Start-Service' + Remove-Item -Path 'function:/Stop-Service' + Remove-Item -Path 'function:/Get-Service' + } } Context 'When restarting a Report Services default instance' { @@ -2975,7 +3085,7 @@ Describe 'SqlServerDsc.Common\Test-ActiveNode' -Tag 'TestActiveNode' { It 'Should return when the node name is ' -ForEach @( @{ - ComputerNamePhysicalNetBIOS = $env:COMPUTERNAME + ComputerNamePhysicalNetBIOS = Get-ComputerName Result = $true }, @{ @@ -2993,12 +3103,12 @@ Describe 'SqlServerDsc.Common\Test-ActiveNode' -Tag 'TestActiveNode' { Describe 'SqlServerDsc.Common\Invoke-SqlScript' -Tag 'InvokeSqlScript' { BeforeAll { $invokeScriptFileParameters = @{ - ServerInstance = $env:COMPUTERNAME + ServerInstance = Get-ComputerName InputFile = 'set.sql' } $invokeScriptQueryParameters = @{ - ServerInstance = $env:COMPUTERNAME + ServerInstance = Get-ComputerName Query = 'Test Query' } } @@ -3116,7 +3226,7 @@ Describe 'SqlServerDsc.Common\Invoke-SqlScript' -Tag 'InvokeSqlScript' { It 'Should call Invoke-SqlCmd with correct File ParameterSet parameters' { $mockInvokeScriptFileParameters = @{ - ServerInstance = $env:COMPUTERNAME + ServerInstance = Get-ComputerName InputFile = 'set.sql' Encrypt = 'Optional' } @@ -3130,7 +3240,7 @@ Describe 'SqlServerDsc.Common\Invoke-SqlScript' -Tag 'InvokeSqlScript' { It 'Should call Invoke-SqlCmd with correct Query ParameterSet parameters' { $mockInvokeScriptQueryParameters = @{ - ServerInstance = $env:COMPUTERNAME + ServerInstance = Get-ComputerName Query = 'Test Query' Encrypt = 'Optional' } @@ -3158,7 +3268,7 @@ Describe 'SqlServerDsc.Common\Invoke-SqlScript' -Tag 'InvokeSqlScript' { It 'Should call Invoke-SqlCmd with correct File ParameterSet parameters' { $mockInvokeScriptFileParameters = @{ - ServerInstance = $env:COMPUTERNAME + ServerInstance = Get-ComputerName InputFile = 'set.sql' Encrypt = 'Optional' } @@ -3172,7 +3282,7 @@ Describe 'SqlServerDsc.Common\Invoke-SqlScript' -Tag 'InvokeSqlScript' { It 'Should call Invoke-SqlCmd with correct Query ParameterSet parameters' { $mockInvokeScriptQueryParameters = @{ - ServerInstance = $env:COMPUTERNAME + ServerInstance = Get-ComputerName Query = 'Test Query' Encrypt = 'Optional' } @@ -3361,16 +3471,20 @@ Describe 'SqlServerDsc.Common\Get-ServerProtocolObject' -Tag 'GetServerProtocolO } Describe 'SqlServerDsc.Common\ConvertTo-ServerInstanceName' -Tag 'ConvertToServerInstanceName' { + BeforeAll { + $mockComputerName = Get-ComputerName + } + It 'Should return correct service instance for a default instance' { - $result = ConvertTo-ServerInstanceName -InstanceName 'MSSQLSERVER' -ServerName $env:COMPUTERNAME + $result = ConvertTo-ServerInstanceName -InstanceName 'MSSQLSERVER' -ServerName $mockComputerName - $result | Should -BeExactly $env:COMPUTERNAME + $result | Should -BeExactly $mockComputerName } It 'Should return correct service instance for a name instance' { - $result = ConvertTo-ServerInstanceName -InstanceName 'MyInstance' -ServerName $env:COMPUTERNAME + $result = ConvertTo-ServerInstanceName -InstanceName 'MyInstance' -ServerName $mockComputerName - $result | Should -BeExactly ('{0}\{1}' -f $env:COMPUTERNAME, 'MyInstance') + $result | Should -BeExactly ('{0}\{1}' -f $mockComputerName, 'MyInstance') } }