From b44c9a8515ec08143f67cdf7d5798043b34e8c75 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 10 Aug 2026 19:47:45 -0700 Subject: [PATCH] refactor(qa): simplify gateway lifecycle state (#121809) Amp-Thread-ID: https://ampcode.com/threads/T-019fec02-d170-73eb-aec8-b364ab706745 Co-authored-by: Amp --- extensions/qa-lab/src/gateway-child.ts | 4 +- extensions/qa-lab/src/gateway-rpc-client.ts | 42 +++++++-------------- 2 files changed, 15 insertions(+), 31 deletions(-) diff --git a/extensions/qa-lab/src/gateway-child.ts b/extensions/qa-lab/src/gateway-child.ts index ba992350d2a0..7250dace25e5 100644 --- a/extensions/qa-lab/src/gateway-child.ts +++ b/extensions/qa-lab/src/gateway-child.ts @@ -682,7 +682,6 @@ function createQaGatewayChildLogCollector() { }; let recent = ""; let end = 0; - let dropped = false; const readFrom = (mark: number) => { const start = end - recent.length; @@ -697,7 +696,6 @@ function createQaGatewayChildLogCollector() { recent += text; if (recent.length > QA_GATEWAY_CHILD_RECENT_LOG_CHARS) { recent = sliceUtf16Safe(recent, -QA_GATEWAY_CHILD_RECENT_LOG_CHARS); - dropped = true; } }, mark() { @@ -707,7 +705,7 @@ function createQaGatewayChildLogCollector() { return readFrom(mark); }, text() { - return `${dropped ? QA_GATEWAY_CHILD_LOG_TRUNCATION_MARKER : ""}${recent}`.trim(); + return `${end > recent.length ? QA_GATEWAY_CHILD_LOG_TRUNCATION_MARKER : ""}${recent}`.trim(); }, }; } diff --git a/extensions/qa-lab/src/gateway-rpc-client.ts b/extensions/qa-lab/src/gateway-rpc-client.ts index 09048637baa7..5763d7bfc5d9 100644 --- a/extensions/qa-lab/src/gateway-rpc-client.ts +++ b/extensions/qa-lab/src/gateway-rpc-client.ts @@ -18,10 +18,10 @@ type QaGatewayRpcClient = { }; type QaGatewayConnectionGate = { + connected: boolean; promise: Promise; resolve: () => void; reject: (error: Error) => void; - state: "pending" | "connected" | "failed"; }; const QA_GATEWAY_RPC_TIMEOUT_MS = 20_000; @@ -29,30 +29,13 @@ const QA_GATEWAY_RPC_TIMEOUT_MS = 20_000; function createQaGatewayConnectionGate(): QaGatewayConnectionGate { let resolvePromise!: () => void; let rejectPromise!: (error: Error) => void; - const gate: QaGatewayConnectionGate = { - promise: new Promise((resolve, reject) => { - resolvePromise = resolve; - rejectPromise = reject; - }), - resolve: () => { - if (gate.state !== "pending") { - return; - } - gate.state = "connected"; - resolvePromise(); - }, - reject: (error) => { - if (gate.state !== "pending") { - return; - } - gate.state = "failed"; - rejectPromise(error); - }, - state: "pending", - }; + const promise = new Promise((resolve, reject) => { + resolvePromise = resolve; + rejectPromise = reject; + }); // A terminal reconnect error can arrive without an active request waiter. - void gate.promise.catch(() => {}); - return gate; + void promise.catch(() => {}); + return { connected: false, promise, reject: rejectPromise, resolve: resolvePromise }; } function formatQaGatewayRpcError(error: unknown, logs: () => string) { @@ -110,9 +93,12 @@ export async function startQaGatewayRpcClient(params: { deviceIdentity: null, mode: "backend", scopes: ["operator.admin"], - onHelloOk: () => connection.resolve(), + onHelloOk: () => { + connection.connected = true; + connection.resolve(); + }, onClose: () => { - if (!stopped && connection.state === "connected") { + if (!stopped && connection.connected) { connection = createQaGatewayConnectionGate(); } }, @@ -120,7 +106,7 @@ export async function startQaGatewayRpcClient(params: { const error = new Error( `gateway reconnect paused (${info.code}): ${info.reason}${info.detailCode ? ` [${info.detailCode}]` : ""}`, ); - if (connection.state === "connected") { + if (connection.connected) { connection = createQaGatewayConnectionGate(); } connection.reject(error); @@ -175,7 +161,7 @@ export async function startQaGatewayRpcClient(params: { assertNotStopped(); // A close can race between gate resolution and request dispatch. No frame was sent, // so waiting for the next hello and retrying is safe even for non-idempotent methods. - if (connection === requestConnection && connection.state === "connected") { + if (connection === requestConnection && connection.connected) { connection = createQaGatewayConnectionGate(); } }