Skip to content

Commit 049df19

Browse files
bolenscursoragent
andcommitted
fix(test): honor command overrides after bootstrap reload
Bootstrap redefines Test-CachedCommand mid-shard, so availability stubs stopped applying and real tools leaked into isolation tests. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 6087157 commit 049df19

7 files changed

Lines changed: 75 additions & 41 deletions

File tree

profile.d/bootstrap/CloudProviderBase.ps1

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -496,8 +496,9 @@ try {
496496
}
497497
}
498498
else {
499-
Write-Error "Either Service/Action or Arguments must be provided."
500-
return $null
499+
$missingArgsMessage = 'Either Service/Action or Arguments must be provided.'
500+
Write-Error $missingArgsMessage
501+
throw $missingArgsMessage
501502
}
502503

503504
# Build context

profile.d/bootstrap/CommandCache.ps1

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,32 @@ function global:Test-CachedCommand {
5050
}
5151

5252
$normalizedName = $Name.Trim()
53+
$cacheKey = $normalizedName.ToLowerInvariant()
54+
55+
# Test harness overrides win over assumed commands / real lookups so
56+
# Mark-TestCommandsUnavailable still works after bootstrap redefines this function.
57+
if ($env:PS_PROFILE_TEST_MODE -eq '1') {
58+
$overrideTable = Get-Variable -Name 'TestCommandAvailabilityOverrides' -Scope Global -ErrorAction SilentlyContinue
59+
if ($null -ne $overrideTable -and $null -ne $overrideTable.Value) {
60+
$overrides = $overrideTable.Value
61+
if ($overrides.ContainsKey($normalizedName)) {
62+
return [bool]$overrides[$normalizedName]
63+
}
64+
if ($overrides.ContainsKey($cacheKey)) {
65+
return [bool]$overrides[$cacheKey]
66+
}
67+
}
68+
}
5369

5470
# Check assumed commands first (bypasses actual command lookup for optional tools)
5571
if ($global:AssumedAvailableCommands -and $global:AssumedAvailableCommands.ContainsKey($normalizedName)) {
72+
$assumed = $global:AssumedAvailableCommands[$normalizedName]
73+
if ($assumed -is [bool]) {
74+
return $assumed
75+
}
5676
return $true
5777
}
5878

59-
$cacheKey = $normalizedName.ToLowerInvariant()
6079
$now = Get-Date
6180

6281
# Check cache for existing entry that hasn't expired (if caching enabled)

profile.d/system/NetworkOperations.ps1

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ function Get-NetworkPorts {
2222
else {
2323
try {
2424
if (-not (Test-CachedCommand 'netstat')) {
25-
Write-Error "netstat command not found. This command is typically available on Windows and Unix systems."
26-
return
25+
$missingNetstat = 'netstat command not found. This command is typically available on Windows and Unix systems.'
26+
Write-Error $missingNetstat
27+
throw $missingNetstat
2728
}
2829
& netstat -an
2930
}

tests/TestSupport/TestCommandAvailability.ps1

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,29 @@
44
# ===============================================
55

66
$script:OriginalTestCachedCommand = $null
7+
$script:StubbedTestCachedCommand = $null
78
$script:TestCachedCommandStubInstalled = $false
89

910
<#
1011
.SYNOPSIS
1112
Installs a function wrapper for Test-CachedCommand that honors availability overrides.
1213
#>
1314
function Register-TestCommandAvailabilityStub {
14-
if ($script:TestCachedCommandStubInstalled) {
15-
return
16-
}
17-
1815
$existing = Get-Command Test-CachedCommand -ErrorAction SilentlyContinue
1916
if (-not $existing -or $existing.CommandType -ne 'Function') {
2017
return
2118
}
2219

20+
# Bootstrap/profile often redefines Test-CachedCommand; re-wrap when that happens.
21+
if ($script:TestCachedCommandStubInstalled -and $script:StubbedTestCachedCommand) {
22+
$currentBlock = $existing.ScriptBlock
23+
if ($currentBlock -eq $script:StubbedTestCachedCommand) {
24+
return
25+
}
26+
$script:OriginalTestCachedCommand = $currentBlock
27+
$script:TestCachedCommandStubInstalled = $false
28+
}
29+
2330
if (-not $script:OriginalTestCachedCommand) {
2431
$script:OriginalTestCachedCommand = $existing.ScriptBlock
2532
}
@@ -57,6 +64,7 @@ function Register-TestCommandAvailabilityStub {
5764
}.GetNewClosure()
5865

5966
Set-Item -Path 'Function:\global:Test-CachedCommand' -Value $wrapper -Force -ErrorAction SilentlyContinue
67+
$script:StubbedTestCachedCommand = $wrapper
6068
$script:TestCachedCommandStubInstalled = $true
6169
}
6270

@@ -76,6 +84,7 @@ function Clear-TestCommandAvailabilityStub {
7684
Set-Item -Path 'Function:\global:Test-CachedCommand' -Value $script:OriginalTestCachedCommand -Force -ErrorAction SilentlyContinue
7785
}
7886

87+
$script:StubbedTestCachedCommand = $null
7988
$script:TestCachedCommandStubInstalled = $false
8089
}
8190

tests/TestSupport/TestModuleLoading.ps1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,10 @@ function Initialize-SystemUtilityIntegration {
11401140

11411141
Initialize-TestProfile -ProfileDir $ProfileDir -LoadBootstrap -LoadFilesFragment
11421142

1143+
if (Get-Command Register-TestCommandAvailabilityStub -ErrorAction SilentlyContinue) {
1144+
Register-TestCommandAvailabilityStub
1145+
}
1146+
11431147
$systemFragment = Join-Path $ProfileDir 'system.ps1'
11441148
if (Test-Path -LiteralPath $systemFragment) {
11451149
$null = . $systemFragment

tests/unit/library/path/library-path-resolution-extended.tests.ps1

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -142,14 +142,8 @@ Describe 'PathResolution extended scenarios' {
142142

143143
try {
144144
InModuleScope -ModuleName PathResolution {
145-
Mock Get-Command {
146-
param($Name)
147-
if ($Name -eq 'Exit-WithCode') {
148-
return $null
149-
}
150-
151-
return Microsoft.PowerShell.Core\Get-Command @PSBoundParameters
152-
}
145+
# ParameterFilter avoids Mock Get-Command recursion (stack overflow under coverage).
146+
Mock Get-Command -ParameterFilter { $Name -eq 'Exit-WithCode' } -MockWith { $null }
153147

154148
{ Get-RepoRootSafe -ScriptPath $using:invalidPath -ExitOnError } | Should -Throw
155149
}
@@ -376,14 +370,8 @@ Describe 'PathResolution extended scenarios' {
376370

377371
try {
378372
InModuleScope -ModuleName PathResolution {
379-
Mock Get-Command {
380-
param($Name)
381-
if ($Name -eq 'Exit-WithCode') {
382-
return $null
383-
}
384-
385-
return Microsoft.PowerShell.Core\Get-Command @PSBoundParameters
386-
}
373+
# ParameterFilter avoids Mock Get-Command recursion (stack overflow under coverage).
374+
Mock Get-Command -ParameterFilter { $Name -eq 'Exit-WithCode' } -MockWith { $null }
387375

388376
{ Get-RepoRootSafe -ScriptPath $using:invalidPath -ExitOnError } | Should -Throw
389377
}
@@ -472,14 +460,8 @@ Describe 'PathResolution extended scenarios' {
472460

473461
try {
474462
InModuleScope -ModuleName PathResolution {
475-
Mock Get-Command {
476-
param($Name)
477-
if ($Name -eq 'Exit-WithCode') {
478-
return $null
479-
}
480-
481-
return Microsoft.PowerShell.Core\Get-Command @PSBoundParameters
482-
}
463+
# ParameterFilter avoids Mock Get-Command recursion (stack overflow under coverage).
464+
Mock Get-Command -ParameterFilter { $Name -eq 'Exit-WithCode' } -MockWith { $null }
483465

484466
{ Get-RepoRootSafe -ScriptPath $using:invalidPath -ExitOnError } | Should -Throw
485467
}
@@ -629,14 +611,8 @@ Describe 'PathResolution extended scenarios' {
629611
InModuleScope -ModuleName PathResolution -ArgumentList $invalidPath {
630612
param([string]$InvalidPath)
631613

632-
Mock Get-Command {
633-
param($Name)
634-
if ($Name -eq 'Exit-WithCode') {
635-
return $null
636-
}
637-
638-
return Microsoft.PowerShell.Core\Get-Command @PSBoundParameters
639-
}
614+
# ParameterFilter avoids Mock Get-Command recursion (stack overflow under coverage).
615+
Mock Get-Command -ParameterFilter { $Name -eq 'Exit-WithCode' } -MockWith { $null }
640616

641617
{ Get-RepoRootSafe -ScriptPath $InvalidPath -ExitOnError } | Should -Throw
642618
}

tests/unit/library/python/library-python-extended.tests.ps1

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,30 @@ function script:New-FakePythonExecutable {
3636
[int]$ScriptExitCode = 0
3737
)
3838

39+
# Windows CI cannot execute .sh probes via `& path`; use a .cmd shim there.
40+
if ($IsWindows -or $env:OS -eq 'Windows_NT') {
41+
$exePath = Join-Path $script:TempDir ("fake-python-{0}.cmd" -f (Get-Random))
42+
$lines = New-Object System.Collections.Generic.List[string]
43+
$null = $lines.Add('@echo off')
44+
$null = $lines.Add('setlocal EnableExtensions')
45+
$null = $lines.Add('if /I "%~1"=="--version" (')
46+
$null = $lines.Add(' echo Python 3.11.0')
47+
$null = $lines.Add(" exit /b $VersionExitCode")
48+
$null = $lines.Add(')')
49+
foreach ($entry in $PackageExitCodes.GetEnumerator()) {
50+
$pkg = $entry.Key
51+
$code = [int]$entry.Value
52+
$null = $lines.Add("findstr /C:`"$pkg`" `"%~1`" >nul 2>&1 && exit /b $code")
53+
}
54+
$null = $lines.Add('if not "%~2"=="" if exist "%~2" (')
55+
$null = $lines.Add(" echo $ScriptOutput")
56+
$null = $lines.Add(" exit /b $ScriptExitCode")
57+
$null = $lines.Add(')')
58+
$null = $lines.Add('exit /b 0')
59+
Set-Content -LiteralPath $exePath -Value ($lines -join "`r`n") -Encoding ASCII
60+
return $exePath
61+
}
62+
3963
$exePath = Join-Path $script:TempDir ("fake-python-{0}.sh" -f (Get-Random))
4064
$lines = New-Object System.Collections.Generic.List[string]
4165
$null = $lines.Add('#!/bin/sh')

0 commit comments

Comments
 (0)