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", diff --git a/sample/06-mongoose/src/cats/cats.controller.spec.ts b/sample/06-mongoose/src/cats/cats.controller.spec.ts index 3d166b4a241..1bf6a074da5 100644 --- a/sample/06-mongoose/src/cats/cats.controller.spec.ts +++ b/sample/06-mongoose/src/cats/cats.controller.spec.ts @@ -2,22 +2,19 @@ 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(), + findOne: jest.fn(), + update: jest.fn(), + delete: 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 +22,124 @@ 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('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 = { + _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..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 { @@ -9,7 +10,7 @@ export class CatsController { @Post() async create(@Body() createCatDto: CreateCatDto) { - await this.catsService.create(createCatDto); + return this.catsService.create(createCatDto); } @Get() @@ -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 7ac8a919e26..e7d46fcf937 100644 --- a/sample/06-mongoose/src/cats/cats.service.spec.ts +++ b/sample/06-mongoose/src/cats/cats.service.spec.ts @@ -1,31 +1,21 @@ 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'; -const mockCat = { - name: 'Cat #1', - breed: 'Breed #1', - age: 4, +const catModelMock = { + create: jest.fn(), + find: jest.fn(), + findOne: jest.fn(), + findByIdAndUpdate: jest.fn(), + findByIdAndRemove: 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 +23,128 @@ 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); + + const createCatDto = { + 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(); + }); }); - it('should insert a new cat', async () => { - jest.spyOn(model, 'create').mockImplementationOnce(() => - Promise.resolve({ + describe('findOne()', () => { + it('should return one cat', async () => { + const mockedCat = { name: 'Cat #1', breed: 'Breed #1', age: 4, - } as any), - ); - const newCat = await service.create({ - 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('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 = { + 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 }); }); - expect(newCat).toEqual(mockCat); }); }); diff --git a/sample/06-mongoose/src/cats/cats.service.ts b/sample/06-mongoose/src/cats/cats.service.ts index 27020406f70..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,7 +22,13 @@ export class CatsService { return this.catModel.findOne({ _id: id }).exec(); } - async delete(id: string) { + 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 }) .exec(); 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; +}