Tests/ImportDotEnv.Tests.ps1
# c:\Users\dani_\Workspaces\ImportDotEnv\Tests\ImportDotEnv.Tests.ps1 #Requires -Modules Pester param( [string]$ModulePath = (Resolve-Path (Join-Path $PSScriptRoot "..\ImportDotEnv.psm1")).Path # Assuming tests are in a subfolder ) # Enable debug messages for this test run # $DebugPreference = 'Continue' $Global:InitialEnvironment = @{} # Import the module before InModuleScope so Pester can find it Write-Host "Top-level: Attempting to import module from '$ModulePath'" if (-not (Test-Path $ModulePath)) { throw "Top-level: ModulePath '$ModulePath' does not exist." } $null = Import-Module $ModulePath -Force -PassThru Write-Host "Top-level: Import-Module executed." # Wrap the entire Describe block in InModuleScope InModuleScope 'ImportDotEnv' { Describe "Import-DotEnv Core and Integration Tests" { BeforeAll { # Runs once before any test in this Describe block $script:ImportDotEnvModule = Get-Module ImportDotEnv # Store it in script scope Write-Host "BeforeAll: Import-Module executed." if (-not $script:ImportDotEnvModule) { throw "BeforeAll: ImportDotEnv module object could not be retrieved after Import-Module." } Write-Host "BeforeAll: ImportDotEnv module IS loaded." # Store initial state of any test-related environment variables $testVarNames = @("TEST_VAR_GLOBAL", "TEST_VAR_A", "TEST_VAR_BASE", "TEST_VAR_OVERRIDE", "TEST_VAR_SUB", "NEW_VAR", "TEST_EMPTY_VAR", "PROJECT_ID", "MANUAL_TEST_VAR") foreach ($varName in $testVarNames) { $Global:InitialEnvironment[$varName] = [Environment]::GetEnvironmentVariable($varName) } # Create temporary directory structure for tests $script:TestRoot = Join-Path $env:TEMP "ImportDotEnvPesterTests" if (Test-Path $script:TestRoot) { Write-Host "BeforeAll: Removing existing TestRoot '$script:TestRoot'" Remove-Item $script:TestRoot -Recurse -Force } New-Item -Path $script:TestRoot -ItemType Directory | Out-Null # --- New: Create a parent directory with a .env file for cross-directory restoration test --- $script:ParentDirOfTestRoot = Split-Path $script:TestRoot -Parent # e.g., C:\Users\dani_\AppData\Local\Temp $script:ParentEnvPath = Join-Path $script:ParentDirOfTestRoot ".env" # e.g., C:\Users\dani_\AppData\Local\Temp\.env $script:DirWithOwnEnv = New-Item -Path (Join-Path $script:TestRoot "DirWithOwnEnv") -ItemType Directory Set-Content -Path (Join-Path $script:DirWithOwnEnv.FullName ".env") -Value "GALLERY_API_KEY=abc123`nGALLERY_2=def456" Write-Host "BeforeAll: Content of DirWithOwnEnv/.env is '$(Get-Content (Join-Path $script:DirWithOwnEnv.FullName ".env") -Raw)'" $script:DirA = New-Item -Path (Join-Path $script:TestRoot "dirA") -ItemType Directory Set-Content -Path (Join-Path $script:DirA.FullName ".env") -Value "TEST_VAR_A=valA`nTEST_VAR_GLOBAL=valA_override" Write-Host "BeforeAll: Content of dirA/.env is '$(Get-Content (Join-Path $script:DirA.FullName ".env") -Raw)'" $script:BaseDir = New-Item -Path (Join-Path $script:TestRoot "baseDir") -ItemType Directory Set-Content -Path (Join-Path $script:BaseDir.FullName ".env") -Value "TEST_VAR_BASE=base_val`nTEST_VAR_OVERRIDE=base_override_val" Write-Host "BeforeAll: Content of baseDir/.env is '$(Get-Content (Join-Path $script:BaseDir.FullName ".env") -Raw)'" $script:SubDir = New-Item -Path (Join-Path $script:BaseDir.FullName "subDir") -ItemType Directory Set-Content -Path (Join-Path $script:SubDir.FullName ".env") -Value "TEST_VAR_SUB=sub_val`nTEST_VAR_OVERRIDE=sub_override_val" Write-Host "BeforeAll: Content of subDir/.env is '$(Get-Content (Join-Path $script:SubDir.FullName ".env") -Raw)'" $script:DirB = New-Item -Path (Join-Path $script:TestRoot "dirB") -ItemType Directory Set-Content -Path (Join-Path $script:DirB.FullName ".env") -Value "NEW_VAR=new_value" Write-Host "BeforeAll: Content of dirB/.env is '$(Get-Content (Join-Path $script:DirB.FullName ".env") -Raw)'" $script:DirC = New-Item -Path (Join-Path $script:TestRoot "dirC") -ItemType Directory Set-Content -Path (Join-Path $script:DirC.FullName ".env") -Value "TEST_EMPTY_VAR=" Write-Host "BeforeAll: Content of dirC/.env is '$(Get-Content (Join-Path $script:DirC.FullName ".env") -Raw)'" $script:Project1Dir = New-Item -Path (Join-Path $script:TestRoot "project1") -ItemType Directory Set-Content -Path (Join-Path $script:Project1Dir.FullName ".env") -Value "PROJECT_ID=P1" Write-Host "BeforeAll: Content of project1/.env is '$(Get-Content (Join-Path $script:Project1Dir.FullName ".env") -Raw)'" $script:Project2Dir = New-Item -Path (Join-Path $script:TestRoot "project2") -ItemType Directory Set-Content -Path (Join-Path $script:Project2Dir.FullName ".env") -Value "PROJECT_ID=P2" Write-Host "BeforeAll: Content of project2/.env is '$(Get-Content (Join-Path $script:Project2Dir.FullName ".env") -Raw)'" $script:NonEnvDir = New-Item -Path (Join-Path $script:TestRoot "nonEnvDir") -ItemType Directory } BeforeEach { # Runs before each It in this Describe block # Variables that are truly global/external and whose pre-Pester state should be restored by BeforeEach $globalTestVarNames = @("TEST_VAR_GLOBAL", "TEST_VAR_BASE", "TEST_VAR_OVERRIDE", "TEST_EMPTY_VAR", "PROJECT_ID", "MANUAL_TEST_VAR") # Variables that are primarily created/manipulated by test scenarios and should always be cleared $scenarioSpecificVarNames = @("TEST_VAR_A", "TEST_VAR_SUB", "NEW_VAR") foreach ($varName in $globalTestVarNames) { $initialVal = $Global:InitialEnvironment[$varName] # This relies on $Global:InitialEnvironment being pristine if ($null -eq $initialVal) { if (Test-Path "Env:\$varName") { Remove-Item "Env:\$varName" -Force -ErrorAction SilentlyContinue } [Environment]::SetEnvironmentVariable($varName, $null) } else { [Environment]::SetEnvironmentVariable($varName, $initialVal) } } foreach ($varName in $scenarioSpecificVarNames) { Write-Host "BeforeEach: Unconditionally clearing scenario-specific var '$varName'. Initial Test-Path: $(Test-Path "Env:\$varName"), Initial Value: '$([Environment]::GetEnvironmentVariable($varName))'" # Attempt to clear the variable from the process environment first. [Environment]::SetEnvironmentVariable($varName, $null) # Then, ensure it's also cleared from PowerShell's Env: drive if it lingers. if (Test-Path "Env:\$varName") { Remove-Item "Env:\$varName" -Force -ErrorAction SilentlyContinue } if ([Environment]::GetEnvironmentVariable($varName) -ne $null) { Write-Warning "BeforeEach: FAILED to clear '$varName'. It is still '$([Environment]::GetEnvironmentVariable($varName))'." } else { Write-Host "BeforeEach: Successfully cleared '$varName'. Current Value: '$([Environment]::GetEnvironmentVariable($varName))', Test-Path: $(Test-Path "Env:\$varName")" } } Write-Host "BeforeEach (Start): Environment variables reset." $currentTestRoot = $script:TestRoot Write-Host "BeforeEach: Value of currentTestRoot (from script:TestRoot) is '$currentTestRoot'" if (-not $currentTestRoot) { throw "BeforeEach: currentTestRoot (from script:TestRoot) is not set!" } if (-not $script:ImportDotEnvModule) { throw "BeforeEach: script:ImportDotEnvModule is not available for state reset!" } Write-Host "BeforeEach: Directly resetting ImportDotEnv module's internal script variables." # Since we are InModuleScope, we can directly set the script-scoped variables $script:trueOriginalEnvironmentVariables = @{} $script:previousEnvFiles = @() $script:previousWorkingDirectory = "RESET_BY_BEFORE_EACH_TEST_HOOK" # Match initial state or a known reset state $currentTrueOriginals = $script:ImportDotEnvModule.SessionState.PSVariable.GetValue('trueOriginalEnvironmentVariables') Write-Host "BeforeEach (Describe): After reset, trueOriginalEnvironmentVariables count: $($currentTrueOriginals.Count). Keys: $($currentTrueOriginals.Keys -join ', ')" -ForegroundColor Cyan if ($script:TestRoot -and (Test-Path $script:TestRoot)) { Microsoft.PowerShell.Management\Set-Location $script:TestRoot Write-Host "Describe-level BeforeEach: PWD reset to $($PWD.Path)" } # $script:ImportDotEnvModule.SessionState.PSVariable.Set('previousWorkingDirectory', "RESET_BY_BEFORE_EACH_TEST_HOOK") # Done above Write-Host "BeforeEach: Module state reset. TrueOriginalEnvironmentVariables count: $($script:ImportDotEnvModule.SessionState.PSVariable.GetValue('trueOriginalEnvironmentVariables').Count)" Write-Host "BeforeEach: Module state reset. PreviousEnvFiles count: $($script:ImportDotEnvModule.SessionState.PSVariable.GetValue('previousEnvFiles').Count)" Write-Host "BeforeEach: Module state reset. PreviousWorkingDirectory: $($script:ImportDotEnvModule.SessionState.PSVariable.GetValue('previousWorkingDirectory'))" } AfterAll { # Runs once after all tests in this Describe block if ($script:TestRoot -and $PWD.Path.StartsWith($script:TestRoot, [System.StringComparison]::OrdinalIgnoreCase)) { $parentOfTestRoot = Split-Path $script:TestRoot -Parent Write-Host "AfterAll: Current PWD '$($PWD.Path)' is inside TestRoot. Changing location to '$parentOfTestRoot'." Microsoft.PowerShell.Management\Set-Location $parentOfTestRoot # Use original SL } if (Test-Path $script:TestRoot) { Remove-Item $script:TestRoot -Recurse -Force } if (Test-Path $script:ParentEnvPath) { # Clean up the parent .env file Remove-Item $script:ParentEnvPath -Force } Write-Host "AfterAll: Restoring initial environment variables." foreach ($varName in $Global:InitialEnvironment.Keys) { $initialVal = $Global:InitialEnvironment[$varName] if ($null -eq $initialVal) { if (Test-Path "Env:\$varName") { Write-Host "AfterAll: Removing environment variable '$varName' as its initial state was null." Remove-Item "Env:\$varName" -Force -ErrorAction SilentlyContinue } } else { Write-Host "AfterAll: Restoring environment variable '$varName' to '$initialVal'." [Environment]::SetEnvironmentVariable($varName, $initialVal) } } if (Get-Command Disable-ImportDotEnvCdIntegration -ErrorAction SilentlyContinue) { Write-Host "AfterAll: Calling Disable-ImportDotEnvCdIntegration." -ForegroundColor Cyan Disable-ImportDotEnvCdIntegration Write-Host "AfterAll: Disable-ImportDotEnvCdIntegration finished." -ForegroundColor Cyan $cdCmdAfterDisable = Get-Command cd -ErrorAction SilentlyContinue Write-Host "AfterAll: State of 'cd' after Disable-ImportDotEnvCdIntegration: Name: $($cdCmdAfterDisable.Name), Type: $($cdCmdAfterDisable.CommandType), Definition: $($cdCmdAfterDisable.Definition)" -ForegroundColor Cyan if ($cdCmdAfterDisable.CommandType -ne [System.Management.Automation.CommandTypes]::Alias) { Write-Warning "AfterAll: 'cd' IS NOT an alias after Disable-ImportDotEnvCdIntegration. This is unexpected." } else { Write-Host "AfterAll: 'cd' IS an alias as expected." -ForegroundColor Green } } else { Write-Warning "AfterAll: Disable-ImportDotEnvCdIntegration command not found. Skipping disable." } Write-Host "AfterAll: Calling Remove-Module ImportDotEnv -Force." -ForegroundColor Cyan Remove-Module ImportDotEnv -Force -ErrorAction SilentlyContinue Write-Host "AfterAll: Remove-Module ImportDotEnv -Force finished." -ForegroundColor Cyan $cdCmdAfterRemoveModule = Get-Command cd -ErrorAction SilentlyContinue Write-Host "AfterAll: State of 'cd' after Remove-Module: Name: $($cdCmdAfterRemoveModule.Name), Type: $($cdCmdAfterRemoveModule.CommandType), Definition: $($cdCmdAfterRemoveModule.Definition)" -ForegroundColor Cyan } # Helper function for mocking Get-EnvFilesUpstream $script:GetEnvFilesUpstreamMock = { param([string]$Directory) $resolvedDir = Convert-Path $Directory #Write-Host "MOCK Get-EnvFilesUpstream called for dir: $resolvedDir (TestRoot: $($script:TestRoot), ParentEnvPath: $($script:ParentEnvPath))" -ForegroundColor Magenta if ($resolvedDir -eq $script:DirA.FullName) { return @(Join-Path $script:DirA.FullName ".env") } if ($resolvedDir -eq $script:DirB.FullName) { return @(Join-Path $script:DirB.FullName ".env") } if ($resolvedDir -eq $script:DirC.FullName) { return @(Join-Path $script:DirC.FullName ".env") } if ($resolvedDir -eq $script:SubDir.FullName) { return @( (Join-Path $script:BaseDir.FullName ".env"), (Join-Path $script:SubDir.FullName ".env") ) } # Hierarchical if ($resolvedDir -eq $script:BaseDir.FullName) { return @(Join-Path $script:BaseDir.FullName ".env") } if ($resolvedDir -eq $script:Project1Dir.FullName) { return @(Join-Path $script:Project1Dir.FullName ".env") } if ($resolvedDir -eq $script:Project2Dir.FullName) { return @(Join-Path $script:Project2Dir.FullName ".env") } if ($resolvedDir -eq $script:NonEnvDir.FullName) { return @() } if ($resolvedDir -eq $script:TestRoot) { $filesToReturn = @() # The real Get-EnvFilesUpstream collects current-to-root, then reverses. # So, parent .env (if exists) comes before current's .env (if exists) in the final list. if (Test-Path $script:ParentEnvPath) { # This is C:\Users\dani_\AppData\Local\Temp\.env $filesToReturn += $script:ParentEnvPath } $testRootOwnEnv = Join-Path $script:TestRoot ".env" # e.g., for the manual test if (Test-Path $testRootOwnEnv) { $filesToReturn += $testRootOwnEnv } #Write-Host "MOCK Get-EnvFilesUpstream for TestRoot returning: $($filesToReturn -join ', ')" -ForegroundColor Magenta return $filesToReturn } if ($resolvedDir -eq $script:ParentDirOfTestRoot) { # e.g. C:\Users\dani_\AppData\Local\Temp # This directory, in the context of our tests, primarily has $script:ParentEnvPath if (Test-Path $script:ParentEnvPath) { #Write-Host "MOCK Get-EnvFilesUpstream for ParentDirOfTestRoot returning: $($script:ParentEnvPath)" -ForegroundColor Magenta return @($script:ParentEnvPath) } #Write-Host "MOCK Get-EnvFilesUpstream for ParentDirOfTestRoot returning empty (no ParentEnvPath)" -ForegroundColor Magenta return @() } # For other non-test specific dirs, return empty #Write-Host "MOCK Get-EnvFilesUpstream for '$resolvedDir' returning empty (default case)" -ForegroundColor Magenta return @() } Context "Core Import-DotEnv Functionality (Manual Invocation)" { It "loads variables when Import-DotEnv is called directly and restores on subsequent call for parent" { $initialManualTestVar = [Environment]::GetEnvironmentVariable("MANUAL_TEST_VAR") # Capture initial state $manualEnvFile = Join-Path $script:TestRoot ".env" Set-Content -Path $manualEnvFile -Value "MANUAL_TEST_VAR=loaded_manual" try { Push-Location $script:TestRoot Mock Get-EnvFilesUpstream -MockWith $script:GetEnvFilesUpstreamMock -ModuleName ImportDotEnv Import-DotEnv -Path "." # Load .env from $script:TestRoot [Environment]::GetEnvironmentVariable("MANUAL_TEST_VAR") | Should -Be "loaded_manual" # Simulate moving out by calling Import-DotEnv for the parent Import-DotEnv -Path $script:ParentDirOfTestRoot # Check if MANUAL_TEST_VAR was restored to its original value or unset if it didn't exist if ($null -eq $initialManualTestVar) { (Test-Path Env:\MANUAL_TEST_VAR) | Should -Be $false } else { [Environment]::GetEnvironmentVariable("MANUAL_TEST_VAR") | Should -Be $initialManualTestVar } Pop-Location } finally { if (Test-Path $manualEnvFile) { Remove-Item $manualEnvFile -Force -ErrorAction SilentlyContinue } # Restore MANUAL_TEST_VAR to its absolute initial state if ($null -eq $initialManualTestVar) { [Environment]::SetEnvironmentVariable("MANUAL_TEST_VAR", $null) } else { [Environment]::SetEnvironmentVariable("MANUAL_TEST_VAR", $initialManualTestVar) } } } } Context "Set-Location Integration (Enable/Disable Functionality)" { AfterEach { Disable-ImportDotEnvCdIntegration -ErrorAction SilentlyContinue } It "should have Set-Location, cd, and sl in their default states after module import (or after disable)" { Disable-ImportDotEnvCdIntegration -ErrorAction SilentlyContinue $cmd = Get-Command Set-Location -ErrorAction SilentlyContinue $cmd.CommandType | Should -Be ([System.Management.Automation.CommandTypes]::Cmdlet) $cmd.ModuleName | Should -Be "Microsoft.PowerShell.Management" $cmd = Get-Command cd -ErrorAction SilentlyContinue $cmd.CommandType | Should -Be ([System.Management.Automation.CommandTypes]::Alias) $cmd.Definition | Should -Be "Set-Location" $cmd.ResolvedCommand.Name | Should -Be "Set-Location" $cmd.ResolvedCommand.CommandType | Should -Be ([System.Management.Automation.CommandTypes]::Cmdlet) $cmd.ResolvedCommand.ModuleName | Should -Be "Microsoft.PowerShell.Management" $cmd = Get-Command sl -ErrorAction SilentlyContinue $cmd.CommandType | Should -Be ([System.Management.Automation.CommandTypes]::Alias) $cmd.Definition | Should -Be "Set-Location" $cmd.ResolvedCommand.Name | Should -Be "Set-Location" $cmd.ResolvedCommand.CommandType | Should -Be ([System.Management.Automation.CommandTypes]::Cmdlet) $cmd.ResolvedCommand.ModuleName | Should -Be "Microsoft.PowerShell.Management" } It "Enable-ImportDotEnvCdIntegration should correctly modify Set-Location, and cd/sl should follow" { Mock Get-EnvFilesUpstream -MockWith $script:GetEnvFilesUpstreamMock -ModuleName ImportDotEnv # Mock for the auto-load on enable Enable-ImportDotEnvCdIntegration $cmd = Get-Command Set-Location -ErrorAction SilentlyContinue $cmd | Should -Not -BeNull $cmd.CommandType | Should -Be ([System.Management.Automation.CommandTypes]::Alias) $cmd.Definition | Should -Be "ImportDotEnv\Invoke-ImportDotEnvSetLocationWrapper" $cmd.ResolvedCommand.Name | Should -Be "Invoke-ImportDotEnvSetLocationWrapper" $cmd.ResolvedCommand.CommandType | Should -Be ([System.Management.Automation.CommandTypes]::Function) $cmd.ResolvedCommand.ModuleName | Should -Be "ImportDotEnv" $cmd = Get-Command cd -ErrorAction SilentlyContinue $cmd | Should -Not -BeNull $cmd.CommandType | Should -Be ([System.Management.Automation.CommandTypes]::Alias) $cmd.Definition | Should -Be "Set-Location" $cmd.ResolvedCommand.Name | Should -Be "Invoke-ImportDotEnvSetLocationWrapper" $cmd.ResolvedCommand.CommandType | Should -Be ([System.Management.Automation.CommandTypes]::Function) $cmd.ResolvedCommand.ModuleName | Should -Be "ImportDotEnv" $cmd.Name | Should -Be "cd" $cmd = Get-Command sl -ErrorAction SilentlyContinue $cmd | Should -Not -BeNull $cmd.CommandType | Should -Be ([System.Management.Automation.CommandTypes]::Alias) $cmd.Definition | Should -Be "Set-Location" $cmd.ResolvedCommand.Name | Should -Be "Invoke-ImportDotEnvSetLocationWrapper" $cmd.ResolvedCommand.CommandType | Should -Be ([System.Management.Automation.CommandTypes]::Function) $cmd.ResolvedCommand.ModuleName | Should -Be "ImportDotEnv" $cmd.Name | Should -Be "sl" } It "Disable-ImportDotEnvCdIntegration should correctly restore Set-Location, and cd/sl should follow" { Mock Get-EnvFilesUpstream -MockWith $script:GetEnvFilesUpstreamMock -ModuleName ImportDotEnv # Mock for the auto-load on enable Enable-ImportDotEnvCdIntegration Disable-ImportDotEnvCdIntegration $cmd = Get-Command Set-Location -ErrorAction SilentlyContinue $cmd.CommandType | Should -Be ([System.Management.Automation.CommandTypes]::Cmdlet) $cmd.Name | Should -Be "Set-Location" $cmd.ModuleName | Should -Be "Microsoft.PowerShell.Management" $cmd = Get-Command cd -ErrorAction SilentlyContinue $cmd.CommandType | Should -Be ([System.Management.Automation.CommandTypes]::Alias) $cmd.Definition | Should -Be "Set-Location" $cmd.ResolvedCommand.Name | Should -Be "Set-Location" $cmd.ResolvedCommand.CommandType | Should -Be ([System.Management.Automation.CommandTypes]::Cmdlet) $cmd.ResolvedCommand.ModuleName | Should -Be "Microsoft.PowerShell.Management" $cmd = Get-Command sl -ErrorAction SilentlyContinue $cmd.CommandType | Should -Be ([System.Management.Automation.CommandTypes]::Alias) $cmd.Definition | Should -Be "Set-Location" $cmd.ResolvedCommand.Name | Should -Be "Set-Location" $cmd.ResolvedCommand.CommandType | Should -Be ([System.Management.Automation.CommandTypes]::Cmdlet) $cmd.ResolvedCommand.ModuleName | Should -Be "Microsoft.PowerShell.Management" } It "Disable-ImportDotEnvCdIntegration keeps vars loaded (no longer unloads)" { $newVarName = "NEW_VAR_FOR_DISABLE_TEST" $existingVarName = "EXISTING_VAR_FOR_DISABLE_TEST" $initialExistingValue = "initial_value_for_existing" if (Test-Path "Env:\$newVarName") { Remove-Item "Env:\$newVarName" -Force } (Test-Path "Env:\$newVarName") | Should -Be $false [Environment]::SetEnvironmentVariable($existingVarName, $initialExistingValue) [Environment]::GetEnvironmentVariable($existingVarName) | Should -Be $initialExistingValue $dirBEnvPath = Join-Path $script:DirB.FullName ".env" $originalDirBEnvContent = Get-Content $dirBEnvPath -Raw -ErrorAction SilentlyContinue Set-Content -Path $dirBEnvPath -Value "$newVarName=new_value_from_env`n$existingVarName=overwritten_by_env" Mock Get-EnvFilesUpstream -MockWith $script:GetEnvFilesUpstreamMock -ModuleName ImportDotEnv Microsoft.PowerShell.Management\Set-Location $script:DirB.FullName # Go to DirB before enabling Enable-ImportDotEnvCdIntegration # This will load DirB's .env [Environment]::GetEnvironmentVariable($newVarName) | Should -Be "new_value_from_env" [Environment]::GetEnvironmentVariable($existingVarName) | Should -Be "overwritten_by_env" Disable-ImportDotEnvCdIntegration (Test-Path "Env:\$newVarName") | Should -Be $true [Environment]::GetEnvironmentVariable($newVarName) | Should -Be "new_value_from_env" [Environment]::GetEnvironmentVariable($existingVarName) | Should -Be "overwritten_by_env" if ($null -ne $originalDirBEnvContent) { Set-Content -Path $dirBEnvPath -Value $originalDirBEnvContent -Force } else { Remove-Item $dirBEnvPath -Force -ErrorAction SilentlyContinue } # Clean up test vars [Environment]::SetEnvironmentVariable($newVarName, $null) [Environment]::SetEnvironmentVariable($existingVarName, $initialExistingValue) # Restore to its specific initial for this test } It "loads .env variables for the current directory upon enabling integration and restores on subsequent cd" { Mock Get-EnvFilesUpstream -MockWith $script:GetEnvFilesUpstreamMock -ModuleName ImportDotEnv Disable-ImportDotEnvCdIntegration -ErrorAction SilentlyContinue (Get-Command Set-Location).CommandType | Should -Be ([System.Management.Automation.CommandTypes]::Cmdlet) $initialTestVarA = "initial_A_for_enable_test" $initialTestVarGlobal = "initial_GLOBAL_for_enable_test" [Environment]::SetEnvironmentVariable("TEST_VAR_A", $initialTestVarA) [Environment]::SetEnvironmentVariable("TEST_VAR_GLOBAL", $initialTestVarGlobal) Microsoft.PowerShell.Management\Set-Location $script:DirA.FullName [Environment]::GetEnvironmentVariable("TEST_VAR_A") | Should -Be $initialTestVarA [Environment]::GetEnvironmentVariable("TEST_VAR_GLOBAL") | Should -Be $initialTestVarGlobal Enable-ImportDotEnvCdIntegration [Environment]::GetEnvironmentVariable("TEST_VAR_A") | Should -Be "valA" [Environment]::GetEnvironmentVariable("TEST_VAR_GLOBAL") | Should -Be "valA_override" Set-Location $script:TestRoot # Changed from Split-Path to $script:TestRoot for consistency with mock [Environment]::GetEnvironmentVariable("TEST_VAR_A") | Should -Be $initialTestVarA [Environment]::GetEnvironmentVariable("TEST_VAR_GLOBAL") | Should -Be $initialTestVarGlobal } It "keeps .env variables loaded upon disabling integration (no longer unloads)" { Mock Get-EnvFilesUpstream -MockWith $script:GetEnvFilesUpstreamMock -ModuleName ImportDotEnv Disable-ImportDotEnvCdIntegration -ErrorAction SilentlyContinue (Get-Command Set-Location).CommandType | Should -Be ([System.Management.Automation.CommandTypes]::Cmdlet) $initialTestVarA = "initial_A_for_disable_test" $initialTestVarGlobal = "initial_GLOBAL_for_disable_test" [Environment]::SetEnvironmentVariable("TEST_VAR_A", $initialTestVarA) [Environment]::SetEnvironmentVariable("TEST_VAR_GLOBAL", $initialTestVarGlobal) Microsoft.PowerShell.Management\Set-Location $script:DirA.FullName Enable-ImportDotEnvCdIntegration [Environment]::GetEnvironmentVariable("TEST_VAR_A") | Should -Be "valA" [Environment]::GetEnvironmentVariable("TEST_VAR_GLOBAL") | Should -Be "valA_override" Disable-ImportDotEnvCdIntegration [Environment]::GetEnvironmentVariable("TEST_VAR_A") | Should -Be "valA" [Environment]::GetEnvironmentVariable("TEST_VAR_GLOBAL") | Should -Be "valA_override" (Get-Command Set-Location).CommandType | Should -Be ([System.Management.Automation.CommandTypes]::Cmdlet) (Get-Command cd).Definition | Should -Be "Set-Location" } } Context "Set-Location Integration - Variable Loading Scenarios" { BeforeEach { # CRITICAL FIX: Ensure trueOriginalEnvironmentVariables is reset before this context's Enable-ImportDotEnvCdIntegration # This prevents state leakage from previous tests that also called Enable-ImportDotEnvCdIntegration. # Since we are InModuleScope, we can directly set the script-scoped variables $script:trueOriginalEnvironmentVariables = @{} # $script:ImportDotEnvModule.SessionState.PSVariable.Set('trueOriginalEnvironmentVariables', @{}) # Old way Write-Host "BeforeEach (Context): Manually reset trueOriginalEnvironmentVariables before Enable." -ForegroundColor Yellow Enable-ImportDotEnvCdIntegration $trueOriginalsAfterEnable = $script:ImportDotEnvModule.SessionState.PSVariable.GetValue('trueOriginalEnvironmentVariables') Write-Host "BeforeEach (Context): After Enable-ImportDotEnvCdIntegration, trueOriginalEnvironmentVariables count: $($trueOriginalsAfterEnable.Count). Keys: $($trueOriginalsAfterEnable.Keys -join ', ')" -ForegroundColor Magenta Write-Host "BeforeEach (Context): Value of TEST_VAR_A in trueOriginals: '$($trueOriginalsAfterEnable['TEST_VAR_A'])'" -ForegroundColor Magenta Mock Get-EnvFilesUpstream -MockWith $script:GetEnvFilesUpstreamMock -ModuleName ImportDotEnv } AfterEach { Disable-ImportDotEnvCdIntegration } It "loads variables from .env and restores global on exit" { $initialTestVarA = $Global:InitialEnvironment["TEST_VAR_A"] # Get the true initial value # Ensure TEST_VAR_A is in its initial state (null or original value) if ($null -eq $initialTestVarA) { [Environment]::SetEnvironmentVariable("TEST_VAR_A", $null) if(Test-Path Env:\TEST_VAR_A) { Remove-Item Env:\TEST_VAR_A -Force } } else { [Environment]::SetEnvironmentVariable("TEST_VAR_A", $initialTestVarA) } $trueOriginalsBeforeDirA = $script:ImportDotEnvModule.SessionState.PSVariable.GetValue('trueOriginalEnvironmentVariables') # Explicitly ensure TEST_VAR_A is null right before the Set-Location that will capture its original state [Environment]::SetEnvironmentVariable("TEST_VAR_A", $null) if(Test-Path Env:\TEST_VAR_A) { Remove-Item Env:\TEST_VAR_A -Force -ErrorAction SilentlyContinue } Set-Location $script:DirA.FullName [Environment]::GetEnvironmentVariable("TEST_VAR_A") | Should -Be "valA" $trueOriginalsAfterDirA = $script:ImportDotEnvModule.SessionState.PSVariable.GetValue('trueOriginalEnvironmentVariables') Set-Location $script:TestRoot # Go to TestRoot, which might have its own or parent .env via mock $actualValue = [Environment]::GetEnvironmentVariable("TEST_VAR_A") $existsInPSDrive = Test-Path "Env:\TEST_VAR_A" $trueOriginalsAtAssert = $script:ImportDotEnvModule.SessionState.PSVariable.GetValue('trueOriginalEnvironmentVariables') # Only emit essential debug info now that all tests are passing Write-Debug "At assertion, value of TEST_VAR_A in trueOriginals: '$($trueOriginalsAtAssert['TEST_VAR_A'])'" if ($null -eq $initialTestVarA) { $actualValue | Should -BeNullOrEmpty $existsInPSDrive | Should -Be $false } else { $actualValue | Should -Be $initialTestVarA } } It "loads hierarchically and restores correctly level by level" { $initialTestVarBase = $Global:InitialEnvironment["TEST_VAR_BASE"] $initialTestVarOverride = $Global:InitialEnvironment["TEST_VAR_OVERRIDE"] $initialTestVarSub = $Global:InitialEnvironment["TEST_VAR_SUB"] # Set to known initial states for this test if ($null -ne $initialTestVarBase) { [Environment]::SetEnvironmentVariable("TEST_VAR_BASE", $initialTestVarBase) } else { [Environment]::SetEnvironmentVariable("TEST_VAR_BASE", $null); if(Test-Path Env:\TEST_VAR_BASE){Remove-Item Env:\TEST_VAR_BASE -Force} } if ($null -ne $initialTestVarOverride) { [Environment]::SetEnvironmentVariable("TEST_VAR_OVERRIDE", $initialTestVarOverride) } else { [Environment]::SetEnvironmentVariable("TEST_VAR_OVERRIDE", $null); if(Test-Path Env:\TEST_VAR_OVERRIDE){Remove-Item Env:\TEST_VAR_OVERRIDE -Force} } if ($null -ne $initialTestVarSub) { [Environment]::SetEnvironmentVariable("TEST_VAR_SUB", $initialTestVarSub) } else { [Environment]::SetEnvironmentVariable("TEST_VAR_SUB", $null); if(Test-Path Env:\TEST_VAR_SUB){Remove-Item Env:\TEST_VAR_SUB -Force} } Set-Location $script:SubDir.FullName [Environment]::GetEnvironmentVariable("TEST_VAR_BASE") | Should -Be "base_val" [Environment]::GetEnvironmentVariable("TEST_VAR_SUB") | Should -Be "sub_val" [Environment]::GetEnvironmentVariable("TEST_VAR_OVERRIDE") | Should -Be "sub_override_val" Set-Location $script:BaseDir.FullName if ($null -eq $initialTestVarSub) { (Test-Path "Env:\TEST_VAR_SUB") | Should -Be $false } else { [Environment]::GetEnvironmentVariable("TEST_VAR_SUB") | Should -Be $initialTestVarSub } [Environment]::GetEnvironmentVariable("TEST_VAR_OVERRIDE") | Should -Be "base_override_val" # Restored from sub, set by base Set-Location $script:TestRoot # Go to TestRoot if ($null -eq $initialTestVarBase) { (Test-Path "Env:\TEST_VAR_BASE") | Should -Be $false } else { [Environment]::GetEnvironmentVariable("TEST_VAR_BASE") | Should -Be $initialTestVarBase } if ($null -eq $initialTestVarSub) { (Test-Path "Env:\TEST_VAR_SUB") | Should -Be $false } else { [Environment]::GetEnvironmentVariable("TEST_VAR_SUB") | Should -Be $initialTestVarSub } if ($null -eq $initialTestVarOverride) { (Test-Path "Env:\TEST_VAR_OVERRIDE") | Should -Be $false } else { [Environment]::GetEnvironmentVariable("TEST_VAR_OVERRIDE") | Should -Be $initialTestVarOverride } } It "creates a new variable and removes it on exit (restores to non-existent)" { $initialNewVar = $Global:InitialEnvironment["NEW_VAR"] if ($null -eq $initialNewVar) { [Environment]::SetEnvironmentVariable("NEW_VAR", $null); if(Test-Path Env:\NEW_VAR){Remove-Item Env:\NEW_VAR -Force} } else { [Environment]::SetEnvironmentVariable("NEW_VAR", $initialNewVar) } Set-Location $script:DirB.FullName [Environment]::GetEnvironmentVariable("NEW_VAR") | Should -Be "new_value" Set-Location $script:TestRoot if ($null -eq $initialNewVar) { (Test-Path "Env:\NEW_VAR") | Should -Be $false } else { [Environment]::GetEnvironmentVariable("NEW_VAR") | Should -Be $initialNewVar } } It "sets variable to empty string from .env and restores previous value on exit" { $initialEmptyVar = "initial_empty_test_val_specific" # Use a specific initial value for this test [Environment]::SetEnvironmentVariable("TEST_EMPTY_VAR", $initialEmptyVar) Set-Location $script:DirC.FullName [Environment]::GetEnvironmentVariable("TEST_EMPTY_VAR") | Should -Be "" Set-Location $script:TestRoot [Environment]::GetEnvironmentVariable("TEST_EMPTY_VAR") | Should -Be $initialEmptyVar } It "correctly unloads project1 vars and loads project2 vars, then restores global" { $initialProjectId = "global_project_id_specific" # Specific initial [Environment]::SetEnvironmentVariable("PROJECT_ID", $initialProjectId) Set-Location $script:Project1Dir.FullName [Environment]::GetEnvironmentVariable("PROJECT_ID") | Should -Be "P1" Set-Location $script:Project2Dir.FullName [Environment]::GetEnvironmentVariable("PROJECT_ID") | Should -Be "P2" Set-Location $script:TestRoot [Environment]::GetEnvironmentVariable("PROJECT_ID") | Should -Be $initialProjectId } It "should not alter existing environment variables when moving to a dir with no .env" { $initialGlobalVar = "no_env_test_initial_specific" # Specific initial [Environment]::SetEnvironmentVariable("TEST_VAR_GLOBAL", $initialGlobalVar) Set-Location $script:NonEnvDir.FullName [Environment]::GetEnvironmentVariable("TEST_VAR_GLOBAL") | Should -Be $initialGlobalVar Set-Location $script:TestRoot [Environment]::GetEnvironmentVariable("TEST_VAR_GLOBAL") | Should -Be $initialGlobalVar } # Moved test It "loads variables from .env files in the correct order when using Set-Location" { # This test relies on the BeforeEach to set up integration and mock. # Initial state of these vars will be from $Global:InitialEnvironment or null if not present there. $initialTestVarBase = $Global:InitialEnvironment["TEST_VAR_BASE"] $initialTestVarSub = $Global:InitialEnvironment["TEST_VAR_SUB"] $initialTestVarOverride = $Global:InitialEnvironment["TEST_VAR_OVERRIDE"] # Ensure a clean start for these specific vars based on their true initial state if ($null -ne $initialTestVarBase) { [Environment]::SetEnvironmentVariable("TEST_VAR_BASE", $initialTestVarBase) } else { [Environment]::SetEnvironmentVariable("TEST_VAR_BASE", $null); if(Test-Path Env:\TEST_VAR_BASE){Remove-Item Env:\TEST_VAR_BASE -Force} } if ($null -ne $initialTestVarSub) { [Environment]::SetEnvironmentVariable("TEST_VAR_SUB", $initialTestVarSub) } else { [Environment]::SetEnvironmentVariable("TEST_VAR_SUB", $null); if(Test-Path Env:\TEST_VAR_SUB){Remove-Item Env:\TEST_VAR_SUB -Force} } if ($null -ne $initialTestVarOverride) { [Environment]::SetEnvironmentVariable("TEST_VAR_OVERRIDE", $initialTestVarOverride) } else { [Environment]::SetEnvironmentVariable("TEST_VAR_OVERRIDE", $null); if(Test-Path Env:\TEST_VAR_OVERRIDE){Remove-Item Env:\TEST_VAR_OVERRIDE -Force} } Set-Location $script:SubDir.FullName [Environment]::GetEnvironmentVariable("TEST_VAR_BASE") | Should -Be "base_val" [Environment]::GetEnvironmentVariable("TEST_VAR_SUB") | Should -Be "sub_val" [Environment]::GetEnvironmentVariable("TEST_VAR_OVERRIDE") | Should -Be "sub_override_val" # From subDir, overrides baseDir } } # Removed empty Context "Integration Tests - Hierarchical Loading" } # End of Describe "Import-DotEnv Core and Integration Tests" } # End of InModuleScope |