tests/Invoke-DFToolCompanion.Tests.ps1
|
BeforeAll { . "$PSScriptRoot/../Private/ConvertTo-DFPath.ps1" . "$PSScriptRoot/../Private/Get-DFToolSetupState.ps1" . "$PSScriptRoot/../Public/Complete-DFToolSetup.ps1" . "$PSScriptRoot/../Public/New-DFDirectory.ps1" . "$PSScriptRoot/../Private/Invoke-DFToolCompanion.ps1" } Describe 'Invoke-DFToolCompanion' { BeforeEach { $script:TmpTools = Join-Path $TestDrive 'tools' New-Item -ItemType Directory -Force -Path $script:TmpTools | Out-Null $script:SavedStateHome = $Env:XDG_STATE_HOME $Env:XDG_STATE_HOME = Join-Path $TestDrive 'state' } AfterEach { $Env:XDG_STATE_HOME = $script:SavedStateHome Remove-Variable -Name CompanionSawCurrentTool -Scope Global -ErrorAction Ignore } It 'dot-sources the regular companion, exposing $DFCurrentTool to it' { '$global:CompanionSawCurrentTool = $DFCurrentTool.name' | Set-Content (Join-Path $script:TmpTools 'companiontool.ps1') $tool = '{ "name": "companiontool" }' | ConvertFrom-Json Invoke-DFToolCompanion -Tool $tool -ToolsPath $script:TmpTools $global:CompanionSawCurrentTool | Should -Be 'companiontool' } It 'does not leak $DFCurrentTool beyond Invoke-DFToolCompanion''s own scope' { # PowerShell tears down a function's local scope automatically on return, # so this holds regardless of whether Remove-Variable executes -- confirmed # empirically during review. This guards the invariant itself (no accidental # global/script leak), not the Remove-Variable calls, whose own effect isn't # independently observable from outside the function. 'Set-Content (Join-Path $TestDrive "marker.txt") -Value "ran"' | Set-Content (Join-Path $script:TmpTools 'nodfcurrenttool.ps1') $tool = '{ "name": "nodfcurrenttool" }' | ConvertFrom-Json Invoke-DFToolCompanion -Tool $tool -ToolsPath $script:TmpTools Get-Variable -Name DFCurrentTool -Scope Global -ErrorAction Ignore | Should -BeNullOrEmpty Get-Variable -Name DFCurrentTool -ErrorAction Ignore | Should -BeNullOrEmpty } It 'does nothing when no companion .ps1 exists' { $tool = '{ "name": "nocompaniontool" }' | ConvertFrom-Json { Invoke-DFToolCompanion -Tool $tool -ToolsPath $script:TmpTools } | Should -Not -Throw } It 'runs the one-time setup companion and it can call Complete-DFToolSetup' { 'Complete-DFToolSetup -Name $DFCurrentTool.name' | Set-Content (Join-Path $script:TmpTools 'setuptool.setup.ps1') $tool = '{ "name": "setuptool" }' | ConvertFrom-Json Invoke-DFToolCompanion -Tool $tool -ToolsPath $script:TmpTools (Get-DFToolSetupState).PSObject.Properties['setuptool'] | Should -Not -BeNullOrEmpty } It 'does not re-run one-time setup on a second call' { $runCountFile = Join-Path $TestDrive 'runcount.txt' # Double-quoted here-string interpolates $runCountFile now, but the backtick # keeps `$DFCurrentTool` literal -- it must only be evaluated later, when # Invoke-DFToolCompanion dot-sources this generated file. @" 'x' | Add-Content -Path '$runCountFile' Complete-DFToolSetup -Name `$DFCurrentTool.name "@ | Set-Content (Join-Path $script:TmpTools 'oncetool.setup.ps1') $tool = '{ "name": "oncetool" }' | ConvertFrom-Json Invoke-DFToolCompanion -Tool $tool -ToolsPath $script:TmpTools Invoke-DFToolCompanion -Tool $tool -ToolsPath $script:TmpTools (Get-Content $runCountFile).Count | Should -Be 1 } It 'skips one-time setup when the tool is in -SkipSetup' { 'Complete-DFToolSetup -Name $DFCurrentTool.name' | Set-Content (Join-Path $script:TmpTools 'skippedtool.setup.ps1') $tool = '{ "name": "skippedtool" }' | ConvertFrom-Json Invoke-DFToolCompanion -Tool $tool -ToolsPath $script:TmpTools -SkipSetup @('skippedtool') (Get-DFToolSetupState).PSObject.Properties['skippedtool'] | Should -BeNullOrEmpty } It 'warns and continues when the one-time setup script throws' { 'throw "boom"' | Set-Content (Join-Path $script:TmpTools 'throwtool.setup.ps1') $tool = '{ "name": "throwtool" }' | ConvertFrom-Json { Invoke-DFToolCompanion -Tool $tool -ToolsPath $script:TmpTools -WarningVariable warns 3>$null } | Should -Not -Throw (Get-DFToolSetupState).PSObject.Properties['throwtool'] | Should -BeNullOrEmpty } } |