Phase 3 Completed
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import { RoleSyncService } from '../src/modules/auth/role-sync.service';
|
||||
|
||||
describe('RoleSyncService', () => {
|
||||
const mockPrisma: any = {};
|
||||
const mockPermissionCache: any = { invalidate: jest.fn() };
|
||||
const mockEvents: any = { publish: jest.fn() };
|
||||
const mockGroupHash: any = { compute: (g: any) => require('crypto').createHash('sha256').update((g||[]).slice().sort().join(','), 'utf8').digest('hex') };
|
||||
let service: RoleSyncService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockPrisma.user = { findUnique: jest.fn() };
|
||||
mockPrisma.authGroupRoleMapping = { findMany: jest.fn() };
|
||||
mockPrisma.userRole = { findMany: jest.fn() };
|
||||
mockPrisma.auditLog = { create: jest.fn() };
|
||||
mockPrisma.user = mockPrisma.user;
|
||||
mockPrisma.$transaction = jest.fn(async (cb: any) => {
|
||||
// simulate transaction by calling provided callback with tx = mockPrisma
|
||||
await cb(mockPrisma);
|
||||
});
|
||||
|
||||
service = new RoleSyncService(mockPrisma as any, mockGroupHash as any, mockPermissionCache as any, mockEvents as any);
|
||||
});
|
||||
|
||||
test('computeGroupHash consistent and order independent', () => {
|
||||
const a = ['b', 'a', 'c'];
|
||||
const h1 = service.computeGroupHash(a);
|
||||
const h2 = service.computeGroupHash(['a', 'b', 'c']);
|
||||
expect(h1).toBe(h2);
|
||||
});
|
||||
|
||||
test('skips sync when group hash unchanged', async () => {
|
||||
const groups = ['RL-Owner'];
|
||||
const hash = service.computeGroupHash(groups);
|
||||
mockPrisma.user.findUnique.mockResolvedValue({ id: 'uid', lastGroupHash: hash });
|
||||
|
||||
const res = await service.syncUserRolesFromAuthentik('uid', groups);
|
||||
expect(res.skipped).toBe(true);
|
||||
expect(mockPrisma.$transaction).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('performs transaction and writes audit when changed', async () => {
|
||||
const groups = ['RL-Owner'];
|
||||
mockPrisma.user.findUnique.mockResolvedValue({ id: 'uid', lastGroupHash: 'old' });
|
||||
mockPrisma.authGroupRoleMapping.findMany.mockResolvedValue([{ roleId: 'r1' }]);
|
||||
mockPrisma.userRole.findMany.mockResolvedValue([{ roleId: 'r_old' }]);
|
||||
|
||||
// spy on tx ops
|
||||
mockPrisma.userRole.deleteMany = jest.fn();
|
||||
mockPrisma.userRole.createMany = jest.fn();
|
||||
mockPrisma.user.update = jest.fn();
|
||||
|
||||
mockPrisma.auditLog.create = jest.fn();
|
||||
|
||||
const res = await service.syncUserRolesFromAuthentik('uid', groups);
|
||||
|
||||
expect(mockPrisma.$transaction).toHaveBeenCalled();
|
||||
expect(mockPrisma.userRole.deleteMany).toHaveBeenCalledWith({ where: { userId: 'uid', source: 'AUTHENTIK' } });
|
||||
expect(mockPrisma.userRole.createMany).toHaveBeenCalled();
|
||||
expect(mockPrisma.user.update).toHaveBeenCalled();
|
||||
expect(mockPermissionCache.invalidate).toHaveBeenCalledWith('uid');
|
||||
expect(mockEvents.publish).toHaveBeenCalledWith(expect.objectContaining({ type: 'RolesSynchronized' }));
|
||||
expect(res.skipped).toBe(false);
|
||||
expect(res.assignedRoleIds).toEqual(['r1']);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user