diff --git a/src/db-sessions.test.ts b/src/db-sessions.test.ts new file mode 100644 index 0000000..3e2e84d --- /dev/null +++ b/src/db-sessions.test.ts @@ -0,0 +1,28 @@ +import { beforeEach, describe, expect, it } from 'vitest'; + +import { + _initTestDatabase, + deleteAllSessionsForGroup, + getSession, + setSession, +} from './db.js'; + +describe('session accessors', () => { + beforeEach(() => { + _initTestDatabase(); + }); + + it('deletes owner, reviewer, and arbiter role-scoped sessions for a group', () => { + setSession('group-a', 'owner-session'); + setSession('group-a:reviewer', 'reviewer-session'); + setSession('group-a:arbiter', 'arbiter-session'); + setSession('group-b', 'other-session'); + + deleteAllSessionsForGroup('group-a'); + + expect(getSession('group-a')).toBeUndefined(); + expect(getSession('group-a:reviewer')).toBeUndefined(); + expect(getSession('group-a:arbiter')).toBeUndefined(); + expect(getSession('group-b')).toBe('other-session'); + }); +}); diff --git a/src/db/sessions.ts b/src/db/sessions.ts index e834992..07bf82a 100644 --- a/src/db/sessions.ts +++ b/src/db/sessions.ts @@ -141,8 +141,11 @@ export function deleteAllSessionsForGroupFromDatabase( groupFolder: string, ): void { database - .prepare('DELETE FROM sessions WHERE group_folder = ?') - .run(groupFolder); + .prepare( + `DELETE FROM sessions + WHERE group_folder IN (?, ?, ?)`, + ) + .run(groupFolder, `${groupFolder}:reviewer`, `${groupFolder}:arbiter`); } export function getAllSessionsForAgentTypeFromDatabase( diff --git a/src/runtime-state.test.ts b/src/runtime-state.test.ts new file mode 100644 index 0000000..85323ce --- /dev/null +++ b/src/runtime-state.test.ts @@ -0,0 +1,37 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +const db = vi.hoisted(() => ({ + assignRoom: vi.fn(), + deleteAllSessionsForGroup: vi.fn(), + deleteSession: vi.fn(), + getAllRoomBindings: vi.fn(() => ({})), + getAllSessions: vi.fn(() => ({})), + getRouterState: vi.fn(() => ''), + setRouterState: vi.fn(), + setSession: vi.fn(), +})); + +vi.mock('./db.js', () => db); + +import { createRuntimeState } from './runtime-state.js'; + +describe('createRuntimeState', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('clears role-scoped sessions from memory when allRoles is requested', () => { + const state = createRuntimeState(); + + state.persistSession('group-a', 'owner-session'); + state.persistSession('group-a:reviewer', 'reviewer-session'); + state.persistSession('group-a:arbiter', 'arbiter-session'); + state.persistSession('group-b', 'other-session'); + + state.clearSession('group-a', { allRoles: true }); + + expect(state.getSessions()).toEqual({ 'group-b': 'other-session' }); + expect(db.deleteAllSessionsForGroup).toHaveBeenCalledWith('group-a'); + expect(db.deleteSession).not.toHaveBeenCalled(); + }); +}); diff --git a/src/runtime-state.ts b/src/runtime-state.ts index 536811c..597588e 100644 --- a/src/runtime-state.ts +++ b/src/runtime-state.ts @@ -48,10 +48,17 @@ export function createRuntimeState(): RuntimeState { groupFolder: string, opts?: { allRoles?: boolean }, ): void => { - delete sessions[groupFolder]; if (opts?.allRoles) { + for (const key of [ + groupFolder, + `${groupFolder}:reviewer`, + `${groupFolder}:arbiter`, + ]) { + delete sessions[key]; + } deleteAllSessionsForGroup(groupFolder); } else { + delete sessions[groupFolder]; deleteSession(groupFolder); } };