import { describe, it, expect } from 'vitest'; import { SessionGate } from '../src/gate/session-gate.js'; import type { TagMatchResult, PromptIndexEntry } from '../src/gate/tag-matcher.js'; function makeMatchResult(names: string[]): TagMatchResult { return { fullContent: names.map((name) => ({ name, priority: 5, summary: null, chapters: null, content: `Content of ${name}`, })), indexOnly: [], remaining: [], }; } describe('SessionGate', () => { it('creates a gated session when project is gated', () => { const gate = new SessionGate(); gate.createSession('s1', true); expect(gate.isGated('s1')).toBe(true); }); it('creates an ungated session when project is not gated', () => { const gate = new SessionGate(); gate.createSession('s1', false); expect(gate.isGated('s1')).toBe(false); }); it('unknown sessions are treated as ungated', () => { const gate = new SessionGate(); expect(gate.isGated('nonexistent')).toBe(false); }); it('getSession returns null for unknown sessions', () => { const gate = new SessionGate(); expect(gate.getSession('nonexistent')).toBeNull(); }); it('getSession returns session state', () => { const gate = new SessionGate(); gate.createSession('s1', true); const state = gate.getSession('s1'); expect(state).not.toBeNull(); expect(state!.gated).toBe(true); expect(state!.tags).toEqual([]); expect(state!.retrievedPrompts.size).toBe(0); expect(state!.briefing).toBeNull(); }); it('ungate marks session as ungated and records tags', () => { const gate = new SessionGate(); gate.createSession('s1', true); gate.ungate('s1', ['zigbee', 'mqtt'], makeMatchResult(['prompt-a', 'prompt-b'])); expect(gate.isGated('s1')).toBe(false); const state = gate.getSession('s1'); expect(state!.tags).toEqual(['zigbee', 'mqtt']); expect(state!.retrievedPrompts.has('prompt-a')).toBe(true); expect(state!.retrievedPrompts.has('prompt-b')).toBe(true); }); it('ungate appends tags on repeated calls', () => { const gate = new SessionGate(); gate.createSession('s1', true); gate.ungate('s1', ['zigbee'], makeMatchResult(['p1'])); gate.ungate('s1', ['mqtt'], makeMatchResult(['p2'])); const state = gate.getSession('s1'); expect(state!.tags).toEqual(['zigbee', 'mqtt']); expect(state!.retrievedPrompts.has('p1')).toBe(true); expect(state!.retrievedPrompts.has('p2')).toBe(true); }); it('ungate is no-op for unknown sessions', () => { const gate = new SessionGate(); // Should not throw gate.ungate('nonexistent', ['tag'], makeMatchResult(['p'])); }); it('addRetrievedPrompts records additional prompts', () => { const gate = new SessionGate(); gate.createSession('s1', true); gate.ungate('s1', ['zigbee'], makeMatchResult(['p1'])); gate.addRetrievedPrompts('s1', ['mqtt', 'lights'], ['p2', 'p3']); const state = gate.getSession('s1'); expect(state!.tags).toEqual(['zigbee', 'mqtt', 'lights']); expect(state!.retrievedPrompts.has('p2')).toBe(true); expect(state!.retrievedPrompts.has('p3')).toBe(true); }); it('addRetrievedPrompts is no-op for unknown sessions', () => { const gate = new SessionGate(); gate.addRetrievedPrompts('nonexistent', ['tag'], ['p']); }); it('filterAlreadySent removes already-sent prompts', () => { const gate = new SessionGate(); gate.createSession('s1', true); gate.ungate('s1', ['zigbee'], makeMatchResult(['p1'])); const prompts: PromptIndexEntry[] = [ { name: 'p1', priority: 5, summary: 'already sent', chapters: null, content: 'x' }, { name: 'p2', priority: 5, summary: 'new', chapters: null, content: 'y' }, ]; const filtered = gate.filterAlreadySent('s1', prompts); expect(filtered).toHaveLength(1); expect(filtered[0]!.name).toBe('p2'); }); it('filterAlreadySent returns all prompts for unknown sessions', () => { const gate = new SessionGate(); const prompts: PromptIndexEntry[] = [ { name: 'p1', priority: 5, summary: null, chapters: null, content: 'x' }, ]; const filtered = gate.filterAlreadySent('nonexistent', prompts); expect(filtered).toHaveLength(1); }); it('removeSession cleans up state', () => { const gate = new SessionGate(); gate.createSession('s1', true); expect(gate.getSession('s1')).not.toBeNull(); gate.removeSession('s1'); expect(gate.getSession('s1')).toBeNull(); expect(gate.isGated('s1')).toBe(false); }); it('removeSession is safe for unknown sessions', () => { const gate = new SessionGate(); gate.removeSession('nonexistent'); // Should not throw }); it('manages multiple sessions independently', () => { const gate = new SessionGate(); gate.createSession('s1', true); gate.createSession('s2', false); expect(gate.isGated('s1')).toBe(true); expect(gate.isGated('s2')).toBe(false); gate.ungate('s1', ['zigbee'], makeMatchResult(['p1'])); expect(gate.isGated('s1')).toBe(false); expect(gate.getSession('s2')!.tags).toEqual([]); // s2 untouched }); });