test/concurrently.tests.ps1
|
BeforeDiscovery { . "$PSScriptRoot\test-utils.ps1" } BeforeAll { Get-Module ConfigMap -ErrorAction SilentlyContinue | Remove-Module Import-Module $PSScriptRoot\..\configmap.psm1 } Describe "Format-QBuildCommand" { It "builds qbuild with entry and switch parameters" { InModuleScope ConfigMap { Format-QBuildCommand -mainCommand 'qbuild' -Entry 'build.ui' -BoundParameters @{ NoRestore = $true } -RemainingArguments @() | Should -Be 'qbuild build.ui -NoRestore' } } It "includes map path when map is a string" { InModuleScope ConfigMap { Format-QBuildCommand -mainCommand 'qbuild' -Entry 'build.ui' -BoundParameters @{ map = '.\.build.map.ps1' } -RemainingArguments @() | Should -Be "qbuild -map '.\.build.map.ps1' build.ui" } } It "appends passthrough arguments after --" { InModuleScope ConfigMap { Format-QBuildCommand -mainCommand 'qbuild' -Entry 'build.ui' -BoundParameters @{} -RemainingArguments @('--config=Release') | Should -Be 'qbuild build.ui -- --config=Release' } } } Describe "Test-ConcurrentlyAvailable" { It "returns false when npx is not available" { InModuleScope ConfigMap { Mock Get-Command { return $null } Test-ConcurrentlyAvailable | Should -Be $false } } It "returns false when concurrently is not available" { InModuleScope ConfigMap { Mock Get-Command { return [pscustomobject]@{ Name = 'npx' } } Mock Test-ConcurrentlyPackageAvailable { return $false } Test-ConcurrentlyAvailable | Should -Be $false } } It "returns true when npx and concurrently are available" { InModuleScope ConfigMap { Mock Get-Command { return [pscustomobject]@{ Name = 'npx' } } Mock Test-ConcurrentlyPackageAvailable { return $true } Test-ConcurrentlyAvailable | Should -Be $true } } It "falls back to local execution when npx is not available" { $mapFile = Join-Path $TestDrive ".build.map.ps1" @' @{ "build" = @{ "ui" = { Write-Host "ui" } "api" = { Write-Host "api" } } } '@ | Set-Content $mapFile -Encoding utf8 InModuleScope ConfigMap -ArgumentList $mapFile { param($MapFile) $script:ConfigMapConcurrentlyInvoker = { throw 'concurrently should not run' } Mock Get-Command { return $null } Mock Write-Host Mock Get-TmuxInfo { return $null } qbuild -map $MapFile "build.all" Should -Invoke Write-Host -Exactly 1 -ParameterFilter { $Object -eq "ui" } Should -Invoke Write-Host -Exactly 1 -ParameterFilter { $Object -eq "api" } } } It "falls back to local execution when concurrently package is missing" { $mapFile = Join-Path $TestDrive ".build.map.ps1" @' @{ "build" = @{ "ui" = { Write-Host "ui" } "api" = { Write-Host "api" } } } '@ | Set-Content $mapFile -Encoding utf8 InModuleScope ConfigMap -ArgumentList $mapFile { param($MapFile) $script:ConfigMapConcurrentlyInvoker = { throw 'concurrently should not run' } Mock Get-Command { return [pscustomobject]@{ Name = 'npx' } } Mock Test-ConcurrentlyPackageAvailable { return $false } Mock Write-Host Mock Get-TmuxInfo { return $null } qbuild -map $MapFile "build.all" Should -Invoke Write-Host -Exactly 1 -ParameterFilter { $Object -eq "ui" } Should -Invoke Write-Host -Exactly 1 -ParameterFilter { $Object -eq "api" } } } } Describe "Test-ConcurrentlyEnabled" { BeforeEach { $script:concurrentlyBackup = $env:QCONF_CONCURRENTLY Remove-Item env:QCONF_CONCURRENTLY -ErrorAction SilentlyContinue } AfterEach { if ($null -eq $script:concurrentlyBackup) { Remove-Item env:QCONF_CONCURRENTLY -ErrorAction SilentlyContinue } else { $env:QCONF_CONCURRENTLY = $script:concurrentlyBackup } } It "is enabled when the env var is unset" { InModuleScope ConfigMap { Test-ConcurrentlyEnabled | Should -Be $true } } It "is disabled for falsy values" { foreach ($value in '0', 'false', 'no', 'off', 'FALSE', 'OFF') { $env:QCONF_CONCURRENTLY = $value InModuleScope ConfigMap { Test-ConcurrentlyEnabled | Should -Be $false } } } } Describe "qbuild concurrently" { BeforeEach { $script:concurrentlyBackup = $env:QCONF_CONCURRENTLY $script:capturedConcurrently = $null Remove-Item env:QCONF_CONCURRENTLY -ErrorAction SilentlyContinue } AfterEach { if ($null -eq $script:concurrentlyBackup) { Remove-Item env:QCONF_CONCURRENTLY -ErrorAction SilentlyContinue } else { $env:QCONF_CONCURRENTLY = $script:concurrentlyBackup } InModuleScope ConfigMap { $script:ConfigMapConcurrentlyInvoker = $null } } It "runs virtual build.all sequentially when map is an in-memory hashtable" { InModuleScope ConfigMap { Mock Write-Host $targets = @{ "build" = @{ "ui" = { Write-Host "ui" } "api" = { Write-Host "api" } } } qbuild -map $targets "build.all" Should -Invoke Write-Host -Exactly 1 -ParameterFilter { $Object -eq "ui" } Should -Invoke Write-Host -Exactly 1 -ParameterFilter { $Object -eq "api" } } } It "runs explicit build.all locally" { InModuleScope ConfigMap { Mock Write-Host $targets = @{ "build" = @{ "ui" = { Write-Host "ui" } "all" = { Write-Host "custom all" } } } qbuild -map $targets "build.all" Should -Invoke Write-Host -Exactly 1 -ParameterFilter { $Object -eq "custom all" } Should -Invoke Write-Host -Times 0 -ParameterFilter { $Object -eq "ui" } } } It "delegates virtual build.all to concurrently when using a map file" { $mapFile = Join-Path $TestDrive ".build.map.ps1" @' @{ "build" = @{ "ui" = { Write-Host "ui" } "api" = { Write-Host "api" } } } '@ | Set-Content $mapFile -Encoding utf8 InModuleScope ConfigMap -ArgumentList $mapFile { param($MapFile) $script:ConfigMapConcurrentlyInvoker = { param($Commands, $Names) $script:capturedConcurrently = @{ Commands = $Commands Names = $Names } } Mock Invoke-EntryCommand Mock Get-TmuxInfo { return $null } Mock Test-ConcurrentlyAvailable { return $true } qbuild -map $MapFile "build.all" $script:capturedConcurrently.Names | Should -Contain 'build.ui' $script:capturedConcurrently.Names | Should -Contain 'build.api' $script:capturedConcurrently.Commands.Count | Should -Be 2 ($script:capturedConcurrently.Commands | Where-Object { $_ -match "build\.ui$" }).Count | Should -Be 1 ($script:capturedConcurrently.Commands | Where-Object { $_ -match "build\.api$" }).Count | Should -Be 1 Should -Invoke Invoke-EntryCommand -Times 0 -Exactly } } It "prefers tmux over concurrently for build.all when inside tmux" { $mapFile = Join-Path $TestDrive ".build.map.ps1" @' @{ "build" = @{ "ui" = { Write-Host "ui" } "api" = { Write-Host "api" } } } '@ | Set-Content $mapFile -Encoding utf8 InModuleScope ConfigMap -ArgumentList $mapFile { param($MapFile) $script:ConfigMapConcurrentlyInvoker = { throw 'concurrently should not run when tmux handles build.all' } Mock Write-Host Mock Invoke-EntryCommand Mock Invoke-TmuxCommand Mock Get-TmuxInfo { return [pscustomobject]@{ sessionName = 'dev'; windowName = 'shell' } } qbuild -map $MapFile "build.all" Should -Invoke Invoke-TmuxCommand -Times 2 -Exactly Should -Invoke Invoke-EntryCommand -Times 0 -Exactly } } It "passes qbuild arguments in concurrently child commands" { $mapFile = Join-Path $TestDrive ".build.map.ps1" @' @{ "build" = @{ "ui" = { Write-Host "ui" } "api" = { Write-Host "api" } } } '@ | Set-Content $mapFile -Encoding utf8 InModuleScope ConfigMap -ArgumentList $mapFile { param($MapFile) $script:ConfigMapConcurrentlyInvoker = { param($Commands, $Names) $script:capturedConcurrently = @{ Commands = $Commands Names = $Names } } Mock Invoke-EntryCommand Mock Get-TmuxInfo { return $null } Mock Test-ConcurrentlyAvailable { return $true } qbuild -map $MapFile "build.all" -NoRestore $script:capturedConcurrently.Commands.Count | Should -Be 2 foreach ($command in $script:capturedConcurrently.Commands) { $command | Should -Match '-NoRestore' } } } It "runs entry locally when QCONF_CONCURRENTLY is disabled" { $mapFile = Join-Path $TestDrive ".build.map.ps1" @' @{ "build" = @{ "ui" = { Write-Host "ui" } "api" = { Write-Host "api" } } } '@ | Set-Content $mapFile -Encoding utf8 $env:QCONF_CONCURRENTLY = '0' InModuleScope ConfigMap -ArgumentList $mapFile { param($MapFile) $script:ConfigMapConcurrentlyInvoker = { throw 'concurrently should not run' } Mock Write-Host Mock Get-TmuxInfo { return $null } qbuild -map $MapFile "build.all" Should -Invoke Write-Host -Exactly 1 -ParameterFilter { $Object -eq "ui" } Should -Invoke Write-Host -Exactly 1 -ParameterFilter { $Object -eq "api" } } } } |