Skip to content

Commit 745076b

Browse files
authored
Fix broken Invoke-PesterTests runner (#1663)
* update so tests can be run on mac with powershell * tighten up Invoke-SharedPesterTest.ps1 * further slim Invoke-SharedPesterTest.ps1 * try to remove diffs in tests * update so json is does not care about whitespace * revert changes to existing failing tests
1 parent 8e78b5f commit 745076b

9 files changed

Lines changed: 198 additions & 61 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ step-templates/*.py
1515
diff-output/
1616
/.vs
1717
!.vscode
18+
*copy.ps1
Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1+
param(
2+
[string] $Filter = "*"
3+
)
4+
15
$ErrorActionPreference = "Stop";
26
Set-StrictMode -Version "Latest";
37

48
$thisScript = $MyInvocation.MyCommand.Path;
59
$thisFolder = [System.IO.Path]::GetDirectoryName($thisScript);
610
$rootFolder = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($thisFolder, "..", "..")); # Adjust to always point to the root
7-
$testFiles = Get-ChildItem -Path "$thisFolder" -Filter "*.tests.ps1" -Recurse
11+
$sharedRunner = Join-Path $rootFolder "tools" "Invoke-SharedPesterTests.ps1"
812

913
function Unpack-Scripts-Under-Test {
14+
$testFiles = Get-ChildItem -Path "$thisFolder" -Filter "*.tests.ps1" -Recurse
15+
1016
foreach ($testFile in $testFiles) {
1117
$baseName = $testFile.BaseName -replace "\.ScriptBody.Tests$"
1218
$scriptFileName = "$baseName.ScriptBody.ps1"
@@ -35,28 +41,10 @@ function Unpack-Scripts-Under-Test {
3541
}
3642
}
3743

38-
function Import-Pester {
39-
# Attempt to use local Pester module, fallback to global if not found
40-
try {
41-
$packagesFolder = [System.IO.Path]::Combine($rootFolder, "packages")
42-
$pester3Path = [System.IO.Path]::Combine($packagesFolder, "Pester\tools\Pester")
43-
# Import the specific version of Pester 3.4.0
44-
Import-Module -Name $pester3Path -RequiredVersion 3.4.0 -ErrorAction Stop
45-
} catch {
46-
Write-Host "Using globally installed Pester module version 3.4.0."
47-
# Specify the exact version of Pester 3.x you have installed
48-
Import-Module -Name Pester -RequiredVersion 3.4.0 -ErrorAction Stop
49-
}
50-
}
51-
52-
function Run-Tests {
53-
# Find and run all Pester test files in the tests directory
54-
foreach ($testFile in $testFiles) {
55-
Write-Host "Running tests in: $($testFile.FullName)"
56-
Invoke-Pester -Path $testFile.FullName
57-
}
58-
}
59-
60-
Import-Pester
61-
Unpack-Scripts-Under-Test
62-
Run-Tests
44+
& $sharedRunner `
45+
-TestRoot $thisFolder `
46+
-Filter $Filter `
47+
-BeforeRun ${function:Unpack-Scripts-Under-Test} `
48+
-UsePassThruFailureCheck `
49+
-PreferredPesterVersion "3.4.0" `
50+
-SuiteName "step-templates"

step-templates/tests/sql-backup-database.ScriptBody.Tests.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
$ErrorActionPreference = "Stop";
22
Set-StrictMode -Version "Latest";
33

4-
. "$PSScriptRoot\..\sql-backup-database.ScriptBody.ps1"
4+
. (Join-Path $PSScriptRoot ".." "sql-backup-database.ScriptBody.ps1")
55

66
function SetupTestEnvironment {
77
param(
@@ -79,7 +79,7 @@ function SetupTestEnvironment {
7979
Describe "ApplyRetentionPolicy Tests" {
8080

8181
BeforeAll {
82-
$script:BackupDirectory = "C:\Backups"
82+
$script:BackupDirectory = Join-Path ([System.IO.Path]::GetTempPath()) "OctopusDeployLibrary-SqlBackupTests"
8383
$script:DatabaseName = "ExampleDB"
8484
$script:StartDate = Get-Date
8585
$script:timestampFormat = "yyyy-MM-dd-HHmmss"

step-templates/tests/windows-scheduled-task-create.ScriptBody.Tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
$ErrorActionPreference = "Stop";
22
Set-StrictMode -Version "Latest";
33

4-
. "$PSScriptRoot\..\windows-scheduled-task-create.ScriptBody.ps1"
4+
. (Join-Path $PSScriptRoot ".." "windows-scheduled-task-create.ScriptBody.ps1")
55

66
Describe "Create-ScheduledTask" {
77

tools/Invoke-SharedPesterTests.ps1

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
param(
2+
[Parameter(Mandatory = $true)]
3+
[string] $TestRoot,
4+
[string] $Filter = "*",
5+
[scriptblock] $BeforeRun,
6+
[string[]] $ImportModules = @(),
7+
[switch] $UsePassThruFailureCheck,
8+
[string] $PreferredPesterVersion,
9+
[string] $SuiteName = "tests"
10+
)
11+
12+
$ErrorActionPreference = "Stop";
13+
Set-StrictMode -Version "Latest";
14+
15+
$testRootPath = [System.IO.Path]::GetFullPath($TestRoot)
16+
$repoRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot ".."))
17+
$originalSystemRoot = $env:SystemRoot
18+
$originalTemp = $env:TEMP
19+
20+
function Get-PesterModuleSpec {
21+
$packagesFolder = Join-Path $repoRoot "packages"
22+
$attempts = @()
23+
$localPesterPaths = @()
24+
25+
if ($PreferredPesterVersion) {
26+
$localPesterPaths += (Join-Path $packagesFolder ("Pester.{0}" -f $PreferredPesterVersion) "tools" "Pester")
27+
}
28+
$localPesterPaths += (Join-Path $packagesFolder "Pester" "tools" "Pester")
29+
30+
foreach ($localPesterPath in $localPesterPaths | Select-Object -Unique) {
31+
$attempts += $localPesterPath
32+
if (Test-Path -Path $localPesterPath) {
33+
$localManifestPath = Join-Path $localPesterPath "Pester.psd1"
34+
$modulePath = $localPesterPath
35+
if (Test-Path -Path $localManifestPath) {
36+
$modulePath = $localManifestPath
37+
}
38+
39+
$module = Test-ModuleManifest -Path $modulePath -ErrorAction Stop
40+
if ($module.Version.Major -eq 3 -and ((-not $PreferredPesterVersion) -or $module.Version -eq [version]$PreferredPesterVersion)) {
41+
return [pscustomobject]@{
42+
ModulePath = $module.Path
43+
Version = $module.Version.ToString()
44+
Source = "repository packages"
45+
}
46+
}
47+
}
48+
}
49+
50+
$availablePesterModules = @(Get-Module -ListAvailable Pester | Sort-Object Version -Descending)
51+
$globalPester = $null
52+
53+
if ($PreferredPesterVersion) {
54+
$globalPester = $availablePesterModules | Where-Object { $_.Version -eq [version]$PreferredPesterVersion } | Select-Object -First 1
55+
}
56+
57+
if (-not $globalPester) {
58+
$globalPester = $availablePesterModules | Where-Object { $_.Version.Major -eq 3 } | Select-Object -First 1
59+
}
60+
61+
if ($globalPester) {
62+
return [pscustomobject]@{
63+
ModulePath = $globalPester.Path
64+
Version = $globalPester.Version.ToString()
65+
Source = "installed modules"
66+
}
67+
}
68+
69+
$preferredVersionMessage = if ($PreferredPesterVersion) { "preferred version $PreferredPesterVersion" } else { "a Pester 3.x version" }
70+
$attemptedPathsMessage = if ($attempts.Count -gt 0) { " Tried package paths: $($attempts -join ', ')." } else { "" }
71+
throw "Pester $preferredVersionMessage for suite '$SuiteName' was not found in the repository packages folder or installed modules.$attemptedPathsMessage"
72+
}
73+
74+
function Invoke-SelectedTests {
75+
param(
76+
[Parameter(Mandatory = $true)]
77+
[System.IO.FileInfo[]] $TestFiles
78+
)
79+
80+
foreach ($testFile in $TestFiles) {
81+
Write-Host "Running tests in: $($testFile.FullName)"
82+
if ($UsePassThruFailureCheck) {
83+
$result = Invoke-Pester -Path $testFile.FullName -PassThru
84+
if ($result.FailedCount -gt 0) {
85+
throw "Tests failed in $($testFile.FullName)."
86+
}
87+
} else {
88+
Invoke-Pester -Path $testFile.FullName
89+
}
90+
}
91+
}
92+
93+
try {
94+
if (-not $env:SystemRoot) {
95+
$env:SystemRoot = "C:\Windows"
96+
}
97+
if (-not $env:TEMP) {
98+
$env:TEMP = [System.IO.Path]::GetTempPath()
99+
}
100+
101+
foreach ($modulePath in $ImportModules) {
102+
Import-Module -Name $modulePath -ErrorAction Stop
103+
}
104+
105+
if ($BeforeRun) {
106+
& $BeforeRun
107+
}
108+
109+
$testFiles = @(Get-ChildItem -Path $testRootPath -Filter "*.tests.ps1" -Recurse)
110+
if (-not [string]::IsNullOrWhiteSpace($Filter) -and $Filter -ne "*") {
111+
$testFiles = @($testFiles | Where-Object { $_.Name -like $Filter -or $_.FullName -like $Filter })
112+
}
113+
114+
if ($testFiles.Count -eq 0) {
115+
Write-Host "No matching test files found under $testRootPath for filter '$Filter'."
116+
return
117+
}
118+
119+
if ($PSVersionTable.PSEdition -eq "Core" -and -not $IsWindows) {
120+
$referenceAssembliesPath = Join-Path $PSHOME "ref"
121+
if (-not (Test-Path -Path $referenceAssembliesPath)) {
122+
throw "Pester 3.4.3 on macOS requires a compatible pwsh installation with reference assemblies under '$referenceAssembliesPath'. This runner is intentionally lean and does not patch Pester at runtime."
123+
}
124+
}
125+
126+
$pesterModule = Get-PesterModuleSpec
127+
Write-Host "Using Pester module version $($pesterModule.Version) from $($pesterModule.Source)."
128+
Import-Module -Name $pesterModule.ModulePath -RequiredVersion $pesterModule.Version -ErrorAction Stop
129+
Invoke-SelectedTests -TestFiles $testFiles
130+
} finally {
131+
$env:SystemRoot = $originalSystemRoot
132+
$env:TEMP = $originalTemp
133+
}

tools/StepTemplatePacker/tests/ConvertTo-OctopusJson.Tests.ps1

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
$ErrorActionPreference = "Stop";
22
Set-StrictMode -Version "Latest";
3+
. (Join-Path (Split-Path -Parent $MyInvocation.MyCommand.Path) "Test-JsonAssertions.ps1")
34

45
Describe "ConvertTo-OctopusDeploy" {
56

@@ -55,8 +56,7 @@ Describe "ConvertTo-OctopusDeploy" {
5556
It "InputObject is a populated array" {
5657
$input = @( $null, 100, "my string" );
5758
$expected = "[`r`n null,`r`n 100,`r`n `"my string`"`r`n]";
58-
ConvertTo-OctopusJson -InputObject $input `
59-
| Should Be $expected;
59+
ConvertTo-OctopusJson -InputObject $input | Should BeJsonEquivalent $expected
6060
}
6161

6262
It "InputObject is an empty PSCustomObject" {
@@ -90,15 +90,15 @@ Describe "ConvertTo-OctopusDeploy" {
9090
"myPsObject": {
9191
"childProperty": "childValue"
9292
}
93-
}
93+
}
9494
"@
95-
ConvertTo-OctopusJson -InputObject $input `
96-
| Should Be $expected;
95+
$expected = $expected.Trim()
96+
ConvertTo-OctopusJson -InputObject $input | Should BeJsonEquivalent $expected
9797
}
9898

9999
It "InputObject is an unhandled type" {
100100
{ ConvertTo-OctopusJson -InputObject ([System.Guid]::NewGuid()) } `
101101
| Should Throw "Unhandled input object type 'System.Guid'.";
102102
}
103103

104-
}
104+
}
Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1+
param(
2+
[string] $Filter = "*"
3+
)
4+
15
$ErrorActionPreference = "Stop";
26
Set-StrictMode -Version "Latest";
37

48
$thisScript = $MyInvocation.MyCommand.Path;
59
$thisFolder = [System.IO.Path]::GetDirectoryName($thisScript);
6-
7-
$packagesFolder = $thisFolder;
8-
$packagesFolder = [System.IO.Path]::GetDirectoryName($packagesFolder);
9-
$packagesFolder = [System.IO.Path]::GetDirectoryName($packagesFolder);
10-
$packagesFolder = [System.IO.Path]::GetDirectoryName($packagesFolder);
11-
$packagesFolder = [System.IO.Path]::Combine($packagesFolder, "packages");
12-
10+
$repoRoot = [System.IO.Path]::GetFullPath((Join-Path $thisFolder ".." ".." ".."));
1311
$packer = [System.IO.Path]::GetDirectoryName($thisFolder);
12+
$sharedRunner = Join-Path $repoRoot "tools" "Invoke-SharedPesterTests.ps1";
1413

15-
Import-Module -Name $packer;
16-
Import-Module -Name ([System.IO.Path]::Combine($packagesFolder, "Pester.3.4.3\tools\Pester"));
17-
18-
Invoke-Pester;
14+
& $sharedRunner `
15+
-TestRoot $thisFolder `
16+
-Filter $Filter `
17+
-ImportModules @($packer) `
18+
-PreferredPesterVersion "3.4.3" `
19+
-SuiteName "StepTemplatePacker";
Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
$ErrorActionPreference = "Stop";
22
Set-StrictMode -Version "Latest";
3+
. (Join-Path (Split-Path -Parent $MyInvocation.MyCommand.Path) "Test-JsonAssertions.ps1")
34

45
Describe "Set-OctopusStepTemplateProperty" {
56

@@ -9,8 +10,7 @@ Describe "Set-OctopusStepTemplateProperty" {
910
-PropertyName "Octopus.Action.Script.Syntax" `
1011
-Value "PowerShell";
1112
$expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}";
12-
ConvertTo-OctopusJson -InputObject $stepJson `
13-
| Should Be $expected;
13+
ConvertTo-OctopusJson -InputObject $stepJson | Should BeJsonEquivalent $expected
1414
}
1515

1616
It "No properties exist" {
@@ -19,8 +19,7 @@ Describe "Set-OctopusStepTemplateProperty" {
1919
-PropertyName "Octopus.Action.Script.Syntax" `
2020
-Value "PowerShell";
2121
$expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}";
22-
ConvertTo-OctopusJson -InputObject $stepJson `
23-
| Should Be $expected;
22+
ConvertTo-OctopusJson -InputObject $stepJson | Should BeJsonEquivalent $expected
2423
}
2524

2625
It "Specified property does not exist" {
@@ -29,8 +28,7 @@ Describe "Set-OctopusStepTemplateProperty" {
2928
-PropertyName "Octopus.Action.Script.Syntax" `
3029
-Value "PowerShell";
3130
$expected = "{`r`n `"Properties`": {`r`n `"otherProperty`": `"`",`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}";
32-
ConvertTo-OctopusJson -InputObject $stepJson `
33-
| Should Be $expected;
31+
ConvertTo-OctopusJson -InputObject $stepJson | Should BeJsonEquivalent $expected
3432
}
3533

3634
It "Property does not exist" {
@@ -39,8 +37,7 @@ Describe "Set-OctopusStepTemplateProperty" {
3937
-PropertyName "Octopus.Action.Script.Syntax" `
4038
-Value "PowerShell";
4139
$expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}";
42-
ConvertTo-OctopusJson -InputObject $stepJson `
43-
| Should Be $expected;
40+
ConvertTo-OctopusJson -InputObject $stepJson | Should BeJsonEquivalent $expected
4441
}
4542

4643
It "Property exists with a null value" {
@@ -49,8 +46,7 @@ Describe "Set-OctopusStepTemplateProperty" {
4946
-PropertyName "Octopus.Action.Script.Syntax" `
5047
-Value "PowerShell";
5148
$expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}";
52-
ConvertTo-OctopusJson -InputObject $stepJson `
53-
| Should Be $expected;
49+
ConvertTo-OctopusJson -InputObject $stepJson | Should BeJsonEquivalent $expected
5450
}
5551

5652
It "Property exists with an empty string value" {
@@ -59,8 +55,7 @@ Describe "Set-OctopusStepTemplateProperty" {
5955
-PropertyName "Octopus.Action.Script.Syntax" `
6056
-Value "PowerShell";
6157
$expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}";
62-
ConvertTo-OctopusJson -InputObject $stepJson `
63-
| Should Be $expected;
58+
ConvertTo-OctopusJson -InputObject $stepJson | Should BeJsonEquivalent $expected
6459
}
6560

6661
It "Property exists with a string value" {
@@ -69,9 +64,8 @@ Describe "Set-OctopusStepTemplateProperty" {
6964
-PropertyName "Octopus.Action.Script.Syntax" `
7065
-Value "PowerShell";
7166
$expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}";
72-
ConvertTo-OctopusJson -InputObject $stepJson `
73-
| Should Be $expected;
67+
ConvertTo-OctopusJson -InputObject $stepJson | Should BeJsonEquivalent $expected
7468
}
7569

7670

77-
}
71+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
function global:ConvertTo-CompressedJsonForAssertion {
2+
param(
3+
[Parameter(Mandatory = $true)]
4+
[string] $Json
5+
)
6+
7+
return (ConvertFrom-Json -InputObject $Json | ConvertTo-Json -Depth 10 -Compress)
8+
}
9+
10+
function global:PesterBeJsonEquivalent($value, $expected) {
11+
return (ConvertTo-CompressedJsonForAssertion -Json $value) -eq (ConvertTo-CompressedJsonForAssertion -Json $expected)
12+
}
13+
14+
function global:PesterBeJsonEquivalentFailureMessage($value, $expected) {
15+
return "Expected JSON equivalent to: {$expected}`nBut was: {$value}"
16+
}
17+
18+
function global:NotPesterBeJsonEquivalentFailureMessage($value, $expected) {
19+
return "Expected JSON not equivalent to: {$expected}`nBut was: {$value}"
20+
}

0 commit comments

Comments
 (0)