feat(#159): suppressions métier dossiers/parents/enfants/AM/staff.
SuppressionService + DELETE /dossiers/:numero, cascades DELETE /users et DELETE /enfants?deleteDossier, flag sans_enfant, specs front #160. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
import { ForbiddenException, NotFoundException } from '@nestjs/common';
|
||||
import { SuppressionService } from './suppression.service';
|
||||
import { RoleType } from 'src/entities/users.entity';
|
||||
|
||||
describe('SuppressionService (#159)', () => {
|
||||
const dataSource = {
|
||||
transaction: jest.fn(async (cb: (m: unknown) => Promise<unknown>) =>
|
||||
cb({
|
||||
delete: jest.fn(),
|
||||
query: jest.fn(),
|
||||
}),
|
||||
),
|
||||
};
|
||||
|
||||
const usersRepository = {
|
||||
findOne: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
count: jest.fn(),
|
||||
};
|
||||
const parentsRepository = {
|
||||
findOne: jest.fn(),
|
||||
find: jest.fn(),
|
||||
query: jest.fn(),
|
||||
};
|
||||
const amRepository = {
|
||||
findOne: jest.fn(),
|
||||
};
|
||||
const childrenRepository = {
|
||||
findOne: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
save: jest.fn(),
|
||||
};
|
||||
const parentsChildrenRepository = {
|
||||
find: jest.fn(),
|
||||
};
|
||||
const amChildrenRepository = {
|
||||
find: jest.fn(),
|
||||
save: jest.fn(),
|
||||
count: jest.fn(),
|
||||
};
|
||||
|
||||
let service: SuppressionService;
|
||||
|
||||
const staff = {
|
||||
id: 'staff-1',
|
||||
role: RoleType.GESTIONNAIRE,
|
||||
} as never;
|
||||
|
||||
const admin = {
|
||||
id: 'admin-1',
|
||||
role: RoleType.ADMINISTRATEUR,
|
||||
} as never;
|
||||
|
||||
const superAdmin = {
|
||||
id: 'sa-1',
|
||||
role: RoleType.SUPER_ADMIN,
|
||||
} as never;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
service = new SuppressionService(
|
||||
dataSource as never,
|
||||
usersRepository as never,
|
||||
parentsRepository as never,
|
||||
amRepository as never,
|
||||
childrenRepository as never,
|
||||
parentsChildrenRepository as never,
|
||||
amChildrenRepository as never,
|
||||
);
|
||||
});
|
||||
|
||||
it('refuse self-delete', async () => {
|
||||
usersRepository.findOne.mockResolvedValue({
|
||||
id: 'admin-1',
|
||||
role: RoleType.ADMINISTRATEUR,
|
||||
});
|
||||
await expect(service.deleteUser('admin-1', admin)).rejects.toBeInstanceOf(
|
||||
ForbiddenException,
|
||||
);
|
||||
});
|
||||
|
||||
it('refuse gestionnaire deleting another gestionnaire', async () => {
|
||||
usersRepository.findOne.mockResolvedValue({
|
||||
id: 'g2',
|
||||
role: RoleType.GESTIONNAIRE,
|
||||
});
|
||||
await expect(service.deleteUser('g2', staff)).rejects.toBeInstanceOf(
|
||||
ForbiddenException,
|
||||
);
|
||||
});
|
||||
|
||||
it('dernier admin : refus si pas super_admin', async () => {
|
||||
usersRepository.findOne.mockResolvedValue({
|
||||
id: 'admin-2',
|
||||
role: RoleType.ADMINISTRATEUR,
|
||||
});
|
||||
usersRepository.count.mockResolvedValue(1);
|
||||
await expect(service.deleteUser('admin-2', admin)).rejects.toBeInstanceOf(
|
||||
ForbiddenException,
|
||||
);
|
||||
});
|
||||
|
||||
it('dernier admin : OK pour super_admin', async () => {
|
||||
usersRepository.findOne.mockResolvedValue({
|
||||
id: 'admin-2',
|
||||
role: RoleType.ADMINISTRATEUR,
|
||||
});
|
||||
usersRepository.count.mockResolvedValue(1);
|
||||
usersRepository.delete.mockResolvedValue({ affected: 1 });
|
||||
const res = await service.deleteUser('admin-2', superAdmin);
|
||||
expect(res.deleted_user_ids).toEqual(['admin-2']);
|
||||
});
|
||||
|
||||
it('delete AM : clos placements, pas d’enfants deleted', async () => {
|
||||
usersRepository.findOne.mockResolvedValue({
|
||||
id: 'am-1',
|
||||
role: RoleType.ASSISTANTE_MATERNELLE,
|
||||
});
|
||||
amRepository.findOne.mockResolvedValue({
|
||||
user_id: 'am-1',
|
||||
numero_dossier: '2026-000015',
|
||||
});
|
||||
amChildrenRepository.find.mockResolvedValue([
|
||||
{
|
||||
amId: 'am-1',
|
||||
enfantId: 'e1',
|
||||
child: { id: 'e1', status: 'garde' },
|
||||
},
|
||||
]);
|
||||
amChildrenRepository.count.mockResolvedValue(0);
|
||||
amChildrenRepository.save.mockImplementation(async (x) => x);
|
||||
childrenRepository.save.mockResolvedValue({});
|
||||
usersRepository.delete.mockResolvedValue({ affected: 1 });
|
||||
|
||||
const res = await service.deleteUser('am-1', staff);
|
||||
expect(res.deleted_enfant_ids).toEqual([]);
|
||||
expect(res.deleted_user_ids).toEqual(['am-1']);
|
||||
expect(res.type).toBe('assistante_maternelle');
|
||||
});
|
||||
|
||||
it('delete dossier famille introuvable', async () => {
|
||||
parentsRepository.findOne.mockResolvedValue(null);
|
||||
amRepository.findOne.mockResolvedValue(null);
|
||||
await expect(
|
||||
service.deleteDossier('2026-999999', staff),
|
||||
).rejects.toBeInstanceOf(NotFoundException);
|
||||
});
|
||||
|
||||
it('co-parent : delete user seul', async () => {
|
||||
usersRepository.findOne.mockResolvedValue({
|
||||
id: 'p2',
|
||||
role: RoleType.PARENT,
|
||||
});
|
||||
parentsRepository.findOne.mockResolvedValue({
|
||||
user_id: 'p2',
|
||||
numero_dossier: '2026-000010',
|
||||
co_parent: { id: 'p1' },
|
||||
});
|
||||
parentsRepository.query.mockResolvedValue([
|
||||
{ id: 'p1' },
|
||||
{ id: 'p2' },
|
||||
]);
|
||||
dataSource.transaction.mockImplementation(async (cb) =>
|
||||
cb({
|
||||
delete: jest.fn(),
|
||||
query: jest.fn(),
|
||||
}),
|
||||
);
|
||||
|
||||
const res = await service.deleteUser('p2', staff);
|
||||
expect(res.deleted_enfant_ids).toEqual([]);
|
||||
expect(res.deleted_user_ids).toEqual(['p2']);
|
||||
expect(res.message).toMatch(/co-parent/i);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user