psmux 3.3.4

Terminal multiplexer for Windows - tmux alternative for PowerShell and Windows Terminal
# test_kill_server.ps1 — kill-server reliability tests
# Verifies:
#   1. kill-server kills ALL sessions and their child processes
#   2. kill-server with -L only kills namespaced sessions
#   3. All aliases (psmux, pmux, tmux) are handled correctly
#   4. Port files are cleaned up

$ErrorActionPreference = "Continue"
$PSMUX = (Get-Command psmux -ErrorAction SilentlyContinue).Source
if (-not $PSMUX) { $PSMUX = "$PSScriptRoot\..\target\release\psmux.exe" }
$PMUX  = (Get-Command pmux  -ErrorAction SilentlyContinue).Source
$TMUX  = (Get-Command tmux  -ErrorAction SilentlyContinue).Source

$script:Passed = 0
$script:Failed = 0
$PsmuxDir = "$env:USERPROFILE\.psmux"

function Pass($msg) { Write-Host "  PASS: $msg" -ForegroundColor Green; $script:Passed++ }
function Fail($msg) { Write-Host "  FAIL: $msg" -ForegroundColor Red;   $script:Failed++ }
function Test($msg) { Write-Host "  TEST: $msg" -ForegroundColor Cyan }

function WaitForSession($name, $timeoutSec = 10) {
    $deadline = (Get-Date).AddSeconds($timeoutSec)
    while ((Get-Date) -lt $deadline) {
        $result = & $PSMUX has-session -t $name 2>&1
        if ($LASTEXITCODE -eq 0) { return $true }
        Start-Sleep -Milliseconds 300
    }
    return $false
}

function SessionExists($name) {
    & $PSMUX has-session -t $name 2>&1 | Out-Null
    return ($LASTEXITCODE -eq 0)
}

function CleanupAll() {
    & $PSMUX kill-server 2>&1 | Out-Null
    Start-Sleep -Seconds 2
}

Write-Host ""
Write-Host "================================================"
Write-Host "kill-server Reliability Test Suite"
Write-Host "================================================"
Write-Host ""

# ─── Cleanup from previous runs ───
CleanupAll

# ═════════════════════════════════════════════
# Group 1: Basic kill-server kills all sessions
# ═════════════════════════════════════════════
Write-Host "[Test Group 1] Basic kill-server kills all sessions"

& $PSMUX new-session -d -s ks-basic1 2>&1 | Out-Null
& $PSMUX new-session -d -s ks-basic2 2>&1 | Out-Null
& $PSMUX new-session -d -s ks-basic3 2>&1 | Out-Null
Start-Sleep -Seconds 3

Test "All 3 sessions created"
$all_exist = (SessionExists "ks-basic1") -and (SessionExists "ks-basic2") -and (SessionExists "ks-basic3")
if ($all_exist) { Pass "All 3 sessions exist" } else { Fail "Not all sessions were created" }

Test "kill-server kills all"
& $PSMUX kill-server 2>&1 | Out-Null
Start-Sleep -Seconds 3

$any_alive = (SessionExists "ks-basic1") -or (SessionExists "ks-basic2") -or (SessionExists "ks-basic3")
if (-not $any_alive) { Pass "All sessions killed by kill-server" } else { Fail "Some sessions survived kill-server" }

Test "Port files cleaned up"
$stale = @(Get-ChildItem "$PsmuxDir\ks-basic*.port" -ErrorAction SilentlyContinue)
if ($stale.Count -eq 0) { Pass "No stale port files remain" } else { Fail "Stale port files remain: $($stale.Name -join ', ')" }

# ═════════════════════════════════════════════
# Group 2: kill-server cleans up child processes
# ═════════════════════════════════════════════
Write-Host ""
Write-Host "[Test Group 2] kill-server cleans up child processes"

& $PSMUX new-session -d -s ks-child1 2>&1 | Out-Null
WaitForSession "ks-child1" | Out-Null
# Create extra panes to have multiple child processes
& $PSMUX -t ks-child1 split-window -v 2>&1 | Out-Null
& $PSMUX -t ks-child1 split-window -h 2>&1 | Out-Null
Start-Sleep -Seconds 2

# Get the pane PIDs before killing
$pane_ids = & $PSMUX -t ks-child1 list-panes -F '#{pane_pid}' 2>&1
$pids = @()
if ($pane_ids) {
    $pids = $pane_ids -split "`n" | Where-Object { $_ -match '^\d+$' } | ForEach-Object { [int]$_ }
}

Test "Session has panes with PIDs"
if ($pids.Count -gt 0) { Pass "Found $($pids.Count) pane PIDs" } else { Pass "Pane PID tracking (may not be supported)" }

& $PSMUX kill-server 2>&1 | Out-Null
Start-Sleep -Seconds 3

Test "Session gone after kill-server"
if (-not (SessionExists "ks-child1")) { Pass "Session ks-child1 killed" } else { Fail "Session ks-child1 still alive" }

Test "Child processes terminated"
if ($pids.Count -gt 0) {
    $alive = $pids | Where-Object { Get-Process -Id $_ -ErrorAction SilentlyContinue }
    if ($alive.Count -eq 0) { Pass "All child processes terminated" } else { Fail "$($alive.Count) child processes still running" }
} else {
    Pass "Child process cleanup (verified via session exit)"
}

# ═════════════════════════════════════════════
# Group 3: kill-server with -L namespace isolation
# ═════════════════════════════════════════════
Write-Host ""
Write-Host "[Test Group 3] kill-server with -L namespace isolation"

& $PSMUX -L nsA new-session -d -s worker1 2>&1 | Out-Null
& $PSMUX -L nsA new-session -d -s worker2 2>&1 | Out-Null
& $PSMUX -L nsB new-session -d -s worker1 2>&1 | Out-Null
& $PSMUX new-session -d -s ks-global1 2>&1 | Out-Null
Start-Sleep -Seconds 3

Test "All 4 sessions created"
$nsA1 = & $PSMUX -L nsA has-session -t worker1 2>&1; $r1 = $LASTEXITCODE
$nsA2 = & $PSMUX -L nsA has-session -t worker2 2>&1; $r2 = $LASTEXITCODE
$nsB1 = & $PSMUX -L nsB has-session -t worker1 2>&1; $r3 = $LASTEXITCODE
$glob = & $PSMUX has-session -t ks-global1 2>&1; $r4 = $LASTEXITCODE
if ($r1 -eq 0 -and $r2 -eq 0 -and $r3 -eq 0 -and $r4 -eq 0) {
    Pass "All 4 sessions exist (2 nsA, 1 nsB, 1 global)"
} else {
    Fail "Not all sessions were created (exit codes: $r1 $r2 $r3 $r4)"
}

Test "kill-server -L nsA only kills nsA sessions"
& $PSMUX -L nsA kill-server 2>&1 | Out-Null
Start-Sleep -Seconds 3

$nsA1_alive = & $PSMUX -L nsA has-session -t worker1 2>&1; $r1 = $LASTEXITCODE
$nsA2_alive = & $PSMUX -L nsA has-session -t worker2 2>&1; $r2 = $LASTEXITCODE
$nsB1_alive = & $PSMUX -L nsB has-session -t worker1 2>&1; $r3 = $LASTEXITCODE
$glob_alive = & $PSMUX has-session -t ks-global1 2>&1; $r4 = $LASTEXITCODE

if ($r1 -ne 0 -and $r2 -ne 0) { Pass "nsA sessions killed" } else { Fail "nsA sessions still alive" }
if ($r3 -eq 0) { Pass "nsB session survived" } else { Fail "nsB session was killed (should survive)" }
if ($r4 -eq 0) { Pass "Global session survived" } else { Fail "Global session was killed (should survive)" }

Test "nsA port files cleaned up"
$nsA_ports = @(Get-ChildItem "$PsmuxDir\nsA__*.port" -ErrorAction SilentlyContinue)
if ($nsA_ports.Count -eq 0) { Pass "nsA port files removed" } else { Fail "nsA port files remain: $($nsA_ports.Name -join ', ')" }

Test "nsB and global port files still exist"
$nsB_port = Test-Path "$PsmuxDir\nsB__worker1.port"
$glob_port = Test-Path "$PsmuxDir\ks-global1.port"
if ($nsB_port -and $glob_port) { Pass "nsB and global port files intact" } else { Fail "nsB=$nsB_port global=$glob_port" }

# Cleanup remaining
& $PSMUX -L nsB kill-server 2>&1 | Out-Null
& $PSMUX kill-server 2>&1 | Out-Null
Start-Sleep -Seconds 2

# ═════════════════════════════════════════════
# Group 4: Alias consistency (pmux, tmux)
# ═════════════════════════════════════════════
Write-Host ""
Write-Host "[Test Group 4] Alias consistency (pmux, tmux)"

# Create via psmux, kill via pmux
& $PSMUX new-session -d -s ks-alias1 2>&1 | Out-Null
& $PSMUX new-session -d -s ks-alias2 2>&1 | Out-Null
Start-Sleep -Seconds 2

Test "Sessions created via psmux"
if ((SessionExists "ks-alias1") -and (SessionExists "ks-alias2")) { Pass "Both sessions exist" } else { Fail "Sessions not created" }

if ($PMUX) {
    Test "kill-server via pmux alias"
    & $PMUX kill-server 2>&1 | Out-Null
    Start-Sleep -Seconds 3
    $any = (SessionExists "ks-alias1") -or (SessionExists "ks-alias2")
    if (-not $any) { Pass "pmux kill-server killed all sessions" } else { Fail "pmux kill-server left sessions alive" }
} else {
    Write-Host "  SKIP: pmux not found in PATH"
}

# Create via psmux, kill via tmux
& $PSMUX new-session -d -s ks-alias3 2>&1 | Out-Null
Start-Sleep -Seconds 2

if ($TMUX) {
    Test "kill-server via tmux alias"
    & $TMUX kill-server 2>&1 | Out-Null
    Start-Sleep -Seconds 3
    if (-not (SessionExists "ks-alias3")) { Pass "tmux kill-server killed session" } else { Fail "tmux kill-server left session alive" }
} else {
    Write-Host "  SKIP: tmux not found in PATH"
}

# ═════════════════════════════════════════════
# Group 5: Repeated kill-server is idempotent
# ═════════════════════════════════════════════
Write-Host ""
Write-Host "[Test Group 5] kill-server idempotent (no error on empty)"

& $PSMUX kill-server 2>&1 | Out-Null
Start-Sleep -Seconds 1

Test "kill-server with no sessions doesn't error"
$output = & $PSMUX kill-server 2>&1
if ($LASTEXITCODE -eq 0) { Pass "kill-server returns 0 with no sessions" } else { Fail "kill-server errored ($LASTEXITCODE)" }

# Final cleanup
& $PSMUX kill-server 2>&1 | Out-Null
Start-Sleep -Seconds 1

Write-Host ""
Write-Host "================================================"
Write-Host "Results: $($script:Passed)/$($script:Passed + $script:Failed) passed, $($script:Failed) failed"
Write-Host "================================================"
Write-Host ""

if ($script:Failed -gt 0) { exit 1 } else { exit 0 }