Skip to content

Commit 2eee794

Browse files
authored
Integration test missing for Test-SqlDscIsDatabasePrincipal (#2260)
1 parent ae7fcc1 commit 2eee794

File tree

9 files changed

+352
-4
lines changed

9 files changed

+352
-4
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3939
- Added integration tests for `Test-SqlDscIsLogin` command to ensure it functions
4040
correctly in real environments
4141
[issue #2230](https://github.com/dsccommunity/SqlServerDsc/issues/2230).
42+
- Added integration tests for `Test-SqlDscIsDatabasePrincipal` command to ensure it
43+
functions correctly in real environments
44+
[issue #2231](https://github.com/dsccommunity/SqlServerDsc/issues/2231).
4245
- Added integration tests for `Set-SqlDscAudit` command to ensure it functions
4346
correctly in real environments
4447
[issue #2236](https://github.com/dsccommunity/SqlServerDsc/issues/2236).

azure-pipelines.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ stages:
317317
'tests/Integration/Commands/New-SqlDscRole.Integration.Tests.ps1'
318318
'tests/Integration/Commands/Get-SqlDscRole.Integration.Tests.ps1'
319319
'tests/Integration/Commands/Test-SqlDscIsRole.Integration.Tests.ps1'
320+
'tests/Integration/Commands/Test-SqlDscIsDatabasePrincipal.Integration.Tests.ps1'
320321
'tests/Integration/Commands/Grant-SqlDscServerPermission.Integration.Tests.ps1'
321322
'tests/Integration/Commands/Get-SqlDscServerPermission.Integration.Tests.ps1'
322323
'tests/Integration/Commands/Set-SqlDscServerPermission.Integration.Tests.ps1'

source/Public/Remove-SqlDscDatabase.ps1

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@
2424
on instances with a large amount of databases it might be better to make
2525
sure the **ServerObject** is recent enough, or pass in **DatabaseObject**.
2626
27+
.PARAMETER DropConnections
28+
Specifies that all active connections to the database should be dropped
29+
before removing the database. This sets the database to single-user mode
30+
with immediate rollback of active transactions, which forcibly disconnects
31+
all users and allows the database to be removed even when there are
32+
active connections.
33+
2734
.EXAMPLE
2835
$serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance'
2936
$databaseObject = $serverObject | Get-SqlDscDatabase -Name 'MyDatabase'
@@ -43,6 +50,14 @@
4350
4451
Removes the database named **MyDatabase** without prompting for confirmation.
4552
53+
.EXAMPLE
54+
$serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance'
55+
$serverObject | Remove-SqlDscDatabase -Name 'MyDatabase' -DropConnections -Force
56+
57+
Drops all active connections to the database named **MyDatabase** and then removes it
58+
without prompting for confirmation. This is useful when the database has active
59+
connections that prevent removal.
60+
4661
.OUTPUTS
4762
None.
4863
#>
@@ -72,7 +87,11 @@ function Remove-SqlDscDatabase
7287

7388
[Parameter(ParameterSetName = 'ServerObject')]
7489
[System.Management.Automation.SwitchParameter]
75-
$Refresh
90+
$Refresh,
91+
92+
[Parameter()]
93+
[System.Management.Automation.SwitchParameter]
94+
$DropConnections
7695
)
7796

7897
process
@@ -126,6 +145,23 @@ function Remove-SqlDscDatabase
126145
{
127146
try
128147
{
148+
# Drop all active connections if requested
149+
if ($DropConnections.IsPresent)
150+
{
151+
Write-Verbose -Message ($script:localizedData.Database_DroppingConnections -f $Name)
152+
153+
try
154+
{
155+
$DatabaseObject.UserAccess = 'Single'
156+
$DatabaseObject.Alter([Microsoft.SqlServer.Management.Smo.TerminationClause]::RollbackTransactionsImmediately)
157+
}
158+
catch
159+
{
160+
$errorMessage = $script:localizedData.Database_DropConnectionsFailed -f $Name
161+
New-InvalidOperationException -Message $errorMessage -ErrorRecord $_
162+
}
163+
}
164+
129165
Write-Verbose -Message ($script:localizedData.Database_Removing -f $Name)
130166

131167
$DatabaseObject.Drop()

source/en-US/SqlServerDsc.strings.psd1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,8 @@ ConvertFrom-StringData @'
384384
Database_Removed = Database '{0}' was removed successfully.
385385
Database_RemoveFailed = Failed to remove database '{0}' from instance '{1}'.
386386
Database_CannotRemoveSystem = Cannot remove system database '{0}'.
387+
Database_DroppingConnections = Dropping all active connections to database '{0}'.
388+
Database_DropConnectionsFailed = Failed to drop active connections for database '{0}'.
387389
Database_Remove_ShouldProcessVerboseDescription = Removing the database '{0}' from the instance '{1}'.
388390
Database_Remove_ShouldProcessVerboseWarning = Are you sure you want to remove the database '{0}'?
389391
# This string shall not end with full stop (.) since it is used as a title of ShouldProcess messages.

tests/Integration/Commands/Invoke-SqlDscQuery.Integration.Tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ INSERT INTO TestTable (Name, Value) VALUES ('Test1', 100), ('Test2', 200), ('Tes
6565
{
6666
try
6767
{
68-
Remove-SqlDscDatabase -ServerObject $script:serverObject -Name $script:testDatabaseName -Force -ErrorAction 'Stop'
68+
Remove-SqlDscDatabase -ServerObject $script:serverObject -Name $script:testDatabaseName -Force -DropConnections -ErrorAction 'Stop'
6969
}
7070
catch
7171
{

tests/Integration/Commands/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ Test-SqlDscIsLoginEnabled | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DS
7171
New-SqlDscRole | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | SqlDscIntegrationTestRole_Persistent role
7272
Get-SqlDscRole | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | -
7373
Test-SqlDscIsRole | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | -
74+
Test-SqlDscIsDatabasePrincipal | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | Test database and database principals
7475
Grant-SqlDscServerPermission | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | Grants CreateEndpoint permission to role
7576
Get-SqlDscServerPermission | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | -
7677
Set-SqlDscServerPermission | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | -
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Suppressing this rule because Script Analyzer does not understand Pester syntax.')]
2+
param ()
3+
4+
BeforeDiscovery {
5+
try
6+
{
7+
if (-not (Get-Module -Name 'DscResource.Test'))
8+
{
9+
# Assumes dependencies have been resolved, so if this module is not available, run 'noop' task.
10+
if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable))
11+
{
12+
# Redirect all streams to $null, except the error stream (stream 2)
13+
& "$PSScriptRoot/../../../build.ps1" -Tasks 'noop' 3>&1 4>&1 5>&1 6>&1 > $null
14+
}
15+
16+
# If the dependencies have not been resolved, this will throw an error.
17+
Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop'
18+
}
19+
}
20+
catch [System.IO.FileNotFoundException]
21+
{
22+
throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks noop" first.'
23+
}
24+
}
25+
26+
BeforeAll {
27+
$script:moduleName = 'SqlServerDsc'
28+
29+
Import-Module -Name $script:moduleName -Force -ErrorAction 'Stop'
30+
}
31+
32+
Describe 'Test-SqlDscIsDatabasePrincipal' -Tag @('Integration_SQL2017', 'Integration_SQL2019', 'Integration_SQL2022') {
33+
BeforeAll {
34+
$script:mockInstanceName = 'DSCSQLTEST'
35+
36+
$mockSqlAdministratorUserName = 'SqlAdmin' # Using computer name as NetBIOS name throw exception.
37+
$mockSqlAdministratorPassword = ConvertTo-SecureString -String 'P@ssw0rd1' -AsPlainText -Force
38+
39+
$script:mockSqlAdminCredential = [System.Management.Automation.PSCredential]::new($mockSqlAdministratorUserName, $mockSqlAdministratorPassword)
40+
41+
$script:serverObject = Connect-SqlDscDatabaseEngine -InstanceName $script:mockInstanceName -Credential $script:mockSqlAdminCredential
42+
43+
# Use a test database that should exist - we'll create it if it doesn't exist
44+
$script:testDatabaseName = 'IntegrationTestDatabase'
45+
46+
# Create test database if it doesn't exist
47+
if (-not $script:serverObject.Databases[$script:testDatabaseName])
48+
{
49+
$null = New-SqlDscDatabase -ServerObject $script:serverObject -Name $script:testDatabaseName -Force -ErrorAction 'Stop'
50+
}
51+
52+
# Test principals that should exist in the database
53+
$script:testUserName = 'IntegrationTestUser'
54+
$script:testRoleName = 'IntegrationTestRole'
55+
$script:testAppRoleName = 'IntegrationTestAppRole'
56+
57+
# Create test database user if it doesn't exist
58+
$testDatabase = $script:serverObject.Databases[$script:testDatabaseName]
59+
if (-not $testDatabase.Users[$script:testUserName])
60+
{
61+
$sqlCommand = "USE [$script:testDatabaseName]; CREATE USER [$script:testUserName] WITHOUT LOGIN;"
62+
$null = $script:serverObject.ConnectionContext.ExecuteNonQuery($sqlCommand)
63+
}
64+
65+
# Create test database role if it doesn't exist
66+
if (-not $testDatabase.Roles[$script:testRoleName])
67+
{
68+
$sqlCommand = "USE [$script:testDatabaseName]; CREATE ROLE [$script:testRoleName];"
69+
$null = $script:serverObject.ConnectionContext.ExecuteNonQuery($sqlCommand)
70+
}
71+
72+
# Create test application role if it doesn't exist
73+
if (-not $testDatabase.ApplicationRoles[$script:testAppRoleName])
74+
{
75+
$sqlCommand = "USE [$script:testDatabaseName]; CREATE APPLICATION ROLE [$script:testAppRoleName] WITH PASSWORD = 'TestPassword123';"
76+
$null = $script:serverObject.ConnectionContext.ExecuteNonQuery($sqlCommand)
77+
}
78+
79+
# Refresh database objects to ensure they are loaded
80+
$null = $testDatabase.Users.Refresh()
81+
$null = $testDatabase.Roles.Refresh()
82+
$null = $testDatabase.ApplicationRoles.Refresh()
83+
}
84+
85+
AfterAll {
86+
Disconnect-SqlDscDatabaseEngine -ServerObject $script:serverObject
87+
}
88+
89+
Context 'When testing database user existence' {
90+
It 'Should return True when database user exists' {
91+
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testUserName
92+
93+
$result | Should -BeOfType [System.Boolean]
94+
$result | Should -BeTrue
95+
}
96+
97+
It 'Should return False when database user does not exist' {
98+
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name 'NonExistentUser'
99+
100+
$result | Should -BeOfType [System.Boolean]
101+
$result | Should -BeFalse
102+
}
103+
104+
It 'Should return False when database user exists but ExcludeUsers is specified' {
105+
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testUserName -ExcludeUsers
106+
107+
$result | Should -BeOfType [System.Boolean]
108+
$result | Should -BeFalse
109+
}
110+
111+
It 'Should return True for dbo user' {
112+
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name 'dbo'
113+
114+
$result | Should -BeOfType [System.Boolean]
115+
$result | Should -BeTrue
116+
}
117+
}
118+
119+
Context 'When testing database role existence' {
120+
It 'Should return True when database role exists' {
121+
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testRoleName
122+
123+
$result | Should -BeOfType [System.Boolean]
124+
$result | Should -BeTrue
125+
}
126+
127+
It 'Should return True when fixed role exists' {
128+
# Test with built-in db_datareader role
129+
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name 'db_datareader'
130+
131+
$result | Should -BeOfType [System.Boolean]
132+
$result | Should -BeTrue
133+
}
134+
135+
It 'Should return False when fixed role exists but ExcludeFixedRoles is specified' {
136+
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name 'db_datareader' -ExcludeFixedRoles
137+
138+
$result | Should -BeOfType [System.Boolean]
139+
$result | Should -BeFalse
140+
}
141+
142+
It 'Should return False when database role exists but ExcludeRoles is specified' {
143+
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testRoleName -ExcludeRoles
144+
145+
$result | Should -BeOfType [System.Boolean]
146+
$result | Should -BeFalse
147+
}
148+
149+
It 'Should return True for user-defined role when ExcludeFixedRoles is specified' {
150+
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testRoleName -ExcludeFixedRoles
151+
152+
$result | Should -BeOfType [System.Boolean]
153+
$result | Should -BeTrue
154+
}
155+
}
156+
157+
Context 'When testing application role existence' {
158+
It 'Should return True when application role exists' {
159+
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testAppRoleName
160+
161+
$result | Should -BeOfType [System.Boolean]
162+
$result | Should -BeTrue
163+
}
164+
165+
It 'Should return False when application role exists but ExcludeApplicationRoles is specified' {
166+
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testAppRoleName -ExcludeApplicationRoles
167+
168+
$result | Should -BeOfType [System.Boolean]
169+
$result | Should -BeFalse
170+
}
171+
}
172+
173+
Context 'When testing multiple exclusion parameters' {
174+
It 'Should return False when principal exists but all types are excluded' {
175+
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testUserName -ExcludeUsers -ExcludeRoles -ExcludeApplicationRoles
176+
177+
$result | Should -BeOfType [System.Boolean]
178+
$result | Should -BeFalse
179+
}
180+
181+
It 'Should work with combination of exclusion parameters' {
182+
# Test role when users and app roles are excluded
183+
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testRoleName -ExcludeUsers -ExcludeApplicationRoles
184+
185+
$result | Should -BeOfType [System.Boolean]
186+
$result | Should -BeTrue
187+
}
188+
}
189+
190+
Context 'When testing pipeline parameter support' {
191+
It 'Should accept ServerObject from pipeline' {
192+
$result = $script:serverObject | Test-SqlDscIsDatabasePrincipal -DatabaseName $script:testDatabaseName -Name $script:testUserName
193+
194+
$result | Should -BeOfType [System.Boolean]
195+
$result | Should -BeTrue
196+
}
197+
}
198+
199+
Context 'When testing error conditions' {
200+
It 'Should throw when database does not exist' {
201+
{ Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName 'NonExistentDatabase' -Name 'SomePrincipal' -ErrorAction 'Stop' } | Should -Throw
202+
}
203+
}
204+
205+
Context 'When testing case sensitivity' {
206+
It 'Should handle case differences correctly for database names' {
207+
# Database names are case-insensitive in SQL Server
208+
$result1 = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName.ToUpper() -Name $script:testUserName
209+
$result2 = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName.ToLower() -Name $script:testUserName
210+
211+
$result1 | Should -BeOfType [System.Boolean]
212+
$result2 | Should -BeOfType [System.Boolean]
213+
$result1 | Should -Be $result2
214+
}
215+
216+
It 'Should handle case differences correctly for principal names' {
217+
# Principal names are case-insensitive in SQL Server
218+
$result1 = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testUserName.ToUpper()
219+
$result2 = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testUserName.ToLower()
220+
221+
$result1 | Should -BeOfType [System.Boolean]
222+
$result2 | Should -BeOfType [System.Boolean]
223+
$result1 | Should -Be $result2
224+
}
225+
}
226+
}

0 commit comments

Comments
 (0)