param( [string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1", [string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa", [string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700", [string]$NodeName = "test-1", [string]$ResultPath = "artifacts\c19b-remote-workspace-adapter-contract-smoke-result.json" ) Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" $repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath $runId = "c19b-" + (Get-Date -Format "yyyyMMdd-HHmmss") function Invoke-Api { param( [string]$Method, [string]$Path, [object]$Body = $null ) $uri = "$ApiBaseUrl$Path" try { if ($null -eq $Body) { return Invoke-RestMethod -Method $Method -Uri $uri -TimeoutSec 30 } return Invoke-RestMethod -Method $Method -Uri $uri -ContentType "application/json" -Body ($Body | ConvertTo-Json -Depth 80) -TimeoutSec 30 } catch { $statusCode = $null if ($_.Exception.Response) { $statusCode = [int]$_.Exception.Response.StatusCode } $details = $_.ErrorDetails.Message if (-not $details) { $details = $_.Exception.Message } throw "$Method $Path failed with HTTP $statusCode`: $details" } } function Get-NodeByName { param([string]$Name) $nodes = (Invoke-Api -Method GET -Path "/clusters/$ClusterID/nodes?actor_user_id=$ActorUserID").nodes $node = @($nodes | Where-Object { $_.name -eq $Name }) | Select-Object -First 1 if ($null -eq $node) { throw "Node '$Name' was not found in cluster $ClusterID" } return $node } function Wait-ForWorkloadStatus { param( [string]$NodeID, [string]$ServiceType, [string]$ExpectedState, [int]$TimeoutSeconds = 90 ) $deadline = (Get-Date).AddSeconds($TimeoutSeconds) $last = $null while ((Get-Date) -lt $deadline) { Start-Sleep -Seconds 5 $statuses = (Invoke-Api -Method GET -Path "/clusters/$ClusterID/nodes/$NodeID/workloads/status?actor_user_id=$ActorUserID").workload_statuses $last = @($statuses | Where-Object { $_.service_type -eq $ServiceType } | Select-Object -First 1) if ($null -ne $last -and [string]$last.reported_state -eq $ExpectedState) { return $last } } throw "Timed out waiting for $ServiceType=$ExpectedState. Last observed: $($last | ConvertTo-Json -Depth 20)" } function Get-Channel { param( [object[]]$Channels, [string]$Name ) return @($Channels | Where-Object { $_.name -eq $Name } | Select-Object -First 1) } $node = Get-NodeByName -Name $NodeName Invoke-Api -Method PUT -Path "/clusters/$ClusterID/nodes/$($node.id)/workloads/rdp-worker/desired" -Body @{ actor_user_id = $ActorUserID desired_state = "enabled" version = "c19b-remote-workspace-adapter-$runId" runtime_mode = "native" artifact_ref = "builtin:rdp-worker-adapter-contract-probe" config = @{ adapter_contract_probe = $true service_class = "remote_workspace" run_id = $runId } environment = @{} } | Out-Null $status = Wait-ForWorkloadStatus -NodeID $node.id -ServiceType "rdp-worker" -ExpectedState "running" $payload = $status.status_payload $channels = @($payload.channels) $inputChannel = Get-Channel -Channels $channels -Name "input" $displayChannel = Get-Channel -Channels $channels -Name "display" $cursorChannel = Get-Channel -Channels $channels -Name "cursor" $fileChannel = Get-Channel -Channels $channels -Name "file_transfer" $checks = [ordered]@{ rdp_worker_probe_running = [bool]([string]$status.reported_state -eq "running") service_class_remote_workspace = [bool]([string]$payload.service_class -eq "remote_workspace") fabric_service_channel_required = [bool]($payload.fabric_service_channel_required -eq $true) backend_not_steady_state = [bool]($payload.backend_relay_steady_state -eq $false) no_payload_traffic = [bool]([string]$payload.traffic -eq "none") channel_count = [bool]($channels.Count -eq 9) input_is_critical = [bool]($null -ne $inputChannel -and [string]$inputChannel.priority -eq "critical" -and $inputChannel.droppable -eq $true -and $inputChannel.may_block_input -eq $false) display_is_droppable = [bool]($null -ne $displayChannel -and $displayChannel.droppable -eq $true) cursor_is_droppable = [bool]($null -ne $cursorChannel -and $cursorChannel.droppable -eq $true) file_transfer_is_reliable_chunked = [bool]($null -ne $fileChannel -and [string]$fileChannel.reliability -eq "reliable_chunked") } $result = [ordered]@{ schema_version = "c19b.remote_workspace_adapter_contract_smoke.v1" run_id = $runId base_url = $ApiBaseUrl cluster_id = $ClusterID node = [ordered]@{ id = $node.id name = $node.name } status = $status checks = $checks passed = -not ($checks.Values -contains $false) } $fullResultPath = Join-Path $repoRoot $ResultPath $resultDir = Split-Path -Parent $fullResultPath if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null } $result | ConvertTo-Json -Depth 40 | Set-Content -Encoding UTF8 -Path $fullResultPath if (-not $result.passed) { throw "C19B remote workspace adapter contract smoke failed. Result: $fullResultPath" } Write-Host "C19B remote workspace adapter contract smoke passed. Result: $fullResultPath" $result