mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
refactor(mcp): move invalidateMcpSessions into sessionManager
The body was a hand-inlined duplicate of sessionManager's own closeSession helper, so it moves next to revokeUserSessions and reuses it. The barrel re-exports it, leaving admin.service.ts and admin.controller.ts untouched. The reason to move it is that callers outside src/mcp/ are coming. index.ts evaluates readEnv().mcp at module scope and installs the sweep interval, so importing the barrel from a domain module drags both into every test file that mocks app-config with a partial export set, which is the hazard kill-switch.ts documents. sessionManager.ts has no module-scope side effects. The log line loses "due to addon state change"; addons stop being the only thing that invalidates a session surface. SESS-017 to SESS-020 cover it.
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
/**
|
||||
* Unit tests for MCP sessionManager — SESS-001 to SESS-016.
|
||||
* Covers revokeUserSessions, revokeUserSessionsForClient and evictOldestSessionForUser.
|
||||
* Unit tests for MCP sessionManager — SESS-001 to SESS-020.
|
||||
* Covers revokeUserSessions, revokeUserSessionsForClient, evictOldestSessionForUser
|
||||
* and invalidateMcpSessions.
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { sessions, revokeUserSessions, revokeUserSessionsForClient, evictOldestSessionForUser, McpSession } from '../../../src/mcp/sessionManager';
|
||||
import { sessions, revokeUserSessions, revokeUserSessionsForClient, evictOldestSessionForUser, invalidateMcpSessions, McpSession } from '../../../src/mcp/sessionManager';
|
||||
|
||||
function makeSession(overrides: Partial<McpSession> = {}): McpSession {
|
||||
return {
|
||||
@@ -179,3 +180,42 @@ describe('evictOldestSessionForUser', () => {
|
||||
expect(sessions.has('sid-1')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('invalidateMcpSessions', () => {
|
||||
it('SESS-017: drops every session regardless of user or client', () => {
|
||||
sessions.set('sid-1', makeSession({ userId: 1, clientId: 'client-a' }));
|
||||
sessions.set('sid-2', makeSession({ userId: 2, clientId: null }));
|
||||
|
||||
invalidateMcpSessions();
|
||||
|
||||
expect(sessions.size).toBe(0);
|
||||
});
|
||||
|
||||
it('SESS-018: closes both halves of every session', () => {
|
||||
const a = makeSession({ userId: 1 });
|
||||
const b = makeSession({ userId: 2 });
|
||||
sessions.set('sid-1', a);
|
||||
sessions.set('sid-2', b);
|
||||
|
||||
invalidateMcpSessions();
|
||||
|
||||
expect(a.server.close).toHaveBeenCalledOnce();
|
||||
expect(a.transport.close).toHaveBeenCalledOnce();
|
||||
expect(b.server.close).toHaveBeenCalledOnce();
|
||||
expect(b.transport.close).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('SESS-019: still empties the map when close() throws', () => {
|
||||
const s = makeSession({ userId: 1 });
|
||||
(s.server.close as ReturnType<typeof vi.fn>).mockImplementation(() => { throw new Error('close failed'); });
|
||||
sessions.set('sid-1', s);
|
||||
|
||||
expect(() => invalidateMcpSessions()).not.toThrow();
|
||||
expect(sessions.size).toBe(0);
|
||||
});
|
||||
|
||||
it('SESS-020: is a no-op on an empty map', () => {
|
||||
expect(() => invalidateMcpSessions()).not.toThrow();
|
||||
expect(sessions.size).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user