[CmdletBinding()] param( [string]$ConfigPath = '', [string]$ModeId = 'baby_nap', [int]$TimeoutSeconds = 30 ) $ErrorActionPreference = 'Stop' $projectRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path if (-not $ConfigPath) { $ConfigPath = Join-Path $projectRoot 'config\migration.psd1' } if (-not [System.IO.Path]::IsPathRooted($ConfigPath)) { $ConfigPath = Join-Path $projectRoot $ConfigPath } $config = Import-PowerShellDataFile -LiteralPath $ConfigPath if (([string]$config.ExecutionTarget).Trim().ToLowerInvariant() -ne 'dry_run') { throw 'Safety interlock: this smoke test requires ExecutionTarget=dry_run.' } $tokenPath = [string]$config.TokenFile if (-not [System.IO.Path]::IsPathRooted($tokenPath)) { $tokenPath = Join-Path $projectRoot $tokenPath } $token = (Get-Content -LiteralPath $tokenPath -Raw).Trim() $baseUrl = ([string]$config.ServerAUrl).TrimEnd('/') $vehicleId = ([string]$config.VehicleId).Trim() $headers = @{ Authorization = "Bearer $token" 'Content-Type' = 'application/json; charset=utf-8' 'Idempotency-Key' = "server-b-smoke-$([guid]::NewGuid().ToString('N'))" } $payload = @{ mode_id = $ModeId execution_target = 'dry_run' source = 'android' source_device_id = 'n7-server-b-smoke' } | ConvertTo-Json $accepted = Invoke-RestMethod ` -Uri "$baseUrl/api/v1/vehicles/$vehicleId/mode-executions" ` -Method Post -Headers $headers -Body $payload -TimeoutSec 10 $deadline = (Get-Date).AddSeconds($TimeoutSeconds) $terminal = $accepted do { if ($terminal.status -in @( 'succeeded', 'partially_succeeded', 'failed', 'cancelled', 'timed_out' )) { break } Start-Sleep -Milliseconds 500 $terminal = Invoke-RestMethod ` -Uri "$baseUrl/api/v1/mode-executions/$($accepted.execution_id)" ` -Headers @{ Authorization = "Bearer $token" } -TimeoutSec 10 } while ((Get-Date) -lt $deadline) $token = $null if ($terminal.execution_target -ne 'dry_run') { throw "Safety failure: server used $($terminal.execution_target)." } if ($terminal.status -ne 'succeeded') { throw "Dry-run smoke test failed: $($terminal.status) $($terminal.message)" } [pscustomobject]@{ Passed = $true CanExecuted = $false ExecutionTarget = $terminal.execution_target ExecutionId = $terminal.execution_id ModeId = $terminal.mode_id Status = $terminal.status }