test(builder): pin active /mcp sessions survive across idle-window sweeps
This commit is contained in:
@@ -471,6 +471,68 @@ describe('streamable HTTP gating - idle session reaper', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('streamable HTTP gating - active sessions survive idle-window sweeps', () => {
|
||||
let ctx: RawServerHandle;
|
||||
|
||||
before(async () => {
|
||||
// Same env mechanism and the same thresholds as the idle-reaper block
|
||||
// above, deliberately: this test is that one's mirror image. That test
|
||||
// proves an INACTIVE session dies; this one proves an ACTIVE session -
|
||||
// kept alive by real requests, each one bumping streamableLastSeen -
|
||||
// does not, even once the TOTAL elapsed time comfortably exceeds the
|
||||
// idle window. Nothing in the idle-reaper test (or anywhere else in this
|
||||
// file) exercises the per-request lastSeen bump itself: dropping it
|
||||
// would not fail that test, since it never issues a mid-session request
|
||||
// at all. This is the test that closes that gap.
|
||||
ctx = await spawnServer({ ...AUTH_ENV, VECTOR_MCP_SESSION_IDLE_MS: '400', VECTOR_MCP_SESSION_SWEEP_MS: '100' });
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await stopRawServer(ctx);
|
||||
});
|
||||
|
||||
test('a session kept active by periodic requests survives past what would otherwise be its idle deadline', async () => {
|
||||
const init = await postMcp(ctx.port, { Authorization: `Bearer ${TOKEN_ALICE}` }, initializeRequest());
|
||||
assert.equal(init.status, 200);
|
||||
const sessionId = init.headers['mcp-session-id'] as string | undefined;
|
||||
assert.ok(sessionId);
|
||||
|
||||
// Six polls, 150ms apart: each individual gap (150ms) stays well under
|
||||
// the 400ms idle window - even accounting for the 100ms sweep cadence,
|
||||
// the worst-case time since the last bump a sweep can observe is
|
||||
// roughly pollIntervalMs + sweepMs = 250ms, still comfortably under
|
||||
// 400ms (see the idle-reaper test above for the same worst-case
|
||||
// argument applied to sweep phase alignment). But the TOTAL span across
|
||||
// all six polls (900ms) is more than twice the 400ms idle window - so a
|
||||
// server that only stamped lastSeen once, at session creation, would
|
||||
// have let this session go idle and reaped it well before this loop
|
||||
// finishes. Each poll is asserted individually so a regression fails at
|
||||
// the specific iteration where the session stopped responding, not just
|
||||
// at the end.
|
||||
const POLL_INTERVAL_MS = 150;
|
||||
const POLL_COUNT = 6;
|
||||
for (let i = 0; i < POLL_COUNT; i++) {
|
||||
await new Promise((resolveWait) => setTimeout(resolveWait, POLL_INTERVAL_MS));
|
||||
const poll = await postMcp(
|
||||
ctx.port,
|
||||
{ Authorization: `Bearer ${TOKEN_ALICE}`, 'mcp-session-id': sessionId! },
|
||||
toolsListRequest(),
|
||||
);
|
||||
assert.equal(poll.status, 200, `poll ${i + 1}/${POLL_COUNT} (t~${(i + 1) * POLL_INTERVAL_MS}ms) must still find the session alive`);
|
||||
}
|
||||
|
||||
// Headline assertion: after a total span well past the idle window,
|
||||
// reached entirely in small, sub-threshold bumps, the session is still
|
||||
// there - not reaped, not evicted.
|
||||
const stillAlive = await postMcp(
|
||||
ctx.port,
|
||||
{ Authorization: `Bearer ${TOKEN_ALICE}`, 'mcp-session-id': sessionId! },
|
||||
toolsListRequest(),
|
||||
);
|
||||
assert.equal(stillAlive.status, 200, 'an actively-used session must still be alive after spanning more than one idle window in small, sub-threshold bumps');
|
||||
});
|
||||
});
|
||||
|
||||
describe('streamable HTTP gating - per-identity session cap', () => {
|
||||
let ctx: RawServerHandle;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user