import { describe, it, expect, vi, beforeEach } from 'vitest'; vi.mock('../../prisma.js', () => ({ prisma: { user: { findUnique: vi.fn() }, permission: { findFirst: vi.fn(), findMany: vi.fn(), upsert: vi.fn(), deleteMany: vi.fn() }, userGroup: { findMany: vi.fn() } } })); import { prisma } from '../../prisma.js'; import * as permissionService from '../permissionService.js'; const mockUser = prisma.user as unknown as Record>; const mockPermission = prisma.permission as unknown as Record>; const mockUserGroup = prisma.userGroup as unknown as Record>; describe('permissionService', () => { beforeEach(() => { vi.clearAllMocks(); }); describe('checkPermission', () => { it('grants full access to admins', async () => { mockUser.findUnique.mockResolvedValue({ role: 'admin' }); const result = await permissionService.checkPermission( 'board', 'b1', 'admin-user', 'edit' ); expect(result.hasPermission).toBe(true); expect(result.effectiveLevel).toBe('admin'); expect(result.source).toBe('admin'); }); it('checks direct user permission', async () => { mockUser.findUnique.mockResolvedValue({ role: 'user' }); mockPermission.findFirst.mockResolvedValue({ level: 'edit' }); const result = await permissionService.checkPermission( 'board', 'b1', 'user1', 'view' ); expect(result.hasPermission).toBe(true); expect(result.effectiveLevel).toBe('edit'); expect(result.source).toBe('user'); }); it('denies when user permission is insufficient', async () => { mockUser.findUnique.mockResolvedValue({ role: 'user' }); mockPermission.findFirst.mockResolvedValue({ level: 'view' }); const result = await permissionService.checkPermission( 'board', 'b1', 'user1', 'admin' ); expect(result.hasPermission).toBe(false); }); it('falls back to group permissions', async () => { mockUser.findUnique.mockResolvedValue({ role: 'user' }); mockPermission.findFirst.mockResolvedValue(null); mockUserGroup.findMany.mockResolvedValue([{ groupId: 'g1' }]); mockPermission.findMany.mockResolvedValue([{ level: 'edit' }]); const result = await permissionService.checkPermission( 'board', 'b1', 'user1', 'view' ); expect(result.hasPermission).toBe(true); expect(result.source).toBe('group'); }); it('denies when no permission found', async () => { mockUser.findUnique.mockResolvedValue({ role: 'user' }); mockPermission.findFirst.mockResolvedValue(null); mockUserGroup.findMany.mockResolvedValue([]); const result = await permissionService.checkPermission( 'board', 'b1', 'user1', 'view' ); expect(result.hasPermission).toBe(false); expect(result.effectiveLevel).toBeNull(); expect(result.source).toBeNull(); }); }); describe('grantPermission', () => { it('upserts a permission', async () => { const perm = { entityType: 'board' as const, entityId: 'b1', targetType: 'user' as const, targetId: 'u1', level: 'edit' as const }; mockPermission.upsert.mockResolvedValue({ id: 'p1', ...perm }); const result = await permissionService.grantPermission(perm); expect(result.level).toBe('edit'); }); }); describe('revokePermission', () => { it('deletes matching permissions', async () => { mockPermission.deleteMany.mockResolvedValue({ count: 1 }); await permissionService.revokePermission('board', 'b1', 'user', 'u1'); expect(mockPermission.deleteMany).toHaveBeenCalledWith({ where: { entityType: 'board', entityId: 'b1', targetType: 'user', targetId: 'u1' } }); }); }); describe('getPermissionsForEntity', () => { it('returns permissions for an entity', async () => { const perms = [{ id: 'p1', level: 'view' }]; mockPermission.findMany.mockResolvedValue(perms); const result = await permissionService.getPermissionsForEntity('board', 'b1'); expect(result).toEqual(perms); }); }); });