import { beforeEach, describe, expect, it, vi } from 'vitest'; const mocks = vi.hoisted(() => ({ database: { prepare: vi.fn(), exec: vi.fn(), }, getDatabase: vi.fn(), getPermissions: vi.fn(), hasRecentAal2: vi.fn(), requirePermission: vi.fn(), getSessionUser: vi.fn(), recordIsacAudit: vi.fn(), countActiveFullAdministrators: vi.fn(), runs: [] as unknown[][], })); vi.mock('$lib/server/auth/session', () => ({ getSessionUser: mocks.getSessionUser, })); vi.mock('$lib/server/auth/authorization', () => ({ getPermissions: mocks.getPermissions, hasRecentAal2: mocks.hasRecentAal2, requirePermission: mocks.requirePermission, })); vi.mock('$lib/server/auth/bootstrap', () => ({ countActiveFullAdministrators: mocks.countActiveFullAdministrators, })); vi.mock('$lib/server/database', () => ({ getDatabase: mocks.getDatabase, })); vi.mock('$lib/server/audit', () => ({ recordIsacAudit: mocks.recordIsacAudit, })); import { actions } from './+page.server'; const postRole = (id = '', permissions: string[] = []) => { const form = new FormData(); form.set('id', id); form.set('name', 'Field Operators'); form.set('description', 'Ordinary access role'); for (const permission of permissions) form.append('permissionIds', permission); return new Request('http://localhost/isac/roles?/save', { method: 'POST', body: form, }); }; const postDelete = () => { const form = new FormData(); form.set('id', 'role-user-operator'); form.set('confirmation', 'Existing'); return new Request('http://localhost/isac/roles?/delete', { method: 'POST', body: form, }); }; describe('ISAC role authority actions', () => { beforeEach(() => { vi.clearAllMocks(); mocks.runs.length = 0; mocks.getSessionUser.mockResolvedValue({ id: 'operator-1' }); mocks.getPermissions.mockResolvedValue(['users.roles']); mocks.hasRecentAal2.mockReturnValue(true); mocks.requirePermission.mockImplementation(async (user) => user); mocks.getDatabase.mockResolvedValue(mocks.database); mocks.countActiveFullAdministrators.mockResolvedValue(1); mocks.database.prepare.mockImplementation((sql: string) => ({ get: vi.fn(async (...args: unknown[]) => { if (sql.includes('SELECT id,name,description FROM roles WHERE id=?')) return args[0] ? { id: args[0], name: 'Existing', description: null } : null; if (sql.includes('SELECT id FROM roles WHERE name=')) return null; if (sql.includes('COUNT(DISTINCT users.id)')) return { count: 1 }; return null; }), all: vi.fn(async (...args: unknown[]) => { if (sql.includes('SELECT id,name FROM permissions')) return args.map((id) => ({ id, name: 'users.view' })); if (sql.includes('SELECT permissions.name FROM role_permissions')) return []; return []; }), run: vi.fn(async (...args: unknown[]) => { mocks.runs.push(args); return { changes: 1 }; }), })); }); it('rolls back role writes when audit recording fails', async () => { mocks.recordIsacAudit.mockRejectedValueOnce(new Error('audit failed')); await expect( actions.save?.({ cookies: {}, request: postRole('', ['permission-users-view']), } as never), ).rejects.toThrow('audit failed'); expect(mocks.database.exec.mock.calls).toEqual([ ['BEGIN IMMEDIATE'], ['ROLLBACK'], ]); expect(mocks.database.exec).not.toHaveBeenCalledWith('COMMIT'); expect(mocks.recordIsacAudit).toHaveBeenCalledWith( expect.objectContaining({ database: mocks.database }), ); }); it('rejects a role mutation that would leave no active full administrator', async () => { mocks.countActiveFullAdministrators.mockResolvedValue(0); const result = await actions.save?.({ cookies: {}, request: postRole('', ['permission-users-view']), } as never); expect(result).toMatchObject({ status: 409 }); expect(mocks.database.exec.mock.calls).toEqual([ ['BEGIN IMMEDIATE'], ['ROLLBACK'], ]); expect(mocks.recordIsacAudit).not.toHaveBeenCalled(); }); it('keeps role deletion and its audit evidence in one transaction', async () => { mocks.getPermissions.mockResolvedValue(['*']); const result = await actions.delete?.({ cookies: {}, request: postDelete(), } as never); expect(result).toMatchObject({ roleDeleted: true, resultToken: expect.any(String), }); expect(mocks.database.exec.mock.calls).toEqual([ ['BEGIN IMMEDIATE'], ['COMMIT'], ]); expect(mocks.recordIsacAudit).toHaveBeenCalledWith( expect.objectContaining({ action: 'role.deleted', database: mocks.database, }), ); }); });