tests/Requires.Tests.ps1
|
BeforeAll { . "$PSScriptRoot/TestSupport.ps1" foreach ($f in Get-DFTestModuleFile) { . $f } # Each companion appends its name, so activation order is observable. function script:New-ReqTool([string]$Name, [string]$Extra = '') { Set-Content (Join-Path $script:Tools "$Name.json") "{ ""name"": ""$Name"", ""executable"": ""$Name.exe""$Extra }" "`$global:DFTestOrder += '$Name'" | Set-Content (Join-Path $script:Tools "$Name.ps1") } } Describe 'requires (tools and role:<name>)' { BeforeEach { Reset-DFTestSession Set-DFTestXdg Set-DFTestConfig $null $script:DFToolAvailability = @{} $script:Tools = Join-Path $TestDrive "req-$([guid]::NewGuid().ToString('N').Substring(0, 6))" New-Item -ItemType Directory $script:Tools | Out-Null $global:DFTestOrder = @() $script:Installed = @() Mock Test-DFToolAvailable { $Executable -in $script:Installed } Mock Write-DFConflictNotice { } } AfterEach { Restore-DFTestXdg Set-DFTestConfig $null Remove-Variable DFTestOrder -Scope Global -ErrorAction Ignore } It 'requests a required tool automatically and activates it first' { New-ReqTool 'app' ', "requires": ["lib"]' New-ReqTool 'lib' $script:Installed = 'app.exe', 'lib.exe' Start-DFSession -Config @{ Tools = @('app') } -ToolsPath $script:Tools 3>$null $global:DFTestOrder | Should -Be @('lib', 'app') (Get-DFToolStatus -Name lib).RequestedBy | Should -Be 'requires (app)' } It 'does not activate a tool whose required tool is missing' { New-ReqTool 'app' ', "requires": ["lib"]' New-ReqTool 'lib' $script:Installed = 'app.exe' Start-DFSession -Config @{ Tools = @('app') } -ToolsPath $script:Tools 3>$null $global:DFTestOrder | Should -BeNullOrEmpty (Get-DFToolStatus -Name app).State | Should -Be 'Missing' (Get-DFToolStatus -Name app).Detail | Should -Match 'requires lib' } It 'reports a tool whose required tool is excluded' { New-ReqTool 'app' ', "requires": ["lib"]' New-ReqTool 'lib' $script:Installed = 'app.exe', 'lib.exe' Start-DFSession -Config @{ Tools = @('app'); ExcludeTools = @('lib') } -ToolsPath $script:Tools 3>$null (Get-DFToolStatus -Name app).State | Should -Be 'Missing' (Get-DFToolStatus -Name app).Detail | Should -Match 'requires lib, which is excluded' } It 'treats a required tool excluded through a +group as excluded' { New-ReqTool 'app' ', "requires": ["lib"]' New-ReqTool 'lib' $script:Installed = 'app.exe', 'lib.exe' $script:DFGroupDb = Get-DFGroupDb -Path (New-Item (Join-Path $script:Tools 'groups.txt') -Force -Value '{ "libs": { "description": "d", "tools": ["lib"] } }').FullName try { Start-DFSession -Config @{ Tools = @('app'); ExcludeTools = @('+libs') } -ToolsPath $script:Tools 3>$null } finally { $script:DFGroupDb = $null } (Get-DFToolStatus -Name app).Detail | Should -Match 'requires lib, which is excluded' } It 'orders a tool after every requested member of a role named in after' { New-ReqTool 'rt' ', "after": ["role:version-manager"]' New-ReqTool 'vm1' ', "roles": { "version-manager": {} }' $script:Installed = 'rt.exe', 'vm1.exe' Start-DFSession -Config @{ Tools = @('rt', 'vm1') } -ToolsPath $script:Tools 3>$null $global:DFTestOrder | Should -Be @('vm1', 'rt') } Context 'role:js-runtime (npm needs a JavaScript runtime, not a particular one)' { It 'orders the tool after the requested member of the role' { New-ReqTool 'npmx' ', "requires": ["role:js-runtime"]' New-ReqTool 'nodeprov' ', "roles": { "js-runtime": { "priority": 10 } }' $script:Installed = 'npmx.exe', 'nodeprov.exe' Start-DFSession -Config @{ Tools = @('npmx', 'nodeprov') } -ToolsPath $script:Tools 3>$null $global:DFTestOrder | Should -Be @('nodeprov', 'npmx') } It 'never requests a member on the user''s behalf: which manager or runtime is their choice' { New-ReqTool 'npmx' ', "requires": ["role:js-runtime"]' New-ReqTool 'provlow' ', "roles": { "js-runtime": { "priority": 10 } }' New-ReqTool 'provhigh' ', "roles": { "js-runtime": { "priority": 20 } }' $script:Installed = 'npmx.exe', 'provlow.exe', 'provhigh.exe' Start-DFSession -Config @{ Tools = @('npmx'); Defaults = @{ 'js-runtime' = 'provlow' } } -ToolsPath $script:Tools 3>$null $global:DFTestOrder | Should -Be @('npmx') Get-DFToolStatus -Name provlow | Should -BeNullOrEmpty Get-DFToolStatus -Name provhigh | Should -BeNullOrEmpty } It 'does not block the tool when no member is available (a runtime may come from outside DotForge)' { New-ReqTool 'npmx' ', "requires": ["role:js-runtime"]' New-ReqTool 'provgone' ', "roles": { "js-runtime": { "priority": 30 } }' $script:Installed = 'npmx.exe' Start-DFSession -Config @{ Tools = @('npmx') } -ToolsPath $script:Tools 3>$null (Get-DFToolStatus -Name npmx).State | Should -Be 'Active' Get-DFToolStatus -Name provgone | Should -BeNullOrEmpty } It 'tells the user which tools could fill the role when a tool is missing and no member is requested' { New-ReqTool 'npmx' ', "requires": ["role:js-runtime"]' New-ReqTool 'provgone' ', "roles": { "js-runtime": { "priority": 30 } }' New-ReqTool 'provother' ', "roles": { "js-runtime": {} }' Start-DFSession -Config @{ Tools = @('npmx') } -ToolsPath $script:Tools 3>$null (Get-DFToolStatus -Name npmx).Detail | Should -Match "needs a js-runtime: add provgone or provother to Tools" } It 'reads every tool record only when a tool is missing' { New-ReqTool 'npmx' ', "requires": ["role:js-runtime"]' $script:Installed = 'npmx.exe' Mock Get-DFRoleRequirementHint { 'hint' } Start-DFSession -Config @{ Tools = @('npmx') } -ToolsPath $script:Tools 3>$null Should -Invoke Get-DFRoleRequirementHint -Times 0 } } It 'warns about a requires cycle and still activates both tools' { New-ReqTool 'a' ', "requires": ["b"]' New-ReqTool 'b' ', "requires": ["a"]' $script:Installed = 'a.exe', 'b.exe' $w = Start-DFSession -Config @{ Tools = @('a') } -ToolsPath $script:Tools 3>&1 "$w" | Should -Match 'circular' @($global:DFTestOrder).Count | Should -Be 2 } } Describe 'shipped requires' { It 'names only existing tools and roles' { $roles = (Get-Content "$PSScriptRoot/../data/roles.json" -Raw | ConvertFrom-Json).PSObject.Properties.Name $tools = (Get-ChildItem "$PSScriptRoot/../Tools" -Filter '*.json').BaseName $bad = foreach ($f in Get-ChildItem "$PSScriptRoot/../Tools" -Filter '*.json') { $r = (Get-Content $f.FullName -Raw | ConvertFrom-Json).PSObject.Properties['requires']?.Value foreach ($req in @($r)) { if (-not $req) { continue } if ($req -like 'role:*') { if ($req.Substring(5) -notin $roles) { "$($f.Name): $req" } } elseif ($req -cnotin $tools) { "$($f.Name): $req" } } } @($bad) | Should -BeNullOrEmpty } It 'gives npm node and inshellisense a JavaScript runtime; node is a js-runtime' { (Get-Content "$PSScriptRoot/../Tools/npm.json" -Raw | ConvertFrom-Json).requires | Should -Contain 'node' (Get-Content "$PSScriptRoot/../Tools/inshellisense.json" -Raw | ConvertFrom-Json).requires | Should -Contain 'role:js-runtime' (Get-Content "$PSScriptRoot/../Tools/node.json" -Raw | ConvertFrom-Json).roles.'js-runtime' | Should -Not -BeNullOrEmpty } } |