fix tests + flyer upload (anon)
This commit is contained in:
209
src/services/monitoringService.server.test.ts
Normal file
209
src/services/monitoringService.server.test.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
// src/services/monitoringService.server.test.ts
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import type { Job, Queue } from 'bullmq';
|
||||
import { NotFoundError, ValidationError } from './db/errors.db';
|
||||
import { logger } from './logger.server';
|
||||
|
||||
// --- Hoisted Mocks ---
|
||||
const mocks = vi.hoisted(() => {
|
||||
const createMockWorker = (name: string) => ({
|
||||
name,
|
||||
isRunning: vi.fn().mockReturnValue(true),
|
||||
});
|
||||
|
||||
const createMockQueue = (name: string) => ({
|
||||
name,
|
||||
getJobCounts: vi.fn().mockResolvedValue({}),
|
||||
getJob: vi.fn(),
|
||||
});
|
||||
|
||||
return {
|
||||
flyerWorker: createMockWorker('flyer-processing'),
|
||||
emailWorker: createMockWorker('email-sending'),
|
||||
analyticsWorker: createMockWorker('analytics-reporting'),
|
||||
cleanupWorker: createMockWorker('file-cleanup'),
|
||||
weeklyAnalyticsWorker: createMockWorker('weekly-analytics-reporting'),
|
||||
|
||||
flyerQueue: createMockQueue('flyer-processing'),
|
||||
emailQueue: createMockQueue('email-sending'),
|
||||
analyticsQueue: createMockQueue('analytics-reporting'),
|
||||
cleanupQueue: createMockQueue('file-cleanup'),
|
||||
weeklyAnalyticsQueue: createMockQueue('weekly-analytics-reporting'),
|
||||
};
|
||||
});
|
||||
|
||||
// --- Mock Modules ---
|
||||
vi.mock('./queueService.server', () => ({
|
||||
flyerQueue: mocks.flyerQueue,
|
||||
emailQueue: mocks.emailQueue,
|
||||
analyticsQueue: mocks.analyticsQueue,
|
||||
cleanupQueue: mocks.cleanupQueue,
|
||||
weeklyAnalyticsQueue: mocks.weeklyAnalyticsQueue,
|
||||
}));
|
||||
|
||||
vi.mock('./workers.server', () => ({
|
||||
flyerWorker: mocks.flyerWorker,
|
||||
emailWorker: mocks.emailWorker,
|
||||
analyticsWorker: mocks.analyticsWorker,
|
||||
cleanupWorker: mocks.cleanupWorker,
|
||||
weeklyAnalyticsWorker: mocks.weeklyAnalyticsWorker,
|
||||
}));
|
||||
|
||||
vi.mock('./db/errors.db', () => ({
|
||||
NotFoundError: class NotFoundError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = 'NotFoundError';
|
||||
}
|
||||
},
|
||||
ValidationError: class ValidationError extends Error {
|
||||
constructor(issues: [], message: string) {
|
||||
super(message);
|
||||
this.name = 'ValidationError';
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('./logger.server', () => ({
|
||||
logger: {
|
||||
info: vi.fn(),
|
||||
error: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
debug: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
// Import the service to be tested AFTER all mocks are set up.
|
||||
import { monitoringService } from './monitoringService.server';
|
||||
|
||||
describe('MonitoringService', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('getWorkerStatuses', () => {
|
||||
it('should return the running status of all workers', async () => {
|
||||
// Arrange: one worker is not running
|
||||
mocks.emailWorker.isRunning.mockReturnValue(false);
|
||||
|
||||
// Act
|
||||
const statuses = await monitoringService.getWorkerStatuses();
|
||||
|
||||
// Assert
|
||||
expect(statuses).toEqual([
|
||||
{ name: 'flyer-processing', isRunning: true },
|
||||
{ name: 'email-sending', isRunning: false },
|
||||
{ name: 'analytics-reporting', isRunning: true },
|
||||
{ name: 'file-cleanup', isRunning: true },
|
||||
{ name: 'weekly-analytics-reporting', isRunning: true },
|
||||
]);
|
||||
expect(mocks.flyerWorker.isRunning).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.emailWorker.isRunning).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getQueueStatuses', () => {
|
||||
it('should return job counts for all queues', async () => {
|
||||
// Arrange
|
||||
mocks.flyerQueue.getJobCounts.mockResolvedValue({ active: 1, failed: 2 });
|
||||
mocks.emailQueue.getJobCounts.mockResolvedValue({ completed: 10, waiting: 5 });
|
||||
|
||||
// Act
|
||||
const statuses = await monitoringService.getQueueStatuses();
|
||||
|
||||
// Assert
|
||||
expect(statuses).toEqual(
|
||||
expect.arrayContaining([
|
||||
{ name: 'flyer-processing', counts: { active: 1, failed: 2 } },
|
||||
{ name: 'email-sending', counts: { completed: 10, waiting: 5 } },
|
||||
{ name: 'analytics-reporting', counts: {} },
|
||||
{ name: 'file-cleanup', counts: {} },
|
||||
{ name: 'weekly-analytics-reporting', counts: {} },
|
||||
]),
|
||||
);
|
||||
expect(mocks.flyerQueue.getJobCounts).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.emailQueue.getJobCounts).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('retryFailedJob', () => {
|
||||
const userId = 'admin-user';
|
||||
const jobId = 'failed-job-1';
|
||||
|
||||
it('should throw NotFoundError for an unknown queue name', async () => {
|
||||
await expect(monitoringService.retryFailedJob('unknown-queue', jobId, userId)).rejects.toThrow(
|
||||
new NotFoundError(`Queue 'unknown-queue' not found.`),
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw NotFoundError if the job does not exist in the queue', async () => {
|
||||
mocks.flyerQueue.getJob.mockResolvedValue(null);
|
||||
|
||||
await expect(
|
||||
monitoringService.retryFailedJob('flyer-processing', jobId, userId),
|
||||
).rejects.toThrow(new NotFoundError(`Job with ID '${jobId}' not found in queue 'flyer-processing'.`));
|
||||
});
|
||||
|
||||
it("should throw ValidationError if the job is not in a 'failed' state", async () => {
|
||||
const mockJob = {
|
||||
id: jobId,
|
||||
getState: vi.fn().mockResolvedValue('completed'),
|
||||
retry: vi.fn(),
|
||||
} as unknown as Job;
|
||||
mocks.flyerQueue.getJob.mockResolvedValue(mockJob);
|
||||
|
||||
await expect(
|
||||
monitoringService.retryFailedJob('flyer-processing', jobId, userId),
|
||||
).rejects.toThrow(new ValidationError([], `Job is not in a 'failed' state. Current state: completed.`));
|
||||
});
|
||||
|
||||
it("should call job.retry() and log if the job is in a 'failed' state", async () => {
|
||||
const mockJob = {
|
||||
id: jobId,
|
||||
getState: vi.fn().mockResolvedValue('failed'),
|
||||
retry: vi.fn().mockResolvedValue(undefined),
|
||||
} as unknown as Job;
|
||||
mocks.flyerQueue.getJob.mockResolvedValue(mockJob);
|
||||
|
||||
await monitoringService.retryFailedJob('flyer-processing', jobId, userId);
|
||||
|
||||
expect(mockJob.retry).toHaveBeenCalledTimes(1);
|
||||
expect(logger.info).toHaveBeenCalledWith(
|
||||
`[Admin] User ${userId} manually retried job ${jobId} in queue flyer-processing.`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFlyerJobStatus', () => {
|
||||
const jobId = 'flyer-job-123';
|
||||
|
||||
it('should throw NotFoundError if the job is not found', async () => {
|
||||
mocks.flyerQueue.getJob.mockResolvedValue(null);
|
||||
|
||||
await expect(monitoringService.getFlyerJobStatus(jobId)).rejects.toThrow(
|
||||
new NotFoundError('Job not found.'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should return the job status object if the job is found', async () => {
|
||||
const mockJob = {
|
||||
id: jobId,
|
||||
getState: vi.fn().mockResolvedValue('completed'),
|
||||
progress: 100,
|
||||
returnvalue: { flyerId: 99 },
|
||||
failedReason: null,
|
||||
} as unknown as Job;
|
||||
mocks.flyerQueue.getJob.mockResolvedValue(mockJob);
|
||||
|
||||
const status = await monitoringService.getFlyerJobStatus(jobId);
|
||||
|
||||
expect(status).toEqual({
|
||||
id: jobId,
|
||||
state: 'completed',
|
||||
progress: 100,
|
||||
returnValue: { flyerId: 99 },
|
||||
failedReason: null,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user