# Test zen service command lifecycle on Windows (SCM). # # Requires: Administrator privileges # # Usage: # # From an elevated PowerShell prompt: # .\scripts\test_windows\service-test.ps1 [-ZenBinary ] # # If -ZenBinary is not given, defaults to build\windows\x64\debug\zen.exe # relative to the repository root. param( [string]$ZenBinary ) $ErrorActionPreference = "Stop" $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $RepoRoot = (Resolve-Path "$ScriptDir\..\..").Path if (-not $ZenBinary) { $ZenBinary = Join-Path $RepoRoot "build\windows\x64\debug\zen.exe" } $ZenServerBinary = Join-Path (Split-Path -Parent $ZenBinary) "zenserver.exe" $ServiceName = "ZenServerTest-$PID" $Script:Passed = 0 $Script:Failed = 0 $Script:TestsRun = 0 function Pass($Message) { $Script:TestsRun++ $Script:Passed++ Write-Host " PASS: $Message" -ForegroundColor Green } function Fail($Message, $Detail) { $Script:TestsRun++ $Script:Failed++ Write-Host " FAIL: $Message" -ForegroundColor Red if ($Detail) { Write-Host " $Detail" } } function Cleanup { Write-Host "" Write-Host "--- Cleanup ---" # Stop the service if running $svc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue if ($svc -and $svc.Status -eq "Running") { Write-Host "Stopping test service..." Stop-Service -Name $ServiceName -Force -ErrorAction SilentlyContinue Start-Sleep -Seconds 2 } # Delete the service if it exists if (Get-Service -Name $ServiceName -ErrorAction SilentlyContinue) { Write-Host "Removing test service..." sc.exe delete $ServiceName 2>$null | Out-Null } Write-Host "" Write-Host "==============================" Write-Host " Tests run: $Script:TestsRun" Write-Host " Passed: $Script:Passed" -ForegroundColor Green if ($Script:Failed -gt 0) { Write-Host " Failed: $Script:Failed" -ForegroundColor Red } else { Write-Host " Failed: $Script:Failed" } Write-Host "==============================" } # ── Preflight checks ────────────────────────────────────────────── $IsAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $IsAdmin) { Write-Host "Error: this test must be run from an elevated (Administrator) prompt." -ForegroundColor Red exit 1 } if (-not (Test-Path $ZenBinary)) { Write-Host "Error: zen binary not found at '$ZenBinary'" -ForegroundColor Red Write-Host "Build with: xmake config -m debug && xmake build zen" exit 1 } if (-not (Test-Path $ZenServerBinary)) { Write-Host "Error: zenserver binary not found at '$ZenServerBinary'" -ForegroundColor Red Write-Host "Build with: xmake config -m debug && xmake build zenserver" exit 1 } Write-Host "zen binary: $ZenBinary" Write-Host "zenserver binary: $ZenServerBinary" Write-Host "service name: $ServiceName" Write-Host "" try { # ── Test: status before install (should fail) ───────────────────── Write-Host "--- Test: status before install ---" $Output = & $ZenBinary service status $ServiceName 2>&1 | Out-String if ($Output -match "not installed") { Pass "status reports 'not installed' for non-existent service" } else { Fail "status should report 'not installed'" "got: $Output" } # ── Test: install ───────────────────────────────────────────────── Write-Host "--- Test: install ---" $Output = & $ZenBinary service install $ZenServerBinary $ServiceName --allow-elevation 2>&1 | Out-String $ExitCode = $LASTEXITCODE if ($ExitCode -eq 0) { Pass "install exits with code 0" } else { Fail "install exits with code 0" "got exit code: $ExitCode, output: $Output" } $svc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue if ($svc) { Pass "service registered in SCM" } else { Fail "service registered in SCM" } # Verify service configuration if ($svc) { $svcWmi = Get-CimInstance -ClassName Win32_Service -Filter "Name='$ServiceName'" -ErrorAction SilentlyContinue if ($svcWmi -and $svcWmi.PathName -match "zenserver") { Pass "service binary path contains zenserver" } else { Fail "service binary path contains zenserver" "got: $($svcWmi.PathName)" } if ($svcWmi -and $svcWmi.StartMode -eq "Auto") { Pass "service is set to auto-start" } else { Fail "service is set to auto-start" "got: $($svcWmi.StartMode)" } } # ── Test: install again (already installed, no --full) ──────────── Write-Host "--- Test: install again (idempotent) ---" $Output = & $ZenBinary service install $ZenServerBinary $ServiceName --allow-elevation 2>&1 | Out-String $ExitCode = $LASTEXITCODE if ($ExitCode -eq 0) { Pass "re-install exits with code 0" } else { Fail "re-install exits with code 0" "got exit code: $ExitCode" } if ($Output -match "already installed") { Pass "re-install reports service already installed" } else { Fail "re-install reports service already installed" "got: $Output" } # ── Test: status after install (not yet started) ────────────────── Write-Host "--- Test: status after install (stopped) ---" $Output = & $ZenBinary service status $ServiceName 2>&1 | Out-String if ($Output -match "not running") { Pass "status reports 'not running' for stopped service" } else { Fail "status reports 'not running' for stopped service" "got: $Output" } # ── Test: start ─────────────────────────────────────────────────── Write-Host "--- Test: start ---" $Output = & $ZenBinary service start $ServiceName --allow-elevation 2>&1 | Out-String $ExitCode = $LASTEXITCODE if ($ExitCode -eq 0) { Pass "start exits with code 0" } else { Fail "start exits with code 0" "got exit code: $ExitCode, output: $Output" } Start-Sleep -Seconds 2 $svc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue if ($svc -and $svc.Status -eq "Running") { Pass "service is running after start" # ── Test: status while running ──────────────────────────────── Write-Host "--- Test: status (running) ---" $Output = & $ZenBinary service status $ServiceName 2>&1 | Out-String if ($Output -match "Running") { Pass "status reports 'Running'" } else { Fail "status reports 'Running'" "got: $Output" } if ($Output -match "zenserver") { Pass "status shows executable path" } else { Fail "status shows executable path" "got: $Output" } # ── Test: start again (already running) ─────────────────────── Write-Host "--- Test: start again (already running) ---" $Output = & $ZenBinary service start $ServiceName --allow-elevation 2>&1 | Out-String if ($Output -match "already running") { Pass "start reports service already running" } else { Fail "start reports service already running" "got: $Output" } # ── Test: stop ──────────────────────────────────────────────── Write-Host "--- Test: stop ---" $Output = & $ZenBinary service stop $ServiceName --allow-elevation 2>&1 | Out-String $ExitCode = $LASTEXITCODE if ($ExitCode -eq 0) { Pass "stop exits with code 0" } else { Fail "stop exits with code 0" "got exit code: $ExitCode, output: $Output" } Start-Sleep -Seconds 2 $svc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue if ($svc -and $svc.Status -ne "Running") { Pass "service is not running after stop" } else { Fail "service is not running after stop" } } else { Fail "service is running after start" "(skipping start-dependent tests)" } # ── Test: stop when already stopped ─────────────────────────────── Write-Host "--- Test: stop when already stopped ---" Stop-Service -Name $ServiceName -Force -ErrorAction SilentlyContinue Start-Sleep -Seconds 1 $Output = & $ZenBinary service stop $ServiceName --allow-elevation 2>&1 | Out-String if ($Output -match "not running") { Pass "stop reports 'not running' when already stopped" } else { Fail "stop reports 'not running' when already stopped" "got: $Output" } # ── Test: uninstall while running (should fail) ─────────────────── Write-Host "--- Test: uninstall while running (should fail) ---" & $ZenBinary service start $ServiceName --allow-elevation 2>&1 | Out-Null Start-Sleep -Seconds 2 $svc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue if ($svc -and $svc.Status -eq "Running") { $Output = & $ZenBinary service uninstall $ServiceName --allow-elevation 2>&1 | Out-String $ExitCode = $LASTEXITCODE if ($ExitCode -ne 0 -or $Output -match "running.*stop") { Pass "uninstall refuses while service is running" } else { Fail "uninstall refuses while service is running" "got: exit=$ExitCode, output: $Output" } # Stop it for the real uninstall test & $ZenBinary service stop $ServiceName --allow-elevation 2>&1 | Out-Null Stop-Service -Name $ServiceName -Force -ErrorAction SilentlyContinue Start-Sleep -Seconds 2 } else { Write-Host " (skipped: could not start service for this test)" } # ── Test: uninstall ─────────────────────────────────────────────── Write-Host "--- Test: uninstall ---" Stop-Service -Name $ServiceName -Force -ErrorAction SilentlyContinue Start-Sleep -Seconds 1 $Output = & $ZenBinary service uninstall $ServiceName --allow-elevation 2>&1 | Out-String $ExitCode = $LASTEXITCODE if ($ExitCode -eq 0) { Pass "uninstall exits with code 0" } else { Fail "uninstall exits with code 0" "got exit code: $ExitCode, output: $Output" } $svc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue if (-not $svc) { Pass "service removed from SCM after uninstall" } else { Fail "service removed from SCM after uninstall" } # ── Test: status after uninstall ────────────────────────────────── Write-Host "--- Test: status after uninstall ---" $Output = & $ZenBinary service status $ServiceName 2>&1 | Out-String if ($Output -match "not installed") { Pass "status reports 'not installed' after uninstall" } else { Fail "status reports 'not installed' after uninstall" "got: $Output" } # ── Test: uninstall when not installed (idempotent) ─────────────── Write-Host "--- Test: uninstall when not installed ---" $Output = & $ZenBinary service uninstall $ServiceName --allow-elevation 2>&1 | Out-String $ExitCode = $LASTEXITCODE if ($ExitCode -eq 0) { Pass "uninstall of non-existent service exits with code 0" } else { Fail "uninstall of non-existent service exits with code 0" "got exit code: $ExitCode" } if ($Output -match "not installed") { Pass "uninstall reports service not installed" } else { Fail "uninstall reports service not installed" "got: $Output" } } finally { Cleanup } if ($Script:Failed -gt 0) { exit 1 }