Skip to content

Commit dc3f333

Browse files
DidaraDidara
authored andcommitted
sample(sample/10): add unit and e2e tests for 10-fastify
fix(sample/10): fix id generation and controller return values - fix bug in CatsService (id generation) - fix multiple bugs in CatsController (no return, @res() usage) feat(sample/10): add full test coverage - add complete unit test suite for CatsService - add complete e2e test suite for CatsController - add Jest config and dependencies to the sample Fixes #1539
1 parent ba16478 commit dc3f333

File tree

8 files changed

+200
-20
lines changed

8 files changed

+200
-20
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { Test, TestingModule } from '@nestjs/testing';
2+
import { INestApplication } from '@nestjs/common';
3+
import * as request from 'supertest';
4+
import { CatsModule } from '../src/cats/cats.module';
5+
import {
6+
FastifyAdapter,
7+
NestFastifyApplication,
8+
} from '@nestjs/platform-fastify';
9+
import { CreateCatDto } from '../src/cats/dto/create-cat.dto';
10+
import { RolesGuard } from '../src/common/guards/roles.guard';
11+
12+
describe('CatsController (e2e)', () => {
13+
let app: INestApplication;
14+
15+
const mockRolesGuard = { canActivate: () => true };
16+
17+
beforeAll(async () => {
18+
const moduleFixture: TestingModule = await Test.createTestingModule({
19+
imports: [CatsModule],
20+
})
21+
.overrideGuard(RolesGuard)
22+
.useValue(mockRolesGuard)
23+
.compile();
24+
25+
app = moduleFixture.createNestApplication<NestFastifyApplication>(
26+
new FastifyAdapter(),
27+
);
28+
29+
await app.init();
30+
await app.getHttpAdapter().getInstance().ready();
31+
});
32+
33+
afterAll(async () => {
34+
await app.close();
35+
});
36+
37+
it('/cats (GET) - initial state', () => {
38+
return request(app.getHttpServer())
39+
.get('/cats')
40+
.expect(200)
41+
.expect([]);
42+
});
43+
44+
it('/cats (POST) - create a cat', () => {
45+
const newCatDto: CreateCatDto = { name: 'Milo', age: 2, breed: 'Tabby' };
46+
const expectedCat = {
47+
id: 1,
48+
...newCatDto,
49+
};
50+
51+
return request(app.getHttpServer())
52+
.post('/cats')
53+
.send(newCatDto)
54+
.expect(201)
55+
.expect(expectedCat);
56+
});
57+
58+
it('/cats (GET) - after create', () => {
59+
const expectedCat = {
60+
id: 1,
61+
name: 'Milo',
62+
age: 2,
63+
breed: 'Tabby',
64+
};
65+
66+
return request(app.getHttpServer())
67+
.get('/cats')
68+
.expect(200)
69+
.expect([expectedCat]);
70+
});
71+
72+
it('/cats/:id (GET) - find one', () => {
73+
const expectedCat = {
74+
id: 1,
75+
name: 'Milo',
76+
age: 2,
77+
breed: 'Tabby',
78+
};
79+
80+
return request(app.getHttpServer())
81+
.get('/cats/1')
82+
.expect(200)
83+
.expect(expectedCat);
84+
});
85+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"moduleFileExtensions": ["js", "json", "ts"],
3+
"rootDir": ".",
4+
"testEnvironment": "node",
5+
"testRegex": ".e2e-spec.ts$",
6+
"transform": {
7+
"^.+\\.(t|j)s$": "ts-jest"
8+
}
9+
}

sample/10-fastify/jest.config.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module.exports = {
2+
preset: 'ts-jest',
3+
testEnvironment: 'node',
4+
rootDir: 'src',
5+
testRegex: '.*\\.spec\\.ts$',
6+
moduleFileExtensions: ['js', 'json', 'ts'],
7+
};

sample/10-fastify/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"test:watch": "jest --watch",
1717
"test:cov": "jest --coverage",
1818
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
19-
"test:e2e": "echo 'No e2e tests implemented yet.'"
19+
"test:e2e": "jest --config ./e2e/jest-e2e.json"
2020
},
2121
"dependencies": {
2222
"@nestjs/common": "11.1.8",
@@ -34,6 +34,7 @@
3434
"@nestjs/cli": "11.0.10",
3535
"@nestjs/schematics": "11.0.9",
3636
"@nestjs/testing": "11.1.8",
37+
"@types/jest": "^30.0.0",
3738
"@types/node": "24.9.2",
3839
"@types/supertest": "6.0.3",
3940
"eslint": "9.38.0",
@@ -42,7 +43,7 @@
4243
"jest": "30.2.0",
4344
"prettier": "3.6.2",
4445
"supertest": "7.1.4",
45-
"ts-jest": "29.4.5",
46+
"ts-jest": "^29.4.5",
4647
"ts-loader": "9.5.4",
4748
"ts-node": "10.9.2",
4849
"tsconfig-paths": "4.2.0",
Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,40 @@
1-
import { Body, Controller, Get, Param, Post, UseGuards } from '@nestjs/common';
2-
import { Roles } from '../common/decorators/roles.decorator';
3-
import { RolesGuard } from '../common/guards/roles.guard';
4-
import { ParseIntPipe } from '../common/pipes/parse-int.pipe';
5-
import { CatsService } from './cats.service';
1+
import {
2+
Controller,
3+
Get,
4+
Post,
5+
Body,
6+
Param,
7+
UseGuards,
8+
NotFoundException,
9+
} from '@nestjs/common';
610
import { CreateCatDto } from './dto/create-cat.dto';
11+
import { CatsService } from './cats.service';
12+
import { RolesGuard } from '../common/guards/roles.guard';
13+
import { Roles } from '../common/decorators/roles.decorator';
714
import { Cat } from './interfaces/cat.interface';
815

9-
@UseGuards(RolesGuard)
1016
@Controller('cats')
17+
@UseGuards(RolesGuard)
1118
export class CatsController {
1219
constructor(private readonly catsService: CatsService) {}
1320

1421
@Post()
1522
@Roles('admin')
16-
async create(@Body() createCatDto: CreateCatDto) {
17-
this.catsService.create(createCatDto);
23+
create(@Body() createCatDto: CreateCatDto): Cat {
24+
return this.catsService.create(createCatDto);
1825
}
1926

2027
@Get()
21-
async findAll(): Promise<Cat[]> {
28+
findAll(): Cat[] {
2229
return this.catsService.findAll();
2330
}
2431

2532
@Get(':id')
26-
findOne(
27-
@Param('id', new ParseIntPipe())
28-
id: number,
29-
) {
30-
// get by ID logic
33+
findOne(@Param('id') id: string): Cat {
34+
const cat = this.catsService.findOne(+id);
35+
if (!cat) {
36+
throw new NotFoundException(`Cat with ID ${id} not found`);
37+
}
38+
return cat;
3139
}
32-
}
40+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { Test, TestingModule } from '@nestjs/testing';
2+
import { CatsService } from './cats.service';
3+
import { Cat } from './interfaces/cat.interface';
4+
import { CreateCatDto } from './dto/create-cat.dto';
5+
6+
describe('CatsService', () => {
7+
let service: CatsService;
8+
9+
beforeEach(async () => {
10+
const module: TestingModule = await Test.createTestingModule({
11+
providers: [CatsService],
12+
}).compile();
13+
14+
service = module.get<CatsService>(CatsService);
15+
});
16+
17+
it('should be defined', () => {
18+
expect(service).toBeDefined();
19+
});
20+
21+
describe('create and findAll', () => {
22+
it('should create a new cat, assign an ID, and return all cats', () => {
23+
const catDto: CreateCatDto = { name: 'Milo', age: 2, breed: 'Tabby' };
24+
25+
const createdCat = service.create(catDto);
26+
27+
expect(createdCat.id).toBe(1);
28+
expect(createdCat.name).toBe('Milo');
29+
30+
const allCats = service.findAll();
31+
32+
expect(allCats).toHaveLength(1);
33+
expect(allCats).toEqual([createdCat]);
34+
35+
const catDto2: CreateCatDto = { name: 'Luna', age: 1, breed: 'Siamese' };
36+
const createdCat2 = service.create(catDto2);
37+
expect(createdCat2.id).toBe(2);
38+
});
39+
});
40+
41+
describe('findOne', () => {
42+
it('should return a single cat by its id', () => {
43+
const catDto: CreateCatDto = { name: 'Luna', age: 1, breed: 'Siamese' };
44+
const createdCat = service.create(catDto);
45+
46+
const foundCat = service.findOne(1);
47+
48+
expect(foundCat).toBeDefined();
49+
expect(foundCat).toEqual(createdCat);
50+
});
51+
52+
it('should return undefined if cat is not found', () => {
53+
const foundCat = service.findOne(999);
54+
expect(foundCat).toBeUndefined();
55+
});
56+
});
57+
});
Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
11
import { Injectable } from '@nestjs/common';
22
import { Cat } from './interfaces/cat.interface';
3+
import { CreateCatDto } from './dto/create-cat.dto';
34

45
@Injectable()
56
export class CatsService {
67
private readonly cats: Cat[] = [];
8+
private idCounter = 1;
79

8-
create(cat: Cat) {
9-
this.cats.push(cat);
10+
create(createCatDto: CreateCatDto): Cat {
11+
const newCat: Cat = {
12+
id: this.idCounter++,
13+
...createCatDto,
14+
};
15+
16+
this.cats.push(newCat);
17+
return newCat;
1018
}
1119

1220
findAll(): Cat[] {
1321
return this.cats;
1422
}
15-
}
23+
24+
findOne(id: number): Cat {
25+
return this.cats.find((cat) => cat.id === id);
26+
}
27+
}

sample/10-fastify/src/cats/interfaces/cat.interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export interface Cat {
2+
readonly id: number;
23
readonly name: string;
34
readonly age: number;
45
readonly breed: string;

0 commit comments

Comments
 (0)