feat: add PairingManager and gateway lock tests (Tier 4 feature 4 foundation)

This commit is contained in:
William Valentin
2026-02-09 13:32:59 -08:00
parent 4413c4dc7c
commit 9d4d440ecf
4 changed files with 389 additions and 0 deletions
+96
View File
@@ -219,6 +219,102 @@ describe('GatewayServer integration', () => {
});
});
describe('GatewayServer lock mode', () => {
const LOCK_PORT = 18897;
let lockServer: GatewayServer;
beforeAll(async () => {
lockServer = new GatewayServer({
port: LOCK_PORT,
sessionManager: mockSessionManager as unknown as GatewayServerConfig['sessionManager'],
modelClient: mockModelClient,
systemPrompt: 'Test prompt',
toolRegistry: mockToolRegistry as unknown as GatewayServerConfig['toolRegistry'],
toolExecutor: mockToolExecutor as unknown as GatewayServerConfig['toolExecutor'],
lock: true,
uiDir: resolve(import.meta.dirname, 'ui'),
});
await lockServer.start();
});
afterAll(async () => {
await lockServer.stop();
});
function createLockClient(): Promise<WebSocket> {
return new Promise((resolve, reject) => {
const ws = new WebSocket(`ws://127.0.0.1:${LOCK_PORT}`);
ws.on('open', () => resolve(ws));
ws.on('error', reject);
});
}
it('allows the first client to connect', async () => {
const ws = await createLockClient();
try {
const result = await sendAndReceive(ws, { id: 1, method: 'system.health' });
const response = result as GatewayResponse;
expect((response.result as any).status).toBe('ok');
} finally {
ws.close();
// Wait for the close to propagate so connectionMap is empty
await new Promise(r => setTimeout(r, 100));
}
});
it('rejects second client with code 4003 when locked', async () => {
const ws1 = await createLockClient();
try {
// Second client should be rejected
const closePromise = new Promise<{ code: number; reason: string }>((resolve) => {
const ws2 = new WebSocket(`ws://127.0.0.1:${LOCK_PORT}`);
ws2.on('close', (code, reason) => {
resolve({ code, reason: reason.toString() });
});
});
const { code, reason } = await closePromise;
expect(code).toBe(4003);
expect(reason).toContain('locked');
} finally {
ws1.close();
await new Promise(r => setTimeout(r, 100));
}
});
it('allows a new client after the previous one disconnects', async () => {
const ws1 = await createLockClient();
ws1.close();
// Wait for the close to propagate
await new Promise(r => setTimeout(r, 100));
const ws2 = await createLockClient();
try {
const result = await sendAndReceive(ws2, { id: 2, method: 'system.health' });
const response = result as GatewayResponse;
expect((response.result as any).status).toBe('ok');
} finally {
ws2.close();
await new Promise(r => setTimeout(r, 100));
}
});
it('system.lock handler returns lock status', async () => {
const ws = await createLockClient();
try {
const result = await sendAndReceive(ws, { id: 3, method: 'system.lock' });
const response = result as GatewayResponse;
const r = response.result as { locked: boolean; activeClients: number; maxClients: number | null };
expect(r.locked).toBe(true);
expect(r.activeClients).toBe(1);
expect(r.maxClients).toBe(1);
} finally {
ws.close();
await new Promise(r => setTimeout(r, 100));
}
});
});
describe('GatewayServer HTTP auth', () => {
const AUTH_PORT = 18898;
let authServer: GatewayServer;