[CmdletBinding()] param([string]$BaseUrl = 'http://127.0.0.1:15888', [switch]$ExerciseAccount) $ErrorActionPreference = 'Stop' $BaseUrl = $BaseUrl.TrimEnd('/') $health = Invoke-RestMethod -Uri "$BaseUrl/health" -TimeoutSec 10 if (-not $health.ok -or $health.service -ne 'aiserver' -or $health.gameProtocol -ne 1) { throw 'Wrong backend or unsupported gameplay protocol.' } try { Invoke-WebRequest -Uri "$BaseUrl/game/state" -UseBasicParsing -TimeoutSec 10 | Out-Null throw 'Unauthenticated gameplay endpoint unexpectedly accepted the request.' } catch { if (-not $_.Exception.Response -or [int]$_.Exception.Response.StatusCode -ne 401) { throw } } Write-Output 'PASS health and gameplay route (unauthenticated state returns 401, not 404).' if ($ExerciseAccount) { # Explicit opt-in: this creates one fresh deployment-test account. $username = 'deploy_probe_' + [Guid]::NewGuid().ToString('N') $body = @{ username=$username; password=[Guid]::NewGuid().ToString('N') } | ConvertTo-Json -Compress $session = Invoke-RestMethod -Uri "$BaseUrl/register" -Method Post -ContentType 'application/json' -Body $body -TimeoutSec 10 if (-not $session.token -or -not $session.pid) { throw 'Registration returned no session.' } $login = Invoke-RestMethod -Uri "$BaseUrl/login" -Method Post -ContentType 'application/json' -Body $body -TimeoutSec 10 if (-not $login.token -or $login.pid -ne $session.pid) { throw 'Login did not return the registered identity.' } $session = $login $state = Invoke-RestMethod -Uri "$BaseUrl/game/state" -Headers @{Authorization="Bearer $($session.token)"} -TimeoutSec 15 if (-not $state.state -or $state.state.schema -lt 14 -or -not $state.configVersion) { throw 'Incomplete gameplay snapshot.' } $wsBase = $BaseUrl -replace '^https:', 'wss:' -replace '^http:', 'ws:' $uri = [Uri]("$wsBase/ws?pid=$($session.pid)&token=$([Uri]::EscapeDataString($session.token))") $socket = New-Object System.Net.WebSockets.ClientWebSocket $timeout = New-Object System.Threading.CancellationTokenSource $timeout.CancelAfter(10000) try { [void]$socket.ConnectAsync($uri,$timeout.Token).GetAwaiter().GetResult() if ($socket.State -ne [Net.WebSockets.WebSocketState]::Open) { throw 'WebSocket did not open.' } [void]$socket.CloseAsync([Net.WebSockets.WebSocketCloseStatus]::NormalClosure,'probe complete',$timeout.Token).GetAwaiter().GetResult() } finally { $socket.Dispose(); $timeout.Dispose() } Write-Output "PASS registration, login, authenticated state (schema $($state.state.schema)) and WebSocket. Test pid=$($session.pid)." }