Skip to content

Commit 0f3d639

Browse files
authored
Add integration tests for Complete-SqlDscImage command (#2310)
1 parent 7b219cc commit 0f3d639

16 files changed

+880
-96
lines changed

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
### Added
99

10+
- Added script `Remove-SqlServerFromCIImage.ps1` to remove pre-installed SQL Server
11+
components from Microsoft Hosted agents that conflict with PrepareImage operations.
12+
The script is now run automatically in the CI pipeline before PrepareImage tests
13+
to resolve Sysprep compatibility errors [issue #2212](https://github.com/dsccommunity/SqlServerDsc/issues/2212).
14+
- Added integration tests for `Complete-SqlDscImage` command to ensure command
15+
reliability in prepared image installation workflows. The test runs in a separate
16+
pipeline job `Integration_Test_Commands_SqlServer_PreparedImage` with its own CI
17+
worker, and verifies the completion of SQL Server instances prepared using
18+
`Install-SqlDscServer` with the `-PrepareImage` parameter. The test includes
19+
scenarios with minimal parameters and various service account/directory
20+
configurations [issue #2212](https://github.com/dsccommunity/SqlServerDsc/issues/2212).
21+
- Added integration test for `Install-SqlDscServer` with the `-PrepareImage`
22+
parameter set to support the prepared image installation workflow. This test
23+
(`Install-SqlDscServer.Integration.PrepareImage.Tests.ps1`) runs in the
24+
`Integration_Test_Commands_SqlServer_PreparedImage` pipeline job and prepares
25+
a DSCSQLTEST instance that is later completed by `Complete-SqlDscImage` tests
26+
[issue #2212](https://github.com/dsccommunity/SqlServerDsc/issues/2212).
1027
- Added integration tests for `Initialize-SqlDscRebuildDatabase` command to ensure
1128
command reliability. The test runs in group 8, alongside `Repair-SqlDscServer`,
1229
to verify the rebuild database functionality on the DSCSQLTEST instance

azure-pipelines.yml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,81 @@ stages:
381381
testResultsFiles: '$(buildFolderName)/$(testResultFolderName)/NUnit*.xml'
382382
testRunTitle: 'Integration Commands ($(TEST_CONFIGURATION) / $(JOB_VMIMAGE))'
383383

384+
- stage: Integration_Test_Commands_SqlServer_PreparedImage
385+
displayName: 'Integration Test Commands - SQL Server (Prepared Image)'
386+
dependsOn: Quality_Test_and_Unit_Test
387+
jobs:
388+
- job: Test_Integration_PreparedImage
389+
displayName: 'Commands - Prepared Image'
390+
strategy:
391+
matrix:
392+
SQL2017_WIN2022:
393+
JOB_VMIMAGE: 'windows-2022'
394+
TEST_CONFIGURATION: 'Integration_SQL2017'
395+
SQL2019_WIN2022:
396+
JOB_VMIMAGE: 'windows-2022'
397+
TEST_CONFIGURATION: 'Integration_SQL2019'
398+
SQL2019_WIN2025:
399+
JOB_VMIMAGE: 'windows-2025'
400+
TEST_CONFIGURATION: 'Integration_SQL2019'
401+
SQL2022_WIN2022:
402+
JOB_VMIMAGE: 'windows-2022'
403+
TEST_CONFIGURATION: 'Integration_SQL2022'
404+
SQL2022_WIN2025:
405+
JOB_VMIMAGE: 'windows-2025'
406+
TEST_CONFIGURATION: 'Integration_SQL2022'
407+
pool:
408+
vmImage: $(JOB_VMIMAGE)
409+
timeoutInMinutes: '0'
410+
steps:
411+
- task: DownloadPipelineArtifact@2
412+
displayName: 'Download Build Artifact'
413+
inputs:
414+
buildType: 'current'
415+
artifactName: $(buildArtifactName)
416+
targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)'
417+
- task: PowerShell@2
418+
name: configureWinRM
419+
displayName: 'Configure WinRM'
420+
inputs:
421+
targetType: 'inline'
422+
script: 'winrm quickconfig -quiet'
423+
pwsh: false
424+
- powershell: |
425+
Import-Module -Name ./tests/TestHelpers/CommonTestHelper.psm1
426+
Remove-PowerShellModuleFromCI -Name @('SqlServer', 'SQLPS')
427+
Remove-Module -Name CommonTestHelper
428+
name: cleanCIWorker
429+
displayName: 'Clean CI worker'
430+
- powershell: |
431+
Write-Information -MessageData 'Removing pre-installed SQL Server components that conflict with PrepareImage...' -InformationAction Continue
432+
& ./tests/Integration/Commands/Scripts/Remove-SqlServerFromCIImage.ps1 -Confirm:$false -InformationAction Continue -Verbose
433+
name: removeSqlServerComponents
434+
displayName: 'Remove pre-installed SQL Server components'
435+
- powershell: |
436+
./build.ps1 -Tasks test -CodeCoverageThreshold 0 -PesterTag $(TEST_CONFIGURATION) -PesterPath @(
437+
# Run the integration tests in a specific group order for prepared image workflow.
438+
# Group 0
439+
'tests/Integration/Commands/Prerequisites.Integration.Tests.ps1'
440+
'tests/Integration/Commands/Save-SqlDscSqlServerMediaFile.Integration.Tests.ps1'
441+
'tests/Integration/Commands/Import-SqlDscPreferredModule.Integration.Tests.ps1'
442+
# Group 1
443+
'tests/Integration/Commands/Install-SqlDscServer.Integration.PrepareImage.Tests.ps1'
444+
# Group 2
445+
'tests/Integration/Commands/Complete-SqlDscImage.Integration.Tests.ps1'
446+
# Group 9
447+
'tests/Integration/Commands/Uninstall-SqlDscServer.Integration.Tests.ps1'
448+
)
449+
name: test
450+
displayName: 'Run Integration Test'
451+
- task: PublishTestResults@2
452+
displayName: 'Publish Test Results'
453+
condition: succeededOrFailed()
454+
inputs:
455+
testResultsFormat: 'NUnit'
456+
testResultsFiles: '$(buildFolderName)/$(testResultFolderName)/NUnit*.xml'
457+
testRunTitle: 'Integration Commands - Prepared Image ($(TEST_CONFIGURATION) / $(JOB_VMIMAGE))'
458+
384459
- stage: Integration_Test_Commands_ReportingServices
385460
displayName: 'Integration Test Commands - Reporting Services'
386461
dependsOn: Integration_Test_Commands_SqlServer
@@ -924,6 +999,7 @@ stages:
924999
dependsOn:
9251000
- Quality_Test_and_Unit_Test
9261001
- Integration_Test_Commands_SqlServer
1002+
- Integration_Test_Commands_SqlServer_PreparedImage
9271003
- Integration_Test_Commands_ReportingServices
9281004
- Integration_Test_Commands_BIReportServer
9291005
- Integration_Test_Resources_SqlServer

source/Private/Assert-SetupActionProperties.ps1

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,17 +131,38 @@ function Assert-SetupActionProperties
131131
)
132132
}
133133

134-
# If feature is SQLENGINE, then for specified setup actions the parameter AgtSvcAccount is mandatory.
135-
if ($SetupAction -in ('CompleteImage', 'InstallFailoverCluster', 'PrepareFailoverCluster', 'AddNode'))
134+
if ($SetupAction -in @('CompleteImage'))
136135
{
136+
# If feature is SQLENGINE, then InstanceId, SqlSvcAccount and AgtSvcAccount are mandatory.
137137
if ($Property.ContainsKey('Features') -and $Property.Features -contains 'SQLENGINE')
138138
{
139-
Assert-BoundParameter -BoundParameterList $Property -RequiredParameter @('AgtSvcAccount')
139+
Assert-BoundParameter -BoundParameterList $Property -RequiredParameter @(
140+
'InstanceId'
141+
'SqlSvcAccount'
142+
'AgtSvcAccount'
143+
)
144+
}
145+
}
146+
147+
if ($SetupAction -in @('PrepareImage'))
148+
{
149+
# If feature is SQLENGINE, then InstanceId is mandatory.
150+
if ($Property.ContainsKey('Features') -and $Property.Features -contains 'SQLENGINE')
151+
{
152+
Assert-BoundParameter -BoundParameterList $Property -RequiredParameter @(
153+
'InstanceId'
154+
)
140155
}
141156
}
142157

143-
if ($SetupAction -in ('InstallFailoverCluster', 'PrepareFailoverCluster', 'AddNode'))
158+
if ($SetupAction -in @('InstallFailoverCluster', 'PrepareFailoverCluster', 'AddNode'))
144159
{
160+
# If feature is SQLENGINE, then for specified setup actions the parameter AgtSvcAccount is mandatory.
161+
if ($Property.ContainsKey('Features') -and $Property.Features -contains 'SQLENGINE')
162+
{
163+
Assert-BoundParameter -BoundParameterList $Property -RequiredParameter @('AgtSvcAccount')
164+
}
165+
145166
# The parameter ASSvcAccount is mandatory if feature AS is installed and setup action is InstallFailoverCluster, PrepareFailoverCluster, or AddNode.
146167
if ($Property.ContainsKey('Features') -and $Property.Features -contains 'AS')
147168
{

source/Private/Invoke-SetupAction.ps1

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@
406406
Prepares the server for using the database engine for an instance named 'MyInstance'.
407407
408408
.EXAMPLE
409-
Invoke-SetupAction -CompleteImage -AcceptLicensingTerms -MediaPath 'E:\'
409+
Invoke-SetupAction -CompleteImage -AcceptLicensingTerms -InstanceId 'MSSQLSERVER' -SqlSvcAccount 'NT Service\MSSQLSERVER' -AgtSvcAccount 'NT Service\MSSQLSERVER' -MediaPath 'E:\'
410410
411411
Completes install on a server that was previously prepared (by using prepare image).
412412
@@ -694,6 +694,7 @@ function Invoke-SetupAction
694694

695695
[Parameter(ParameterSetName = 'Install')]
696696
[Parameter(ParameterSetName = 'InstallRole')]
697+
[Parameter(ParameterSetName = 'PrepareImage')]
697698
[Parameter(ParameterSetName = 'InstallFailoverCluster')]
698699
[Parameter(ParameterSetName = 'PrepareFailoverCluster')]
699700
[System.String]
@@ -710,7 +711,7 @@ function Invoke-SetupAction
710711

711712
[Parameter(ParameterSetName = 'Install')]
712713
[Parameter(ParameterSetName = 'InstallRole')]
713-
[Parameter(ParameterSetName = 'PrepareImage', Mandatory = $true)]
714+
[Parameter(ParameterSetName = 'PrepareImage')]
714715
[Parameter(ParameterSetName = 'CompleteImage')]
715716
[Parameter(ParameterSetName = 'Upgrade')]
716717
[Parameter(ParameterSetName = 'InstallFailoverCluster')]

source/Public/Complete-SqlDscImage.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@
161161
None.
162162
163163
.EXAMPLE
164-
Complete-SqlDscImage -AcceptLicensingTerms -MediaPath 'E:\'
164+
Complete-SqlDscImage -AcceptLicensingTerms -MediaPath 'E:\' -InstanceId 'MSSQLSERVER' -SqlSvcAccount 'NT Service\MSSQLSERVER' -AgtSvcAccount 'NT Service\MSSQLSERVER'
165165
166166
Completes the image installation of the SQL Server default instance that
167167
was prepared using `Install-SqlDscServer` with the parameter `-PrepareImage`.

source/Public/Install-SqlDscServer.ps1

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,7 @@ function Install-SqlDscServer
567567

568568
[Parameter(ParameterSetName = 'Install')]
569569
[Parameter(ParameterSetName = 'InstallRole')]
570+
[Parameter(ParameterSetName = 'PrepareImage')]
570571
[Parameter(ParameterSetName = 'InstallFailoverCluster')]
571572
[Parameter(ParameterSetName = 'PrepareFailoverCluster')]
572573
[System.String]
@@ -583,7 +584,7 @@ function Install-SqlDscServer
583584

584585
[Parameter(ParameterSetName = 'Install')]
585586
[Parameter(ParameterSetName = 'InstallRole')]
586-
[Parameter(ParameterSetName = 'PrepareImage', Mandatory = $true)]
587+
[Parameter(ParameterSetName = 'PrepareImage')]
587588
[Parameter(ParameterSetName = 'Upgrade')]
588589
[Parameter(ParameterSetName = 'InstallFailoverCluster')]
589590
[Parameter(ParameterSetName = 'PrepareFailoverCluster')]
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Suppressing this rule because Script Analyzer does not understand Pester syntax.')]
2+
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'Plain text used only for test credentials in CI.')]
3+
param ()
4+
5+
BeforeDiscovery {
6+
try
7+
{
8+
if (-not (Get-Module -Name 'DscResource.Test'))
9+
{
10+
# Assumes dependencies have been resolved, so if this module is not available, run 'noop' task.
11+
if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable))
12+
{
13+
# Redirect all streams to $null, except the error stream (stream 2)
14+
& "$PSScriptRoot/../../../build.ps1" -Tasks 'noop' 3>&1 4>&1 5>&1 6>&1 > $null
15+
}
16+
17+
# If the dependencies have not been resolved, this will throw an error.
18+
Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop'
19+
}
20+
}
21+
catch [System.IO.FileNotFoundException]
22+
{
23+
throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks noop" first.'
24+
}
25+
}
26+
27+
BeforeAll {
28+
$script:moduleName = 'SqlServerDsc'
29+
30+
Import-Module -Name $script:moduleName -Force -ErrorAction 'Stop'
31+
}
32+
33+
# cSpell: ignore DSCSQLTEST, PrepareImage, CompleteImage
34+
Describe 'Complete-SqlDscImage' -Tag @('Integration_SQL2017', 'Integration_SQL2019', 'Integration_SQL2022') {
35+
BeforeAll {
36+
Write-Verbose -Message ('Running integration test as user ''{0}''.' -f $env:UserName) -Verbose
37+
38+
$computerName = Get-ComputerName
39+
}
40+
41+
Context 'When completing a prepared SQL Server image' {
42+
It 'Should run the command without throwing' {
43+
# Set splatting parameters for Complete-SqlDscImage with service account configuration
44+
$completeSqlDscImageParameters = @{
45+
AcceptLicensingTerms = $true
46+
InstanceName = 'DSCSQLTEST'
47+
InstanceId = 'DSCSQLTEST'
48+
SqlSvcAccount = '{0}\svc-SqlPrimary' -f $computerName
49+
SqlSvcPassword = ConvertTo-SecureString -String 'yig-C^Equ3' -AsPlainText -Force
50+
SqlSvcStartupType = 'Automatic'
51+
AgtSvcAccount = '{0}\svc-SqlAgentPri' -f $computerName
52+
AgtSvcPassword = ConvertTo-SecureString -String 'yig-C^Equ3' -AsPlainText -Force
53+
AgtSvcStartupType = 'Automatic'
54+
BrowserSvcStartupType = 'Automatic'
55+
SecurityMode = 'SQL'
56+
SAPwd = ConvertTo-SecureString -String 'P@ssw0rd1' -AsPlainText -Force
57+
SqlSysAdminAccounts = @(
58+
('{0}\SqlAdmin' -f $computerName)
59+
)
60+
InstallSqlDataDir = 'C:\Program Files\Microsoft SQL Server\MSSQL\Data'
61+
SqlBackupDir = 'C:\Program Files\Microsoft SQL Server\MSSQL\Backup'
62+
SqlTempDbDir = 'C:\Program Files\Microsoft SQL Server\MSSQL\Data'
63+
SqlTempDbLogDir = 'C:\Program Files\Microsoft SQL Server\MSSQL\Data'
64+
SqlUserDbDir = 'C:\Program Files\Microsoft SQL Server\MSSQL\Data'
65+
SqlUserDbLogDir = 'C:\Program Files\Microsoft SQL Server\MSSQL\Data'
66+
TcpEnabled = $true
67+
NpEnabled = $false
68+
MediaPath = $env:IsoDrivePath
69+
Verbose = $true
70+
ErrorAction = 'Stop'
71+
Force = $true
72+
}
73+
74+
try
75+
{
76+
$null = Complete-SqlDscImage @completeSqlDscImageParameters
77+
}
78+
catch
79+
{
80+
# Output Summary.txt if it exists to help diagnose the failure
81+
$summaryFiles = Get-ChildItem -Path 'C:\Program Files\Microsoft SQL Server' -Filter 'Summary.txt' -Recurse -ErrorAction SilentlyContinue |
82+
Where-Object { $_.FullName -match '\\Setup Bootstrap\\Log\\' } |
83+
Sort-Object -Property LastWriteTime -Descending |
84+
Select-Object -First 1
85+
86+
if ($summaryFiles)
87+
{
88+
Write-Verbose "==== SQL Server Setup Summary.txt (from $($summaryFiles.FullName)) ====" -Verbose
89+
Get-Content -Path $summaryFiles.FullName | Write-Verbose -Verbose
90+
Write-Verbose "==== End of Summary.txt ====" -Verbose
91+
}
92+
else
93+
{
94+
Write-Verbose 'No Summary.txt file found.' -Verbose
95+
}
96+
97+
# Re-throw the original error
98+
throw $_
99+
}
100+
}
101+
}
102+
}

tests/Integration/Commands/Initialize-SqlDscRebuildDatabase.Integration.Tests.ps1

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ Describe 'Initialize-SqlDscRebuildDatabase' -Tag @('Integration_SQL2017', 'Integ
5050
$sqlService = Get-Service -Name $serviceName -ErrorAction 'Stop'
5151
if ($sqlService.Status -ne 'Stopped')
5252
{
53-
Write-Error -Message "Failed to stop SQL Server service '$serviceName'"
53+
throw "Failed to stop SQL Server service '$serviceName'"
5454
}
5555
}
5656

@@ -70,7 +70,7 @@ Describe 'Initialize-SqlDscRebuildDatabase' -Tag @('Integration_SQL2017', 'Integ
7070
$sqlService = Get-Service -Name $serviceName -ErrorAction 'Stop'
7171
if ($sqlService.Status -ne 'Running')
7272
{
73-
Write-Error -Message "Failed to start SQL Server service '$serviceName'"
73+
throw "Failed to start SQL Server service '$serviceName'"
7474
}
7575
}
7676

@@ -202,6 +202,20 @@ Describe 'Initialize-SqlDscRebuildDatabase' -Tag @('Integration_SQL2017', 'Integ
202202
$sqlService = Get-Service -Name $serviceName -ErrorAction 'Stop'
203203
$sqlService.Status | Should -Be 'Running'
204204
}
205+
206+
It 'Should have the requested TempDB file count' {
207+
$mockSqlAdministratorUserName = 'SqlAdmin'
208+
$mockSqlAdministratorPassword = ConvertTo-SecureString -String 'P@ssw0rd1' -AsPlainText -Force
209+
210+
$mockSqlAdministratorCredential = [System.Management.Automation.PSCredential]::new(
211+
$mockSqlAdministratorUserName,
212+
$mockSqlAdministratorPassword
213+
)
214+
215+
$server = Connect-SqlDscDatabaseEngine -InstanceName 'DSCSQLTEST' -Credential $mockSqlAdministratorCredential -ErrorAction 'Stop'
216+
$server.Databases['tempdb'].FileGroups['PRIMARY'].Files.Count | Should -Be 8
217+
Disconnect-SqlDscDatabaseEngine -ServerObject $server -ErrorAction 'Stop'
218+
}
205219
}
206220

207221
Context 'When specifying optional SqlCollation parameter' {
@@ -258,6 +272,20 @@ Describe 'Initialize-SqlDscRebuildDatabase' -Tag @('Integration_SQL2017', 'Integ
258272
$sqlService = Get-Service -Name $serviceName -ErrorAction 'Stop'
259273
$sqlService.Status | Should -Be 'Running'
260274
}
275+
276+
It 'Should set the requested server collation' {
277+
$mockSqlAdministratorUserName = 'SqlAdmin'
278+
$mockSqlAdministratorPassword = ConvertTo-SecureString -String 'P@ssw0rd1' -AsPlainText -Force
279+
280+
$mockSqlAdminCredential = [System.Management.Automation.PSCredential]::new(
281+
$mockSqlAdministratorUserName,
282+
$mockSqlAdministratorPassword
283+
)
284+
285+
$server = Connect-SqlDscDatabaseEngine -InstanceName 'DSCSQLTEST' -Credential $mockSqlAdminCredential -ErrorAction 'Stop'
286+
$server.Collation | Should -Be 'SQL_Latin1_General_CP1_CI_AS'
287+
Disconnect-SqlDscDatabaseEngine -ServerObject $server -ErrorAction 'Stop'
288+
}
261289
}
262290
}
263291
}

0 commit comments

Comments
 (0)