/** * 机器级会话上限(27-P3C §15.5 第 2 条)。 * * 为什么要这条守卫:池子里每个 session 是一个**常驻** claude CLI 子进程(约 200-300MB RSS), * 而 `sessions.size` 在此之前没有任何人过问 —— 一次心跳泄漏攒到过 82 个进程 / 17.5 GB。 * 批 4 又刚把公司级并行放开(单公司上限 20),机器上同时能起多少完全没闸门。 * * 这里**不起真进程**:直接往 `pool.sessions` 里塞假 session,只验腾位置的判据本身 * (挑最久空闲的、在途的不许动、全在途就抛)。真起进程的路径由 agent-manager 的用例覆盖。 */ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { createRequire } from 'module'; const require_ = createRequire(import.meta.url); const { SessionPool, SessionPoolFullError, DEFAULT_MAX_SESSIONS } = require_('../../src/core/session-pool'); describe('SessionPool 机器级上限', () => { let pool; const OLD = process.env.AGENT_MAX_SESSIONS; beforeEach(() => { pool = new SessionPool(); }); afterEach(() => { if (OLD === undefined) delete process.env.AGENT_MAX_SESSIONS; else process.env.AGENT_MAX_SESSIONS = OLD; }); /** 塞一个假 session;idleSince 为 null = 在途 */ function seed(key, idleSince) { pool.sessions.set(key, { key, idleSince, protocol: null, launcher: null, idleTimer: null }); } it('缺省上限是 12,且从 env 调用时读(不是模块加载时快照)', () => { expect(DEFAULT_MAX_SESSIONS).toBe(12); process.env.AGENT_MAX_SESSIONS = '3'; seed('a', 100); seed('b', 200); seed('c', 300); pool._evictForRoom(); // 3/3 满 → 该腾一个 expect(pool.sessions.size).toBe(2); expect(pool.sessions.has('a')).toBe(false); // 挑的是最久空闲的那个 }); it('在途的会话不许被腾走', () => { process.env.AGENT_MAX_SESSIONS = '2'; seed('inflight', null); // 在途 seed('idle', 100); pool._evictForRoom(); expect(pool.sessions.has('inflight')).toBe(true); expect(pool.sessions.has('idle')).toBe(false); }); it('全部在途时抛 SessionPoolFullError —— 不静默放行', () => { process.env.AGENT_MAX_SESSIONS = '2'; seed('x', null); seed('y', null); expect(() => pool._evictForRoom()).toThrow(SessionPoolFullError); // 关键:一个都没被杀(在途的动不得),也没有偷偷放行 expect(pool.sessions.size).toBe(2); }); it('没满时什么都不做', () => { process.env.AGENT_MAX_SESSIONS = '5'; seed('a', 100); seed('b', 200); pool._evictForRoom(); expect(pool.sessions.size).toBe(2); }); it('非法配置回落缺省,不当成无限', () => { for (const bad of ['0', '-1', 'abc', '']) { process.env.AGENT_MAX_SESSIONS = bad; const { maxSessions } = require_('../../src/core/session-pool'); expect(maxSessions()).toBe(DEFAULT_MAX_SESSIONS); } }); });