/** * E2E R1 — administration du référentiel : équipes, renommages, mises à jour * et TOUS les refus (conflits 409, inconnus 404, invalides 400) — le contrat * d'erreur fait partie du contrat. */ process.env.DEMO_MODE = 'true'; import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import { PrismaClient } from '@prisma/client'; import request from 'supertest'; import { seed } from '../prisma/seed'; import { AppModule } from '../src/app.module'; const GHOST = '00000000-0000-4000-8000-000000000000'; describe('Administration du référentiel (e2e)', () => { let app: INestApplication; let admin: string; const prisma = new PrismaClient(); const http = () => request(app.getHttpServer()); const suffix = `adm-${Date.now().toString(36)}`; beforeAll(async () => { await seed(prisma); const moduleRef = await Test.createTestingModule({ imports: [AppModule.forRoot()], }).compile(); app = moduleRef.createNestApplication(); await app.init(); const { body } = await http().get('/auth/demo-accounts'); const compte = body.accounts.find( (a: { roleName: string }) => a.roleName === 'Administrateur', ); admin = (await http().post('/auth/demo-login').send({ userId: compte.id })).body .accessToken; }); afterAll(async () => { await prisma.team.deleteMany({ where: { name: { contains: suffix } } }); await prisma.category.deleteMany({ where: { name: { contains: suffix } } }); await prisma.location.deleteMany({ where: { name: { contains: suffix } } }); await app?.close(); await prisma.$disconnect(); }); const auth = () => ({ Authorization: `Bearer ${admin}` }); it('équipes : création, doublon 409, membres, 404', async () => { const { body: users } = await http().get('/users').set(auth()); const ahmed = users.users.find( (u: { email: string }) => u.email === 'technicien@demo.siop.ma', ); const team = await http() .post('/teams') .set(auth()) .send({ name: `Équipe ${suffix}`, description: 'Zone de test' }) .expect(201); expect(team.body.members).toHaveLength(0); await http() .post('/teams') .set(auth()) .send({ name: `Équipe ${suffix}` }) .expect(409); const updated = await http() .patch(`/teams/${team.body.id}`) .set(auth()) .send({ memberIds: [ahmed.id], description: 'Zone mise à jour' }) .expect(200); expect(updated.body.members.map((m: { id: string }) => m.id)).toEqual([ahmed.id]); const { body: teams } = await http().get('/teams').set(auth()).expect(200); expect(teams.teams.some((t: { id: string }) => t.id === team.body.id)).toBe(true); await http().patch(`/teams/${GHOST}`).set(auth()).send({ name: 'X' }).expect(404); await http() .patch(`/teams/${team.body.id}`) .set(auth()) .send({ name: 'Casablanca Centre' }) .expect(409); }); it('catégories : création, doublon 409, renommage, 404', async () => { const created = await http() .post('/categories') .set(auth()) .send({ kind: 'EQUIPMENT', name: `Catégorie ${suffix}` }) .expect(201); await http() .post('/categories') .set(auth()) .send({ kind: 'EQUIPMENT', name: `Catégorie ${suffix}` }) .expect(409); const renamed = await http() .patch(`/categories/${created.body.id}`) .set(auth()) .send({ name: `Catégorie ${suffix} v2` }) .expect(200); expect(renamed.body.name).toBe(`Catégorie ${suffix} v2`); await http() .patch(`/categories/${GHOST}`) .set(auth()) .send({ name: 'X' }) .expect(404); }); it('emplacements : renommage, parent inconnu, auto-parent, site avec zones, 404', async () => { const site = await http() .post('/locations') .set(auth()) .send({ name: `Site ${suffix}` }) .expect(201); const zone = await http() .post('/locations') .set(auth()) .send({ name: `Zone ${suffix}`, parentId: site.body.id }) .expect(201); await http() .patch(`/locations/${zone.body.id}`) .set(auth()) .send({ guardianName: 'Gardien Test', guardianPhone: '06 00 00 00 00' }) .expect(200); await http() .post('/locations') .set(auth()) .send({ name: `Orphelin ${suffix}`, parentId: GHOST }) .expect(400); await http() .patch(`/locations/${zone.body.id}`) .set(auth()) .send({ parentId: zone.body.id }) .expect(400); // Le site a une zone : il ne peut pas devenir zone lui-même await http() .patch(`/locations/${site.body.id}`) .set(auth()) .send({ parentId: zone.body.id }) .expect(400); await http().patch(`/locations/${GHOST}`).set(auth()).send({ name: 'X' }).expect(404); }); it('appareils : statut, référence en conflit, emplacement inconnu, 404', async () => { const { body } = await http().get('/assets').set(auth()); const a1 = body.assets.find((a: { reference: string }) => a.reference === 'A1'); const b1 = body.assets.find((a: { reference: string }) => a.reference === 'B1'); const updated = await http() .patch(`/assets/${b1.id}`) .set(auth()) .send({ status: 'UNDER_MAINTENANCE' }) .expect(200); expect(updated.body.status).toBe('UNDER_MAINTENANCE'); await http() .patch(`/assets/${b1.id}`) .set(auth()) .send({ status: 'IN_SERVICE' }) .expect(200); await http() .patch(`/assets/${b1.id}`) .set(auth()) .send({ reference: a1.reference }) .expect(409); await http() .patch(`/assets/${b1.id}`) .set(auth()) .send({ locationId: GHOST }) .expect(400); await http().get(`/assets/${GHOST}`).set(auth()).expect(404); await http() .patch(`/assets/${GHOST}`) .set(auth()) .send({ brand: 'X' }) .expect(404); await http() .post(`/assets/${GHOST}/components`) .set(auth()) .send({ typeId: GHOST }) .expect(404); await http() .delete(`/assets/${b1.id}/components/${GHOST}`) .set(auth()) .expect(404); }); it('personnes : mise à jour rôle/équipes, invitation sur rôle inconnu, 404', async () => { const { body: users } = await http().get('/users').set(auth()); const youssef = users.users.find( (u: { email: string }) => u.email === 'technicien-limite@demo.siop.ma', ); const { body: teams } = await http().get('/teams').set(auth()); const mohammedia = teams.teams.find((t: { name: string }) => t.name === 'Mohammedia'); const updated = await http() .patch(`/users/${youssef.id}`) .set(auth()) .send({ phone: '06 12 34 56 78', teamIds: [mohammedia.id] }) .expect(200); expect(updated.body.teams.map((t: { name: string }) => t.name)).toContain('Mohammedia'); // remise en l'état (seed idempotent : les équipes ne sont pas réécrites) const casa = teams.teams.find((t: { name: string }) => t.name === 'Casablanca Centre'); await http() .patch(`/users/${youssef.id}`) .set(auth()) .send({ teamIds: [casa.id] }) .expect(200); await http().patch(`/users/${GHOST}`).set(auth()).send({ phone: '0' }).expect(404); await http() .post('/users/invitations') .set(auth()) .send({ email: `x-${suffix}@spelev.ma`, displayName: 'X', roleId: GHOST }) .expect(404); await http().post(`/users/${GHOST}/invitation`).set(auth()).expect(404); }); });