fix: defensive rollback in removeRoleMember, type/style cleanup, test coverage

- Wrap removeRoleMember post-write admin rollback in try/catch so a
  transient DB failure cannot leave the system with zero administrators
- Replace double `as unknown[] as IRole[]` cast with `.lean<IRole[]>()`
- Type parsePagination param explicitly; extract DEFAULT/MAX page constants
- Preserve original error cause in updateRoleByName re-throw
- Add test for rollback failure path in removeRoleMember (returns 400)
- Add test for pre-existing roles missing description field (.lean())
This commit is contained in:
Dustin Healy 2026-03-26 17:55:58 -07:00
parent ad47919ecd
commit e55db4bfb4
4 changed files with 48 additions and 7 deletions

View file

@ -1196,6 +1196,28 @@ describe('createAdminRolesHandlers', () => {
});
});
it('returns 400 even when rollback updateUser throws', async () => {
const deps = createDeps({
getRoleByName: jest.fn().mockResolvedValue(mockRole({ name: SystemRoles.ADMIN })),
findUser: jest.fn().mockResolvedValue(mockUser({ role: SystemRoles.ADMIN })),
countUsersByRole: jest.fn().mockResolvedValueOnce(2).mockResolvedValueOnce(0),
updateUser: jest
.fn()
.mockResolvedValueOnce(mockUser({ role: SystemRoles.USER }))
.mockRejectedValueOnce(new Error('rollback failed')),
});
const handlers = createAdminRolesHandlers(deps);
const { req, res, status, json } = createReqRes({
params: { name: SystemRoles.ADMIN, userId: validUserId },
});
await handlers.removeRoleMember(req, res);
expect(status).toHaveBeenCalledWith(400);
expect(json).toHaveBeenCalledWith({ error: 'Cannot remove the last admin user' });
expect(deps.updateUser).toHaveBeenCalledTimes(2);
});
it('returns 500 on unexpected error', async () => {
const deps = createDeps({
getRoleByName: jest.fn().mockResolvedValue(mockRole()),