From d245590c6bcda2357a1811da4e03b2fa62fba08d Mon Sep 17 00:00:00 2001 From: Andreas Jordan Date: Tue, 1 Sep 2026 09:35:30 +0200 Subject: [PATCH 1/3] Tests - Case sensitive lookups and backup cleanup in six test files Found by a full run against case sensitive instances (SQL_Latin1_General_CP1_CS_AS): - Copy-DbaDbViewData.Tests.ps1 selects Id from a column called id. - New-DbaDatabase.Tests.ps1 creates the log file with -LogFileSuffix "_Log" and then looks it up as "_log"; SMO collections follow the server collation, so the lookup returns nothing. - Enable-DbaForceNetworkEncryption.Tests.ps1 left the instance with force encryption enabled; both the Enable and the Disable test now remember the state and put it back. - Connect-DbaInstance.Tests.ps1 took two msdb backups with the same default name and removed them with a silenced Remove-Item, which once failed and left the file behind for every later test file; the backups now get unique names and the removal retries and asserts. - Measure-DbaBackupThroughput.Tests.ps1 removed its backup folder with a single Remove-Item seconds after the backup, which once left the whole folder behind; it retries now too. (do Copy-DbaDbViewData, New-DbaDatabase, Connect-DbaInstance, Measure-DbaBackupThroughput, *ForceNetworkEncryption) --- tests/Connect-DbaInstance.Tests.ps1 | 25 ++++++++++++++++--- tests/Copy-DbaDbViewData.Tests.ps1 | 4 +-- ...isable-DbaForceNetworkEncryption.Tests.ps1 | 22 ++++++++++++++++ ...Enable-DbaForceNetworkEncryption.Tests.ps1 | 8 ++++++ tests/Measure-DbaBackupThroughput.Tests.ps1 | 18 ++++++++++--- tests/New-DbaDatabase.Tests.ps1 | 20 +++++++-------- 6 files changed, 78 insertions(+), 19 deletions(-) diff --git a/tests/Connect-DbaInstance.Tests.ps1 b/tests/Connect-DbaInstance.Tests.ps1 index 1f6db1262e73..314b05b4e2d5 100644 --- a/tests/Connect-DbaInstance.Tests.ps1 +++ b/tests/Connect-DbaInstance.Tests.ps1 @@ -929,16 +929,33 @@ Describe $CommandName -Tag IntegrationTests { # runs against a path that does not exist on the machine running the tests and silently does # nothing. The backup was then left behind on every remote instance, and because this file # runs early in the suite, every later test file reported it as a leftover of its own. - $results = Backup-DbaDatabase -SqlInstance $server -Database msdb -Path $TestConfig.Temp + # Each backup gets its own file name, because two backups within the same minute share the + # default name and the second one appends to the first. + $backupFiles = @() + $results = Backup-DbaDatabase -SqlInstance $server -Database msdb -Path $TestConfig.Temp -FilePath "msdb_$(Get-Random).bak" if ($results.FullName) { - Remove-Item -Path $results.FullName -ErrorAction SilentlyContinue + $backupFiles += $results.FullName } - $results = Backup-DbaDatabase -SqlInstance $server -Database msdb -Path $TestConfig.Temp -WarningVariable warn + $results = Backup-DbaDatabase -SqlInstance $server -Database msdb -Path $TestConfig.Temp -FilePath "msdb_$(Get-Random).bak" -WarningVariable warn $warn | Should -BeNullOrEmpty if ($results.FullName) { - Remove-Item -Path $results.FullName -ErrorAction SilentlyContinue + $backupFiles += $results.FullName + } + + # Right after the backup the share can still hold the file open for a moment, and a silently + # failed removal left it behind once. Retry a few times and then insist that it is gone. + foreach ($backupFile in $backupFiles) { + $attempts = 0 + while ((Test-Path -Path $backupFile) -and $attempts -lt 5) { + $attempts++ + Remove-Item -Path $backupFile -ErrorAction SilentlyContinue + if (Test-Path -Path $backupFile) { + Start-Sleep -Seconds 1 + } + } + Test-Path -Path $backupFile | Should -BeFalse } } } diff --git a/tests/Copy-DbaDbViewData.Tests.ps1 b/tests/Copy-DbaDbViewData.Tests.ps1 index 4838c2a507ba..98d486a9024e 100644 --- a/tests/Copy-DbaDbViewData.Tests.ps1 +++ b/tests/Copy-DbaDbViewData.Tests.ps1 @@ -175,12 +175,12 @@ Describe $CommandName -Tag IntegrationTests { } It "Copy data using a query that relies on the default source database" { - $result = Copy-DbaDbViewData -SqlInstance $TestConfig.InstanceCopy1 -Database tempdb -View dbatoolsci_view_example -Query "SELECT TOP (1) Id FROM dbo.dbatoolsci_view_example4 ORDER BY Id DESC" -DestinationTable dbatoolsci_example3 -Truncate + $result = Copy-DbaDbViewData -SqlInstance $TestConfig.InstanceCopy1 -Database tempdb -View dbatoolsci_view_example -Query "SELECT TOP (1) id FROM dbo.dbatoolsci_view_example4 ORDER BY id DESC" -DestinationTable dbatoolsci_example3 -Truncate $result.RowsCopied | Should -Be 1 } It "Copy data using a query that uses a 3 part query" { - $result = Copy-DbaDbViewData -SqlInstance $TestConfig.InstanceCopy1 -Database tempdb -View dbatoolsci_view_example -Query "SELECT TOP (1) Id FROM tempdb.dbo.dbatoolsci_view_example4 ORDER BY Id DESC" -DestinationTable dbatoolsci_example3 -Truncate + $result = Copy-DbaDbViewData -SqlInstance $TestConfig.InstanceCopy1 -Database tempdb -View dbatoolsci_view_example -Query "SELECT TOP (1) id FROM tempdb.dbo.dbatoolsci_view_example4 ORDER BY id DESC" -DestinationTable dbatoolsci_example3 -Truncate $result.RowsCopied | Should -Be 1 } } \ No newline at end of file diff --git a/tests/Disable-DbaForceNetworkEncryption.Tests.ps1 b/tests/Disable-DbaForceNetworkEncryption.Tests.ps1 index b92f96180a9d..a744fbd160a6 100644 --- a/tests/Disable-DbaForceNetworkEncryption.Tests.ps1 +++ b/tests/Disable-DbaForceNetworkEncryption.Tests.ps1 @@ -21,6 +21,28 @@ Describe $CommandName -Tag UnitTests { } Describe $CommandName -Tag IntegrationTests { + BeforeAll { + # We want to run all commands in the BeforeAll block with EnableException to ensure that the test fails if the setup fails. + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + + # Remember the state of the instance, so that AfterAll can put it back. + $forceEncryptionBefore = (Get-DbaForceNetworkEncryption -SqlInstance $TestConfig.InstanceSingle).ForceEncryption + + # We want to run all commands outside of the BeforeAll block without EnableException to be able to test for specific warnings. + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") + } + + AfterAll { + # We want to run all commands in the AfterAll block with EnableException to ensure that the test fails if the cleanup fails. + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + + if ($forceEncryptionBefore) { + $null = Enable-DbaForceNetworkEncryption -SqlInstance $TestConfig.InstanceSingle + } + + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") + } + Context "When disabling force network encryption" { It "Returns results with ForceEncryption set to false" { $results = Disable-DbaForceNetworkEncryption -SqlInstance $TestConfig.InstanceSingle -EnableException diff --git a/tests/Enable-DbaForceNetworkEncryption.Tests.ps1 b/tests/Enable-DbaForceNetworkEncryption.Tests.ps1 index c68f1b1417db..736980b9f463 100644 --- a/tests/Enable-DbaForceNetworkEncryption.Tests.ps1 +++ b/tests/Enable-DbaForceNetworkEncryption.Tests.ps1 @@ -25,6 +25,10 @@ Describe $CommandName -Tag IntegrationTests { # We want to run all commands in the BeforeAll block with EnableException to ensure that the test fails if the setup fails. $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + # Remember the state of the instance, so that AfterAll can put it back. The registry value survives everything + # except an explicit Disable, so without this the instance stays with force encryption after the test. + $forceEncryptionBefore = (Get-DbaForceNetworkEncryption -SqlInstance $TestConfig.InstanceSingle).ForceEncryption + # We want to run all commands outside of the BeforeAll block without EnableException to be able to test for specific warnings. $PSDefaultParameterValues.Remove("*-Dba*:EnableException") } @@ -33,6 +37,10 @@ Describe $CommandName -Tag IntegrationTests { # We want to run all commands in the AfterAll block with EnableException to ensure that the test fails if the cleanup fails. $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + if (-not $forceEncryptionBefore) { + $null = Disable-DbaForceNetworkEncryption -SqlInstance $TestConfig.InstanceSingle + } + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") } diff --git a/tests/Measure-DbaBackupThroughput.Tests.ps1 b/tests/Measure-DbaBackupThroughput.Tests.ps1 index 8211cd551c43..21e6f0cf6411 100644 --- a/tests/Measure-DbaBackupThroughput.Tests.ps1 +++ b/tests/Measure-DbaBackupThroughput.Tests.ps1 @@ -51,8 +51,20 @@ Describe $CommandName -Tag IntegrationTests { $null = Remove-DbaDatabase -SqlInstance $TestConfig.InstanceSingle -Database $testDb - # Remove the backup directory. - Remove-Item -Path $backupPath -Recurse + # Remove the backup directory. The backup was written seconds ago and the share can still hold the + # file open for a moment, which once made a single Remove-Item leave the whole folder behind. + # Retry a few times and let the last attempt report its error. + $attempts = 0 + while ((Test-Path -Path $backupPath) -and $attempts -lt 5) { + $attempts++ + Remove-Item -Path $backupPath -Recurse -ErrorAction SilentlyContinue + if (Test-Path -Path $backupPath) { + Start-Sleep -Seconds 1 + } + } + if (Test-Path -Path $backupPath) { + Remove-Item -Path $backupPath -Recurse + } $PSDefaultParameterValues.Remove("*-Dba*:EnableException") } @@ -64,4 +76,4 @@ Describe $CommandName -Tag IntegrationTests { $testResults | Should -Not -BeNullOrEmpty } } -} \ No newline at end of file +} \ No newline at end of file diff --git a/tests/New-DbaDatabase.Tests.ps1 b/tests/New-DbaDatabase.Tests.ps1 index fdd11e91daa4..31c450881080 100644 --- a/tests/New-DbaDatabase.Tests.ps1 +++ b/tests/New-DbaDatabase.Tests.ps1 @@ -273,10 +273,10 @@ Describe $CommandName -Tag IntegrationTests { $serverMulti1.Databases[$newDbName].FileGroups["PRIMARY"].Files["$($newDbName)_PRIMARY"].Growth | Should -Be 65536 $serverMulti1.Databases[$newDbName].FileGroups["PRIMARY"].Files["$($newDbName)_PRIMARY"].GrowthType | Should -Be "KB" - $serverMulti1.Databases[$newDbName].LogFiles["$($newDbName)_log"].Size | Should -Be 32768 - $serverMulti1.Databases[$newDbName].LogFiles["$($newDbName)_log"].MaxSize | Should -Be 524288 - $serverMulti1.Databases[$newDbName].LogFiles["$($newDbName)_log"].Growth | Should -Be 32768 - $serverMulti1.Databases[$newDbName].LogFiles["$($newDbName)_log"].GrowthType | Should -Be "KB" + $serverMulti1.Databases[$newDbName].LogFiles["$($newDbName)_Log"].Size | Should -Be 32768 + $serverMulti1.Databases[$newDbName].LogFiles["$($newDbName)_Log"].MaxSize | Should -Be 524288 + $serverMulti1.Databases[$newDbName].LogFiles["$($newDbName)_Log"].Growth | Should -Be 32768 + $serverMulti1.Databases[$newDbName].LogFiles["$($newDbName)_Log"].GrowthType | Should -Be "KB" $serverMulti1.Databases[$newDbName].FileGroups["$($newDbName)_MainData"].Files[0].Size | Should -Be 65536 $serverMulti1.Databases[$newDbName].FileGroups["$($newDbName)_MainData"].Files[0].MaxSize | Should -Be 524288 @@ -288,10 +288,10 @@ Describe $CommandName -Tag IntegrationTests { $serverMulti2.Databases[$newDbName].FileGroups["PRIMARY"].Files["$($newDbName)_PRIMARY"].Growth | Should -Be 65536 $serverMulti2.Databases[$newDbName].FileGroups["PRIMARY"].Files["$($newDbName)_PRIMARY"].GrowthType | Should -Be "KB" - $serverMulti2.Databases[$newDbName].LogFiles["$($newDbName)_log"].Size | Should -Be 32768 - $serverMulti2.Databases[$newDbName].LogFiles["$($newDbName)_log"].MaxSize | Should -Be 524288 - $serverMulti2.Databases[$newDbName].LogFiles["$($newDbName)_log"].Growth | Should -Be 32768 - $serverMulti2.Databases[$newDbName].LogFiles["$($newDbName)_log"].GrowthType | Should -Be "KB" + $serverMulti2.Databases[$newDbName].LogFiles["$($newDbName)_Log"].Size | Should -Be 32768 + $serverMulti2.Databases[$newDbName].LogFiles["$($newDbName)_Log"].MaxSize | Should -Be 524288 + $serverMulti2.Databases[$newDbName].LogFiles["$($newDbName)_Log"].Growth | Should -Be 32768 + $serverMulti2.Databases[$newDbName].LogFiles["$($newDbName)_Log"].GrowthType | Should -Be "KB" $serverMulti2.Databases[$newDbName].FileGroups["$($newDbName)_MainData"].Files[0].Size | Should -Be 65536 $serverMulti2.Databases[$newDbName].FileGroups["$($newDbName)_MainData"].Files[0].MaxSize | Should -Be 524288 @@ -313,8 +313,8 @@ Describe $CommandName -Tag IntegrationTests { $serverMulti1.Databases[$bug6780DbName].FileGroups["PRIMARY"].Files["$($bug6780DbName)_PRIMARY"].Growth | Should -Be $serverMulti1.Databases["model"].FileGroups["PRIMARY"].Files["modeldev"].Growth $serverMulti1.Databases[$bug6780DbName].FileGroups["PRIMARY"].Files["$($bug6780DbName)_PRIMARY"].GrowthType | Should -Be $serverMulti1.Databases["model"].FileGroups["PRIMARY"].Files["modeldev"].GrowthType - $serverMulti1.Databases[$bug6780DbName].LogFiles["$($bug6780DbName)_log"].Growth | Should -Be $serverMulti1.Databases["model"].LogFiles["modellog"].Growth - $serverMulti1.Databases[$bug6780DbName].LogFiles["$($bug6780DbName)_log"].GrowthType | Should -Be $serverMulti1.Databases["model"].LogFiles["modellog"].GrowthType + $serverMulti1.Databases[$bug6780DbName].LogFiles["$($bug6780DbName)_Log"].Growth | Should -Be $serverMulti1.Databases["model"].LogFiles["modellog"].Growth + $serverMulti1.Databases[$bug6780DbName].LogFiles["$($bug6780DbName)_Log"].GrowthType | Should -Be $serverMulti1.Databases["model"].LogFiles["modellog"].GrowthType # also check the randomDb since it was created without any additional params $serverMulti1.Databases[$($randomDb.Name)].FileGroups["PRIMARY"].Files["$($randomDb.Name)"].Growth | Should -Be $serverMulti1.Databases["model"].FileGroups["PRIMARY"].Files["modeldev"].Growth From 5fccaeb01e43589d85db011dfbf6c150e1850974 Mon Sep 17 00:00:00 2001 From: Andreas Jordan Date: Tue, 1 Sep 2026 10:53:59 +0200 Subject: [PATCH 2/3] Sync-DbaLoginPermission.Tests - Spell master in lower case and capture the warning for real The setup batch says USE Master, which a case sensitive instance rejects ("Database 'Master' does not exist"), so the whole batch is dropped, the login never exists on the source and the sync finds nothing. The setup now runs with EnableException like the other files, so a failing setup fails the file instead of two tests later on. The test also passed -WarningVariable $warn, an empty variable name, which hid that warning and made the warning assertion vacuous. (do Sync-DbaLoginPermission) --- tests/Sync-DbaLoginPermission.Tests.ps1 | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/Sync-DbaLoginPermission.Tests.ps1 b/tests/Sync-DbaLoginPermission.Tests.ps1 index 470a77bc3210..092577fc308d 100644 --- a/tests/Sync-DbaLoginPermission.Tests.ps1 +++ b/tests/Sync-DbaLoginPermission.Tests.ps1 @@ -26,12 +26,15 @@ Describe $CommandName -Tag UnitTests { Describe $CommandName -Tag IntegrationTests { BeforeAll { + # We want to run all commands in the BeforeAll block with EnableException to ensure that the test fails if the setup fails. + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + $tempguid = [guid]::newguid() $DBUserName = "dbatoolssci_$($tempguid.guid)" $CreateTestUser = @" CREATE LOGIN [$DBUserName] WITH PASSWORD = '$($tempguid.guid)'; -USE Master; +USE master; CREATE USER [$DBUserName] FOR LOGIN [$DBUserName] WITH DEFAULT_SCHEMA = dbo; GRANT VIEW ANY DEFINITION to [$DBUserName]; @@ -43,10 +46,18 @@ GRANT VIEW ANY DEFINITION to [$DBUserName]; CREATE LOGIN [$DBUserName] WITH PASSWORD = '$($tempguid.guid)'; "@ + + # We want to run all commands outside of the BeforeAll block without EnableException to be able to test for specific warnings. + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") } AfterAll { + # We want to run all commands in the AfterAll block with EnableException to ensure that the test fails if the cleanup fails. + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + $DropTestUser = "DROP LOGIN [$DBUserName]" Invoke-DbaQuery -SqlInstance $TestConfig.InstanceMulti1, $TestConfig.InstanceMulti2 -Query $DropTestUser -Database master + + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") } Context "Command execution and functionality" { @@ -59,7 +70,7 @@ CREATE LOGIN [$DBUserName] It "Should execute against active nodes" { # Creates the user on Invoke-DbaQuery -SqlInstance $TestConfig.InstanceMulti2 -Query $CreateTestLogin - $results = Sync-DbaLoginPermission -Source $TestConfig.InstanceMulti1 -Destination $TestConfig.InstanceMulti2 -Login $DBUserName -ExcludeLogin 'NotaLogin' -WarningVariable $warn + $results = Sync-DbaLoginPermission -Source $TestConfig.InstanceMulti1 -Destination $TestConfig.InstanceMulti2 -Login $DBUserName -ExcludeLogin 'NotaLogin' -WarningVariable warn $results.Status | Should -Be 'Successful' $warn | Should -BeNullOrEmpty } From c9efe82530cb711672feb3867fa6a29f89cb7d56 Mon Sep 17 00:00:00 2001 From: Andreas Jordan Date: Tue, 1 Sep 2026 12:10:29 +0200 Subject: [PATCH 3/3] Sync-DbaLoginPermission.Tests - Use double quotes for the PowerShell string literals (do Sync-DbaLoginPermission) --- tests/Sync-DbaLoginPermission.Tests.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Sync-DbaLoginPermission.Tests.ps1 b/tests/Sync-DbaLoginPermission.Tests.ps1 index 092577fc308d..3c7298a15222 100644 --- a/tests/Sync-DbaLoginPermission.Tests.ps1 +++ b/tests/Sync-DbaLoginPermission.Tests.ps1 @@ -70,14 +70,14 @@ CREATE LOGIN [$DBUserName] It "Should execute against active nodes" { # Creates the user on Invoke-DbaQuery -SqlInstance $TestConfig.InstanceMulti2 -Query $CreateTestLogin - $results = Sync-DbaLoginPermission -Source $TestConfig.InstanceMulti1 -Destination $TestConfig.InstanceMulti2 -Login $DBUserName -ExcludeLogin 'NotaLogin' -WarningVariable warn - $results.Status | Should -Be 'Successful' + $results = Sync-DbaLoginPermission -Source $TestConfig.InstanceMulti1 -Destination $TestConfig.InstanceMulti2 -Login $DBUserName -ExcludeLogin "NotaLogin" -WarningVariable warn + $results.Status | Should -Be "Successful" $warn | Should -BeNullOrEmpty } # The copy failes on Appveyor with: Failed to create or use STIG schema on APPVYR-WIN\sql2017 It "Should have copied the user permissions of $DBUserName" -Skip:$env:appveyor { - $permissionsAfter = Get-DbaUserPermission -SqlInstance $TestConfig.InstanceMulti2 -Database master | Where-Object { $_.member -eq $DBUserName -and $_.permission -eq 'VIEW ANY DEFINITION' } + $permissionsAfter = Get-DbaUserPermission -SqlInstance $TestConfig.InstanceMulti2 -Database master | Where-Object { $_.member -eq $DBUserName -and $_.permission -eq "VIEW ANY DEFINITION" } $permissionsAfter.member | Should -Be $DBUserName } }