Răsfoiți Sursa

(feat) mise en place des tests unitaire et e2e avec jest

Brin AMONKONAN 1 an în urmă
părinte
comite
ebbbb79ff2

+ 9 - 0
node_app/jest-e2e.config.js

@@ -0,0 +1,9 @@
+module.exports = {
+  moduleFileExtensions: ['js', 'json', 'ts'],
+  rootDir: '.',
+  testEnvironment: 'node',
+  testRegex: '.e2e-spec.ts$',
+  transform: {
+    '^.+\\.(t|j)s$': 'ts-jest',
+  },
+};

+ 11 - 0
node_app/jest.config.js

@@ -0,0 +1,11 @@
+module.exports = {
+  moduleFileExtensions: ['js', 'json', 'ts'],
+  rootDir: 'src',
+  testRegex: '.*\\.spec\\.ts$',
+  transform: {
+    '^.+\\.(t|j)s$': 'ts-jest',
+  },
+  collectCoverageFrom: ['**/*.(t|j)s'],
+  coverageDirectory: '../coverage',
+  testEnvironment: 'node',
+};

+ 6 - 6
node_app/package.json

@@ -14,12 +14,12 @@
     "start:debug": "nest start --debug --watch",
     "start:prod": "node dist/main",
     "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
-    "test": "jest",
-    "test:watch": "jest --watch",
-    "test:cov": "jest --coverage",
-    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
-    "test:e2e": "jest --config ./test/jest-e2e.json",
-    "mock": "ts-node src/mocks/run-mock.ts"
+    "test": "jest --config=jest.config.js",
+    "test:watch": "jest --watch --config=jest.config.js",
+    "test:cov": "jest --coverage --config=jest.config.js",
+    "test:e2e": "jest --config ./jest-e2e.config.js",
+    "test:debug:e2e": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
+    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand"
   },
   "dependencies": {
     "@fastify/static": "^8.1.1",

+ 113 - 0
node_app/src/infrastructure/services/auth.service.spec.ts

@@ -0,0 +1,113 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { JwtService } from '@nestjs/jwt';
+import { AuthService } from './auth.service';
+import { MockAuthService } from './mocks/auth.service.mock';
+
+describe('AuthService', () => {
+  let service: AuthService;
+  let jwtService: JwtService;
+  let userRepository: any;
+  let hasherService: any;
+
+  beforeEach(async () => {
+    // Créer des mocks pour les dépendances
+    userRepository = {
+      findByEmail: jest.fn(),
+    };
+
+    hasherService = {
+      compare: jest.fn(),
+    };
+
+    jwtService = {
+      sign: jest.fn(),
+      verify: jest.fn(),
+    } as unknown as JwtService;
+
+    const module: TestingModule = await Test.createTestingModule({
+      providers: [
+        AuthService,
+        {
+          provide: 'IUserRepository',
+          useValue: userRepository,
+        },
+        {
+          provide: 'IHasherService',
+          useValue: hasherService,
+        },
+        {
+          provide: JwtService,
+          useValue: jwtService,
+        },
+      ],
+    }).compile();
+
+    service = module.get<AuthService>(AuthService);
+  });
+
+  it('should be defined', () => {
+    expect(service).toBeDefined();
+  });
+
+  describe('validateUser', () => {
+    it('should return user object when credentials are valid', async () => {
+      // Arrange
+      const mockUser = {
+        id: 1,
+        email: 'test@example.com',
+        password: 'hashedPassword',
+        name: 'Test User',
+      };
+      
+      userRepository.findByEmail.mockResolvedValue(mockUser);
+      hasherService.compare.mockResolvedValue(true);
+
+      // Act
+      const result = await service.validateUser('test@example.com', 'password');
+
+      // Assert
+      expect(result).toEqual({
+        id: 1,
+        email: 'test@example.com',
+        name: 'Test User',
+      });
+      expect(userRepository.findByEmail).toHaveBeenCalledWith('test@example.com');
+      expect(hasherService.compare).toHaveBeenCalledWith('password', 'hashedPassword');
+    });
+
+    it('should return null when user is not found', async () => {
+      // Arrange
+      userRepository.findByEmail.mockResolvedValue(null);
+
+      // Act
+      const result = await service.validateUser('nonexistent@example.com', 'password');
+
+      // Assert
+      expect(result).toBeNull();
+      expect(userRepository.findByEmail).toHaveBeenCalledWith('nonexistent@example.com');
+      expect(hasherService.compare).not.toHaveBeenCalled();
+    });
+  });
+
+  describe('generateToken', () => {
+    it('should generate a JWT token', async () => {
+      // Arrange
+      const mockUser = {
+        id: 1,
+        email: 'test@example.com',
+      };
+      const mockToken = 'jwt-token';
+      jwtService.sign.mockReturnValue(mockToken);
+
+      // Act
+      const result = await service.generateToken(mockUser);
+
+      // Assert
+      expect(result).toBe(mockToken);
+      expect(jwtService.sign).toHaveBeenCalledWith({
+        email: 'test@example.com',
+        sub: 1,
+      });
+    });
+  });
+});

+ 39 - 0
node_app/src/infrastructure/services/mocks/auth.service.mock.ts

@@ -0,0 +1,39 @@
+import { Injectable } from '@nestjs/common';
+import { IAuthService } from '../../../application/interfaces/i-auth.service';
+
+@Injectable()
+export class MockAuthService implements IAuthService {
+  // Utilisateur fictif pour les tests
+  private readonly mockUser = {
+    id: 1,
+    email: 'test@example.com',
+    name: 'Utilisateur Test',
+    address: '123 Rue de Test',
+    telephone: '+33123456789',
+    otherName: 'Test',
+  };
+
+  // Token fictif pour les tests
+  private readonly mockToken = 'mock-jwt-token-for-testing-purposes';
+
+  async validateUser(email: string, password: string): Promise<any> {
+    // Simuler la validation d'un utilisateur
+    if (email === 'test@example.com' && password === 'password') {
+      return this.mockUser;
+    }
+    return null;
+  }
+
+  async generateToken(user: any): Promise<string> {
+    // Retourner un token fictif
+    return this.mockToken;
+  }
+
+  async verifyToken(token: string): Promise<any> {
+    // Simuler la vérification d'un token
+    if (token === this.mockToken) {
+      return { email: this.mockUser.email, sub: this.mockUser.id };
+    }
+    throw new Error('Token invalide');
+  }
+}

+ 53 - 0
node_app/src/infrastructure/services/mocks/user.repository.mock.ts

@@ -0,0 +1,53 @@
+import { Injectable } from '@nestjs/common';
+import { IUserRepository } from '../../../application/interfaces/i-user.repository';
+import { User } from '../../../domain/entities/user.entity';
+
+@Injectable()
+export class MockUserRepository implements IUserRepository {
+  private users: User[] = [
+    {
+      id: 1,
+      name: 'Utilisateur Test',
+      address: '123 Rue de Test',
+      telephone: '+33123456789',
+      otherName: 'Test',
+      email: 'test@example.com',
+      password: 'hashedPassword',
+    },
+  ];
+
+  async create(user: User): Promise<User> {
+    const newUser = { ...user, id: this.users.length + 1 };
+    this.users.push(newUser);
+    return newUser;
+  }
+
+  async findById(id: number): Promise<User | null> {
+    return this.users.find((user) => user.id === id) || null;
+  }
+
+  async findByEmail(email: string): Promise<User | null> {
+    return this.users.find((user) => user.email === email) || null;
+  }
+
+  async findAll(): Promise<User[]> {
+    return [...this.users];
+  }
+
+  async update(id: number, user: Partial<User>): Promise<User | null> {
+    const index = this.users.findIndex((u) => u.id === id);
+    if (index === -1) return null;
+    
+    this.users[index] = { ...this.users[index], ...user };
+    return this.users[index];
+  }
+
+  async delete(id: number): Promise<User | null> {
+    const index = this.users.findIndex((u) => u.id === id);
+    if (index === -1) return null;
+    
+    const deletedUser = this.users[index];
+    this.users.splice(index, 1);
+    return deletedUser;
+  }
+}

+ 44 - 0
node_app/test/e2e/company.e2e-spec.ts

@@ -0,0 +1,44 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { INestApplication } from '@nestjs/common';
+import * as request from 'supertest';
+import { AppModule } from '../../src/app.module';
+import { ValidationPipe } from '@nestjs/common';
+import { TransformInterceptor } from '../../src/presentation/interceptors/transform.interceptor';
+
+describe('CompanyController (e2e)', () => {
+  let app: INestApplication;
+
+  beforeEach(async () => {
+    const moduleFixture: TestingModule = await Test.createTestingModule({
+      imports: [AppModule],
+    }).compile();
+
+    app = moduleFixture.createNestApplication();
+    
+    // Appliquer les mêmes pipes et intercepteurs que dans l'application principale
+    app.useGlobalPipes(
+      new ValidationPipe({
+        whitelist: true,
+        transform: true,
+      }),
+    );
+    app.useGlobalInterceptors(new TransformInterceptor());
+    
+    await app.init();
+  });
+
+  afterEach(async () => {
+    await app.close();
+  });
+
+  it('/api/companies (GET)', () => {
+    return request(app.getHttpServer())
+      .get('/api/companies')
+      .expect(200)
+      .expect((res) => {
+        expect(Array.isArray(res.body)).toBe(true);
+      });
+  });
+
+  // Ajoutez d'autres tests e2e selon vos besoins
+});