Skip to content

Commit fe742d1

Browse files
committed
refactor(health): move readiness status logic from controller to service
1 parent f66eb3d commit fe742d1

2 files changed

Lines changed: 53 additions & 7 deletions

File tree

‎src/health/health.service.spec.ts‎

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,18 @@ import { PrismaService } from 'src/prisma/prisma.service';
44

55
describe('HealthService', () => {
66
let service: HealthService;
7+
const prismaMock = {
8+
$queryRaw: jest.fn(),
9+
};
710

811
beforeEach(async () => {
12+
jest.clearAllMocks();
913
const module: TestingModule = await Test.createTestingModule({
1014
providers: [
1115
HealthService,
1216
{
1317
provide: PrismaService,
14-
useValue: {
15-
$queryRaw: jest.fn(),
16-
},
18+
useValue: prismaMock,
1719
},
1820
],
1921
}).compile();
@@ -24,4 +26,24 @@ describe('HealthService', () => {
2426
it('should be defined', () => {
2527
expect(service).toBeDefined();
2628
});
29+
30+
it('returns 200 when database is reachable', async () => {
31+
prismaMock.$queryRaw.mockResolvedValue(1);
32+
33+
const health = await service.getHealth();
34+
35+
expect(health.httpStatus).toBe(200);
36+
expect(health.payload.status).toBe('ok');
37+
expect(health.payload.services.database).toBe('up');
38+
});
39+
40+
it('returns 503 when database check fails', async () => {
41+
prismaMock.$queryRaw.mockRejectedValue(new Error('db down'));
42+
43+
const health = await service.getHealth();
44+
45+
expect(health.httpStatus).toBe(503);
46+
expect(health.payload.status).toBe('degraded');
47+
expect(health.payload.services.database).toBe('down');
48+
});
2749
});

‎src/health/health.service.ts‎

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,39 @@
1-
import { Injectable } from '@nestjs/common';
1+
import { HttpStatus, Injectable } from '@nestjs/common';
22
import { PrismaService } from 'src/prisma/prisma.service';
33
import packageJson from '../../package.json';
44

5+
export type HealthPayload = {
6+
status: 'ok' | 'degraded';
7+
timestamp: string;
8+
uptime: number;
9+
version: string;
10+
services: {
11+
database: 'up' | 'down';
12+
};
13+
};
14+
15+
export type HealthResult = {
16+
httpStatus: number;
17+
payload: HealthPayload;
18+
};
19+
520
@Injectable()
621
export class HealthService {
722
constructor(private prisma: PrismaService) {}
823

9-
async getHealth() {
24+
async getHealth(): Promise<HealthResult> {
1025
const timestamp = new Date().toISOString();
1126
const uptime = Math.floor(process.uptime());
1227
const version = packageJson.version;
13-
let dbStatus = 'up';
28+
let dbStatus: 'up' | 'down' = 'up';
1429

1530
try {
1631
await this.prisma.$queryRaw`SELECT 1`;
1732
} catch {
1833
dbStatus = 'down';
1934
}
20-
return {
35+
36+
const payload: HealthPayload = {
2137
status: dbStatus === 'up' ? 'ok' : 'degraded',
2238
timestamp,
2339
uptime,
@@ -26,5 +42,13 @@ export class HealthService {
2642
database: dbStatus,
2743
},
2844
};
45+
46+
return {
47+
httpStatus:
48+
payload.status === 'ok'
49+
? HttpStatus.OK
50+
: HttpStatus.SERVICE_UNAVAILABLE,
51+
payload,
52+
};
2953
}
3054
}

0 commit comments

Comments
 (0)