Skip to content

Commit c0feb7a

Browse files
Tests - use new Test-Config instead of constants.ps1 (#9524)
1 parent d615e36 commit c0feb7a

File tree

716 files changed

+5272
-5133
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

716 files changed

+5272
-5133
lines changed

.github/PULL_REQUEST_TEMPLATE.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
- [ ] Bug fix (non-breaking change, fixes #<!--issue number--> )
55
- [ ] New feature (non-breaking change, adds functionality, fixes #<!--issue number--> )
66
- [ ] Breaking change (affects multiple commands or functionality, fixes #<!--issue number--> )
7-
- [ ] Ran manual Pester test and has passed (`.\tests\manual.pester.ps1`)
7+
- [ ] Ran manual Pester test and has passed (`Invoke-ManualPester`)
88
- [ ] Adding code coverage to existing functionality
99
- [ ] Pester test is included
1010
- [ ] If new file reference added for test, has is been added to github.com/dataplat/appveyor-lab ?

dbatools.psm1

+5
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,11 @@ if (-not (Test-Path -Path "$script:PSModuleRoot\dbatools.dat") -or $script:seria
166166
. $file.FullName
167167
}
168168

169+
# All internal functions privately available within the toolset
170+
foreach ($file in (Get-ChildItem -Path "$script:PSModuleRoot/private/testing/" -Recurse -Filter *.ps1)) {
171+
. $file.FullName
172+
}
173+
169174
Write-ImportTime -Text "Loading internal commands via dotsource"
170175

171176
# All exported functions

private/testing/Get-TestConfig.ps1

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
function Get-TestConfig {
2+
param(
3+
[string]$LocalConfigPath = "$script:PSModuleRoot/tests/constants.local.ps1"
4+
)
5+
$config = [ordered]@{}
6+
7+
if (Test-Path $LocalConfigPath) {
8+
Write-Host "Tests will use local constants file: tests\constants.local.ps1." -ForegroundColor Cyan
9+
. $LocalConfigPath
10+
# Note: Local constants are sourced but not explicitly added to $config
11+
} elseif ($env:CODESPACES -and ($env:TERM_PROGRAM -eq 'vscode' -and $env:REMOTE_CONTAINERS)) {
12+
$config['Instance1'] = "dbatools1"
13+
$config['Instance2'] = "dbatools2"
14+
$config['Instance3'] = "dbatools3"
15+
$config['Instances'] = @($config['Instance1'], $config['Instance2'])
16+
17+
$config['SqlCred'] = [PSCredential]::new('sa', (ConvertTo-SecureString $env:SA_PASSWORD -AsPlainText -Force))
18+
$config['PSDefaultParameterValues'] = @{
19+
"*:SqlCredential" = $config['SqlCred']
20+
}
21+
} elseif ($env:GITHUB_WORKSPACE) {
22+
$config['DbaToolsCi_Computer'] = "localhost"
23+
$config['Instance1'] = "localhost"
24+
$config['Instance2'] = "localhost:14333"
25+
$config['Instance2SQLUserName'] = $null # placeholders for -SqlCredential testing
26+
$config['Instance2SQLPassword'] = $null
27+
$config['Instance3'] = "localhost"
28+
$config['Instance2_Detailed'] = "localhost,14333" # Just to make sure things parse a port properly
29+
$config['AppveyorLabRepo'] = "/tmp/appveyor-lab"
30+
$config['Instances'] = @($config['Instance1'], $config['Instance2'])
31+
$config['SsisServer'] = "localhost\sql2016"
32+
$config['AzureBlob'] = "https://dbatools.blob.core.windows.net/sql"
33+
$config['AzureBlobAccount'] = "dbatools"
34+
$config['AzureServer'] = 'psdbatools.database.windows.net'
35+
$config['AzureSqlDbLogin'] = "[email protected]"
36+
} else {
37+
$config['DbaToolsCi_Computer'] = "localhost"
38+
$config['Instance1'] = "localhost\sql2008r2sp2"
39+
$config['Instance2'] = "localhost\sql2016"
40+
$config['Instance2SQLUserName'] = $null # placeholders for -SqlCredential testing
41+
$config['Instance2SQLPassword'] = $null
42+
$config['Instance3'] = "localhost\sql2017"
43+
$config['Instance2_Detailed'] = "localhost,14333\sql2016" # Just to make sure things parse a port properly
44+
$config['AppveyorLabRepo'] = "C:\github\appveyor-lab"
45+
$config['Instances'] = @($config['Instance1'], $config['Instance2'])
46+
$config['SsisServer'] = "localhost\sql2016"
47+
$config['AzureBlob'] = "https://dbatools.blob.core.windows.net/sql"
48+
$config['AzureBlobAccount'] = "dbatools"
49+
$config['AzureServer'] = 'psdbatools.database.windows.net'
50+
$config['AzureSqlDbLogin'] = "[email protected]"
51+
$config['BigDatabaseBackup'] = 'C:\github\StackOverflowMini.bak'
52+
$config['BigDatabaseBackupSourceUrl'] = 'https://github.com/BrentOzarULTD/Stack-Overflow-Database/releases/download/20230114/StackOverflowMini.bak'
53+
}
54+
55+
if ($env:appveyor) {
56+
$config['PSDefaultParameterValues'] = @{
57+
'*:WarningAction' = 'SilentlyContinue'
58+
}
59+
}
60+
61+
[pscustomobject]$config
62+
}
+252
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
function Invoke-ManualPester {
2+
<#
3+
.SYNOPSIS
4+
Runs dbatools tests.
5+
6+
.DESCRIPTION
7+
This is an helper to automate running tests locally
8+
9+
.PARAMETER Path
10+
The Path to the test files to run. It accepts multiple test file paths passed in (e.g. .\Find-DbaOrphanedFile.Tests.ps1) as well
11+
as simple strings (e.g. "orphaned" will run all files matching .\*orphaned*.Tests.ps1)
12+
13+
.PARAMETER Show
14+
Gets passed down to Pester's -Show parameter (useful if you want to reduce verbosity)
15+
16+
.PARAMETER PassThru
17+
Gets passed down to Pester's -PassThru parameter (useful if you want to return an object to analyze)
18+
19+
.PARAMETER TestIntegration
20+
dbatools's suite has unittests and integrationtests. This switch enables IntegrationTests, which need live instances
21+
see constants.ps1 for customizations
22+
23+
.PARAMETER Coverage
24+
Enables measuring code coverage on the tested function
25+
26+
.PARAMETER DependencyCoverage
27+
Enables measuring code coverage also of "lower level" (i.e. called) functions
28+
29+
.PARAMETER ScriptAnalyzer
30+
Enables checking the called function's code with Invoke-ScriptAnalyzer, with dbatools's profile
31+
32+
.EXAMPLE
33+
Invoke-ManualPester -Path Find-DbaOrphanedFile.Tests.ps1 -TestIntegration -Coverage -DependencyCoverage -ScriptAnalyzer
34+
35+
The most complete number of checks:
36+
- Runs both unittests and integrationtests
37+
- Gathers and shows code coverage measurement for Find-DbaOrphanedFile and all its dependencies
38+
- Checks Find-DbaOrphanedFile with Invoke-ScriptAnalyzer
39+
40+
.EXAMPLE
41+
Invoke-ManualPester -Path Find-DbaOrphanedFile.Tests.ps1
42+
43+
Runs unittests stored in Find-DbaOrphanedFile.Tests.ps1
44+
45+
.EXAMPLE
46+
Invoke-ManualPester -Path Find-DbaOrphanedFile.Tests.ps1 -PassThru
47+
48+
Runs unittests stored in Find-DbaOrphanedFile.Tests.ps1 and returns an object that can be analyzed
49+
50+
.EXAMPLE
51+
Invoke-ManualPester -Path orphan
52+
53+
Runs unittests for all tests matching in `*orphan*.Tests.ps1
54+
55+
.EXAMPLE
56+
Invoke-ManualPester -Path Find-DbaOrphanedFile.Tests.ps1 -Show Default
57+
58+
Runs unittests stored in Find-DbaOrphanedFile.Tests.ps1, with reduced verbosity
59+
60+
.EXAMPLE
61+
Invoke-ManualPester -Path Find-DbaOrphanedFile.Tests.ps1 -TestIntegration
62+
63+
Runs both unittests and integrationtests stored in Find-DbaOrphanedFile.Tests.ps1
64+
65+
.EXAMPLE
66+
Invoke-ManualPester -Path Find-DbaOrphanedFile.Tests.ps1 -TestIntegration -Coverage
67+
68+
Gathers and shows code coverage measurement for Find-DbaOrphanedFile
69+
70+
.EXAMPLE
71+
Invoke-ManualPester -Path Find-DbaOrphanedFile.Tests.ps1 -TestIntegration -Coverage -DependencyCoverage
72+
73+
Gathers and shows code coverage measurement for Find-DbaOrphanedFile and all its dependencies
74+
75+
#>
76+
77+
[CmdletBinding()]
78+
param (
79+
[Parameter(Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)]
80+
[Alias('FullName')]
81+
[string[]]$Path,
82+
[ValidateSet('None', 'Default', 'Passed', 'Failed', 'Pending', 'Skipped', 'Inconclusive', 'Describe', 'Context', 'Summary', 'Header', 'All', 'Fails')]
83+
[string]$Show = "All",
84+
[switch]$PassThru,
85+
[switch]$TestIntegration,
86+
[switch]$Coverage,
87+
[switch]$DependencyCoverage,
88+
[switch]$ScriptAnalyzer
89+
)
90+
91+
<#
92+
Remove-Module -Name Pester
93+
Import-Module -name Pester -MaximumVersion 4.*
94+
#>
95+
96+
$invokeFormatterVersion = (Get-Command Invoke-Formatter -ErrorAction SilentlyContinue).Version
97+
$HasScriptAnalyzer = $null -ne $invokeFormatterVersion
98+
$MinimumPesterVersion = [Version] '3.4.5.0' # Because this is when -Show was introduced
99+
$MaximumPesterVersion = [Version] '5.0.0.0' # Because our tests (and runners) are only compatible with 4.*
100+
$PesterVersion = (Get-Command Invoke-Pester -ErrorAction SilentlyContinue).Version
101+
$HasPester = $null -ne $PesterVersion
102+
$ScriptAnalyzerCorrectVersion = '1.18.2'
103+
104+
if (!($HasScriptAnalyzer)) {
105+
Write-Warning "Please install PSScriptAnalyzer"
106+
Write-Warning " Install-Module -Name PSScriptAnalyzer -RequiredVersion '$ScriptAnalyzerCorrectVersion'"
107+
Write-Warning " or go to https://github.com/PowerShell/PSScriptAnalyzer"
108+
} else {
109+
if ($invokeFormatterVersion -ne $ScriptAnalyzerCorrectVersion) {
110+
Remove-Module PSScriptAnalyzer
111+
try {
112+
Import-Module PSScriptAnalyzer -RequiredVersion $ScriptAnalyzerCorrectVersion -ErrorAction Stop
113+
} catch {
114+
Write-Warning "Please install PSScriptAnalyzer $ScriptAnalyzerCorrectVersion"
115+
Write-Warning " Install-Module -Name PSScriptAnalyzer -RequiredVersion '$ScriptAnalyzerCorrectVersion'"
116+
}
117+
}
118+
}
119+
if (!($HasPester)) {
120+
Write-Warning "Please install Pester"
121+
Write-Warning " Install-Module -Name Pester -Force -SkipPublisherCheck"
122+
Write-Warning " or go to https://github.com/pester/Pester"
123+
}
124+
if ($PesterVersion -lt $MinimumPesterVersion) {
125+
Write-Warning "Please update Pester to at least 3.4.5"
126+
Write-Warning " Install-Module -Name Pester -MaximumVersion '4.10' -Force -SkipPublisherCheck"
127+
Write-Warning " or go to https://github.com/pester/Pester"
128+
}
129+
if ($PesterVersion -gt $MaximumPesterVersion) {
130+
Write-Warning "Please get Pester to the 4.* release"
131+
Write-Warning " Install-Module -Name Pester -MaximumVersion '4.10' -Force -SkipPublisherCheck"
132+
Write-Warning " or go to https://github.com/pester/Pester"
133+
}
134+
135+
if (($HasPester -and $HasScriptAnalyzer -and ($PesterVersion -ge $MinimumPesterVersion) -and ($PesterVersion -lt $MaximumPesterVersion) -and ($invokeFormatterVersion -eq $ScriptAnalyzerCorrectVersion)) -eq $false) {
136+
Write-Warning "Exiting..."
137+
return
138+
}
139+
140+
$ModuleBase = Split-Path -Path $PSScriptRoot -Parent
141+
142+
if (-not(Test-Path "$ModuleBase\.git" -Type Container)) {
143+
New-Item -Type Container -Path "$ModuleBase\.git" -Force
144+
}
145+
146+
#removes previously imported dbatools, if any
147+
# No need the force will do it
148+
# Remove-Module dbatools -ErrorAction Ignore
149+
#imports the module making sure DLL is loaded ok
150+
Import-Module "$ModuleBase\dbatools.psd1" -DisableNameChecking -Force
151+
#imports the psm1 to be able to use internal functions in tests
152+
Import-Module "$ModuleBase\dbatools.psm1" -DisableNameChecking -Force
153+
154+
$ScriptAnalyzerRulesExclude = @('PSUseOutputTypeCorrectly', 'PSAvoidUsingPlainTextForPassword', 'PSUseBOMForUnicodeEncodedFile')
155+
156+
$testInt = $false
157+
if ($config_TestIntegration) {
158+
$testInt = $true
159+
}
160+
if ($TestIntegration) {
161+
$testInt = $true
162+
}
163+
164+
function Get-CoverageIndications($Path, $ModuleBase) {
165+
# takes a test file path and figures out what to analyze for coverage (i.e. dependencies)
166+
$CBHRex = [regex]'(?smi)<#(.*)#>'
167+
$everything = (Get-Module dbatools).ExportedCommands.Values
168+
$everyfunction = $everything.Name
169+
$funcs = @()
170+
$leaf = Split-Path $path -Leaf
171+
# assuming Get-DbaFoo.Tests.ps1 wants coverage for "Get-DbaFoo"
172+
# but allowing also Get-DbaFoo.one.Tests.ps1 and Get-DbaFoo.two.Tests.ps1
173+
$func_name += ($leaf -replace '^([^.]+)(.+)?.Tests.ps1', '$1')
174+
if ($func_name -in $everyfunction) {
175+
$funcs += $func_name
176+
$f = $everything | Where-Object Name -eq $func_name
177+
$source = $f.Definition
178+
$CBH = $CBHRex.match($source).Value
179+
$cmdonly = $source.Replace($CBH, '')
180+
foreach ($e in $everyfunction) {
181+
# hacky, I know, but every occurrence of any function plus a space kinda denotes usage !?
182+
$searchme = "$e "
183+
if ($cmdonly.contains($searchme)) {
184+
$funcs += $e
185+
}
186+
}
187+
}
188+
$testpaths = @()
189+
$allfiles = Get-ChildItem -File -Path "$ModuleBase\private\functions", "$ModuleBase\public" -Filter '*.ps1'
190+
foreach ($f in $funcs) {
191+
# exclude always used functions ?!
192+
if ($f -in ('Connect-DbaInstance', 'Select-DefaultView', 'Stop-Function', 'Write-Message')) { continue }
193+
# can I find a correspondence to a physical file (again, on the convenience of having Get-DbaFoo.ps1 actually defining Get-DbaFoo)?
194+
$res = $allfiles | Where-Object { $_.Name.Replace('.ps1', '') -eq $f }
195+
if ($res.count -gt 0) {
196+
$testpaths += $res.FullName
197+
}
198+
}
199+
return @() + ($testpaths | Select-Object -Unique)
200+
}
201+
202+
$files = @()
203+
204+
if ($Path) {
205+
foreach ($item in $path) {
206+
if (Test-Path $item) {
207+
$files += Get-ChildItem -Path $item
208+
} else {
209+
$files += Get-ChildItem -Path "$ModuleBase\tests\*$item*.Tests.ps1"
210+
}
211+
}
212+
}
213+
214+
if ($files.Length -eq 0) {
215+
Write-Warning "No tests to be run"
216+
}
217+
218+
$AllTestsWithinScenario = $files
219+
220+
foreach ($f in $AllTestsWithinScenario) {
221+
$PesterSplat = @{
222+
'Script' = $f.FullName
223+
'Show' = $show
224+
'PassThru' = $passThru
225+
}
226+
#opt-in
227+
$HeadFunctionPath = $f.FullName
228+
229+
if ($Coverage -or $ScriptAnalyzer) {
230+
$CoverFiles = Get-CoverageIndications -Path $f -ModuleBase $ModuleBase
231+
$HeadFunctionPath = $CoverFiles | Select-Object -First 1
232+
}
233+
if ($Coverage) {
234+
if ($DependencyCoverage) {
235+
$CoverFilesPester = $CoverFiles
236+
} else {
237+
$CoverFilesPester = $HeadFunctionPath
238+
}
239+
$PesterSplat['CodeCoverage'] = $CoverFilesPester
240+
}
241+
if (!($testInt)) {
242+
$PesterSplat['ExcludeTag'] = "IntegrationTests"
243+
}
244+
Invoke-Pester @PesterSplat
245+
if ($ScriptAnalyzer) {
246+
if ($Show -ne "None") {
247+
Write-Host -ForegroundColor green -Object "ScriptAnalyzer check for $HeadFunctionPath"
248+
}
249+
Invoke-ScriptAnalyzer -Path $HeadFunctionPath -ExcludeRule $ScriptAnalyzerRulesExclude
250+
}
251+
}
252+
}

tests/Add-DbaAgDatabase.Tests.ps1

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
$CommandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
22
Write-Host -Object "Running $PSCommandpath" -ForegroundColor Cyan
3-
. "$PSScriptRoot\constants.ps1"
3+
$global:TestConfig = Get-TestConfig
44

55
Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
66
Context "Validate parameters" {
@@ -15,14 +15,14 @@ Describe "$CommandName Unit Tests" -Tag 'UnitTests' {
1515

1616
Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
1717
BeforeAll {
18-
$null = Get-DbaProcess -SqlInstance $script:instance3 -Program 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess -WarningAction SilentlyContinue
19-
$server = Connect-DbaInstance -SqlInstance $script:instance3
18+
$null = Get-DbaProcess -SqlInstance $TestConfig.instance3 -Program 'dbatools PowerShell module - dbatools.io' | Stop-DbaProcess -WarningAction SilentlyContinue
19+
$server = Connect-DbaInstance -SqlInstance $TestConfig.instance3
2020
$agname = "dbatoolsci_addagdb_agroup"
2121
$dbname = "dbatoolsci_addagdb_agroupdb"
2222
$newdbname = "dbatoolsci_addag_agroupdb_2"
2323
$server.Query("create database $dbname")
24-
$backup = Get-DbaDatabase -SqlInstance $script:instance3 -Database $dbname | Backup-DbaDatabase
25-
$ag = New-DbaAvailabilityGroup -Primary $script:instance3 -Name $agname -ClusterType None -FailoverMode Manual -Database $dbname -Confirm:$false -Certificate dbatoolsci_AGCert
24+
$backup = Get-DbaDatabase -SqlInstance $TestConfig.instance3 -Database $dbname | Backup-DbaDatabase
25+
$ag = New-DbaAvailabilityGroup -Primary $TestConfig.instance3 -Name $agname -ClusterType None -FailoverMode Manual -Database $dbname -Confirm:$false -Certificate dbatoolsci_AGCert
2626
}
2727
AfterAll {
2828
$null = Remove-DbaAvailabilityGroup -SqlInstance $server -AvailabilityGroup $agname -Confirm:$false
@@ -31,11 +31,11 @@ Describe "$commandname Integration Tests" -Tag "IntegrationTests" {
3131
Context "adds ag db" {
3232
It "returns proper results" {
3333
$server.Query("create database $newdbname")
34-
$backup = Get-DbaDatabase -SqlInstance $script:instance3 -Database $newdbname | Backup-DbaDatabase
35-
$results = Add-DbaAgDatabase -SqlInstance $script:instance3 -AvailabilityGroup $agname -Database $newdbname -Confirm:$false
34+
$backup = Get-DbaDatabase -SqlInstance $TestConfig.instance3 -Database $newdbname | Backup-DbaDatabase
35+
$results = Add-DbaAgDatabase -SqlInstance $TestConfig.instance3 -AvailabilityGroup $agname -Database $newdbname -Confirm:$false
3636
$results.AvailabilityGroup | Should -Be $agname
3737
$results.Name | Should -Be $newdbname
3838
$results.IsJoined | Should -Be $true
3939
}
4040
}
41-
} #$script:instance2 for appveyor
41+
} #$TestConfig.instance2 for appveyor

0 commit comments

Comments
 (0)