diff options
Diffstat (limited to 'scripts/test_windows/service-test.ps1')
| -rw-r--r-- | scripts/test_windows/service-test.ps1 | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/scripts/test_windows/service-test.ps1 b/scripts/test_windows/service-test.ps1 new file mode 100644 index 000000000..a2723681e --- /dev/null +++ b/scripts/test_windows/service-test.ps1 @@ -0,0 +1,355 @@ +# Test zen service command lifecycle on Windows (SCM). +# +# Requires: Administrator privileges +# +# Usage: +# # From an elevated PowerShell prompt: +# .\scripts\test_windows\service-test.ps1 [-ZenBinary <path>] +# +# 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" + +function Zen { & $ZenBinary --enable-execution-history=false @args } +$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 = Zen 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 = Zen 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 = Zen 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 = Zen 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 = Zen 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 = Zen 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 = Zen 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 = Zen 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 = Zen 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) ---" + +Zen 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 = Zen 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 + Zen 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 = Zen 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 = Zen 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 = Zen 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 +} |