118 lines
3.9 KiB
TypeScript
118 lines
3.9 KiB
TypeScript
// src/services/queueService.test.ts
|
|
import { describe, it, expect, vi, beforeEach, afterEach, type Mock } from 'vitest';
|
|
|
|
// --- Hoisted Mocks ---
|
|
const mocks = vi.hoisted(() => {
|
|
const createMockQueue = (name: string) => ({
|
|
name,
|
|
close: vi.fn().mockResolvedValue(undefined),
|
|
add: vi.fn(),
|
|
});
|
|
|
|
return {
|
|
flyerQueue: createMockQueue('flyer-processing'),
|
|
emailQueue: createMockQueue('email-sending'),
|
|
analyticsQueue: createMockQueue('analytics-reporting'),
|
|
weeklyAnalyticsQueue: createMockQueue('weekly-analytics-reporting'),
|
|
cleanupQueue: createMockQueue('file-cleanup'),
|
|
tokenCleanupQueue: createMockQueue('token-cleanup'),
|
|
redisConnection: {
|
|
quit: vi.fn().mockResolvedValue('OK'),
|
|
},
|
|
logger: {
|
|
info: vi.fn(),
|
|
warn: vi.fn(),
|
|
error: vi.fn(),
|
|
debug: vi.fn(),
|
|
},
|
|
};
|
|
});
|
|
|
|
// --- Mock Modules ---
|
|
vi.mock('./queues.server', () => ({
|
|
flyerQueue: mocks.flyerQueue,
|
|
emailQueue: mocks.emailQueue,
|
|
analyticsQueue: mocks.analyticsQueue,
|
|
weeklyAnalyticsQueue: mocks.weeklyAnalyticsQueue,
|
|
cleanupQueue: mocks.cleanupQueue,
|
|
tokenCleanupQueue: mocks.tokenCleanupQueue,
|
|
}));
|
|
|
|
vi.mock('./redis.server', () => ({
|
|
connection: mocks.redisConnection,
|
|
}));
|
|
|
|
vi.mock('./logger.server', () => ({
|
|
logger: mocks.logger,
|
|
}));
|
|
|
|
// --- Test ---
|
|
describe('Queue Service (API Shutdown)', () => {
|
|
let gracefulShutdown: (signal: string) => Promise<void>;
|
|
let processExitSpy: Mock;
|
|
|
|
beforeEach(async () => {
|
|
vi.clearAllMocks();
|
|
vi.resetModules();
|
|
|
|
// Spy on process.exit and prevent it from actually exiting
|
|
processExitSpy = vi.spyOn(process, 'exit').mockImplementation(() => undefined as never);
|
|
|
|
// Dynamically import the module under test
|
|
const queueService = await import('./queueService.server');
|
|
gracefulShutdown = queueService.gracefulShutdown;
|
|
});
|
|
|
|
afterEach(() => {
|
|
processExitSpy.mockRestore();
|
|
});
|
|
|
|
it('should attempt to close all queues and the redis connection on shutdown', async () => {
|
|
await gracefulShutdown('SIGINT');
|
|
|
|
expect(mocks.flyerQueue.close).toHaveBeenCalledTimes(1);
|
|
expect(mocks.emailQueue.close).toHaveBeenCalledTimes(1);
|
|
expect(mocks.analyticsQueue.close).toHaveBeenCalledTimes(1);
|
|
expect(mocks.cleanupQueue.close).toHaveBeenCalledTimes(1);
|
|
expect(mocks.weeklyAnalyticsQueue.close).toHaveBeenCalledTimes(1);
|
|
expect(mocks.tokenCleanupQueue.close).toHaveBeenCalledTimes(1);
|
|
expect(mocks.redisConnection.quit).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should log success and exit with code 0 if all resources close successfully', async () => {
|
|
await gracefulShutdown('SIGINT');
|
|
|
|
expect(mocks.logger.info).toHaveBeenCalledWith(
|
|
'[Shutdown] All queues and connections closed successfully.',
|
|
);
|
|
expect(processExitSpy).toHaveBeenCalledWith(0);
|
|
});
|
|
|
|
it('should log a warning and exit with code 1 if a queue fails to close', async () => {
|
|
const closeError = new Error('Queue failed to close');
|
|
mocks.emailQueue.close.mockRejectedValue(closeError);
|
|
|
|
await gracefulShutdown('SIGTERM');
|
|
|
|
expect(mocks.logger.error).toHaveBeenCalledWith(
|
|
{ err: closeError, resource: 'emailQueue' },
|
|
'[Shutdown] Error closing resource.',
|
|
);
|
|
expect(mocks.logger.warn).toHaveBeenCalledWith('[Shutdown] Graceful shutdown completed with errors.');
|
|
expect(processExitSpy).toHaveBeenCalledWith(1);
|
|
});
|
|
|
|
it('should log a warning and exit with code 1 if the redis connection fails to close', async () => {
|
|
const redisError = new Error('Redis quit failed');
|
|
mocks.redisConnection.quit.mockRejectedValue(redisError);
|
|
|
|
await gracefulShutdown('SIGTERM');
|
|
|
|
expect(mocks.logger.error).toHaveBeenCalledWith(
|
|
{ err: redisError, resource: 'redisConnection' },
|
|
'[Shutdown] Error closing resource.',
|
|
);
|
|
expect(mocks.logger.warn).toHaveBeenCalledWith('[Shutdown] Graceful shutdown completed with errors.');
|
|
expect(processExitSpy).toHaveBeenCalledWith(1);
|
|
});
|
|
}); |