Skip to content

Commit abcdd7c

Browse files
bolenscursoragent
andcommitted
fix(test): stop unloading Pester mid-suite (mock cascade root cause)
library-module*.tests.ps1 called Remove-Module Pester / reimported Pester while Invoke-Pester was running. That destroyed mock infrastructure and cascaded "Mock data are not setup for this scope" into later shard files (NodeJs, Parallel, PathResolution, PlatformPaths, ProfileFragmentLoader) plus TestDrive collisions on Windows. - Use a disposable PSModulePath probe module instead of Pester - Hide real choco on Windows CheckCommand tests - Ensure Read-FileContent exists before Mock on Windows fragment tests - Keep command-lookup handler cleanup in Reset-TestIsolationState Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 7d83cc2 commit abcdd7c

5 files changed

Lines changed: 70 additions & 31 deletions

tests/unit/library/chocolatey/library-chocolatey-detection-extended.tests.ps1

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ function script:Disable-ChocolateyDefaultLocations {
5858
if ([string]::IsNullOrWhiteSpace($cmdName) -and $args.Count -gt 0) {
5959
$cmdName = [string]$args[0]
6060
}
61-
if ($cmdName -eq 'Test-ValidPath') {
61+
# Hide Validation helpers and the real choco binary (Windows runners often have
62+
# Chocolatey installed — CheckCommand tests must see it as unavailable).
63+
if ($cmdName -in @('Test-ValidPath', 'choco')) {
6264
return $null
6365
}
6466
return Microsoft.PowerShell.Core\Get-Command @PSBoundParameters

tests/unit/library/chocolatey/library-chocolatey-detection.tests.ps1

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ function script:Disable-ChocolateyDefaultLocations {
5353
if ([string]::IsNullOrWhiteSpace($cmdName) -and $args.Count -gt 0) {
5454
$cmdName = [string]$args[0]
5555
}
56-
if ($cmdName -eq 'Test-ValidPath') {
56+
# Hide Validation helpers and the real choco binary (Windows runners often have
57+
# Chocolatey installed — CheckCommand tests must see it as unavailable).
58+
if ($cmdName -in @('Test-ValidPath', 'choco')) {
5759
return $null
5860
}
5961
return Microsoft.PowerShell.Core\Get-Command @PSBoundParameters

tests/unit/library/fragment/library-fragment-loading-extended.tests.ps1

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@ function script:Simulate-UnreadableFragmentFile {
5454
}
5555

5656
# Windows cannot chmod away readability; force the module read path to fail.
57+
# Ensure Read-FileContent is resolvable before Mock — FileContent may not be loaded
58+
# in every shard ordering, and Pester 5 cannot mock a missing command.
59+
if (-not (Get-Command Read-FileContent -ErrorAction SilentlyContinue)) {
60+
function global:Read-FileContent {
61+
param([Parameter(Mandatory)][string]$Path)
62+
return Get-Content -LiteralPath $Path -Raw -ErrorAction Stop
63+
}
64+
}
65+
5766
Mock Read-FileContent {
5867
throw [System.UnauthorizedAccessException]::new("Access to the path is denied.")
5968
} -ModuleName FragmentLoading

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

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,20 @@ BeforeAll {
2020
$script:LibPath = Get-TestPath -RelativePath 'scripts\lib' -StartPath $PSScriptRoot -EnsureExists
2121
$script:ProfileDir = Join-Path $script:RepoRoot 'profile.d'
2222
Import-Module (Join-Path $script:LibPath 'runtime' 'Module.psm1') -DisableNameChecking -Force
23+
24+
# Disposable probe module for import/ensure tests. NEVER unload Pester here — that
25+
# destroys the running test runner's mock infrastructure and cascades
26+
# "Mock data are not setup for this scope" into later files in the same shard.
27+
$script:ProbeName = 'PsProfileModuleProbe'
28+
$script:ProbeRoot = New-TestTempDirectory -Prefix 'ModuleProbeExt'
29+
$probeDir = Join-Path $script:ProbeRoot $script:ProbeName
30+
New-Item -ItemType Directory -Path $probeDir -Force | Out-Null
31+
Set-Content -LiteralPath (Join-Path $probeDir "$($script:ProbeName).psm1") -Value @'
32+
function Get-PsProfileModuleProbe { 'ok' }
33+
Export-ModuleMember -Function Get-PsProfileModuleProbe
34+
'@
35+
$script:OriginalPSModulePath = $env:PSModulePath
36+
$env:PSModulePath = "$($script:ProbeRoot)$([System.IO.Path]::PathSeparator)$env:PSModulePath"
2337
}
2438

2539
function script:Invoke-InModuleWithStubs {
@@ -52,18 +66,19 @@ function script:Invoke-InModuleWithStubs {
5266

5367
AfterAll {
5468
Remove-Module Module -ErrorAction SilentlyContinue -Force
69+
if ($script:ProbeName) {
70+
Remove-Module -Name $script:ProbeName -Force -ErrorAction SilentlyContinue
71+
}
72+
if ($null -ne $script:OriginalPSModulePath) {
73+
$env:PSModulePath = $script:OriginalPSModulePath
74+
}
5575
}
5676

5777
Describe 'Module extended scenarios' {
5878
Context 'Import-RequiredModule' {
5979
It 'Does not throw when importing an already loaded module' {
60-
if (-not (Get-Module -ListAvailable -Name 'Pester' -ErrorAction SilentlyContinue)) {
61-
Set-ItResult -Skipped -Because 'Pester is not available for import tests'
62-
return
63-
}
64-
65-
Import-RequiredModule -ModuleName 'Pester' -ErrorAction SilentlyContinue
66-
{ Import-RequiredModule -ModuleName 'Pester' } | Should -Not -Throw
80+
Import-RequiredModule -ModuleName $script:ProbeName -ErrorAction SilentlyContinue
81+
{ Import-RequiredModule -ModuleName $script:ProbeName } | Should -Not -Throw
6782
}
6883

6984
It 'Requires ModuleName parameter' {
@@ -375,15 +390,11 @@ Describe 'Module extended scenarios' {
375390
}
376391

377392
Context 'Ensure-ModuleAvailable' {
378-
It 'Imports Pester without error when the module is already installed' {
379-
if (-not (Get-Module -ListAvailable -Name 'Pester' -ErrorAction SilentlyContinue)) {
380-
Set-ItResult -Skipped -Because 'Pester is not available'
381-
return
382-
}
383-
384-
Remove-Module Pester -ErrorAction SilentlyContinue -Force
385-
{ Ensure-ModuleAvailable -ModuleName 'Pester' } | Should -Not -Throw
386-
Get-Module Pester | Should -Not -BeNullOrEmpty
393+
It 'Imports probe module without error when the module is already installed' {
394+
Remove-Module -Name $script:ProbeName -Force -ErrorAction SilentlyContinue
395+
{ Ensure-ModuleAvailable -ModuleName $script:ProbeName } | Should -Not -Throw
396+
# Ensure-ModuleAvailable imports via Module.psm1 session — use -All to observe it.
397+
Get-Module -Name $script:ProbeName -All | Should -Not -BeNullOrEmpty
387398
}
388399

389400
It 'Installs and imports when both steps succeed' {

tests/unit/library/module/library-module.tests.ps1

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,20 @@ BeforeAll {
2828
}
2929

3030
Import-Module $script:ModulePath -DisableNameChecking -ErrorAction Stop -Force
31+
32+
# Disposable probe module for import/ensure tests. NEVER unload Pester here — that
33+
# destroys the running test runner's mock infrastructure and cascades
34+
# "Mock data are not setup for this scope" into later files in the same shard.
35+
$script:ProbeName = 'PsProfileModuleProbe'
36+
$script:ProbeRoot = New-TestTempDirectory -Prefix 'ModuleProbe'
37+
$probeDir = Join-Path $script:ProbeRoot $script:ProbeName
38+
New-Item -ItemType Directory -Path $probeDir -Force | Out-Null
39+
Set-Content -LiteralPath (Join-Path $probeDir "$($script:ProbeName).psm1") -Value @'
40+
function Get-PsProfileModuleProbe { 'ok' }
41+
Export-ModuleMember -Function Get-PsProfileModuleProbe
42+
'@
43+
$script:OriginalPSModulePath = $env:PSModulePath
44+
$env:PSModulePath = "$($script:ProbeRoot)$([System.IO.Path]::PathSeparator)$env:PSModulePath"
3145
}
3246
catch {
3347
$errorDetails = @{
@@ -42,17 +56,22 @@ BeforeAll {
4256

4357
AfterAll {
4458
Remove-Module Module -ErrorAction SilentlyContinue -Force
59+
if ($script:ProbeName) {
60+
Remove-Module -Name $script:ProbeName -Force -ErrorAction SilentlyContinue
61+
}
62+
if ($null -ne $script:OriginalPSModulePath) {
63+
$env:PSModulePath = $script:OriginalPSModulePath
64+
}
4565
}
4666

4767
Describe 'Module Module Functions' {
4868
Context 'Import-RequiredModule' {
4969
It 'Imports an available module successfully' {
50-
# Use a module that should be available (Pester for testing)
51-
if (Get-Module -ListAvailable -Name 'Pester' -ErrorAction SilentlyContinue) {
52-
Remove-Module Pester -ErrorAction SilentlyContinue -Force
53-
{ Import-RequiredModule -ModuleName 'Pester' } | Should -Not -Throw
54-
Get-Module Pester | Should -Not -BeNullOrEmpty
55-
}
70+
Remove-Module -Name $script:ProbeName -Force -ErrorAction SilentlyContinue
71+
{ Import-RequiredModule -ModuleName $script:ProbeName } | Should -Not -Throw
72+
# Import-RequiredModule runs inside Module.psm1, so the import lands in that
73+
# module's session — use -All so the test scope can observe it.
74+
Get-Module -Name $script:ProbeName -All | Should -Not -BeNullOrEmpty
5675
}
5776

5877
It 'Throws error when module does not exist' {
@@ -61,10 +80,8 @@ Describe 'Module Module Functions' {
6180
}
6281

6382
It 'Forces reimport when Force is specified' {
64-
if (Get-Module -ListAvailable -Name 'Pester' -ErrorAction SilentlyContinue) {
65-
Import-RequiredModule -ModuleName 'Pester' -ErrorAction SilentlyContinue
66-
{ Import-RequiredModule -ModuleName 'Pester' -Force } | Should -Not -Throw
67-
}
83+
Import-RequiredModule -ModuleName $script:ProbeName -ErrorAction SilentlyContinue
84+
{ Import-RequiredModule -ModuleName $script:ProbeName -Force } | Should -Not -Throw
6885
}
6986

7087
It 'Exports Import-RequiredModule function' {
@@ -81,9 +98,7 @@ Describe 'Module Module Functions' {
8198
Set-ItResult -Skipped -Because 'Install-RequiredModule parameters not parseable (ModuleScope enum dependency)'
8299
return
83100
}
84-
if (Get-Module -ListAvailable -Name 'Pester' -ErrorAction SilentlyContinue) {
85-
{ Install-RequiredModule -ModuleName 'Pester' } | Should -Not -Throw
86-
}
101+
{ Install-RequiredModule -ModuleName $script:ProbeName } | Should -Not -Throw
87102
}
88103

89104
It 'Uses CurrentUser scope by default' {

0 commit comments

Comments
 (0)