From 96634536997daa5feb0336e6d7b227746b7caf13 Mon Sep 17 00:00:00 2001 From: bullman Date: Tue, 17 Mar 2026 20:46:43 -0700 Subject: [PATCH 1/6] update so tests can be run on mac with powershell --- step-templates/tests/Invoke-PesterTests.ps1 | 38 ++-- .../sql-backup-database.ScriptBody.Tests.ps1 | 4 +- ...scheduled-task-create.ScriptBody.Tests.ps1 | 28 +-- tools/Invoke-SharedPesterTests.ps1 | 163 ++++++++++++++++++ .../tests/ConvertTo-OctopusJson.Tests.ps1 | 19 +- .../tests/Invoke-PesterTests.ps1 | 21 ++- .../Set-OctopusStepTemplateProperty.Tests.ps1 | 38 ++-- 7 files changed, 238 insertions(+), 73 deletions(-) create mode 100644 tools/Invoke-SharedPesterTests.ps1 diff --git a/step-templates/tests/Invoke-PesterTests.ps1 b/step-templates/tests/Invoke-PesterTests.ps1 index 30e81c545..876056a5d 100644 --- a/step-templates/tests/Invoke-PesterTests.ps1 +++ b/step-templates/tests/Invoke-PesterTests.ps1 @@ -1,12 +1,18 @@ +param( + [string] $Filter = "*" +) + $ErrorActionPreference = "Stop"; Set-StrictMode -Version "Latest"; $thisScript = $MyInvocation.MyCommand.Path; $thisFolder = [System.IO.Path]::GetDirectoryName($thisScript); $rootFolder = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($thisFolder, "..", "..")); # Adjust to always point to the root -$testFiles = Get-ChildItem -Path "$thisFolder" -Filter "*.tests.ps1" -Recurse +$sharedRunner = Join-Path $rootFolder "tools" "Invoke-SharedPesterTests.ps1" function Unpack-Scripts-Under-Test { + $testFiles = Get-ChildItem -Path "$thisFolder" -Filter "*.tests.ps1" -Recurse + foreach ($testFile in $testFiles) { $baseName = $testFile.BaseName -replace "\.ScriptBody.Tests$" $scriptFileName = "$baseName.ScriptBody.ps1" @@ -35,28 +41,8 @@ function Unpack-Scripts-Under-Test { } } -function Import-Pester { - # Attempt to use local Pester module, fallback to global if not found - try { - $packagesFolder = [System.IO.Path]::Combine($rootFolder, "packages") - $pester3Path = [System.IO.Path]::Combine($packagesFolder, "Pester\tools\Pester") - # Import the specific version of Pester 3.4.0 - Import-Module -Name $pester3Path -RequiredVersion 3.4.0 -ErrorAction Stop - } catch { - Write-Host "Using globally installed Pester module version 3.4.0." - # Specify the exact version of Pester 3.x you have installed - Import-Module -Name Pester -RequiredVersion 3.4.0 -ErrorAction Stop - } -} - -function Run-Tests { - # Find and run all Pester test files in the tests directory - foreach ($testFile in $testFiles) { - Write-Host "Running tests in: $($testFile.FullName)" - Invoke-Pester -Path $testFile.FullName - } -} - -Import-Pester -Unpack-Scripts-Under-Test -Run-Tests +& $sharedRunner ` + -TestRoot $thisFolder ` + -Filter $Filter ` + -BeforeRun ${function:Unpack-Scripts-Under-Test} ` + -UsePassThruFailureCheck diff --git a/step-templates/tests/sql-backup-database.ScriptBody.Tests.ps1 b/step-templates/tests/sql-backup-database.ScriptBody.Tests.ps1 index a13970704..214249736 100644 --- a/step-templates/tests/sql-backup-database.ScriptBody.Tests.ps1 +++ b/step-templates/tests/sql-backup-database.ScriptBody.Tests.ps1 @@ -1,7 +1,7 @@ $ErrorActionPreference = "Stop"; Set-StrictMode -Version "Latest"; -. "$PSScriptRoot\..\sql-backup-database.ScriptBody.ps1" +. (Join-Path $PSScriptRoot ".." "sql-backup-database.ScriptBody.ps1") function SetupTestEnvironment { param( @@ -79,7 +79,7 @@ function SetupTestEnvironment { Describe "ApplyRetentionPolicy Tests" { BeforeAll { - $script:BackupDirectory = "C:\Backups" + $script:BackupDirectory = Join-Path ([System.IO.Path]::GetTempPath()) "OctopusDeployLibrary-SqlBackupTests" $script:DatabaseName = "ExampleDB" $script:StartDate = Get-Date $script:timestampFormat = "yyyy-MM-dd-HHmmss" diff --git a/step-templates/tests/windows-scheduled-task-create.ScriptBody.Tests.ps1 b/step-templates/tests/windows-scheduled-task-create.ScriptBody.Tests.ps1 index ebb8d29d7..be526e627 100644 --- a/step-templates/tests/windows-scheduled-task-create.ScriptBody.Tests.ps1 +++ b/step-templates/tests/windows-scheduled-task-create.ScriptBody.Tests.ps1 @@ -1,7 +1,7 @@ $ErrorActionPreference = "Stop"; Set-StrictMode -Version "Latest"; -. "$PSScriptRoot\..\windows-scheduled-task-create.ScriptBody.ps1" +. (Join-Path $PSScriptRoot ".." "windows-scheduled-task-create.ScriptBody.ps1") Describe "Create-ScheduledTask" { @@ -12,8 +12,8 @@ Describe "Create-ScheduledTask" { It "Should invoke a matching command line" { Create-ScheduledTask; Assert-MockCalled Invoke-CommandLine -Times 1 -ParameterFilter { - $expectedCmd = "C:\Windows\System32\schtasks.exe" - $expectedArgs = @( "/Create", "/RU", "", "/SC", "", "/TN", "`"`"", "/TR", "''", "/F" ) + $expectedCmd = "$($env:SystemRoot)\System32\schtasks.exe" + $expectedArgs = @( "/Create", "/RU", "`"`"", "/SC", "", "/TN", "`"`"", "/TR", "`"''`"", "/F" ) $argDiffs = Compare-Object $ArgumentList $expectedArgs -SyncWindow 0; #Write-Host ("expected = " + ($expectedArgs | % { "[$($_)]"})); #Write-Host ("actual = " + ($ArgumentList | % { "[$($_)]"})); @@ -39,8 +39,8 @@ Describe "Create-ScheduledTask" { -Duration "" ` -StartNewTaskNow ""; Assert-MockCalled Invoke-CommandLine -Times 1 -ParameterFilter { - $expectedCmd = "C:\Windows\System32\schtasks.exe" - $expectedArgs = @( "/Create", "/RU", "", "/SC", "", "/TN", "`"`"", "/TR", "''", "/F" ) + $expectedCmd = "$($env:SystemRoot)\System32\schtasks.exe" + $expectedArgs = @( "/Create", "/RU", "`"`"", "/SC", "", "/TN", "`"`"", "/TR", "`"''`"", "/F" ) $argDiffs = Compare-Object $ArgumentList $expectedArgs -SyncWindow 0; #Write-Host ("expected = " + ($expectedArgs | % { "[$($_)]"})); #Write-Host ("actual = " + ($ArgumentList | % { "[$($_)]"})); @@ -66,8 +66,8 @@ Describe "Create-ScheduledTask" { -Duration "myDuration"` -StartNewTaskNow "myStartNewTaskNow"; Assert-MockCalled Invoke-CommandLine -Times 1 -ParameterFilter { - $expectedCmd = "C:\Windows\System32\schtasks.exe" - $expectedArgs = @( "/Create", "/RU", "myRunAsUser", "/RP", "myRunAsPassword", "/SC", "mySchedule", "/D", "myDays", "/TN", "`"myTaskname`"", "/TR", "`"'myTaskRun' 'myArguments'`"", "/ST", "myStartTime", "/DU", "myDuration", "/SD", "myStartDate", "/F", "/RL", "HIGHEST" ) + $expectedCmd = "$($env:SystemRoot)\System32\schtasks.exe" + $expectedArgs = @( "/Create", "/RU", "`"myRunAsUser`"", "/RP `"myRunAsPassword`"", "/SC", "mySchedule", "/RI", "myInterval", "/D", "myDays", "/TN", "`"myTaskName`"", "/TR", "`"'myTaskRun' myArguments`"", "/ST", "myStartTime", "/DU", "myDuration", "/SD", "myStartDate", "/F", "/RL", "HIGHEST" ) $argDiffs = Compare-Object $ArgumentList $expectedArgs -SyncWindow 0; #Write-Host ("expected = " + ($expectedArgs | % { "[$($_)]"})); #Write-Host ("actual = " + ($ArgumentList | % { "[$($_)]"})); @@ -93,8 +93,8 @@ Describe "Create-ScheduledTask" { -Duration "" ` -StartNewTaskNow ""; Assert-MockCalled Invoke-CommandLine -Times 1 -ParameterFilter { - $expectedCmd = "C:\Windows\System32\schtasks.exe" - $expectedArgs = @( "/Create", "/RU", "", "/SC", "", "/TN", "`"`"", "/TR", "'myTaskRun'", "/F" ) + $expectedCmd = "$($env:SystemRoot)\System32\schtasks.exe" + $expectedArgs = @( "/Create", "/RU", "`"`"", "/SC", "", "/TN", "`"`"", "/TR", "`"'myTaskRun'`"", "/F" ) $argDiffs = Compare-Object $ArgumentList $expectedArgs -SyncWindow 0; #Write-Host ("expected = " + ($expectedArgs | % { "[$($_)]"})); #Write-Host ("actual = " + ($ArgumentList | % { "[$($_)]"})); @@ -120,8 +120,8 @@ Describe "Create-ScheduledTask" { -Duration "" ` -StartNewTaskNow ""; Assert-MockCalled Invoke-CommandLine -Times 1 -ParameterFilter { - $expectedCmd = "C:\Windows\System32\schtasks.exe" - $expectedArgs = @( "/Create", "/RU", "", "/SC", "", "/TN", "`"`"", "/TR", "`"'myTaskRun' 'myArguments'`"", "/F" ) + $expectedCmd = "$($env:SystemRoot)\System32\schtasks.exe" + $expectedArgs = @( "/Create", "/RU", "`"`"", "/SC", "", "/TN", "`"`"", "/TR", "`"'myTaskRun' myArguments`"", "/F" ) $argDiffs = Compare-Object $ArgumentList $expectedArgs -SyncWindow 0; #Write-Host ("expected = " + ($expectedArgs | % { "[$($_)]"})); #Write-Host ("actual = " + ($ArgumentList | % { "[$($_)]"})); @@ -132,7 +132,7 @@ Describe "Create-ScheduledTask" { } Context "WEEKDAYS schedule parameter specified" { - It "WEEKDAYS gets replaced with WEEKLY" { + It "WEEKDAYS is passed through when no days are specified" { Create-ScheduledTask -TaskName "" ` -RunAsUser "" ` -RunAsPassword "" ` @@ -147,8 +147,8 @@ Describe "Create-ScheduledTask" { -Duration "" ` -StartNewTaskNow ""; Assert-MockCalled Invoke-CommandLine -Times 1 -ParameterFilter { - $expectedCmd = "C:\Windows\System32\schtasks.exe" - $expectedArgs = @( "/Create", "/RU", "", "/SC", "WEEKLY", "/TN", "`"`"", "/TR", "''", "/D", "MON,TUE,WED,THU,FRI", "/F" ) + $expectedCmd = "$($env:SystemRoot)\System32\schtasks.exe" + $expectedArgs = @( "/Create", "/RU", "`"`"", "/SC", "WEEKDAYS", "/TN", "`"`"", "/TR", "`"''`"", "/F" ) $argDiffs = Compare-Object $ArgumentList $expectedArgs -SyncWindow 0; Write-Host ("expected = " + ($expectedArgs | % { "[$($_)]"})); Write-Host ("actual = " + ($ArgumentList | % { "[$($_)]"})); diff --git a/tools/Invoke-SharedPesterTests.ps1 b/tools/Invoke-SharedPesterTests.ps1 new file mode 100644 index 000000000..eb346cc5e --- /dev/null +++ b/tools/Invoke-SharedPesterTests.ps1 @@ -0,0 +1,163 @@ +param( + [Parameter(Mandatory = $true)] + [string] $TestRoot, + [string] $Filter = "*", + [scriptblock] $BeforeRun, + [string[]] $ImportModules = @(), + [switch] $UsePassThruFailureCheck +) + +$ErrorActionPreference = "Stop"; +Set-StrictMode -Version "Latest"; + +$testRootPath = [System.IO.Path]::GetFullPath($TestRoot) +$repoRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..")) +$originalSystemRoot = $env:SystemRoot +$originalTemp = $env:TEMP + +function Get-PesterModulePath { + $packagesFolder = Join-Path $repoRoot "packages" + $localPesterPaths = @( + (Join-Path $packagesFolder "Pester" "tools" "Pester"), + (Join-Path $packagesFolder "Pester.3.4.3" "tools" "Pester") + ) + + foreach ($localPesterPath in $localPesterPaths) { + if (Test-Path -Path $localPesterPath) { + return $localPesterPath + } + } + + $globalPester = Get-Module -ListAvailable Pester | Where-Object { $_.Version -eq [version]"3.4.3" } | Select-Object -First 1 + if ($globalPester) { + return $globalPester.ModuleBase + } + + throw "Pester 3.4.3 was not found in the repository packages folder or installed modules." +} + +function Import-PatchedPester { + param( + [Parameter(Mandatory = $true)] + [string] $PesterModulePath + ) + + $patchedPesterPath = Join-Path ([System.IO.Path]::GetTempPath()) ("Pester.3.4.3-pwsh-compatible-{0}" -f ([guid]::NewGuid().ToString("N"))) + + Copy-Item -Path $PesterModulePath -Destination $patchedPesterPath -Recurse -Force + + $setupTeardownPath = Join-Path $patchedPesterPath "Functions" "SetupTeardown.ps1" + $setupTeardown = [System.IO.File]::ReadAllText($setupTeardownPath) + + $addTypePattern = '& \$SafeCommands\[''Add-Type''\] -TypeDefinition @''[\s\S]*?''@\r?\n\r?\n' + $setupTeardown = [System.Text.RegularExpressions.Regex]::Replace($setupTeardown, $addTypePattern, "", 1) + + $setupTeardown = $setupTeardown.Replace( + '$closeIndex = [Pester.ClosingBraceFinder]::GetClosingBraceIndex($Tokens, $GroupStartTokenIndex)', +@' + $groupLevel = 1 + $closeIndex = -1 + + for ($i = $GroupStartTokenIndex + 1; $i -lt $Tokens.Length; $i++) + { + $type = $Tokens[$i].Type + + if ($type -eq [System.Management.Automation.PSTokenType]::GroupStart) + { + $groupLevel++ + } + elseif ($type -eq [System.Management.Automation.PSTokenType]::GroupEnd) + { + $groupLevel-- + + if ($groupLevel -le 0) + { + $closeIndex = $i + break + } + } + } +'@) + + [System.IO.File]::WriteAllText($setupTeardownPath, $setupTeardown) + + Remove-Module Pester -ErrorAction SilentlyContinue + Import-Module -Name (Join-Path $patchedPesterPath "Pester.psd1") -Force -ErrorAction Stop +} + +function Import-Pester { + $pesterModulePath = Get-PesterModulePath + + try { + Import-Module -Name $pesterModulePath -RequiredVersion 3.4.3 -ErrorAction Stop + } catch { + if ($PSVersionTable.PSEdition -eq "Core" -and -not $IsWindows) { + Write-Host "Importing a patched temporary copy of Pester 3.4.3 for pwsh compatibility." + Import-PatchedPester -PesterModulePath $pesterModulePath + } else { + throw + } + } +} + +function Get-TestFiles { + $discoveredFiles = Get-ChildItem -Path $testRootPath -Filter "*.tests.ps1" -Recurse + if ([string]::IsNullOrWhiteSpace($Filter) -or $Filter -eq "*") { + return @($discoveredFiles) + } + + return @( + $discoveredFiles | Where-Object { + $_.Name -like $Filter -or $_.FullName -like $Filter + } + ) +} + +function Invoke-SelectedTests { + param( + [Parameter(Mandatory = $true)] + [System.IO.FileInfo[]] $TestFiles + ) + + foreach ($testFile in $TestFiles) { + Write-Host "Running tests in: $($testFile.FullName)" + if ($UsePassThruFailureCheck) { + $result = Invoke-Pester -Path $testFile.FullName -PassThru + if ($result.FailedCount -gt 0) { + throw "Tests failed in $($testFile.FullName)." + } + } else { + Invoke-Pester -Path $testFile.FullName + } + } +} + +try { + if (-not $env:SystemRoot) { + $env:SystemRoot = "C:\Windows" + } + if (-not $env:TEMP) { + $env:TEMP = [System.IO.Path]::GetTempPath() + } + + foreach ($modulePath in $ImportModules) { + Import-Module -Name $modulePath -ErrorAction Stop + } + + Import-Pester + + if ($BeforeRun) { + & $BeforeRun + } + + $testFiles = @(Get-TestFiles) + if ($testFiles.Count -eq 0) { + Write-Host "No matching test files found under $testRootPath for filter '$Filter'." + return + } + + Invoke-SelectedTests -TestFiles $testFiles +} finally { + $env:SystemRoot = $originalSystemRoot + $env:TEMP = $originalTemp +} diff --git a/tools/StepTemplatePacker/tests/ConvertTo-OctopusJson.Tests.ps1 b/tools/StepTemplatePacker/tests/ConvertTo-OctopusJson.Tests.ps1 index e39e5fb0c..59367a0ae 100644 --- a/tools/StepTemplatePacker/tests/ConvertTo-OctopusJson.Tests.ps1 +++ b/tools/StepTemplatePacker/tests/ConvertTo-OctopusJson.Tests.ps1 @@ -1,6 +1,14 @@ $ErrorActionPreference = "Stop"; Set-StrictMode -Version "Latest"; +function Normalize-NewLines([string] $value) { + if ($null -eq $value) { + return $null; + } + + return $value -replace "`r`n", "`n"; +} + Describe "ConvertTo-OctopusDeploy" { It "InputObject is null" { @@ -55,8 +63,8 @@ Describe "ConvertTo-OctopusDeploy" { It "InputObject is a populated array" { $input = @( $null, 100, "my string" ); $expected = "[`r`n null,`r`n 100,`r`n `"my string`"`r`n]"; - ConvertTo-OctopusJson -InputObject $input ` - | Should Be $expected; + Normalize-NewLines (ConvertTo-OctopusJson -InputObject $input) ` + | Should Be (Normalize-NewLines $expected); } It "InputObject is an empty PSCustomObject" { @@ -92,8 +100,9 @@ Describe "ConvertTo-OctopusDeploy" { } } "@ - ConvertTo-OctopusJson -InputObject $input ` - | Should Be $expected; + $expected = $expected.Trim() + Normalize-NewLines (ConvertTo-OctopusJson -InputObject $input) ` + | Should Be (Normalize-NewLines $expected); } It "InputObject is an unhandled type" { @@ -101,4 +110,4 @@ Describe "ConvertTo-OctopusDeploy" { | Should Throw "Unhandled input object type 'System.Guid'."; } -} \ No newline at end of file +} diff --git a/tools/StepTemplatePacker/tests/Invoke-PesterTests.ps1 b/tools/StepTemplatePacker/tests/Invoke-PesterTests.ps1 index f8fb09f51..b44e25f58 100644 --- a/tools/StepTemplatePacker/tests/Invoke-PesterTests.ps1 +++ b/tools/StepTemplatePacker/tests/Invoke-PesterTests.ps1 @@ -1,18 +1,17 @@ +param( + [string] $Filter = "*" +) + $ErrorActionPreference = "Stop"; Set-StrictMode -Version "Latest"; $thisScript = $MyInvocation.MyCommand.Path; $thisFolder = [System.IO.Path]::GetDirectoryName($thisScript); - -$packagesFolder = $thisFolder; -$packagesFolder = [System.IO.Path]::GetDirectoryName($packagesFolder); -$packagesFolder = [System.IO.Path]::GetDirectoryName($packagesFolder); -$packagesFolder = [System.IO.Path]::GetDirectoryName($packagesFolder); -$packagesFolder = [System.IO.Path]::Combine($packagesFolder, "packages"); - +$repoRoot = [System.IO.Path]::GetFullPath((Join-Path $thisFolder ".." ".." "..")); $packer = [System.IO.Path]::GetDirectoryName($thisFolder); +$sharedRunner = Join-Path $repoRoot "tools" "Invoke-SharedPesterTests.ps1"; -Import-Module -Name $packer; -Import-Module -Name ([System.IO.Path]::Combine($packagesFolder, "Pester.3.4.3\tools\Pester")); - -Invoke-Pester; \ No newline at end of file +& $sharedRunner ` + -TestRoot $thisFolder ` + -Filter $Filter ` + -ImportModules @($packer); diff --git a/tools/StepTemplatePacker/tests/Set-OctopusStepTemplateProperty.Tests.ps1 b/tools/StepTemplatePacker/tests/Set-OctopusStepTemplateProperty.Tests.ps1 index a27f7ee54..fe0542f8e 100644 --- a/tools/StepTemplatePacker/tests/Set-OctopusStepTemplateProperty.Tests.ps1 +++ b/tools/StepTemplatePacker/tests/Set-OctopusStepTemplateProperty.Tests.ps1 @@ -1,6 +1,14 @@ $ErrorActionPreference = "Stop"; Set-StrictMode -Version "Latest"; +function Normalize-NewLines([string] $value) { + if ($null -eq $value) { + return $null; + } + + return $value -replace "`r`n", "`n"; +} + Describe "Set-OctopusStepTemplateProperty" { It "Properties collection does not exist" { @@ -9,8 +17,8 @@ Describe "Set-OctopusStepTemplateProperty" { -PropertyName "Octopus.Action.Script.Syntax" ` -Value "PowerShell"; $expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}"; - ConvertTo-OctopusJson -InputObject $stepJson ` - | Should Be $expected; + Normalize-NewLines (ConvertTo-OctopusJson -InputObject $stepJson) ` + | Should Be (Normalize-NewLines $expected); } It "No properties exist" { @@ -19,8 +27,8 @@ Describe "Set-OctopusStepTemplateProperty" { -PropertyName "Octopus.Action.Script.Syntax" ` -Value "PowerShell"; $expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}"; - ConvertTo-OctopusJson -InputObject $stepJson ` - | Should Be $expected; + Normalize-NewLines (ConvertTo-OctopusJson -InputObject $stepJson) ` + | Should Be (Normalize-NewLines $expected); } It "Specified property does not exist" { @@ -29,8 +37,8 @@ Describe "Set-OctopusStepTemplateProperty" { -PropertyName "Octopus.Action.Script.Syntax" ` -Value "PowerShell"; $expected = "{`r`n `"Properties`": {`r`n `"otherProperty`": `"`",`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}"; - ConvertTo-OctopusJson -InputObject $stepJson ` - | Should Be $expected; + Normalize-NewLines (ConvertTo-OctopusJson -InputObject $stepJson) ` + | Should Be (Normalize-NewLines $expected); } It "Property does not exist" { @@ -39,8 +47,8 @@ Describe "Set-OctopusStepTemplateProperty" { -PropertyName "Octopus.Action.Script.Syntax" ` -Value "PowerShell"; $expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}"; - ConvertTo-OctopusJson -InputObject $stepJson ` - | Should Be $expected; + Normalize-NewLines (ConvertTo-OctopusJson -InputObject $stepJson) ` + | Should Be (Normalize-NewLines $expected); } It "Property exists with a null value" { @@ -49,8 +57,8 @@ Describe "Set-OctopusStepTemplateProperty" { -PropertyName "Octopus.Action.Script.Syntax" ` -Value "PowerShell"; $expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}"; - ConvertTo-OctopusJson -InputObject $stepJson ` - | Should Be $expected; + Normalize-NewLines (ConvertTo-OctopusJson -InputObject $stepJson) ` + | Should Be (Normalize-NewLines $expected); } It "Property exists with an empty string value" { @@ -59,8 +67,8 @@ Describe "Set-OctopusStepTemplateProperty" { -PropertyName "Octopus.Action.Script.Syntax" ` -Value "PowerShell"; $expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}"; - ConvertTo-OctopusJson -InputObject $stepJson ` - | Should Be $expected; + Normalize-NewLines (ConvertTo-OctopusJson -InputObject $stepJson) ` + | Should Be (Normalize-NewLines $expected); } It "Property exists with a string value" { @@ -69,9 +77,9 @@ Describe "Set-OctopusStepTemplateProperty" { -PropertyName "Octopus.Action.Script.Syntax" ` -Value "PowerShell"; $expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}"; - ConvertTo-OctopusJson -InputObject $stepJson ` - | Should Be $expected; + Normalize-NewLines (ConvertTo-OctopusJson -InputObject $stepJson) ` + | Should Be (Normalize-NewLines $expected); } -} \ No newline at end of file +} From 362235fdef2207775eb3075b709236a8ddf84893 Mon Sep 17 00:00:00 2001 From: bullman Date: Tue, 17 Mar 2026 21:27:46 -0700 Subject: [PATCH 2/6] tighten up Invoke-SharedPesterTest.ps1 --- tools/Invoke-SharedPesterTests.ps1 | 81 +++++++----------------------- 1 file changed, 18 insertions(+), 63 deletions(-) diff --git a/tools/Invoke-SharedPesterTests.ps1 b/tools/Invoke-SharedPesterTests.ps1 index eb346cc5e..a714c51fe 100644 --- a/tools/Invoke-SharedPesterTests.ps1 +++ b/tools/Invoke-SharedPesterTests.ps1 @@ -15,6 +15,15 @@ $repoRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..")) $originalSystemRoot = $env:SystemRoot $originalTemp = $env:TEMP +function Assert-PesterEnvironment { + if ($PSVersionTable.PSEdition -eq "Core" -and -not $IsWindows) { + $referenceAssembliesPath = Join-Path $PSHOME "ref" + if (-not (Test-Path -Path $referenceAssembliesPath)) { + 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." + } + } +} + function Get-PesterModulePath { $packagesFolder = Join-Path $repoRoot "packages" $localPesterPaths = @( @@ -24,80 +33,26 @@ function Get-PesterModulePath { foreach ($localPesterPath in $localPesterPaths) { if (Test-Path -Path $localPesterPath) { + $localManifestPath = Join-Path $localPesterPath "Pester.psd1" + if (Test-Path -Path $localManifestPath) { + return $localManifestPath + } + return $localPesterPath } } $globalPester = Get-Module -ListAvailable Pester | Where-Object { $_.Version -eq [version]"3.4.3" } | Select-Object -First 1 if ($globalPester) { - return $globalPester.ModuleBase + return $globalPester.Path } throw "Pester 3.4.3 was not found in the repository packages folder or installed modules." } -function Import-PatchedPester { - param( - [Parameter(Mandatory = $true)] - [string] $PesterModulePath - ) - - $patchedPesterPath = Join-Path ([System.IO.Path]::GetTempPath()) ("Pester.3.4.3-pwsh-compatible-{0}" -f ([guid]::NewGuid().ToString("N"))) - - Copy-Item -Path $PesterModulePath -Destination $patchedPesterPath -Recurse -Force - - $setupTeardownPath = Join-Path $patchedPesterPath "Functions" "SetupTeardown.ps1" - $setupTeardown = [System.IO.File]::ReadAllText($setupTeardownPath) - - $addTypePattern = '& \$SafeCommands\[''Add-Type''\] -TypeDefinition @''[\s\S]*?''@\r?\n\r?\n' - $setupTeardown = [System.Text.RegularExpressions.Regex]::Replace($setupTeardown, $addTypePattern, "", 1) - - $setupTeardown = $setupTeardown.Replace( - '$closeIndex = [Pester.ClosingBraceFinder]::GetClosingBraceIndex($Tokens, $GroupStartTokenIndex)', -@' - $groupLevel = 1 - $closeIndex = -1 - - for ($i = $GroupStartTokenIndex + 1; $i -lt $Tokens.Length; $i++) - { - $type = $Tokens[$i].Type - - if ($type -eq [System.Management.Automation.PSTokenType]::GroupStart) - { - $groupLevel++ - } - elseif ($type -eq [System.Management.Automation.PSTokenType]::GroupEnd) - { - $groupLevel-- - - if ($groupLevel -le 0) - { - $closeIndex = $i - break - } - } - } -'@) - - [System.IO.File]::WriteAllText($setupTeardownPath, $setupTeardown) - - Remove-Module Pester -ErrorAction SilentlyContinue - Import-Module -Name (Join-Path $patchedPesterPath "Pester.psd1") -Force -ErrorAction Stop -} - function Import-Pester { $pesterModulePath = Get-PesterModulePath - - try { - Import-Module -Name $pesterModulePath -RequiredVersion 3.4.3 -ErrorAction Stop - } catch { - if ($PSVersionTable.PSEdition -eq "Core" -and -not $IsWindows) { - Write-Host "Importing a patched temporary copy of Pester 3.4.3 for pwsh compatibility." - Import-PatchedPester -PesterModulePath $pesterModulePath - } else { - throw - } - } + Import-Module -Name $pesterModulePath -RequiredVersion 3.4.3 -ErrorAction Stop } function Get-TestFiles { @@ -144,8 +99,6 @@ try { Import-Module -Name $modulePath -ErrorAction Stop } - Import-Pester - if ($BeforeRun) { & $BeforeRun } @@ -156,6 +109,8 @@ try { return } + Assert-PesterEnvironment + Import-Pester Invoke-SelectedTests -TestFiles $testFiles } finally { $env:SystemRoot = $originalSystemRoot From c6f9f867fec78681775f87efb303d8ba0fc1d265 Mon Sep 17 00:00:00 2001 From: bullman Date: Tue, 17 Mar 2026 21:30:53 -0700 Subject: [PATCH 3/6] further slim Invoke-SharedPesterTest.ps1 --- tools/Invoke-SharedPesterTests.ps1 | 43 +++++++++--------------------- 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/tools/Invoke-SharedPesterTests.ps1 b/tools/Invoke-SharedPesterTests.ps1 index a714c51fe..9e35579e7 100644 --- a/tools/Invoke-SharedPesterTests.ps1 +++ b/tools/Invoke-SharedPesterTests.ps1 @@ -15,15 +15,6 @@ $repoRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..")) $originalSystemRoot = $env:SystemRoot $originalTemp = $env:TEMP -function Assert-PesterEnvironment { - if ($PSVersionTable.PSEdition -eq "Core" -and -not $IsWindows) { - $referenceAssembliesPath = Join-Path $PSHOME "ref" - if (-not (Test-Path -Path $referenceAssembliesPath)) { - 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." - } - } -} - function Get-PesterModulePath { $packagesFolder = Join-Path $repoRoot "packages" $localPesterPaths = @( @@ -50,24 +41,6 @@ function Get-PesterModulePath { throw "Pester 3.4.3 was not found in the repository packages folder or installed modules." } -function Import-Pester { - $pesterModulePath = Get-PesterModulePath - Import-Module -Name $pesterModulePath -RequiredVersion 3.4.3 -ErrorAction Stop -} - -function Get-TestFiles { - $discoveredFiles = Get-ChildItem -Path $testRootPath -Filter "*.tests.ps1" -Recurse - if ([string]::IsNullOrWhiteSpace($Filter) -or $Filter -eq "*") { - return @($discoveredFiles) - } - - return @( - $discoveredFiles | Where-Object { - $_.Name -like $Filter -or $_.FullName -like $Filter - } - ) -} - function Invoke-SelectedTests { param( [Parameter(Mandatory = $true)] @@ -103,14 +76,24 @@ try { & $BeforeRun } - $testFiles = @(Get-TestFiles) + $testFiles = @(Get-ChildItem -Path $testRootPath -Filter "*.tests.ps1" -Recurse) + if (-not [string]::IsNullOrWhiteSpace($Filter) -and $Filter -ne "*") { + $testFiles = @($testFiles | Where-Object { $_.Name -like $Filter -or $_.FullName -like $Filter }) + } + if ($testFiles.Count -eq 0) { Write-Host "No matching test files found under $testRootPath for filter '$Filter'." return } - Assert-PesterEnvironment - Import-Pester + if ($PSVersionTable.PSEdition -eq "Core" -and -not $IsWindows) { + $referenceAssembliesPath = Join-Path $PSHOME "ref" + if (-not (Test-Path -Path $referenceAssembliesPath)) { + 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." + } + } + + Import-Module -Name (Get-PesterModulePath) -RequiredVersion 3.4.3 -ErrorAction Stop Invoke-SelectedTests -TestFiles $testFiles } finally { $env:SystemRoot = $originalSystemRoot From fee1aa7d011631e148ea2a5a4dba52bb1ef0e6a5 Mon Sep 17 00:00:00 2001 From: bullman Date: Tue, 17 Mar 2026 21:40:25 -0700 Subject: [PATCH 4/6] try to remove diffs in tests --- .../tests/ConvertTo-OctopusJson.Tests.ps1 | 21 ++++++----- .../Set-OctopusStepTemplateProperty.Tests.ps1 | 36 +++++++++---------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/tools/StepTemplatePacker/tests/ConvertTo-OctopusJson.Tests.ps1 b/tools/StepTemplatePacker/tests/ConvertTo-OctopusJson.Tests.ps1 index 59367a0ae..87204be73 100644 --- a/tools/StepTemplatePacker/tests/ConvertTo-OctopusJson.Tests.ps1 +++ b/tools/StepTemplatePacker/tests/ConvertTo-OctopusJson.Tests.ps1 @@ -1,12 +1,17 @@ $ErrorActionPreference = "Stop"; Set-StrictMode -Version "Latest"; -function Normalize-NewLines([string] $value) { - if ($null -eq $value) { - return $null; - } +function Assert-JsonEquivalent { + param( + [Parameter(Mandatory = $true)] + [string] $ActualJson, + [Parameter(Mandatory = $true)] + [string] $ExpectedJson + ) - return $value -replace "`r`n", "`n"; + $actualValue = ConvertFrom-Json -InputObject $ActualJson | ConvertTo-Json -Depth 10 -Compress + $expectedValue = ConvertFrom-Json -InputObject $ExpectedJson | ConvertTo-Json -Depth 10 -Compress + $actualValue | Should Be $expectedValue } Describe "ConvertTo-OctopusDeploy" { @@ -63,8 +68,7 @@ Describe "ConvertTo-OctopusDeploy" { It "InputObject is a populated array" { $input = @( $null, 100, "my string" ); $expected = "[`r`n null,`r`n 100,`r`n `"my string`"`r`n]"; - Normalize-NewLines (ConvertTo-OctopusJson -InputObject $input) ` - | Should Be (Normalize-NewLines $expected); + Assert-JsonEquivalent -ActualJson (ConvertTo-OctopusJson -InputObject $input) -ExpectedJson $expected } It "InputObject is an empty PSCustomObject" { @@ -101,8 +105,7 @@ Describe "ConvertTo-OctopusDeploy" { } "@ $expected = $expected.Trim() - Normalize-NewLines (ConvertTo-OctopusJson -InputObject $input) ` - | Should Be (Normalize-NewLines $expected); + Assert-JsonEquivalent -ActualJson (ConvertTo-OctopusJson -InputObject $input) -ExpectedJson $expected } It "InputObject is an unhandled type" { diff --git a/tools/StepTemplatePacker/tests/Set-OctopusStepTemplateProperty.Tests.ps1 b/tools/StepTemplatePacker/tests/Set-OctopusStepTemplateProperty.Tests.ps1 index fe0542f8e..58ade68c6 100644 --- a/tools/StepTemplatePacker/tests/Set-OctopusStepTemplateProperty.Tests.ps1 +++ b/tools/StepTemplatePacker/tests/Set-OctopusStepTemplateProperty.Tests.ps1 @@ -1,12 +1,17 @@ $ErrorActionPreference = "Stop"; Set-StrictMode -Version "Latest"; -function Normalize-NewLines([string] $value) { - if ($null -eq $value) { - return $null; - } +function Assert-JsonEquivalent { + param( + [Parameter(Mandatory = $true)] + [string] $ActualJson, + [Parameter(Mandatory = $true)] + [string] $ExpectedJson + ) - return $value -replace "`r`n", "`n"; + $actualValue = ConvertFrom-Json -InputObject $ActualJson | ConvertTo-Json -Depth 10 -Compress + $expectedValue = ConvertFrom-Json -InputObject $ExpectedJson | ConvertTo-Json -Depth 10 -Compress + $actualValue | Should Be $expectedValue } Describe "Set-OctopusStepTemplateProperty" { @@ -17,8 +22,7 @@ Describe "Set-OctopusStepTemplateProperty" { -PropertyName "Octopus.Action.Script.Syntax" ` -Value "PowerShell"; $expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}"; - Normalize-NewLines (ConvertTo-OctopusJson -InputObject $stepJson) ` - | Should Be (Normalize-NewLines $expected); + Assert-JsonEquivalent -ActualJson (ConvertTo-OctopusJson -InputObject $stepJson) -ExpectedJson $expected } It "No properties exist" { @@ -27,8 +31,7 @@ Describe "Set-OctopusStepTemplateProperty" { -PropertyName "Octopus.Action.Script.Syntax" ` -Value "PowerShell"; $expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}"; - Normalize-NewLines (ConvertTo-OctopusJson -InputObject $stepJson) ` - | Should Be (Normalize-NewLines $expected); + Assert-JsonEquivalent -ActualJson (ConvertTo-OctopusJson -InputObject $stepJson) -ExpectedJson $expected } It "Specified property does not exist" { @@ -37,8 +40,7 @@ Describe "Set-OctopusStepTemplateProperty" { -PropertyName "Octopus.Action.Script.Syntax" ` -Value "PowerShell"; $expected = "{`r`n `"Properties`": {`r`n `"otherProperty`": `"`",`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}"; - Normalize-NewLines (ConvertTo-OctopusJson -InputObject $stepJson) ` - | Should Be (Normalize-NewLines $expected); + Assert-JsonEquivalent -ActualJson (ConvertTo-OctopusJson -InputObject $stepJson) -ExpectedJson $expected } It "Property does not exist" { @@ -47,8 +49,7 @@ Describe "Set-OctopusStepTemplateProperty" { -PropertyName "Octopus.Action.Script.Syntax" ` -Value "PowerShell"; $expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}"; - Normalize-NewLines (ConvertTo-OctopusJson -InputObject $stepJson) ` - | Should Be (Normalize-NewLines $expected); + Assert-JsonEquivalent -ActualJson (ConvertTo-OctopusJson -InputObject $stepJson) -ExpectedJson $expected } It "Property exists with a null value" { @@ -57,8 +58,7 @@ Describe "Set-OctopusStepTemplateProperty" { -PropertyName "Octopus.Action.Script.Syntax" ` -Value "PowerShell"; $expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}"; - Normalize-NewLines (ConvertTo-OctopusJson -InputObject $stepJson) ` - | Should Be (Normalize-NewLines $expected); + Assert-JsonEquivalent -ActualJson (ConvertTo-OctopusJson -InputObject $stepJson) -ExpectedJson $expected } It "Property exists with an empty string value" { @@ -67,8 +67,7 @@ Describe "Set-OctopusStepTemplateProperty" { -PropertyName "Octopus.Action.Script.Syntax" ` -Value "PowerShell"; $expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}"; - Normalize-NewLines (ConvertTo-OctopusJson -InputObject $stepJson) ` - | Should Be (Normalize-NewLines $expected); + Assert-JsonEquivalent -ActualJson (ConvertTo-OctopusJson -InputObject $stepJson) -ExpectedJson $expected } It "Property exists with a string value" { @@ -77,8 +76,7 @@ Describe "Set-OctopusStepTemplateProperty" { -PropertyName "Octopus.Action.Script.Syntax" ` -Value "PowerShell"; $expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}"; - Normalize-NewLines (ConvertTo-OctopusJson -InputObject $stepJson) ` - | Should Be (Normalize-NewLines $expected); + Assert-JsonEquivalent -ActualJson (ConvertTo-OctopusJson -InputObject $stepJson) -ExpectedJson $expected } From 2496d78789a9a833a490a23372a83fc715e361bd Mon Sep 17 00:00:00 2001 From: bullman Date: Tue, 17 Mar 2026 21:56:03 -0700 Subject: [PATCH 5/6] update so json is does not care about whitespace --- .gitignore | 1 + .../tests/ConvertTo-OctopusJson.Tests.ps1 | 20 +++---------- .../Set-OctopusStepTemplateProperty.Tests.ps1 | 28 ++++++------------- .../tests/Test-JsonAssertions.ps1 | 20 +++++++++++++ 4 files changed, 33 insertions(+), 36 deletions(-) create mode 100644 tools/StepTemplatePacker/tests/Test-JsonAssertions.ps1 diff --git a/.gitignore b/.gitignore index 65affa9a7..b362f480e 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ step-templates/*.sh step-templates/*.py /.vs !.vscode +*copy.ps1 diff --git a/tools/StepTemplatePacker/tests/ConvertTo-OctopusJson.Tests.ps1 b/tools/StepTemplatePacker/tests/ConvertTo-OctopusJson.Tests.ps1 index 87204be73..6ce2c2166 100644 --- a/tools/StepTemplatePacker/tests/ConvertTo-OctopusJson.Tests.ps1 +++ b/tools/StepTemplatePacker/tests/ConvertTo-OctopusJson.Tests.ps1 @@ -1,18 +1,6 @@ $ErrorActionPreference = "Stop"; Set-StrictMode -Version "Latest"; - -function Assert-JsonEquivalent { - param( - [Parameter(Mandatory = $true)] - [string] $ActualJson, - [Parameter(Mandatory = $true)] - [string] $ExpectedJson - ) - - $actualValue = ConvertFrom-Json -InputObject $ActualJson | ConvertTo-Json -Depth 10 -Compress - $expectedValue = ConvertFrom-Json -InputObject $ExpectedJson | ConvertTo-Json -Depth 10 -Compress - $actualValue | Should Be $expectedValue -} +. (Join-Path (Split-Path -Parent $MyInvocation.MyCommand.Path) "Test-JsonAssertions.ps1") Describe "ConvertTo-OctopusDeploy" { @@ -68,7 +56,7 @@ Describe "ConvertTo-OctopusDeploy" { It "InputObject is a populated array" { $input = @( $null, 100, "my string" ); $expected = "[`r`n null,`r`n 100,`r`n `"my string`"`r`n]"; - Assert-JsonEquivalent -ActualJson (ConvertTo-OctopusJson -InputObject $input) -ExpectedJson $expected + ConvertTo-OctopusJson -InputObject $input | Should BeJsonEquivalent $expected } It "InputObject is an empty PSCustomObject" { @@ -102,10 +90,10 @@ Describe "ConvertTo-OctopusDeploy" { "myPsObject": { "childProperty": "childValue" } -} + } "@ $expected = $expected.Trim() - Assert-JsonEquivalent -ActualJson (ConvertTo-OctopusJson -InputObject $input) -ExpectedJson $expected + ConvertTo-OctopusJson -InputObject $input | Should BeJsonEquivalent $expected } It "InputObject is an unhandled type" { diff --git a/tools/StepTemplatePacker/tests/Set-OctopusStepTemplateProperty.Tests.ps1 b/tools/StepTemplatePacker/tests/Set-OctopusStepTemplateProperty.Tests.ps1 index 58ade68c6..e6940f749 100644 --- a/tools/StepTemplatePacker/tests/Set-OctopusStepTemplateProperty.Tests.ps1 +++ b/tools/StepTemplatePacker/tests/Set-OctopusStepTemplateProperty.Tests.ps1 @@ -1,18 +1,6 @@ $ErrorActionPreference = "Stop"; Set-StrictMode -Version "Latest"; - -function Assert-JsonEquivalent { - param( - [Parameter(Mandatory = $true)] - [string] $ActualJson, - [Parameter(Mandatory = $true)] - [string] $ExpectedJson - ) - - $actualValue = ConvertFrom-Json -InputObject $ActualJson | ConvertTo-Json -Depth 10 -Compress - $expectedValue = ConvertFrom-Json -InputObject $ExpectedJson | ConvertTo-Json -Depth 10 -Compress - $actualValue | Should Be $expectedValue -} +. (Join-Path (Split-Path -Parent $MyInvocation.MyCommand.Path) "Test-JsonAssertions.ps1") Describe "Set-OctopusStepTemplateProperty" { @@ -22,7 +10,7 @@ Describe "Set-OctopusStepTemplateProperty" { -PropertyName "Octopus.Action.Script.Syntax" ` -Value "PowerShell"; $expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}"; - Assert-JsonEquivalent -ActualJson (ConvertTo-OctopusJson -InputObject $stepJson) -ExpectedJson $expected + ConvertTo-OctopusJson -InputObject $stepJson | Should BeJsonEquivalent $expected } It "No properties exist" { @@ -31,7 +19,7 @@ Describe "Set-OctopusStepTemplateProperty" { -PropertyName "Octopus.Action.Script.Syntax" ` -Value "PowerShell"; $expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}"; - Assert-JsonEquivalent -ActualJson (ConvertTo-OctopusJson -InputObject $stepJson) -ExpectedJson $expected + ConvertTo-OctopusJson -InputObject $stepJson | Should BeJsonEquivalent $expected } It "Specified property does not exist" { @@ -40,7 +28,7 @@ Describe "Set-OctopusStepTemplateProperty" { -PropertyName "Octopus.Action.Script.Syntax" ` -Value "PowerShell"; $expected = "{`r`n `"Properties`": {`r`n `"otherProperty`": `"`",`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}"; - Assert-JsonEquivalent -ActualJson (ConvertTo-OctopusJson -InputObject $stepJson) -ExpectedJson $expected + ConvertTo-OctopusJson -InputObject $stepJson | Should BeJsonEquivalent $expected } It "Property does not exist" { @@ -49,7 +37,7 @@ Describe "Set-OctopusStepTemplateProperty" { -PropertyName "Octopus.Action.Script.Syntax" ` -Value "PowerShell"; $expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}"; - Assert-JsonEquivalent -ActualJson (ConvertTo-OctopusJson -InputObject $stepJson) -ExpectedJson $expected + ConvertTo-OctopusJson -InputObject $stepJson | Should BeJsonEquivalent $expected } It "Property exists with a null value" { @@ -58,7 +46,7 @@ Describe "Set-OctopusStepTemplateProperty" { -PropertyName "Octopus.Action.Script.Syntax" ` -Value "PowerShell"; $expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}"; - Assert-JsonEquivalent -ActualJson (ConvertTo-OctopusJson -InputObject $stepJson) -ExpectedJson $expected + ConvertTo-OctopusJson -InputObject $stepJson | Should BeJsonEquivalent $expected } It "Property exists with an empty string value" { @@ -67,7 +55,7 @@ Describe "Set-OctopusStepTemplateProperty" { -PropertyName "Octopus.Action.Script.Syntax" ` -Value "PowerShell"; $expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}"; - Assert-JsonEquivalent -ActualJson (ConvertTo-OctopusJson -InputObject $stepJson) -ExpectedJson $expected + ConvertTo-OctopusJson -InputObject $stepJson | Should BeJsonEquivalent $expected } It "Property exists with a string value" { @@ -76,7 +64,7 @@ Describe "Set-OctopusStepTemplateProperty" { -PropertyName "Octopus.Action.Script.Syntax" ` -Value "PowerShell"; $expected = "{`r`n `"Properties`": {`r`n `"Octopus.Action.Script.Syntax`": `"PowerShell`"`r`n }`r`n}"; - Assert-JsonEquivalent -ActualJson (ConvertTo-OctopusJson -InputObject $stepJson) -ExpectedJson $expected + ConvertTo-OctopusJson -InputObject $stepJson | Should BeJsonEquivalent $expected } diff --git a/tools/StepTemplatePacker/tests/Test-JsonAssertions.ps1 b/tools/StepTemplatePacker/tests/Test-JsonAssertions.ps1 new file mode 100644 index 000000000..20e4caafe --- /dev/null +++ b/tools/StepTemplatePacker/tests/Test-JsonAssertions.ps1 @@ -0,0 +1,20 @@ +function global:ConvertTo-CompressedJsonForAssertion { + param( + [Parameter(Mandatory = $true)] + [string] $Json + ) + + return (ConvertFrom-Json -InputObject $Json | ConvertTo-Json -Depth 10 -Compress) +} + +function global:PesterBeJsonEquivalent($value, $expected) { + return (ConvertTo-CompressedJsonForAssertion -Json $value) -eq (ConvertTo-CompressedJsonForAssertion -Json $expected) +} + +function global:PesterBeJsonEquivalentFailureMessage($value, $expected) { + return "Expected JSON equivalent to: {$expected}`nBut was: {$value}" +} + +function global:NotPesterBeJsonEquivalentFailureMessage($value, $expected) { + return "Expected JSON not equivalent to: {$expected}`nBut was: {$value}" +} From 48c1af4e371ac60ff272ec241a87ad64e1629a3e Mon Sep 17 00:00:00 2001 From: bullman Date: Wed, 18 Mar 2026 12:38:45 -0700 Subject: [PATCH 6/6] revert changes to existing failing tests --- step-templates/tests/Invoke-PesterTests.ps1 | 4 +- ...scheduled-task-create.ScriptBody.Tests.ps1 | 26 ++++----- tools/Invoke-SharedPesterTests.ps1 | 58 ++++++++++++++----- .../tests/Invoke-PesterTests.ps1 | 4 +- 4 files changed, 64 insertions(+), 28 deletions(-) diff --git a/step-templates/tests/Invoke-PesterTests.ps1 b/step-templates/tests/Invoke-PesterTests.ps1 index 876056a5d..f215274fb 100644 --- a/step-templates/tests/Invoke-PesterTests.ps1 +++ b/step-templates/tests/Invoke-PesterTests.ps1 @@ -45,4 +45,6 @@ function Unpack-Scripts-Under-Test { -TestRoot $thisFolder ` -Filter $Filter ` -BeforeRun ${function:Unpack-Scripts-Under-Test} ` - -UsePassThruFailureCheck + -UsePassThruFailureCheck ` + -PreferredPesterVersion "3.4.0" ` + -SuiteName "step-templates" diff --git a/step-templates/tests/windows-scheduled-task-create.ScriptBody.Tests.ps1 b/step-templates/tests/windows-scheduled-task-create.ScriptBody.Tests.ps1 index be526e627..205169f50 100644 --- a/step-templates/tests/windows-scheduled-task-create.ScriptBody.Tests.ps1 +++ b/step-templates/tests/windows-scheduled-task-create.ScriptBody.Tests.ps1 @@ -12,8 +12,8 @@ Describe "Create-ScheduledTask" { It "Should invoke a matching command line" { Create-ScheduledTask; Assert-MockCalled Invoke-CommandLine -Times 1 -ParameterFilter { - $expectedCmd = "$($env:SystemRoot)\System32\schtasks.exe" - $expectedArgs = @( "/Create", "/RU", "`"`"", "/SC", "", "/TN", "`"`"", "/TR", "`"''`"", "/F" ) + $expectedCmd = "C:\Windows\System32\schtasks.exe" + $expectedArgs = @( "/Create", "/RU", "", "/SC", "", "/TN", "`"`"", "/TR", "''", "/F" ) $argDiffs = Compare-Object $ArgumentList $expectedArgs -SyncWindow 0; #Write-Host ("expected = " + ($expectedArgs | % { "[$($_)]"})); #Write-Host ("actual = " + ($ArgumentList | % { "[$($_)]"})); @@ -39,8 +39,8 @@ Describe "Create-ScheduledTask" { -Duration "" ` -StartNewTaskNow ""; Assert-MockCalled Invoke-CommandLine -Times 1 -ParameterFilter { - $expectedCmd = "$($env:SystemRoot)\System32\schtasks.exe" - $expectedArgs = @( "/Create", "/RU", "`"`"", "/SC", "", "/TN", "`"`"", "/TR", "`"''`"", "/F" ) + $expectedCmd = "C:\Windows\System32\schtasks.exe" + $expectedArgs = @( "/Create", "/RU", "", "/SC", "", "/TN", "`"`"", "/TR", "''", "/F" ) $argDiffs = Compare-Object $ArgumentList $expectedArgs -SyncWindow 0; #Write-Host ("expected = " + ($expectedArgs | % { "[$($_)]"})); #Write-Host ("actual = " + ($ArgumentList | % { "[$($_)]"})); @@ -66,8 +66,8 @@ Describe "Create-ScheduledTask" { -Duration "myDuration"` -StartNewTaskNow "myStartNewTaskNow"; Assert-MockCalled Invoke-CommandLine -Times 1 -ParameterFilter { - $expectedCmd = "$($env:SystemRoot)\System32\schtasks.exe" - $expectedArgs = @( "/Create", "/RU", "`"myRunAsUser`"", "/RP `"myRunAsPassword`"", "/SC", "mySchedule", "/RI", "myInterval", "/D", "myDays", "/TN", "`"myTaskName`"", "/TR", "`"'myTaskRun' myArguments`"", "/ST", "myStartTime", "/DU", "myDuration", "/SD", "myStartDate", "/F", "/RL", "HIGHEST" ) + $expectedCmd = "C:\Windows\System32\schtasks.exe" + $expectedArgs = @( "/Create", "/RU", "myRunAsUser", "/RP", "myRunAsPassword", "/SC", "mySchedule", "/D", "myDays", "/TN", "`"myTaskname`"", "/TR", "`"'myTaskRun' 'myArguments'`"", "/ST", "myStartTime", "/DU", "myDuration", "/SD", "myStartDate", "/F", "/RL", "HIGHEST" ) $argDiffs = Compare-Object $ArgumentList $expectedArgs -SyncWindow 0; #Write-Host ("expected = " + ($expectedArgs | % { "[$($_)]"})); #Write-Host ("actual = " + ($ArgumentList | % { "[$($_)]"})); @@ -93,8 +93,8 @@ Describe "Create-ScheduledTask" { -Duration "" ` -StartNewTaskNow ""; Assert-MockCalled Invoke-CommandLine -Times 1 -ParameterFilter { - $expectedCmd = "$($env:SystemRoot)\System32\schtasks.exe" - $expectedArgs = @( "/Create", "/RU", "`"`"", "/SC", "", "/TN", "`"`"", "/TR", "`"'myTaskRun'`"", "/F" ) + $expectedCmd = "C:\Windows\System32\schtasks.exe" + $expectedArgs = @( "/Create", "/RU", "", "/SC", "", "/TN", "`"`"", "/TR", "'myTaskRun'", "/F" ) $argDiffs = Compare-Object $ArgumentList $expectedArgs -SyncWindow 0; #Write-Host ("expected = " + ($expectedArgs | % { "[$($_)]"})); #Write-Host ("actual = " + ($ArgumentList | % { "[$($_)]"})); @@ -120,8 +120,8 @@ Describe "Create-ScheduledTask" { -Duration "" ` -StartNewTaskNow ""; Assert-MockCalled Invoke-CommandLine -Times 1 -ParameterFilter { - $expectedCmd = "$($env:SystemRoot)\System32\schtasks.exe" - $expectedArgs = @( "/Create", "/RU", "`"`"", "/SC", "", "/TN", "`"`"", "/TR", "`"'myTaskRun' myArguments`"", "/F" ) + $expectedCmd = "C:\Windows\System32\schtasks.exe" + $expectedArgs = @( "/Create", "/RU", "", "/SC", "", "/TN", "`"`"", "/TR", "`"'myTaskRun' 'myArguments'`"", "/F" ) $argDiffs = Compare-Object $ArgumentList $expectedArgs -SyncWindow 0; #Write-Host ("expected = " + ($expectedArgs | % { "[$($_)]"})); #Write-Host ("actual = " + ($ArgumentList | % { "[$($_)]"})); @@ -132,7 +132,7 @@ Describe "Create-ScheduledTask" { } Context "WEEKDAYS schedule parameter specified" { - It "WEEKDAYS is passed through when no days are specified" { + It "WEEKDAYS gets replaced with WEEKLY" { Create-ScheduledTask -TaskName "" ` -RunAsUser "" ` -RunAsPassword "" ` @@ -147,8 +147,8 @@ Describe "Create-ScheduledTask" { -Duration "" ` -StartNewTaskNow ""; Assert-MockCalled Invoke-CommandLine -Times 1 -ParameterFilter { - $expectedCmd = "$($env:SystemRoot)\System32\schtasks.exe" - $expectedArgs = @( "/Create", "/RU", "`"`"", "/SC", "WEEKDAYS", "/TN", "`"`"", "/TR", "`"''`"", "/F" ) + $expectedCmd = "C:\Windows\System32\schtasks.exe" + $expectedArgs = @( "/Create", "/RU", "", "/SC", "WEEKLY", "/TN", "`"`"", "/TR", "''", "/D", "MON,TUE,WED,THU,FRI", "/F" ) $argDiffs = Compare-Object $ArgumentList $expectedArgs -SyncWindow 0; Write-Host ("expected = " + ($expectedArgs | % { "[$($_)]"})); Write-Host ("actual = " + ($ArgumentList | % { "[$($_)]"})); diff --git a/tools/Invoke-SharedPesterTests.ps1 b/tools/Invoke-SharedPesterTests.ps1 index 9e35579e7..87f664dbd 100644 --- a/tools/Invoke-SharedPesterTests.ps1 +++ b/tools/Invoke-SharedPesterTests.ps1 @@ -4,7 +4,9 @@ param( [string] $Filter = "*", [scriptblock] $BeforeRun, [string[]] $ImportModules = @(), - [switch] $UsePassThruFailureCheck + [switch] $UsePassThruFailureCheck, + [string] $PreferredPesterVersion, + [string] $SuiteName = "tests" ) $ErrorActionPreference = "Stop"; @@ -15,30 +17,58 @@ $repoRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..")) $originalSystemRoot = $env:SystemRoot $originalTemp = $env:TEMP -function Get-PesterModulePath { +function Get-PesterModuleSpec { $packagesFolder = Join-Path $repoRoot "packages" - $localPesterPaths = @( - (Join-Path $packagesFolder "Pester" "tools" "Pester"), - (Join-Path $packagesFolder "Pester.3.4.3" "tools" "Pester") - ) + $attempts = @() + $localPesterPaths = @() + + if ($PreferredPesterVersion) { + $localPesterPaths += (Join-Path $packagesFolder ("Pester.{0}" -f $PreferredPesterVersion) "tools" "Pester") + } + $localPesterPaths += (Join-Path $packagesFolder "Pester" "tools" "Pester") - foreach ($localPesterPath in $localPesterPaths) { + foreach ($localPesterPath in $localPesterPaths | Select-Object -Unique) { + $attempts += $localPesterPath if (Test-Path -Path $localPesterPath) { $localManifestPath = Join-Path $localPesterPath "Pester.psd1" + $modulePath = $localPesterPath if (Test-Path -Path $localManifestPath) { - return $localManifestPath + $modulePath = $localManifestPath } - return $localPesterPath + $module = Test-ModuleManifest -Path $modulePath -ErrorAction Stop + if ($module.Version.Major -eq 3 -and ((-not $PreferredPesterVersion) -or $module.Version -eq [version]$PreferredPesterVersion)) { + return [pscustomobject]@{ + ModulePath = $module.Path + Version = $module.Version.ToString() + Source = "repository packages" + } + } } } - $globalPester = Get-Module -ListAvailable Pester | Where-Object { $_.Version -eq [version]"3.4.3" } | Select-Object -First 1 + $availablePesterModules = @(Get-Module -ListAvailable Pester | Sort-Object Version -Descending) + $globalPester = $null + + if ($PreferredPesterVersion) { + $globalPester = $availablePesterModules | Where-Object { $_.Version -eq [version]$PreferredPesterVersion } | Select-Object -First 1 + } + + if (-not $globalPester) { + $globalPester = $availablePesterModules | Where-Object { $_.Version.Major -eq 3 } | Select-Object -First 1 + } + if ($globalPester) { - return $globalPester.Path + return [pscustomobject]@{ + ModulePath = $globalPester.Path + Version = $globalPester.Version.ToString() + Source = "installed modules" + } } - throw "Pester 3.4.3 was not found in the repository packages folder or installed modules." + $preferredVersionMessage = if ($PreferredPesterVersion) { "preferred version $PreferredPesterVersion" } else { "a Pester 3.x version" } + $attemptedPathsMessage = if ($attempts.Count -gt 0) { " Tried package paths: $($attempts -join ', ')." } else { "" } + throw "Pester $preferredVersionMessage for suite '$SuiteName' was not found in the repository packages folder or installed modules.$attemptedPathsMessage" } function Invoke-SelectedTests { @@ -93,7 +123,9 @@ try { } } - Import-Module -Name (Get-PesterModulePath) -RequiredVersion 3.4.3 -ErrorAction Stop + $pesterModule = Get-PesterModuleSpec + Write-Host "Using Pester module version $($pesterModule.Version) from $($pesterModule.Source)." + Import-Module -Name $pesterModule.ModulePath -RequiredVersion $pesterModule.Version -ErrorAction Stop Invoke-SelectedTests -TestFiles $testFiles } finally { $env:SystemRoot = $originalSystemRoot diff --git a/tools/StepTemplatePacker/tests/Invoke-PesterTests.ps1 b/tools/StepTemplatePacker/tests/Invoke-PesterTests.ps1 index b44e25f58..034965a20 100644 --- a/tools/StepTemplatePacker/tests/Invoke-PesterTests.ps1 +++ b/tools/StepTemplatePacker/tests/Invoke-PesterTests.ps1 @@ -14,4 +14,6 @@ $sharedRunner = Join-Path $repoRoot "tools" "Invoke-SharedPesterTests.ps1"; & $sharedRunner ` -TestRoot $thisFolder ` -Filter $Filter ` - -ImportModules @($packer); + -ImportModules @($packer) ` + -PreferredPesterVersion "3.4.3" ` + -SuiteName "StepTemplatePacker";