import { readFileSync } from "node:fs"; function assertCondition(condition: unknown, message: string, detail: unknown = {}): void { if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`); } function compareSemver(left: unknown, right: string): number { if (typeof left !== "string") return -1; const a = left.split(".").map((part) => Number.parseInt(part, 10)); const b = right.split(".").map((part) => Number.parseInt(part, 10)); for (let i = 0; i < Math.max(a.length, b.length); i += 1) { const delta = (Number.isFinite(a[i]) ? a[i] : 0) - (Number.isFinite(b[i]) ? b[i] : 0); if (delta !== 0) return delta; } return 0; } const rustBridge = readFileSync("src/components/backend-core/src/ssh_bridge.rs", "utf8"); const rustData = readFileSync("src/components/backend-core/src/ssh_data_channel.rs", "utf8"); const rustMain = readFileSync("src/components/backend-core/src/main.rs", "utf8"); const rustState = readFileSync("src/components/backend-core/src/state.rs", "utf8"); const rustProviderRegistry = readFileSync("src/components/backend-core/src/provider_registry.rs", "utf8"); const provider = readFileSync("src/components/provider-gateway/src/index.ts", "utf8"); const providerRegistryTs = readFileSync("src/components/backend-core/src/provider-registry.ts", "utf8"); const providerPackage = JSON.parse(readFileSync("src/components/provider-gateway/package.json", "utf8")) as { version?: unknown }; const shared = readFileSync("src/components/shared/src/index.ts", "utf8"); const compose = readFileSync("docker-compose.yml", "utf8"); const config = readFileSync("config.json", "utf8"); const backendCoreDockerfile = readFileSync("src/components/backend-core/Dockerfile", "utf8"); assertCondition(rustBridge.includes('"host.ssh.tcp-pool"'), "backend-core ssh bridge must require host.ssh.tcp-pool"); assertCondition(rustBridge.includes("provider-gateway-upgrade-required"), "old provider must fail with upgrade-required classification"); assertCondition(rustBridge.includes("provider-data-pool-exhausted"), "tcp pool exhaustion must be visible"); assertCondition(rustBridge.includes("remove_ssh_data_channel") && rustBridge.includes("ssh_data_channel_removed_after_control_error"), "control-fallback host_ssh_error must remove the claimed data channel"); assertCondition(rustBridge.includes("provider-data-channel-missing"), "missing data channel must be classified for clients"); assertCondition(!rustBridge.includes('"host_ssh_input"'), "ssh input must not fall back to provider control websocket"); assertCondition(!rustBridge.includes('"host_ssh_eof"'), "ssh eof must not fall back to provider control websocket"); assertCondition(!rustBridge.includes('"host_ssh_close"'), "ssh close must not fall back to provider control websocket"); assertCondition(rustBridge.includes('json!({ "type": "input"') && rustBridge.includes('json!({ "type": "eof"'), "ssh bridge must send stdin/eof over data frames"); assertCondition(!rustProviderRegistry.includes('"host_ssh_opened"') && !rustProviderRegistry.includes('"host_ssh_data"') && !rustProviderRegistry.includes('"host_ssh_exit"'), "rust provider registry must not accept old websocket ssh data messages"); assertCondition(rustData.includes("TcpListener") && rustData.includes("read_data_frame") && rustData.includes("write_data_frame"), "backend-core must expose raw TCP data frame listener"); assertCondition(rustData.includes("pub async fn remove_ssh_data_channel") && rustData.includes("channels.remove(&key)") && rustData.includes(".max_by_key(|channel| channel.connected_at_millis)"), "backend-core must purge stale/dead data channels and prefer the freshest idle channel"); assertCondition(rustData.includes("SSH_DATA_PROTOCOL") && rustData.includes("unidesk-host-ssh-tcp-pool-v1"), "tcp data protocol must be explicit"); assertCondition(rustData.includes("base64::engine::general_purpose::STANDARD.encode(payload)"), "only client-facing websocket payload should remain base64 encoded"); assertCondition(rustMain.includes("providerDataTcpUrl") && rustMain.includes("spawn_ssh_data_listener"), "backend-core startup must expose provider data listener visibility"); assertCondition(rustState.includes("active_ssh_data_channels") && rustState.includes("data_channel_id"), "backend state must track data channels separately from provider control socket"); assertCondition(provider.includes("createConnection") && provider.includes("sshDataChannels"), "provider-gateway must use direct TCP sockets for ssh data pool"); assertCondition(provider.includes("PROVIDER_DATA_POOL_SIZE") && provider.includes("providerGatewaySshDataPoolReady"), "provider-gateway must expose pool config and heartbeat labels"); assertCondition(provider.includes('capabilities.push("host.ssh", "host.ssh.tcp-pool")'), "provider-gateway must declare tcp-pool capability only with host ssh"); assertCondition(provider.includes("acquireSshDataChannel") && provider.includes("releaseSshDataChannel"), "provider-gateway must claim/release one data channel per ssh session"); assertCondition(provider.includes("writeSshDataFrame(dataChannel") && provider.includes("sendSshDataSessionFrame"), "provider-gateway ssh output must use tcp data frames"); assertCondition(!provider.includes('parsed.type === "host_ssh_input"') && !provider.includes('parsed.type === "host_ssh_close"'), "provider-gateway must not retain old websocket ssh input handlers"); assertCondition(compareSemver(providerPackage.version, "0.2.29") >= 0, "provider-gateway tcp-pool contract must not regress below the deployed tcp-pool version", providerPackage); assertCondition(shared.includes('transport: "tcp-pool"') && shared.includes("dataChannelId: string"), "shared host_ssh_open contract must require tcp-pool fields"); assertCondition(!shared.includes("CoreHostSshInputMessage") && !shared.includes("ProviderHostSshDataMessage"), "shared protocol must remove old websocket ssh data contracts"); assertCondition(!providerRegistryTs.includes("host_ssh_data") && !providerRegistryTs.includes("host_ssh_exit"), "typescript backend registry must not forward old websocket ssh data messages"); assertCondition(compose.includes("UNIDESK_PROVIDER_DATA_PORT") && compose.includes("PROVIDER_DATA_POOL_SIZE"), "compose must wire provider data port and pool size"); assertCondition(config.includes('"providerData"') && config.includes('"port": 18084') && config.includes('"containerPort": 8082'), "config.json must declare providerData port pair"); assertCondition( backendCoreDockerfile.includes("ARG CARGO_BUILD_JOBS=1") && backendCoreDockerfile.includes('ENV CARGO_BUILD_JOBS=${CARGO_BUILD_JOBS}') && backendCoreDockerfile.includes('cargo build --release --locked --jobs "${CARGO_BUILD_JOBS}"'), "backend-core main-server online build must keep cargo concurrency constrained", ); assertCondition(compose.includes("UNIDESK_BACKEND_CORE_CARGO_BUILD_JOBS") && compose.includes("CARGO_BUILD_JOBS"), "compose must pass backend-core cargo build concurrency explicitly"); console.log(JSON.stringify({ ok: true, test: "ssh-data-tcp-pool-contract" }));