Outils pour utilisateurs

Outils du site


ujusum:3-codage:2-sprints:1-sprint-1

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

ujusum:3-codage:2-sprints:1-sprint-1 [2026/06/08 01:58] – créée adminujusum:3-codage:2-sprints:1-sprint-1 [2026/06/08 02:01] (Version actuelle) admin
Ligne 8222: Ligne 8222:
 ---- ----
  
-====== Étape suivante ======+====== Sprint 1-C.2 — Tests Unitaires Auth & RBAC ======
  
-===== Sprint 1-C.2 =====+===== Objectif =====
  
-Tests Unitaires Auth & RBAC+Sécuriser l'ensemble du module Auth avant de poursuivre les développements métier.
  
-Objectif :+À l'issue de cette étape :
  
-Valider automatiquement :+<code> 
 +✓ PasswordService testé 
 + 
 +✓ TokenService testé 
 + 
 +✓ AuthService testé 
 + 
 +✓ JwtStrategy testée 
 + 
 +✓ RolesGuard testé 
 + 
 +✓ PermissionsGuard testé 
 + 
 +✓ Couverture > 80% 
 + 
 +✓ CI compatible 
 +</code> 
 + 
 +---- 
 + 
 +====== Architecture des tests ====== 
 + 
 +===== Répertoire ===== 
 + 
 +Créer :
  
 <code> <code>
-PasswordService+apps/api/src/modules/auth
  
-AuthService+├── domain 
 +│ 
 +│   └── services 
 +│ 
 +│       ├── auth.service.spec.ts 
 +│       ├── password.service.spec.ts 
 +│       └── token.service.spec.ts 
 +│ 
 +├── infrastructure 
 +│ 
 +│   └── strategies 
 +│ 
 +│       └── jwt.strategy.spec.ts 
 +│ 
 +└── presentation 
 +    │ 
 +    └── guards
  
-JwtStrategy+        ├── roles.guard.spec.ts 
 +        └── permissions.guard.spec.ts 
 +</code>
  
-RolesGuard+----
  
-PermissionsGuard+====== Étape 1 — Configuration Jest ====== 
 + 
 +===== Vérifier ===== 
 + 
 +<code bash> 
 +npm install -D 
 + 
 +jest 
 + 
 +ts-jest 
 + 
 +@types/jest 
 +</code> 
 + 
 +---- 
 + 
 +===== Vérifier ===== 
 + 
 +<code> 
 +apps/api/jest.config.ts 
 +</code> 
 + 
 +---- 
 + 
 +===== Coverage ===== 
 + 
 +<code ts> 
 +coverageThreshold:
 + 
 +  global: { 
 + 
 +    branches: 80, 
 + 
 +    functions: 80, 
 + 
 +    lines: 80, 
 + 
 +    statements: 80 
 +  } 
 +
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 2 — PasswordService ====== 
 + 
 +===== Fichier ===== 
 + 
 +<code> 
 +password.service.spec.ts 
 +</code> 
 + 
 +---- 
 + 
 +===== Test hash ===== 
 + 
 +<code ts> 
 +describe( 
 +  'PasswordService', 
 +  () => { 
 + 
 +    it( 
 +      'should hash password', 
 +      async () => { 
 + 
 +        const hash = 
 + 
 +          await service.hash( 
 +            'Password123!' 
 +          ); 
 + 
 +        expect(hash) 
 +          .toBeDefined(); 
 + 
 +        expect(hash) 
 +          .not 
 +          .toEqual( 
 +            'Password123!' 
 +          ); 
 +      } 
 +    ); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +===== Test compare ===== 
 + 
 +<code ts> 
 +it( 
 +  'should compare password', 
 +  async () => { 
 + 
 +    const hash = 
 + 
 +      await service.hash( 
 +        'Password123!' 
 +      ); 
 + 
 +    const result = 
 + 
 +      await service.compare( 
 + 
 +        'Password123!', 
 + 
 +        hash 
 +      ); 
 + 
 +    expect(result) 
 +      .toBe(true); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 3 — TokenService ====== 
 + 
 +===== Fichier ===== 
 + 
 +<code> 
 +token.service.spec.ts 
 +</code> 
 + 
 +---- 
 + 
 +===== Test Access Token ===== 
 + 
 +<code ts> 
 +it( 
 +  'should generate access token', 
 +  async () => { 
 + 
 +    const token = 
 + 
 +      await service 
 +        .generateAccessToken({ 
 + 
 +          sub: '1', 
 + 
 +          tenantId: '1', 
 + 
 +          email: 
 +            'test@test.com' 
 +        }); 
 + 
 +    expect(token) 
 +      .toBeDefined(); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +===== Test Refresh Token ===== 
 + 
 +<code ts> 
 +it( 
 +  'should generate refresh token', 
 +  async () => { 
 + 
 +    const token = 
 + 
 +      await service 
 +        .generateRefreshToken(); 
 + 
 +    expect(token) 
 +      .toBeDefined(); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 4 — Mock Prisma ====== 
 + 
 +===== Créer ===== 
 + 
 +<code> 
 +test/mocks/prisma.mock.ts 
 +</code> 
 + 
 +---- 
 + 
 +===== Contenu ===== 
 + 
 +<code ts> 
 +export const prismaMock = { 
 + 
 +  user: { 
 + 
 +    findUnique: jest.fn(), 
 + 
 +    create: jest.fn(), 
 + 
 +    update: jest.fn() 
 +  }, 
 + 
 +  role: { 
 + 
 +    findFirst: jest.fn() 
 +  }, 
 + 
 +  userRole: { 
 + 
 +    create: jest.fn() 
 +  }, 
 + 
 +  refreshToken:
 + 
 +    create: jest.fn(), 
 + 
 +    findMany: jest.fn(), 
 + 
 +    update: jest.fn(), 
 + 
 +    updateMany: jest.fn() 
 +  }, 
 + 
 +  session: { 
 + 
 +    create: jest.fn(), 
 + 
 +    updateMany: jest.fn() 
 +  }, 
 + 
 +  tenant: { 
 + 
 +    findUnique: jest.fn() 
 +  } 
 +}; 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 5 — AuthService ====== 
 + 
 +===== Fichier ===== 
 + 
 +<code> 
 +auth.service.spec.ts 
 +</code> 
 + 
 +---- 
 + 
 +===== Setup ===== 
 + 
 +<code ts> 
 +beforeEach( 
 +  async () => { 
 + 
 +    const module = 
 + 
 +      await Test 
 +        .createTestingModule({ 
 + 
 +          providers: [ 
 + 
 +            AuthService, 
 + 
 +            { 
 +              provide: 
 +                PrismaService, 
 + 
 +              useValue: 
 +                prismaMock 
 +            } 
 +          ] 
 +        }) 
 +        .compile(); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 6 — Test Register ====== 
 + 
 +===== Cas nominal ===== 
 + 
 +<code ts> 
 +it( 
 +  'should register user', 
 +  async () => { 
 + 
 +    prismaMock.user 
 +      .findUnique 
 +      .mockResolvedValue( 
 +        null 
 +      ); 
 + 
 +    const result = 
 + 
 +      await service.register({ 
 + 
 +        email: 
 +          'john@test.com', 
 + 
 +        password: 
 +          'Password123!', 
 + 
 +        firstName: 
 +          'John', 
 + 
 +        lastName: 
 +          'Doe' 
 +      }); 
 + 
 +    expect(result) 
 +      .toBeDefined(); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 7 — Email déjà utilisé ====== 
 + 
 +===== Test ===== 
 + 
 +<code ts> 
 +it( 
 +  'should throw conflict', 
 +  async () => { 
 + 
 +    prismaMock.user 
 +      .findUnique 
 +      .mockResolvedValue({}); 
 + 
 +    await expect( 
 + 
 +      service.register( 
 +        dto 
 +      ) 
 + 
 +    ).rejects.toThrow( 
 +      ConflictException 
 +    ); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 8 — Test Login ====== 
 + 
 +===== Succès ===== 
 + 
 +<code ts> 
 +it( 
 +  'should login user', 
 +  async () => { 
 + 
 +    const result = 
 + 
 +      await service.login( 
 +        dto, 
 +        session 
 +      ); 
 + 
 +    expect(result) 
 +      .toBeDefined(); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +===== Mot de passe invalide ===== 
 + 
 +<code ts> 
 +it( 
 +  'should reject login', 
 +  async () => { 
 + 
 +    await expect( 
 + 
 +      service.login( 
 +        dto, 
 +        session 
 +      ) 
 + 
 +    ).rejects.toThrow( 
 +      UnauthorizedException 
 +    ); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 9 — Test Refresh ====== 
 + 
 +===== Succès ===== 
 + 
 +<code ts> 
 +it( 
 +  'should refresh token', 
 +  async () => { 
 + 
 +    const result = 
 + 
 +      await service.refresh({ 
 + 
 +        refreshToken: 
 +          'token' 
 +      }); 
 + 
 +    expect(result) 
 +      .toBeDefined(); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +===== Échec ===== 
 + 
 +<code ts> 
 +it( 
 +  'should reject invalid token', 
 +  async () => { 
 + 
 +    await expect( 
 + 
 +      service.refresh({ 
 + 
 +        refreshToken: 
 +          'bad-token' 
 +      }) 
 + 
 +    ).rejects.toThrow( 
 +      UnauthorizedException 
 +    ); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 10 — Test Logout ====== 
 + 
 +===== Succès ===== 
 + 
 +<code ts> 
 +it( 
 +  'should logout user', 
 +  async () => { 
 + 
 +    const result = 
 + 
 +      await service.logout( 
 +        'user-id' 
 +      ); 
 + 
 +    expect( 
 +      result.success 
 +    ).toBe(true); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 11 — Test Current User ====== 
 + 
 +===== Succès ===== 
 + 
 +<code ts> 
 +it( 
 +  'should return current user', 
 +  async () => { 
 + 
 +    const result = 
 + 
 +      await service.me( 
 +        'user-id' 
 +      ); 
 + 
 +    expect(result) 
 +      .toBeDefined(); 
 + 
 +    expect( 
 +      result.email 
 +    ).toEqual( 
 +      'admin@platform.local' 
 +    ); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 12 — JwtStrategy ====== 
 + 
 +===== Fichier ===== 
 + 
 +<code> 
 +jwt.strategy.spec.ts 
 +</code> 
 + 
 +---- 
 + 
 +===== Test ===== 
 + 
 +<code ts> 
 +it( 
 +  'should validate payload', 
 +  async () => { 
 + 
 +    const result = 
 + 
 +      await strategy.validate({ 
 + 
 +        sub: '1', 
 + 
 +        email: 
 +          'test@test.com', 
 + 
 +        tenantId: 
 +          'tenant' 
 +      }); 
 + 
 +    expect( 
 +      result.id 
 +    ).toEqual('1'); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 13 — RolesGuard ====== 
 + 
 +===== Fichier ===== 
 + 
 +<code> 
 +roles.guard.spec.ts 
 +</code> 
 + 
 +---- 
 + 
 +===== Autorisé ===== 
 + 
 +<code ts> 
 +it( 
 +  'should allow role', 
 +  () => { 
 + 
 +    expect( 
 +      guard.canActivate( 
 +        context 
 +      ) 
 +    ).toBe(true); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +===== Refusé ===== 
 + 
 +<code ts> 
 +it( 
 +  'should reject role', 
 +  () => { 
 + 
 +    expect( 
 +      guard.canActivate( 
 +        context 
 +      ) 
 +    ).toBe(false); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 14 — PermissionsGuard ====== 
 + 
 +===== Fichier ===== 
 + 
 +<code> 
 +permissions.guard.spec.ts 
 +</code> 
 + 
 +---- 
 + 
 +===== Autorisé ===== 
 + 
 +<code ts> 
 +it( 
 +  'should allow permission', 
 +  () => { 
 + 
 +    expect( 
 +      guard.canActivate( 
 +        context 
 +      ) 
 +    ).toBe(true); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +===== Refusé ===== 
 + 
 +<code ts> 
 +it( 
 +  'should reject permission', 
 +  () => { 
 + 
 +    expect( 
 +      guard.canActivate( 
 +        context 
 +      ) 
 +    ).toBe(false); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 15 — Exécution ====== 
 + 
 +===== Lancer ===== 
 + 
 +<code bash> 
 +nx test api 
 +</code> 
 + 
 +---- 
 + 
 +===== Avec coverage ===== 
 + 
 +<code bash> 
 +nx test api \ 
 +--coverage 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 16 — Rapport ====== 
 + 
 +===== Généré dans ===== 
 + 
 +<code> 
 +coverage/apps/api 
 +</code> 
 + 
 +---- 
 + 
 +===== Ouvrir ===== 
 + 
 +<code> 
 +coverage/apps/api/lcov-report/index.html 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 17 — Objectifs ====== 
 + 
 +===== PasswordService ===== 
 + 
 +<code> 
 +100 % 
 +</code> 
 + 
 +---- 
 + 
 +===== TokenService ===== 
 + 
 +<code> 
 +100 % 
 +</code> 
 + 
 +---- 
 + 
 +===== RolesGuard ===== 
 + 
 +<code> 
 +100 % 
 +</code> 
 + 
 +---- 
 + 
 +===== PermissionsGuard ===== 
 + 
 +<code> 
 +100 % 
 +</code> 
 + 
 +---- 
 + 
 +===== AuthService ===== 
 + 
 +<code> 
 +> 80 % 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 18 — Ajout CI ====== 
 + 
 +===== Workflow ===== 
 + 
 +<code> 
 +.github/workflows/ci.yml 
 +</code> 
 + 
 +---- 
 + 
 +===== Ajouter ===== 
 + 
 +<code yaml> 
 +- name: Run Tests 
 + 
 +  run: nx test api 
 +</code> 
 + 
 +---- 
 + 
 +===== Coverage ===== 
 + 
 +<code yaml> 
 +- name: Coverage 
 + 
 +  run: nx test api --coverage 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 19 — Badge Coverage ====== 
 + 
 +===== README ===== 
 + 
 +<code markdown> 
 +![Coverage] 
 +</code> 
 + 
 +---- 
 + 
 +====== Définition de terminé ====== 
 + 
 +Le Sprint 1-C.2 est terminé lorsque : 
 + 
 +<code> 
 +✓ PasswordService testé 
 + 
 +✓ TokenService testé 
 + 
 +✓ AuthService testé 
 + 
 +✓ JwtStrategy testée 
 + 
 +✓ RolesGuard testé 
 + 
 +✓ PermissionsGuard testé 
 + 
 +✓ Coverage > 80 % 
 + 
 +✓ CI verte 
 +</code> 
 + 
 +---- 
 + 
 +====== Livrables ====== 
 + 
 +<code> 
 +auth.service.spec.ts 
 + 
 +password.service.spec.ts 
 + 
 +token.service.spec.ts 
 + 
 +jwt.strategy.spec.ts 
 + 
 +roles.guard.spec.ts 
 + 
 +permissions.guard.spec.ts 
 + 
 +coverage report 
 +</code> 
 + 
 +---- 
 + 
 +====== Sprint 1-C.3 — Tests E2E Authentification ====== 
 + 
 +===== Objectif ===== 
 + 
 +Valider le fonctionnement réel de toute la chaîne d'authentification. 
 + 
 +À l'issue de cette étape : 
 + 
 +<code> 
 +✓ Register testé 
 + 
 +✓ Login testé 
 + 
 +✓ Current User testé 
 + 
 +✓ Refresh testé 
 + 
 +✓ Logout testé 
 + 
 +✓ RBAC testé 
 + 
 +✓ PostgreSQL réel 
 + 
 +✓ JWT réel 
 + 
 +✓ Prisma réel 
 +</code> 
 + 
 +Ces tests constituent la première validation métier complète de la plateforme. 
 + 
 +---- 
 + 
 +====== Architecture E2E ====== 
 + 
 +===== Environnement ===== 
 + 
 +<code> 
 +NestJS 
 + 
 +↓ 
 + 
 +Prisma 
 + 
 +↓ 
 + 
 +PostgreSQL 
 + 
 +↓ 
 + 
 +JWT 
 + 
 +↓ 
 + 
 +API réelle 
 +</code> 
 + 
 +---- 
 + 
 +===== Pas de Mock ===== 
 + 
 +Contrairement aux tests unitaires : 
 + 
 +<code> 
 +Aucun Mock 
 + 
 +Aucun Fake Service 
 + 
 +Base PostgreSQL réelle 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 1 — Création du projet E2E ====== 
 + 
 +===== Vérifier ===== 
 + 
 +<code> 
 +apps/api-e2e 
 +</code> 
 + 
 +---- 
 + 
 +===== Structure ===== 
 + 
 +<code> 
 +apps 
 + 
 +├── api 
 + 
 +└── api-e2e 
 + 
 +    ├── src 
 + 
 +    │   └── auth 
 + 
 +    │       └── auth.e2e-spec.ts 
 + 
 +    │ 
 +    ├── jest.config.ts 
 + 
 +    └── project.json 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 2 — Installation ====== 
 + 
 +===== Vérifier ===== 
 + 
 +<code bash> 
 +npm install -D supertest 
 + 
 +npm install -D @types/supertest 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 3 — Base de test ====== 
 + 
 +===== Créer ===== 
 + 
 +<code> 
 +.env.test 
 +</code> 
 + 
 +---- 
 + 
 +===== Contenu ===== 
 + 
 +<code> 
 +DATABASE_URL= 
 + 
 +postgresql://postgres: 
 +postgres@localhost:5432/ 
 +rental_platform_test 
 + 
 +JWT_SECRET=test-secret 
 + 
 +JWT_EXPIRES_IN=15m 
 + 
 +REFRESH_SECRET=test-refresh 
 + 
 +REFRESH_EXPIRES_IN=30d 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 4 — Création DB Test ====== 
 + 
 +===== PostgreSQL ===== 
 + 
 +<code sql> 
 +CREATE DATABASE 
 +rental_platform_test; 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 5 — Migration ====== 
 + 
 +===== Exécuter ===== 
 + 
 +<code bash> 
 +DATABASE_URL=... 
 + 
 +npx prisma migrate deploy 
 +</code> 
 + 
 +---- 
 + 
 +===== Seed ===== 
 + 
 +<code bash> 
 +DATABASE_URL=... 
 + 
 +npx prisma db seed 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 6 — Bootstrap E2E ====== 
 + 
 +===== Créer ===== 
 + 
 +<code> 
 +apps/api-e2e/src/auth/auth.e2e-spec.ts 
 +</code> 
 + 
 +---- 
 + 
 +===== Base ===== 
 + 
 +<code ts> 
 +describe( 
 +  'Auth E2E', 
 +  () => { 
 + 
 +    let app: 
 +      INestApplication; 
 + 
 +    beforeAll( 
 +      async () => { 
 + 
 +        const moduleRef = 
 + 
 +          await Test 
 +            .createTestingModule({ 
 + 
 +              imports: [ 
 + 
 +                AppModule 
 +              ] 
 +            }) 
 +            .compile(); 
 + 
 +        app = 
 +          moduleRef 
 +            .createNestApplication(); 
 + 
 +        await app.init(); 
 +      } 
 +    ); 
 + 
 +    afterAll( 
 +      async () => { 
 + 
 +        await app.close(); 
 +      } 
 +    ); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 7 — Variables de scénario ====== 
 + 
 +===== Ajouter ===== 
 + 
 +<code ts> 
 +let accessToken: 
 +  string; 
 + 
 +let refreshToken: 
 +  string; 
 + 
 +let userId: 
 +  string; 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 8 — Test Register ====== 
 + 
 +===== Cas nominal ===== 
 + 
 +<code ts> 
 +it( 
 +  'should register user', 
 +  async () => { 
 + 
 +    const response = 
 + 
 +      await request( 
 + 
 +        app.getHttpServer() 
 +      ) 
 + 
 +      .post( 
 +        '/auth/register' 
 +      ) 
 + 
 +      .send({ 
 + 
 +        email: 
 +          'john@test.com', 
 + 
 +        password: 
 +          'Password123!', 
 + 
 +        firstName: 
 +          'John', 
 + 
 +        lastName: 
 +          'Doe' 
 +      }) 
 + 
 +      .expect(201); 
 + 
 +    expect( 
 + 
 +      response.body 
 +        .accessToken 
 + 
 +    ).toBeDefined(); 
 + 
 +    userId = 
 + 
 +      response.body 
 +        .user.id; 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 9 — Test Login ====== 
 + 
 +===== Cas nominal ===== 
 + 
 +<code ts> 
 +it( 
 +  'should login', 
 +  async () => { 
 + 
 +    const response = 
 + 
 +      await request( 
 + 
 +        app.getHttpServer() 
 +      ) 
 + 
 +      .post( 
 +        '/auth/login' 
 +      ) 
 + 
 +      .send({ 
 + 
 +        email: 
 +          'john@test.com', 
 + 
 +        password: 
 +          'Password123!' 
 +      }) 
 + 
 +      .expect(201); 
 + 
 +    accessToken = 
 + 
 +      response.body 
 +        .accessToken; 
 + 
 +    refreshToken = 
 + 
 +      response.body 
 +        .refreshToken; 
 + 
 +    expect( 
 +      accessToken 
 +    ).toBeDefined(); 
 + 
 +    expect( 
 +      refreshToken 
 +    ).toBeDefined(); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 10 — Test Current User ====== 
 + 
 +===== Endpoint ===== 
 + 
 +<code> 
 +GET /auth/me 
 +</code> 
 + 
 +---- 
 + 
 +===== Test ===== 
 + 
 +<code ts> 
 +it( 
 +  'should return current user', 
 +  async () => { 
 + 
 +    const response = 
 + 
 +      await request( 
 + 
 +        app.getHttpServer() 
 +      ) 
 + 
 +      .get( 
 +        '/auth/me' 
 +      ) 
 + 
 +      .set( 
 + 
 +        'Authorization', 
 + 
 +        `Bearer ${accessToken}` 
 +      ) 
 + 
 +      .expect(200); 
 + 
 +    expect( 
 + 
 +      response.body.email 
 + 
 +    ).toEqual( 
 +      'john@test.com' 
 +    ); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 11 — Test RBAC ====== 
 + 
 +===== Endpoint ===== 
 + 
 +<code> 
 +GET /auth/admin 
 +</code> 
 + 
 +---- 
 + 
 +===== CUSTOMER ===== 
 + 
 +<code ts> 
 +it( 
 +  'should reject customer', 
 +  async () => { 
 + 
 +    await request( 
 + 
 +      app.getHttpServer() 
 +    ) 
 + 
 +    .get( 
 +      '/auth/admin' 
 +    ) 
 + 
 +    .set( 
 + 
 +      'Authorization', 
 + 
 +      `Bearer ${accessToken}` 
 +    ) 
 + 
 +    .expect(403); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 12 — Test Refresh ====== 
 + 
 +===== Endpoint ===== 
 + 
 +<code> 
 +POST /auth/refresh 
 +</code> 
 + 
 +---- 
 + 
 +===== Test ===== 
 + 
 +<code ts> 
 +it( 
 +  'should refresh token', 
 +  async () => { 
 + 
 +    const response = 
 + 
 +      await request( 
 + 
 +        app.getHttpServer() 
 +      ) 
 + 
 +      .post( 
 +        '/auth/refresh' 
 +      ) 
 + 
 +      .send({ 
 + 
 +        refreshToken 
 +      }) 
 + 
 +      .expect(201); 
 + 
 +    accessToken = 
 + 
 +      response.body 
 +        .accessToken; 
 + 
 +    refreshToken = 
 + 
 +      response.body 
 +        .refreshToken; 
 + 
 +    expect( 
 +      accessToken 
 +    ).toBeDefined(); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 13 — Test Logout ====== 
 + 
 +===== Endpoint ===== 
 + 
 +<code> 
 +POST /auth/logout 
 +</code> 
 + 
 +---- 
 + 
 +===== Test ===== 
 + 
 +<code ts> 
 +it( 
 +  'should logout', 
 +  async () => { 
 + 
 +    await request( 
 + 
 +      app.getHttpServer() 
 +    ) 
 + 
 +    .post( 
 +      '/auth/logout' 
 +    ) 
 + 
 +    .set( 
 + 
 +      'Authorization', 
 + 
 +      `Bearer ${accessToken}` 
 +    ) 
 + 
 +    .expect(201); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 14 — Vérification sécurité ====== 
 + 
 +===== Ancien Refresh Token ===== 
 + 
 +Ne doit plus fonctionner. 
 + 
 +---- 
 + 
 +===== Test ===== 
 + 
 +<code ts> 
 +it( 
 +  'should reject old refresh token', 
 +  async () => { 
 + 
 +    await request( 
 + 
 +      app.getHttpServer() 
 +    ) 
 + 
 +    .post( 
 +      '/auth/refresh' 
 +    ) 
 + 
 +    .send({ 
 + 
 +      refreshToken 
 +    }) 
 + 
 +    .expect(401); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 15 — Cas erreur Login ====== 
 + 
 +===== Mauvais mot de passe ===== 
 + 
 +<code ts> 
 +it( 
 +  'should reject invalid password', 
 +  async () => { 
 + 
 +    await request( 
 + 
 +      app.getHttpServer() 
 +    ) 
 + 
 +    .post( 
 +      '/auth/login' 
 +    ) 
 + 
 +    .send({ 
 + 
 +      email: 
 +        'john@test.com', 
 + 
 +      password: 
 +        'BAD_PASSWORD' 
 +    }) 
 + 
 +    .expect(401); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 16 — Cas erreur Register ====== 
 + 
 +===== Email existant ===== 
 + 
 +<code ts> 
 +it( 
 +  'should reject duplicate email', 
 +  async () => { 
 + 
 +    await request( 
 + 
 +      app.getHttpServer() 
 +    ) 
 + 
 +    .post( 
 +      '/auth/register' 
 +    ) 
 + 
 +    .send({ 
 + 
 +      email: 
 +        'john@test.com', 
 + 
 +      password: 
 +        'Password123!', 
 + 
 +      firstName: 
 +        'John', 
 + 
 +      lastName: 
 +        'Doe' 
 +    }) 
 + 
 +    .expect(409); 
 +  } 
 +); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 17 — Nettoyage ====== 
 + 
 +===== Supprimer utilisateur ===== 
 + 
 +Dans : 
 + 
 +<code> 
 +afterAll() 
 +</code> 
 + 
 +---- 
 + 
 +===== Exemple ===== 
 + 
 +<code ts> 
 +await prisma.user.deleteMany({ 
 + 
 +  where: { 
 + 
 +    email: 
 +      'john@test.com' 
 +  } 
 +}); 
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 18 — Configuration NX ====== 
 + 
 +===== project.json ===== 
 + 
 +Ajouter : 
 + 
 +<code json> 
 +
 +  "targets":
 + 
 +    "e2e":
 + 
 +      "executor": 
 + 
 +      "@nx/jest:jest" 
 +    } 
 +  } 
 +
 +</code> 
 + 
 +---- 
 + 
 +====== Étape 19 — Exécution ====== 
 + 
 +===== Lancer ===== 
 + 
 +<code bash> 
 +nx e2e api-e2e 
 +</code> 
 + 
 +---- 
 + 
 +===== Résultat attendu ===== 
 + 
 +<code> 
 +PASS
  
 Register Register
 +
 +PASS
  
 Login Login
 +
 +PASS
 +
 +Current User
 +
 +PASS
  
 Refresh Refresh
 +
 +PASS
  
 Logout Logout
  
-Current User+PASS 
 + 
 +RBAC
 </code> </code>
  
-avec une couverture minimale de :+---- 
 + 
 +====== Étape 20 — Intégration CI ====== 
 + 
 +===== Workflow =====
  
 <code> <code>
-80 %+.github/workflows/ci.yml
 </code> </code>
  
 +----
 +
 +===== Ajouter =====
 +
 +<code yaml>
 +- name: E2E Tests
 +
 +  run:
 +
 +    nx e2e api-e2e
 +</code>
 +
 +----
 +
 +====== Étape 21 — Pipeline qualité ======
 +
 +===== Ordre =====
 +
 +<code>
 +Lint
 +
 +
 +
 +Build
 +
 +
 +
 +Unit Tests
 +
 +
 +
 +E2E Tests
 +
 +
 +
 +Docker Build
 +</code>
 +
 +----
 +
 +====== Définition de terminé ======
 +
 +Le Sprint 1-C.3 est terminé lorsque :
 +
 +<code>
 +✓ Register testé
 +
 +✓ Login testé
 +
 +✓ Current User testé
 +
 +✓ Refresh testé
 +
 +✓ Logout testé
 +
 +✓ RBAC testé
 +
 +✓ PostgreSQL réel
 +
 +✓ JWT réel
 +
 +✓ CI verte
 +</code>
 +
 +----
 +
 +====== Livrables ======
 +
 +<code>
 +auth.e2e-spec.ts
 +
 +.env.test
 +
 +Configuration E2E
 +
 +Pipeline CI
 +</code>
 +
 +----
 +
 +====== Bilan Sprint 1 ======
 +
 +Le Sprint 1 est considéré terminé lorsque :
 +
 +<code>
 +✓ AuthModule opérationnel
 +
 +✓ Register opérationnel
 +
 +✓ Login opérationnel
 +
 +✓ Refresh opérationnel
 +
 +✓ Logout opérationnel
 +
 +✓ Current User opérationnel
 +
 +✓ RBAC opérationnel
 +
 +✓ Tests unitaires verts
 +
 +✓ Tests E2E verts
 +
 +✓ Swagger documenté
 +
 +✓ CI verte
 +</code>
 +
 +----
 +
 +====== Étape suivante ======
 +
 +===== Sprint 2 — Gestion des Utilisateurs =====
 +
 +Objectif :
 +
 +Construire le premier domaine métier protégé par le RBAC :
 +
 +<code>
 +User
 +
 +Profile
 +
 +Address
 +
 +Preferences
 +
 +NotificationSettings
 +
 +UserSession
 +
 +ConnectionHistory
 +</code>
 +
 +avec :
 +
 +<code>
 +CRUD Utilisateurs
 +
 +Gestion Profil
 +
 +Gestion Préférences
 +
 +Historique Connexions
 +
 +Administration Utilisateurs
 +</code>
ujusum/3-codage/2-sprints/1-sprint-1.1780876719.txt.gz · Dernière modification : 2026/06/08 01:58 de admin

DokuWiki Appliance - Powered by TurnKey Linux