From 4cb5411237fdb03e98c678a8048e27620207b8bb Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Tue, 22 Sep 2026 01:23:49 -0700 Subject: [PATCH] test(ci): backport concurrent MCP expiry fixture fix (#155577) --- .../runtime/gateway-node-mcp.fixture.mjs | 25 ++++++++++++++----- .../gateway-node-mcp.stress.e2e.test.ts | 5 ++-- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/test/e2e/qa-lab/runtime/gateway-node-mcp.fixture.mjs b/test/e2e/qa-lab/runtime/gateway-node-mcp.fixture.mjs index a702f9137169..178312267a0c 100644 --- a/test/e2e/qa-lab/runtime/gateway-node-mcp.fixture.mjs +++ b/test/e2e/qa-lab/runtime/gateway-node-mcp.fixture.mjs @@ -305,6 +305,7 @@ async function runHttp() { } const app = createMcpExpressApp(); const sessions = new Map(); + const pendingSessionExpirations = new Map(); const records = new Set(); const catalogState = { rotated: false, expiryCalls: 0 }; const appFixtureEnabled = process.env.MCP_APP_GRANT_REVALIDATION_FIXTURE === "1"; @@ -353,14 +354,26 @@ async function runHttp() { const sessionId = req.headers["mcp-session-id"]; let transport; if (typeof sessionId === "string") { - if (req.body?.params?.arguments?.marker === "expire-session") { + const marker = req.body?.params?.arguments?.marker; + if (marker === "expire-session" || marker === "expire-concurrent-session") { catalogState.expiryCalls += 1; + const pending = pendingSessionExpirations.get(sessionId) ?? []; + pending.push({ response: res, id: req.body?.id ?? null }); + if (marker === "expire-concurrent-session" && pending.length < 2) { + // Expire only after both calls reach this session. The first 404 + // otherwise retires the transport before the second request arrives. + pendingSessionExpirations.set(sessionId, pending); + return; + } + pendingSessionExpirations.delete(sessionId); sessions.delete(sessionId); - res.status(404).json({ - jsonrpc: "2.0", - error: { code: -32001, message: "Session not found" }, - id: req.body?.id ?? null, - }); + for (const { response, id } of pending) { + response.status(404).json({ + jsonrpc: "2.0", + error: { code: -32001, message: "Session not found" }, + id, + }); + } return; } const record = sessions.get(sessionId); diff --git a/test/e2e/qa-lab/runtime/gateway-node-mcp.stress.e2e.test.ts b/test/e2e/qa-lab/runtime/gateway-node-mcp.stress.e2e.test.ts index 84e5a29e6850..3fdf39fa4655 100644 --- a/test/e2e/qa-lab/runtime/gateway-node-mcp.stress.e2e.test.ts +++ b/test/e2e/qa-lab/runtime/gateway-node-mcp.stress.e2e.test.ts @@ -285,18 +285,19 @@ describe("Gateway/node MCP real-process stress", () => { descriptors = (await waitForNode(gateway, nodeId, 3)).nodePluginTools ?? []; const streamable = descriptorFor(descriptors, "streamableHttp"); + // The fixture admits both HTTP requests before expiring their shared session. const expired = await Promise.allSettled([ invokeNodeMcpPayload({ gateway, nodeId, descriptor: streamable, - marker: "expire-session", + marker: "expire-concurrent-session", }), invokeNodeMcpPayload({ gateway, nodeId, descriptor: streamable, - marker: "expire-session", + marker: "expire-concurrent-session", }), ]); expect(expired.every((result) => result.status === "rejected")).toBe(true);