Some checks failed
Deploy to Test Environment / deploy-to-test (push) Has been cancelled
153 lines
4.7 KiB
TypeScript
153 lines
4.7 KiB
TypeScript
// src/services/analyticsService.server.test.ts
|
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
import { AnalyticsService } from './analyticsService.server';
|
|
import { logger } from './logger.server';
|
|
import type { Job } from 'bullmq';
|
|
import type { AnalyticsJobData, WeeklyAnalyticsJobData } from '../types/job-data';
|
|
|
|
// Mock logger
|
|
vi.mock('./logger.server', () => ({
|
|
logger: {
|
|
child: vi.fn(),
|
|
info: vi.fn(),
|
|
error: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
describe('AnalyticsService', () => {
|
|
let service: AnalyticsService;
|
|
let mockLoggerInstance: any;
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
vi.useFakeTimers();
|
|
|
|
// Setup mock logger instance returned by child()
|
|
mockLoggerInstance = {
|
|
info: vi.fn(),
|
|
error: vi.fn(),
|
|
warn: vi.fn(),
|
|
debug: vi.fn(),
|
|
};
|
|
vi.mocked(logger.child).mockReturnValue(mockLoggerInstance);
|
|
|
|
service = new AnalyticsService();
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers();
|
|
});
|
|
|
|
const createMockJob = <T>(data: T): Job<T> =>
|
|
({
|
|
id: 'job-123',
|
|
name: 'analytics-job',
|
|
data,
|
|
attemptsMade: 1,
|
|
updateProgress: vi.fn(),
|
|
} as unknown as Job<T>);
|
|
|
|
describe('processDailyReportJob', () => {
|
|
it('should process successfully', async () => {
|
|
const job = createMockJob<AnalyticsJobData>({ reportDate: '2023-10-27' } as AnalyticsJobData);
|
|
|
|
const promise = service.processDailyReportJob(job);
|
|
|
|
// Fast-forward time to bypass the 10s delay
|
|
await vi.advanceTimersByTimeAsync(10000);
|
|
|
|
const result = await promise;
|
|
|
|
expect(result).toEqual({ status: 'success', reportDate: '2023-10-27' });
|
|
expect(logger.child).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
jobId: 'job-123',
|
|
reportDate: '2023-10-27',
|
|
}),
|
|
);
|
|
expect(mockLoggerInstance.info).toHaveBeenCalledWith('Picked up daily analytics job.');
|
|
expect(mockLoggerInstance.info).toHaveBeenCalledWith(
|
|
'Successfully generated report for 2023-10-27.',
|
|
);
|
|
});
|
|
|
|
it('should handle failure when reportDate is FAIL', async () => {
|
|
const job = createMockJob<AnalyticsJobData>({ reportDate: 'FAIL' } as AnalyticsJobData);
|
|
|
|
const promise = service.processDailyReportJob(job);
|
|
|
|
await expect(promise).rejects.toThrow('This is a test failure for the analytics job.');
|
|
|
|
expect(mockLoggerInstance.error).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
err: expect.any(Error),
|
|
attemptsMade: 1,
|
|
}),
|
|
'Daily analytics job failed.',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('processWeeklyReportJob', () => {
|
|
it('should process successfully', async () => {
|
|
const job = createMockJob<WeeklyAnalyticsJobData>({
|
|
reportYear: 2023,
|
|
reportWeek: 43,
|
|
} as WeeklyAnalyticsJobData);
|
|
|
|
const promise = service.processWeeklyReportJob(job);
|
|
|
|
await vi.advanceTimersByTimeAsync(30000);
|
|
|
|
const result = await promise;
|
|
|
|
expect(result).toEqual({ status: 'success', reportYear: 2023, reportWeek: 43 });
|
|
expect(logger.child).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
jobId: 'job-123',
|
|
reportYear: 2023,
|
|
reportWeek: 43,
|
|
}),
|
|
);
|
|
expect(mockLoggerInstance.info).toHaveBeenCalledWith('Picked up weekly analytics job.');
|
|
expect(mockLoggerInstance.info).toHaveBeenCalledWith(
|
|
'Successfully generated weekly report for week 43, 2023.',
|
|
);
|
|
});
|
|
|
|
it('should handle errors during processing', async () => {
|
|
const job = createMockJob<WeeklyAnalyticsJobData>({
|
|
reportYear: 2023,
|
|
reportWeek: 43,
|
|
} as WeeklyAnalyticsJobData);
|
|
|
|
// Make the second info call throw to simulate an error inside the try block
|
|
mockLoggerInstance.info
|
|
.mockImplementationOnce(() => {}) // "Picked up..."
|
|
.mockImplementationOnce(() => {
|
|
throw new Error('Processing failed');
|
|
}); // "Successfully generated..."
|
|
|
|
// Get the promise from the service method.
|
|
const promise = service.processWeeklyReportJob(job);
|
|
|
|
// Capture the expectation promise BEFORE triggering the rejection.
|
|
const expectation = expect(promise).rejects.toThrow('Processing failed');
|
|
|
|
// Advance timers to trigger the part of the code that throws.
|
|
await vi.advanceTimersByTimeAsync(30000);
|
|
|
|
// Await the expectation to ensure assertions ran.
|
|
await expectation;
|
|
|
|
// Verify the side effect (error logging) after the rejection is confirmed.
|
|
expect(mockLoggerInstance.error).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
err: expect.any(Error),
|
|
attemptsMade: 1,
|
|
}),
|
|
'Weekly analytics job failed.',
|
|
);
|
|
});
|
|
});
|
|
}); |