From f0527d08fce088d54a5805365cc3413bba48d767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Vy=C5=A1=C5=88ovsk=C3=BD?= Date: Wed, 14 Aug 2024 12:24:23 +0200 Subject: [PATCH 1/6] chore(deps): update @nestjs/mongoose in 06-mongoose sample --- sample/06-mongoose/package-lock.json | 16 ++++++++-------- sample/06-mongoose/package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sample/06-mongoose/package-lock.json b/sample/06-mongoose/package-lock.json index 3aa8c054673..1bd22844ba8 100644 --- a/sample/06-mongoose/package-lock.json +++ b/sample/06-mongoose/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@nestjs/common": "10.3.2", "@nestjs/core": "10.3.2", - "@nestjs/mongoose": "10.0.2", + "@nestjs/mongoose": "10.0.3", "@nestjs/platform-express": "10.3.8", "mongoose": "8.0.1", "reflect-metadata": "0.2.1", @@ -1915,14 +1915,14 @@ } }, "node_modules/@nestjs/mongoose": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@nestjs/mongoose/-/mongoose-10.0.2.tgz", - "integrity": "sha512-ITHh075DynjPIaKeJh6WkarS21WXYslu4nrLkNPbWaCP6JfxVAOftaA2X5tPSiiE/gNJWgs+QFWsfCFZUUenow==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@nestjs/mongoose/-/mongoose-10.0.3.tgz", + "integrity": "sha512-EW0qIhERafiUpBGkLgHyEEmteHg7xYkjGwApAzDszZk5+osp14jbwtTDhOOLe81pSs03caVepeS2KAl+68xwew==", + "license": "MIT", "peerDependencies": { "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0", "mongoose": "^6.0.2 || ^7.0.0 || ^8.0.0", - "reflect-metadata": "^0.1.12", "rxjs": "^7.0.0" } }, @@ -10952,9 +10952,9 @@ } }, "@nestjs/mongoose": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@nestjs/mongoose/-/mongoose-10.0.2.tgz", - "integrity": "sha512-ITHh075DynjPIaKeJh6WkarS21WXYslu4nrLkNPbWaCP6JfxVAOftaA2X5tPSiiE/gNJWgs+QFWsfCFZUUenow==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@nestjs/mongoose/-/mongoose-10.0.3.tgz", + "integrity": "sha512-EW0qIhERafiUpBGkLgHyEEmteHg7xYkjGwApAzDszZk5+osp14jbwtTDhOOLe81pSs03caVepeS2KAl+68xwew==", "requires": {} }, "@nestjs/platform-express": { diff --git a/sample/06-mongoose/package.json b/sample/06-mongoose/package.json index 810f732b5f8..13894b49b29 100644 --- a/sample/06-mongoose/package.json +++ b/sample/06-mongoose/package.json @@ -21,7 +21,7 @@ "dependencies": { "@nestjs/common": "10.3.2", "@nestjs/core": "10.3.2", - "@nestjs/mongoose": "10.0.2", + "@nestjs/mongoose": "10.0.3", "@nestjs/platform-express": "10.3.8", "mongoose": "8.0.1", "reflect-metadata": "0.2.1", From 3117b241c148aa43376002063a6725ae12ba0558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Vy=C5=A1=C5=88ovsk=C3=BD?= Date: Wed, 14 Aug 2024 12:38:57 +0200 Subject: [PATCH 2/6] test(06-mongoose): refactor tests for controller --- .../src/cats/cats.controller.spec.ts | 114 +++++++++++------- .../06-mongoose/src/cats/cats.controller.ts | 2 +- 2 files changed, 73 insertions(+), 43 deletions(-) diff --git a/sample/06-mongoose/src/cats/cats.controller.spec.ts b/sample/06-mongoose/src/cats/cats.controller.spec.ts index 3d166b4a241..481b1214559 100644 --- a/sample/06-mongoose/src/cats/cats.controller.spec.ts +++ b/sample/06-mongoose/src/cats/cats.controller.spec.ts @@ -2,22 +2,16 @@ import { Test, TestingModule } from '@nestjs/testing'; import { CatsController } from './cats.controller'; import { CreateCatDto } from './dto/create-cat.dto'; import { CatsService } from './cats.service'; +import { Types } from 'mongoose'; + +const catsServiceMock = { + create: jest.fn(), + findAll: jest.fn(), +}; describe('Cats Controller', () => { let controller: CatsController; - let service: CatsService; - const createCatDto: CreateCatDto = { - name: 'Cat #1', - breed: 'Breed #1', - age: 4, - }; - - const mockCat = { - name: 'Cat #1', - breed: 'Breed #1', - age: 4, - _id: 'a id', - }; + let service: jest.Mocked; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -25,65 +19,101 @@ describe('Cats Controller', () => { providers: [ { provide: CatsService, - useValue: { - findAll: jest.fn().mockResolvedValue([ - { - name: 'Cat #1', - breed: 'Bread #1', - age: 4, - }, - { - name: 'Cat #2', - breed: 'Breed #2', - age: 3, - }, - { - name: 'Cat #3', - breed: 'Breed #3', - age: 2, - }, - ]), - create: jest.fn().mockResolvedValue(createCatDto), - }, + useValue: catsServiceMock, }, ], }).compile(); - controller = module.get(CatsController); - service = module.get(CatsService); + controller = module.get(CatsController); + service = module.get(CatsService); }); describe('create()', () => { it('should create a new cat', async () => { - const createSpy = jest - .spyOn(service, 'create') - .mockResolvedValueOnce(mockCat); + const mockedCat = { + _id: new Types.ObjectId(), + name: 'Cat #1', + breed: 'Breed #1', + age: 4, + }; + service.create.mockResolvedValueOnce(mockedCat); + + const createCatDto: CreateCatDto = { + name: 'Cat #1', + breed: 'Breed #1', + age: 4, + }; + const result = await controller.create(createCatDto); - await controller.create(createCatDto); - expect(createSpy).toHaveBeenCalledWith(createCatDto); + expect(result).toEqual(mockedCat); + expect(service.create).toHaveBeenCalledWith(createCatDto); }); }); describe('findAll()', () => { it('should return an array of cats', async () => { - expect(controller.findAll()).resolves.toEqual([ + const mockedCats = [ { + _id: new Types.ObjectId(), name: 'Cat #1', breed: 'Bread #1', age: 4, }, { + _id: new Types.ObjectId(), name: 'Cat #2', breed: 'Breed #2', age: 3, }, { + _id: new Types.ObjectId(), name: 'Cat #3', breed: 'Breed #3', age: 2, }, - ]); + ]; + service.findAll.mockResolvedValueOnce(mockedCats); + + const result = await controller.findAll(); + + expect(result).toEqual(mockedCats); expect(service.findAll).toHaveBeenCalled(); }); }); + + describe('findOne()', () => { + it('should return a single cat', async () => { + const mockedCat = { + _id: new Types.ObjectId(), + name: 'Cat #1', + breed: 'Breed #1', + age: 4, + }; + service.findOne.mockResolvedValueOnce(mockedCat); + + const id = new Types.ObjectId().toString(); + const result = await controller.findOne(id); + + expect(result).toEqual(mockedCat); + expect(service.findOne).toHaveBeenCalledWith(id); + }); + }); + + describe('delete()', () => { + it('should delete a single cat', async () => { + const mockedCat = { + _id: new Types.ObjectId(), + name: 'Cat #1', + breed: 'Breed #1', + age: 4, + }; + service.delete.mockResolvedValueOnce(mockedCat); + + const id = new Types.ObjectId().toString(); + const result = await controller.delete(id); + + expect(result).toEqual(mockedCat); + expect(service.delete).toHaveBeenCalledWith(id); + }); + }); }); diff --git a/sample/06-mongoose/src/cats/cats.controller.ts b/sample/06-mongoose/src/cats/cats.controller.ts index c9aa1f645db..b3843856fc0 100644 --- a/sample/06-mongoose/src/cats/cats.controller.ts +++ b/sample/06-mongoose/src/cats/cats.controller.ts @@ -9,7 +9,7 @@ export class CatsController { @Post() async create(@Body() createCatDto: CreateCatDto) { - await this.catsService.create(createCatDto); + return this.catsService.create(createCatDto); } @Get() From 3c6a295037d6e5caa554b69773efd0568aa84d42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Vy=C5=A1=C5=88ovsk=C3=BD?= Date: Wed, 14 Aug 2024 12:44:43 +0200 Subject: [PATCH 3/6] test(06-mongoose): add missing tests for controller --- sample/06-mongoose/src/cats/cats.controller.spec.ts | 2 ++ sample/06-mongoose/src/cats/cats.service.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sample/06-mongoose/src/cats/cats.controller.spec.ts b/sample/06-mongoose/src/cats/cats.controller.spec.ts index 481b1214559..c45ba736427 100644 --- a/sample/06-mongoose/src/cats/cats.controller.spec.ts +++ b/sample/06-mongoose/src/cats/cats.controller.spec.ts @@ -7,6 +7,8 @@ import { Types } from 'mongoose'; const catsServiceMock = { create: jest.fn(), findAll: jest.fn(), + findOne: jest.fn(), + delete: jest.fn(), }; describe('Cats Controller', () => { diff --git a/sample/06-mongoose/src/cats/cats.service.ts b/sample/06-mongoose/src/cats/cats.service.ts index 27020406f70..f48727d4fd4 100644 --- a/sample/06-mongoose/src/cats/cats.service.ts +++ b/sample/06-mongoose/src/cats/cats.service.ts @@ -21,7 +21,7 @@ export class CatsService { return this.catModel.findOne({ _id: id }).exec(); } - async delete(id: string) { + async delete(id: string): Promise { const deletedCat = await this.catModel .findByIdAndRemove({ _id: id }) .exec(); From 2da82286d2d959c6ba47d49601be15b6f3b2cce4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Vy=C5=A1=C5=88ovsk=C3=BD?= Date: Wed, 14 Aug 2024 12:57:32 +0200 Subject: [PATCH 4/6] test(06-mongoose): refactor tests for service --- .../06-mongoose/src/cats/cats.service.spec.ts | 91 ++++++++++--------- 1 file changed, 47 insertions(+), 44 deletions(-) diff --git a/sample/06-mongoose/src/cats/cats.service.spec.ts b/sample/06-mongoose/src/cats/cats.service.spec.ts index 7ac8a919e26..7283784a8e9 100644 --- a/sample/06-mongoose/src/cats/cats.service.spec.ts +++ b/sample/06-mongoose/src/cats/cats.service.spec.ts @@ -3,29 +3,16 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Model } from 'mongoose'; import { CatsService } from './cats.service'; import { Cat } from './schemas/cat.schema'; +import { CreateCatDto } from './dto/create-cat.dto'; -const mockCat = { - name: 'Cat #1', - breed: 'Breed #1', - age: 4, +const catModelMock = { + create: jest.fn(), + find: jest.fn(), }; describe('CatsService', () => { let service: CatsService; - let model: Model; - - const catsArray = [ - { - name: 'Cat #1', - breed: 'Breed #1', - age: 4, - }, - { - name: 'Cat #2', - breed: 'Breed #2', - age: 2, - }, - ]; + let model: jest.Mocked>; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -33,46 +20,62 @@ describe('CatsService', () => { CatsService, { provide: getModelToken('Cat'), - useValue: { - new: jest.fn().mockResolvedValue(mockCat), - constructor: jest.fn().mockResolvedValue(mockCat), - find: jest.fn(), - create: jest.fn(), - exec: jest.fn(), - }, + useValue: catModelMock, }, ], }).compile(); - service = module.get(CatsService); - model = module.get>(getModelToken('Cat')); + service = module.get(CatsService); + model = module.get(getModelToken('Cat')); }); it('should be defined', () => { expect(service).toBeDefined(); }); - it('should return all cats', async () => { - jest.spyOn(model, 'find').mockReturnValue({ - exec: jest.fn().mockResolvedValueOnce(catsArray), - } as any); - const cats = await service.findAll(); - expect(cats).toEqual(catsArray); - }); + describe('create()', () => { + it('should insert a new cat', async () => { + const mockedCat: CreateCatDto = { + name: 'Cat #1', + breed: 'Breed #1', + age: 4, + }; + model.create.mockResolvedValueOnce(mockedCat as any); - it('should insert a new cat', async () => { - jest.spyOn(model, 'create').mockImplementationOnce(() => - Promise.resolve({ + const createCatDto = { name: 'Cat #1', breed: 'Breed #1', age: 4, - } as any), - ); - const newCat = await service.create({ - name: 'Cat #1', - breed: 'Breed #1', - age: 4, + }; + const result = await service.create(createCatDto); + + expect(result).toEqual(mockedCat); + expect(model.create).toHaveBeenCalledWith(createCatDto); + }); + }); + + describe('findAll()', () => { + it('should return all cats', async () => { + const mockedCats = [ + { + name: 'Cat #1', + breed: 'Breed #1', + age: 4, + }, + { + name: 'Cat #2', + breed: 'Breed #2', + age: 2, + }, + ]; + model.find.mockReturnValueOnce({ + exec: jest.fn().mockResolvedValueOnce(mockedCats), + } as any); + + const result = await service.findAll(); + + expect(result).toEqual(mockedCats); + expect(model.find).toHaveBeenCalled(); }); - expect(newCat).toEqual(mockCat); }); }); From d0b28352938ab7856d16a365a54003a1fa40c789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Vy=C5=A1=C5=88ovsk=C3=BD?= Date: Wed, 14 Aug 2024 13:03:15 +0200 Subject: [PATCH 5/6] test(06-mongoose): add missing tests for service --- .../06-mongoose/src/cats/cats.service.spec.ts | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/sample/06-mongoose/src/cats/cats.service.spec.ts b/sample/06-mongoose/src/cats/cats.service.spec.ts index 7283784a8e9..ae9bb50a697 100644 --- a/sample/06-mongoose/src/cats/cats.service.spec.ts +++ b/sample/06-mongoose/src/cats/cats.service.spec.ts @@ -1,6 +1,6 @@ import { getModelToken } from '@nestjs/mongoose'; import { Test, TestingModule } from '@nestjs/testing'; -import { Model } from 'mongoose'; +import { Model, Types } from 'mongoose'; import { CatsService } from './cats.service'; import { Cat } from './schemas/cat.schema'; import { CreateCatDto } from './dto/create-cat.dto'; @@ -8,6 +8,8 @@ import { CreateCatDto } from './dto/create-cat.dto'; const catModelMock = { create: jest.fn(), find: jest.fn(), + findOne: jest.fn(), + findByIdAndRemove: jest.fn(), }; describe('CatsService', () => { @@ -78,4 +80,42 @@ describe('CatsService', () => { expect(model.find).toHaveBeenCalled(); }); }); + + describe('findOne()', () => { + it('should return one cat', async () => { + const mockedCat = { + name: 'Cat #1', + breed: 'Breed #1', + age: 4, + }; + model.findOne.mockReturnValueOnce({ + exec: jest.fn().mockResolvedValueOnce(mockedCat), + } as any); + + const id = new Types.ObjectId().toString(); + const result = await service.findOne(id); + + expect(result).toEqual(mockedCat); + expect(model.findOne).toHaveBeenCalledWith({ _id: id }); + }); + }); + + describe('delete()', () => { + it('should delete a cat', async () => { + const mockedCat = { + name: 'Cat #1', + breed: 'Breed #1', + age: 4, + }; + model.findByIdAndRemove.mockReturnValueOnce({ + exec: jest.fn().mockResolvedValueOnce(mockedCat), + } as any); + + const id = new Types.ObjectId().toString(); + const result = await service.delete(id); + + expect(result).toEqual(mockedCat); + expect(model.findByIdAndRemove).toHaveBeenCalledWith({ _id: id }); + }); + }); }); From 3e061e9b144545e940ed9c4f2bf6b2d39a225387 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Vy=C5=A1=C5=88ovsk=C3=BD?= Date: Wed, 14 Aug 2024 13:07:18 +0200 Subject: [PATCH 6/6] feat(06-mongoose): add update method --- .../src/cats/cats.controller.spec.ts | 24 +++++++++++++++ .../06-mongoose/src/cats/cats.controller.ts | 6 ++++ .../06-mongoose/src/cats/cats.service.spec.ts | 29 +++++++++++++++++++ sample/06-mongoose/src/cats/cats.service.ts | 7 +++++ .../src/cats/dto/update-cat.dto.ts | 5 ++++ 5 files changed, 71 insertions(+) create mode 100644 sample/06-mongoose/src/cats/dto/update-cat.dto.ts diff --git a/sample/06-mongoose/src/cats/cats.controller.spec.ts b/sample/06-mongoose/src/cats/cats.controller.spec.ts index c45ba736427..1bf6a074da5 100644 --- a/sample/06-mongoose/src/cats/cats.controller.spec.ts +++ b/sample/06-mongoose/src/cats/cats.controller.spec.ts @@ -8,6 +8,7 @@ const catsServiceMock = { create: jest.fn(), findAll: jest.fn(), findOne: jest.fn(), + update: jest.fn(), delete: jest.fn(), }; @@ -101,6 +102,29 @@ describe('Cats Controller', () => { }); }); + describe('update()', () => { + it('should update a single cat', async () => { + const mockedCat = { + _id: new Types.ObjectId(), + name: 'Cat #1', + breed: 'Breed #1', + age: 4, + }; + service.update.mockResolvedValueOnce(mockedCat); + + const id = new Types.ObjectId().toString(); + const updateCatDto: CreateCatDto = { + name: 'Cat #1', + breed: 'Breed #1', + age: 4, + }; + const result = await controller.update(id, updateCatDto); + + expect(result).toEqual(mockedCat); + expect(service.update).toHaveBeenCalledWith(id, updateCatDto); + }); + }); + describe('delete()', () => { it('should delete a single cat', async () => { const mockedCat = { diff --git a/sample/06-mongoose/src/cats/cats.controller.ts b/sample/06-mongoose/src/cats/cats.controller.ts index b3843856fc0..2b8e14b3d09 100644 --- a/sample/06-mongoose/src/cats/cats.controller.ts +++ b/sample/06-mongoose/src/cats/cats.controller.ts @@ -2,6 +2,7 @@ import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common'; import { CatsService } from './cats.service'; import { CreateCatDto } from './dto/create-cat.dto'; import { Cat } from './schemas/cat.schema'; +import { UpdateCatDto } from './dto/update-cat.dto'; @Controller('cats') export class CatsController { @@ -22,6 +23,11 @@ export class CatsController { return this.catsService.findOne(id); } + @Post(':id') + async update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto) { + return this.catsService.update(id, updateCatDto); + } + @Delete(':id') async delete(@Param('id') id: string) { return this.catsService.delete(id); diff --git a/sample/06-mongoose/src/cats/cats.service.spec.ts b/sample/06-mongoose/src/cats/cats.service.spec.ts index ae9bb50a697..e7d46fcf937 100644 --- a/sample/06-mongoose/src/cats/cats.service.spec.ts +++ b/sample/06-mongoose/src/cats/cats.service.spec.ts @@ -9,6 +9,7 @@ const catModelMock = { create: jest.fn(), find: jest.fn(), findOne: jest.fn(), + findByIdAndUpdate: jest.fn(), findByIdAndRemove: jest.fn(), }; @@ -100,6 +101,34 @@ describe('CatsService', () => { }); }); + describe('update()', () => { + it('should update a cat', async () => { + const mockedCat = { + name: 'Cat #1', + breed: 'Breed #1', + age: 4, + }; + model.findByIdAndUpdate.mockReturnValueOnce({ + exec: jest.fn().mockResolvedValueOnce(mockedCat), + } as any); + + const id = new Types.ObjectId().toString(); + const updateCatDto = { + name: 'Cat #1', + breed: 'Breed #1', + age: 4, + }; + const result = await service.update(id, updateCatDto); + + expect(result).toEqual(mockedCat); + expect(model.findByIdAndUpdate).toHaveBeenCalledWith( + { _id: id }, + updateCatDto, + { new: true }, + ); + }); + }); + describe('delete()', () => { it('should delete a cat', async () => { const mockedCat = { diff --git a/sample/06-mongoose/src/cats/cats.service.ts b/sample/06-mongoose/src/cats/cats.service.ts index f48727d4fd4..0027314f260 100644 --- a/sample/06-mongoose/src/cats/cats.service.ts +++ b/sample/06-mongoose/src/cats/cats.service.ts @@ -3,6 +3,7 @@ import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { CreateCatDto } from './dto/create-cat.dto'; import { Cat } from './schemas/cat.schema'; +import { UpdateCatDto } from './dto/update-cat.dto'; @Injectable() export class CatsService { @@ -21,6 +22,12 @@ export class CatsService { return this.catModel.findOne({ _id: id }).exec(); } + async update(id: string, updateCatDto: UpdateCatDto): Promise { + return this.catModel + .findByIdAndUpdate({ _id: id }, updateCatDto, { new: true }) + .exec(); + } + async delete(id: string): Promise { const deletedCat = await this.catModel .findByIdAndRemove({ _id: id }) diff --git a/sample/06-mongoose/src/cats/dto/update-cat.dto.ts b/sample/06-mongoose/src/cats/dto/update-cat.dto.ts new file mode 100644 index 00000000000..be75756b8b8 --- /dev/null +++ b/sample/06-mongoose/src/cats/dto/update-cat.dto.ts @@ -0,0 +1,5 @@ +export class UpdateCatDto { + readonly name?: string; + readonly age?: number; + readonly breed?: string; +}