98 lines
2.9 KiB
TypeScript
98 lines
2.9 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
const loggerError = vi.fn();
|
|
|
|
vi.mock('next/server', () => ({
|
|
NextResponse: {
|
|
json: (data: unknown, init?: { status?: number; headers?: unknown }) => ({
|
|
status: init?.status ?? 200,
|
|
headers: init?.headers,
|
|
json: async () => data,
|
|
}),
|
|
},
|
|
}));
|
|
|
|
vi.mock('@/lib/logger', () => ({
|
|
logger: {
|
|
error: loggerError,
|
|
},
|
|
}));
|
|
|
|
describe('health route', () => {
|
|
beforeEach(() => {
|
|
vi.restoreAllMocks();
|
|
loggerError.mockReset();
|
|
});
|
|
|
|
it('returns healthy for the basic liveness probe even when heap usage is high', async () => {
|
|
vi.spyOn(process, 'memoryUsage').mockReturnValue({
|
|
rss: 120_000_000,
|
|
heapTotal: 45_000_000,
|
|
heapUsed: 43_000_000,
|
|
external: 8_000_000,
|
|
arrayBuffers: 1_000_000,
|
|
});
|
|
|
|
const { GET } = await import('@/app/api/health/route');
|
|
const response = await GET({ nextUrl: new URL('http://localhost/api/health') } as never);
|
|
const payload = await response.json();
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(payload).toMatchObject({
|
|
status: 'healthy',
|
|
});
|
|
expect(payload.warnings).toBeUndefined();
|
|
});
|
|
|
|
it('returns degraded diagnostics in detailed mode without failing the probe', async () => {
|
|
vi.spyOn(process, 'memoryUsage').mockReturnValue({
|
|
rss: 120_000_000,
|
|
heapTotal: 4_100_000_000,
|
|
heapUsed: 4_000_000_000,
|
|
external: 8_000_000,
|
|
arrayBuffers: 1_000_000,
|
|
});
|
|
vi.spyOn(process, 'uptime').mockReturnValue(123.45);
|
|
|
|
const { GET } = await import('@/app/api/health/route');
|
|
const response = await GET({ nextUrl: new URL('http://localhost/api/health?detailed=true') } as never);
|
|
const payload = await response.json();
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(payload.status).toBe('degraded');
|
|
expect(payload.memory).toMatchObject({
|
|
heapUsed: 4_000_000_000,
|
|
heapTotal: 4_100_000_000,
|
|
rss: 120_000_000,
|
|
external: 8_000_000,
|
|
});
|
|
expect(payload.memory.heapSizeLimit).toBeGreaterThan(0);
|
|
expect(payload.warnings).toEqual([
|
|
expect.stringContaining('V8 heap usage is high'),
|
|
]);
|
|
});
|
|
|
|
it('keeps HEAD as a stable liveness probe', async () => {
|
|
const { HEAD } = await import('@/app/api/health/route');
|
|
const response = await HEAD();
|
|
|
|
expect(response.status).toBe(200);
|
|
});
|
|
|
|
it('returns 503 when collecting health diagnostics throws', async () => {
|
|
vi.spyOn(process, 'memoryUsage').mockImplementation(() => {
|
|
throw new Error('boom');
|
|
});
|
|
|
|
const { GET } = await import('@/app/api/health/route');
|
|
const response = await GET({ nextUrl: new URL('http://localhost/api/health') } as never);
|
|
const payload = await response.json();
|
|
|
|
expect(response.status).toBe(503);
|
|
expect(payload).toMatchObject({
|
|
status: 'unhealthy',
|
|
reason: 'boom',
|
|
});
|
|
expect(loggerError).toHaveBeenCalledWith('Health check failed', { error: 'boom' });
|
|
});
|
|
}); |