9 lines
4.1 KiB
PowerShell
9 lines
4.1 KiB
PowerShell
$ErrorActionPreference='Stop';$PSNativeCommandUseErrorActionPreference=$true
|
||
$projectRoot=Split-Path -Parent $PSScriptRoot;Set-Location -LiteralPath $projectRoot
|
||
$api='http://localhost:58080';$tokenUrl='http://localhost:58180/realms/voice-local/protocol/openid-connect/token'
|
||
$deviceId='10000000-0000-0000-0000-000000000511';$secret=[Text.Encoding]::UTF8.GetBytes('local-simulated-device-secret-32-bytes-minimum');$commandId=$null
|
||
function Token([string]$user){return(Invoke-RestMethod -Method Post -Uri $tokenUrl -ContentType 'application/x-www-form-urlencoded' -Body @{grant_type='password';client_id='voice-platform-web';username=$user;password='LocalOnly123!';scope='openid'}).access_token}
|
||
function Signed([string]$method,[string]$path,[string]$body=''){$bytes=[Text.Encoding]::UTF8.GetBytes($body);$digest=[Convert]::ToHexString([Security.Cryptography.SHA256]::HashData($bytes)).ToLowerInvariant();$timestamp=[DateTime]::UtcNow.ToString('yyyy-MM-ddTHH:mm:ssZ');$nonce=[Guid]::NewGuid().ToString('N');$canonical="$method`n$path`n$timestamp`n$nonce`n$digest";$h=[Security.Cryptography.HMACSHA256]::new($secret);try{$signature=[Convert]::ToBase64String($h.ComputeHash([Text.Encoding]::UTF8.GetBytes($canonical))).TrimEnd('=').Replace('+','-').Replace('/','_')}finally{$h.Dispose()};$headers=@{'X-Device-Id'=$deviceId;'X-Device-Timestamp'=$timestamp;'X-Device-Nonce'=$nonce;'X-Device-Content-SHA256'=$digest;'X-Device-Signature'=$signature};$args=@{UseBasicParsing=$true;Method=$method;Uri=($api+$path);Headers=$headers};if($body){$args.ContentType='application/json';$args.Body=$body};return Invoke-WebRequest @args}
|
||
function Expected([scriptblock]$action,[int]$status){try{$r=&$action;if($r.StatusCode-ne$status){throw "预期$status,实际$($r.StatusCode)"}}catch{if($_.Exception.Response.StatusCode.value__-ne$status){throw}}}
|
||
try{$owner=Token 'owner';$outsider=Token 'outsider';$key='device-command-'+[Guid]::NewGuid().ToString('N');$body=@{type='diagnostics';payload=@{checks=@('audio','network')};expires_in_sec=300}|ConvertTo-Json -Compress -Depth 5;$headers=@{Authorization="Bearer $owner";'Idempotency-Key'=$key};$created=Invoke-WebRequest -UseBasicParsing -Method Post -Uri "$api/api/v1/devices/$deviceId/commands" -Headers $headers -ContentType 'application/json' -Body $body;$commandId=[string](($created.Content|ConvertFrom-Json).data.id);if($created.StatusCode-ne 202){throw '命令未排队'};$replay=Invoke-WebRequest -UseBasicParsing -Method Post -Uri "$api/api/v1/devices/$deviceId/commands" -Headers $headers -ContentType 'application/json' -Body $body;if($replay.StatusCode-ne 200-or[string]$replay.Headers.'Idempotency-Replayed'-ne'true'){throw '命令幂等重放失败'};Expected {Invoke-WebRequest -UseBasicParsing -Uri "$api/api/v1/devices/$deviceId/commands" -Headers @{Authorization="Bearer $outsider"}} 403;$pulled=(Signed 'GET' '/device/v2/commands?limit=10').Content|ConvertFrom-Json;$item=$pulled.data.items|Where-Object id -eq $commandId;if(!$item-or$item.status-ne'DELIVERED'){throw '设备未拉取到待执行命令'};$ackBody=@{status='ACKNOWLEDGED';result_summary=@{accepted=$true}}|ConvertTo-Json -Compress;Signed 'POST' "/device/v2/commands/$commandId/ack" $ackBody|Out-Null;$doneBody=@{status='SUCCEEDED';result_summary=@{audio='ok';network='ok'}}|ConvertTo-Json -Compress;Signed 'POST' "/device/v2/commands/$commandId/ack" $doneBody|Out-Null;$list=(Invoke-WebRequest -UseBasicParsing -Uri "$api/api/v1/devices/$deviceId/commands" -Headers @{Authorization="Bearer $owner"}).Content|ConvertFrom-Json;$final=$list.data.items|Where-Object id -eq $commandId;if($final.status-ne'SUCCEEDED'-or$final.delivery_attempts-lt 1){throw '命令终态或投递次数不正确'};Write-Host "设备命令端到端冒烟通过:$commandId(QUEUED → DELIVERED → ACKNOWLEDGED → SUCCEEDED)"}finally{if($commandId-match'^[0-9a-fA-F-]{36}$'){docker compose exec -T postgres psql -U voice -d voice_platform -q -c "DELETE FROM audit_event WHERE resource_id='$commandId'::uuid; DELETE FROM web_event WHERE resource_public_id='$commandId'::uuid; DELETE FROM device_command WHERE public_id='$commandId'::uuid;"|Out-Null;Write-Host "已清理设备命令冒烟数据:$commandId"}}
|