feat(mvp): phase 8 - integration, testing & deployment
Fix all build/type/lint errors (zod 3.25 compat wrapper, Svelte 5 fixes), write 115 unit tests across 10 test files, expand seed script with demo data, update Docker config with migration on startup.
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
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<string, ReturnType<typeof vi.fn>>;
|
||||
const mockPermission = prisma.permission as unknown as Record<string, ReturnType<typeof vi.fn>>;
|
||||
const mockUserGroup = prisma.userGroup as unknown as Record<string, ReturnType<typeof vi.fn>>;
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user