Skip to content

Commit

Permalink
[API] Add unit testing for feature Images
Browse files Browse the repository at this point in the history
  • Loading branch information
TarikNasraoui committed Sep 30, 2024
1 parent fb3136f commit 2912949
Show file tree
Hide file tree
Showing 7 changed files with 349 additions and 10 deletions.
20 changes: 20 additions & 0 deletions backend/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/** @type {import('ts-jest').JestConfigWithTsJest} **/
module.exports = {
preset: 'ts-jest', // Utiliser le preset ts-jest
testEnvironment: 'node', // Environnement de test
transform: {
'^.+\\.tsx?$': 'ts-jest', // Transformation pour TypeScript
},
moduleFileExtensions: ['ts', 'tsx', 'js'], // Extensions reconnues
testMatch: ['**/tests/**/*.test.ts'], // Chemin vers les fichiers de test
clearMocks: true, // Nettoyer les mocks après chaque test
coverageDirectory: 'coverage', // Dossier pour la couverture des tests
collectCoverage: false, // Activer la couverture
collectCoverageFrom: [
'src/**/*.ts', // Cibler les fichiers TS pour la couverture
'!src/**/*.d.ts', // Exclure les fichiers de définition de types
],
moduleNameMapper: {
'^src/(.*)$': '<rootDir>/src/$1', // Mapper les alias pour src
},
};
5 changes: 4 additions & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
"dev": "nodemon server.ts",
"start": "ts-node server.ts",
"migrate": "knex migrate:latest",
"seed": "knex seed:run"
"seed": "knex seed:run",
"test": "jest"
},
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/jest": "^29.5.13",
"@types/node": "^20.10.6",
"@typescript-eslint/eslint-plugin": "^6.17.0",
"@typescript-eslint/parser": "^6.17.0",
Expand All @@ -21,6 +23,7 @@
"eslint-plugin-n": "^16.6.1",
"eslint-plugin-promise": "^6.1.1",
"nodemon": "^3.0.2",
"ts-jest": "^29.2.5",
"ts-node": "^10.9.2",
"typescript": "^5.3.3",
"vite": "^5.0.8"
Expand Down
4 changes: 0 additions & 4 deletions backend/src/repositories/astronaut/AstronautRepository.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { AstronautDTO } from 'src/types/AstronautDTO';
import knex from '../../db';
import { IAstronautRepository } from './IAstronautRepository';
import Astronaut from 'src/entities/Astronaut';
import Planet from 'src/entities/Planet';
import Image from 'src/entities/Image';
import { RetunedAstronaut } from 'src/types/RetunedAstronaut';

class AstronautRepository implements IAstronautRepository {
Expand All @@ -26,7 +23,6 @@ class AstronautRepository implements IAstronautRepository {
.join('images', 'images.id', '=', 'planets.imageId');
return astronauts;
} catch (error) {
console.error('Error fetching astronauts:', error);
throw new Error('Could not fetch astronauts.');
}
}
Expand Down
4 changes: 0 additions & 4 deletions backend/src/services/ImageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ class ImageService {
try {
return await this.imageRepository.getAll();
} catch (error) {
console.error('Error fetching all images:', error);
throw new Error('Could not fetch images.');
}
}
Expand All @@ -19,7 +18,6 @@ class ImageService {
try {
return await this.imageRepository.getById(id);
} catch (error) {
console.error(`Error fetching image with id ${id}:`, error);
throw new Error(`Could not fetch image with id ${id}.`);
}
}
Expand All @@ -29,7 +27,6 @@ class ImageService {
const { name, path } = image;
return await this.imageRepository.create({ name, path });
} catch (error) {
console.error('Error creating image:', error);
throw new Error('Could not create image.');
}
}
Expand All @@ -50,7 +47,6 @@ class ImageService {
try {
return await this.imageRepository.delete(id);
} catch (error) {
console.error(`Error deleting image with id ${id}:`, error);
throw new Error(`Could not delete image with id ${id}.`);
}
}
Expand Down
209 changes: 209 additions & 0 deletions backend/src/tests/repositories/ImageRepository.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import { ImageDTO } from 'src/types/ImageDTO';
import knex from '../../db';
import ImageRepository from 'src/repositories/image/ImageRepository';

jest.mock('../../db');

describe('ImageRepository', () => {
afterEach(() => {
jest.clearAllMocks();
});

describe('ImageRepository.getAll', () => {
afterEach(() => {
jest.clearAllMocks();
});

it('should fetch all images', async () => {
const mockImages = [
{ id: 1, name: 'image1', path: 'path1' },
{ id: 2, name: 'image2', path: 'path2' },
];

const mockKnex = jest.fn().mockReturnValue({
select: jest.fn().mockResolvedValueOnce(mockImages),
});

(knex as unknown) = mockKnex;

const result = await ImageRepository.getAll();
expect(mockKnex).toHaveBeenCalledWith('images');
expect(mockKnex().select).toHaveBeenCalledWith('*');
expect(result).toEqual(mockImages);
});

it('should throw an error if fetching images fails', async () => {
const mockKnex = jest.fn().mockReturnValue({
select: jest.fn().mockRejectedValueOnce(new Error('DB Error')),
});

(knex as unknown) = mockKnex;

await expect(ImageRepository.getAll()).rejects.toThrow('Could not fetch images.');
});
});

describe('getById', () => {
it('should fetch an image by ID', async () => {
const mockImage = { id: 1, name: 'image1', path: 'path1' };

const mockWhere = jest.fn().mockReturnThis();
const mockFirst = jest.fn().mockResolvedValueOnce(mockImage);
(knex as jest.MockedFunction<typeof knex>).mockReturnValue({
where: mockWhere,
first: mockFirst,
} as any);

const result = await ImageRepository.getById(1);

expect(knex).toHaveBeenCalledWith('images');
expect(mockWhere).toHaveBeenCalledWith('id', 1);
expect(mockFirst).toHaveBeenCalled();
expect(result).toEqual(mockImage);
});

it('should throw an error if fetching image fails', async () => {
const mockWhere = jest.fn().mockReturnThis();
const mockFirst = jest.fn().mockRejectedValueOnce(new Error('DB Error'));
(knex as jest.MockedFunction<typeof knex>).mockReturnValue({
where: mockWhere,
first: mockFirst,
} as any);

await expect(ImageRepository.getById(1)).rejects.toThrow('Could not fetch image with id 1.');
});
});

describe('getById', () => {
it('should fetch an image by ID', async () => {
const mockImage = { id: 1, name: 'image1', path: 'path1' };

const mockWhere = jest.fn().mockReturnThis();
const mockFirst = jest.fn().mockResolvedValueOnce(mockImage);
(knex as jest.MockedFunction<typeof knex>).mockReturnValue({
where: mockWhere,
first: mockFirst,
} as any);

const result = await ImageRepository.getById(1);

expect(knex).toHaveBeenCalledWith('images');
expect(mockWhere).toHaveBeenCalledWith('id', 1);
expect(mockFirst).toHaveBeenCalled();
expect(result).toEqual(mockImage);
});

it('should throw an error if fetching image fails', async () => {
const mockWhere = jest.fn().mockReturnThis();
const mockFirst = jest.fn().mockRejectedValueOnce(new Error('DB Error'));
(knex as jest.MockedFunction<typeof knex>).mockReturnValue({
where: mockWhere,
first: mockFirst,
} as any);

await expect(ImageRepository.getById(1)).rejects.toThrow('Could not fetch image with id 1.');
});
});

describe('create', () => {
it('should create a new image and return its ID', async () => {
const mockImage: ImageDTO = { name: 'image1', path: 'path1' };
const mockInsertedId = [1];

const mockInsert = jest.fn().mockReturnThis();
const mockReturning = jest.fn().mockResolvedValueOnce(mockInsertedId);
(knex as jest.MockedFunction<typeof knex>).mockReturnValue({
insert: mockInsert,
returning: mockReturning,
} as any);

const result = await ImageRepository.create(mockImage);

expect(knex).toHaveBeenCalledWith('images');
expect(mockInsert).toHaveBeenCalledWith({ name: mockImage.name, path: mockImage.path });
expect(mockReturning).toHaveBeenCalledWith('id');

expect(result).toBe(mockInsertedId[0]);
});

it('should throw an error if creating an image fails', async () => {
const mockImage: ImageDTO = { name: 'image1', path: 'path1' };

const mockInsert = jest.fn().mockReturnThis();
const mockReturning = jest.fn().mockRejectedValueOnce(new Error('DB Error'));
(knex as jest.MockedFunction<typeof knex>).mockReturnValue({
insert: mockInsert,
returning: mockReturning,
} as any);

await expect(ImageRepository.create(mockImage)).rejects.toThrow('Could not create image.');
});
});

describe('update', () => {
it('should update an image and return the number of rows affected', async () => {
const mockImage: ImageDTO = { id: 1, name: 'updatedImage', path: 'updatedPath' };
const mockRowsAffected = 1;

const mockWhere = jest.fn().mockReturnThis();
const mockUpdate = jest.fn().mockResolvedValueOnce(mockRowsAffected);
(knex as jest.MockedFunction<typeof knex>).mockReturnValue({
where: mockWhere,
update: mockUpdate,
} as any);

const result = await ImageRepository.update(mockImage);

expect(knex).toHaveBeenCalledWith('images');
expect(mockWhere).toHaveBeenCalledWith('id', mockImage.id);
expect(mockUpdate).toHaveBeenCalledWith({ name: mockImage.name, path: mockImage.path });
expect(result).toBe(mockRowsAffected);
});

it('should throw an error if updating image fails', async () => {
const mockImage: ImageDTO = { id: 1, name: 'updatedImage', path: 'updatedPath' };

const mockWhere = jest.fn().mockReturnThis();
const mockUpdate = jest.fn().mockRejectedValueOnce(new Error('DB Error'));
(knex as jest.MockedFunction<typeof knex>).mockReturnValue({
where: mockWhere,
update: mockUpdate,
} as any);

await expect(ImageRepository.update(mockImage)).rejects.toThrow(`Could not update image with id ${mockImage.id}.`);
});
});

describe('delete', () => {
it('should delete an image and return the number of rows deleted', async () => {
const mockId = 1;
const mockRowsDeleted = 1;

const mockWhere = jest.fn().mockReturnThis();
const mockDel = jest.fn().mockResolvedValueOnce(mockRowsDeleted);

(knex as jest.MockedFunction<typeof knex>).mockReturnValue({
where: mockWhere,
del: mockDel,
} as any);
const result = await ImageRepository.delete(mockId);

expect(knex).toHaveBeenCalledWith('images');
expect(mockWhere).toHaveBeenCalledWith('id', mockId);
expect(mockDel).toHaveBeenCalled();
expect(result).toBe(mockRowsDeleted);
});

it('should throw an error if deleting image fails', async () => {
const mockId = 1;
const mockWhere = jest.fn().mockReturnThis();
const mockDel = jest.fn().mockRejectedValueOnce(new Error('DB Error'));
(knex as jest.MockedFunction<typeof knex>).mockReturnValue({
where: mockWhere,
del: mockDel,
} as any);

await expect(ImageRepository.delete(mockId)).rejects.toThrow(`Could not delete image with id ${mockId}.`);
});
});
});
Loading

0 comments on commit 2912949

Please sign in to comment.