more TS fixes + tests
This commit is contained in:
133
src/services/backgroundJobService.test.ts
Normal file
133
src/services/backgroundJobService.test.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
// src/services/backgroundJobService.test.ts
|
||||
import { describe, it, expect, vi, beforeEach, type Mocked } from 'vitest';
|
||||
import cron from 'node-cron';
|
||||
import * as db from './db';
|
||||
import { logger } from './logger.server';
|
||||
import { sendDealNotificationEmail } from './emailService.server';
|
||||
import { runDailyDealCheck, startBackgroundJobs } from './backgroundJobService';
|
||||
import { WatchedItemDeal } from '../types';
|
||||
import { AdminUserView } from './db/admin';
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock('node-cron');
|
||||
vi.mock('./db');
|
||||
vi.mock('./logger.server');
|
||||
vi.mock('./emailService.server');
|
||||
|
||||
const mockedDb = db as Mocked<typeof db>;
|
||||
const mockedCron = cron as Mocked<typeof cron>;
|
||||
const mockedLogger = logger as Mocked<typeof logger>;
|
||||
const mockedEmailService = { sendDealNotificationEmail } as Mocked<{ sendDealNotificationEmail: typeof sendDealNotificationEmail }>;
|
||||
|
||||
describe('Background Job Service', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('runDailyDealCheck', () => {
|
||||
const mockUsers: AdminUserView[] = [
|
||||
{ user_id: 'user-1', email: 'user1@test.com', full_name: 'User One', role: 'user', created_at: '', avatar_url: null },
|
||||
{ user_id: 'user-2', email: 'user2@test.com', full_name: 'User Two', role: 'user', created_at: '', avatar_url: null },
|
||||
];
|
||||
|
||||
const mockDeals: WatchedItemDeal[] = [
|
||||
{ master_item_id: 1, item_name: 'Apples', best_price_in_cents: 199, store_name: 'Green Grocer', flyer_id: 101, valid_to: '2024-10-20' },
|
||||
];
|
||||
|
||||
it('should do nothing if no users are found', async () => {
|
||||
mockedDb.getAllUsers.mockResolvedValue([]);
|
||||
|
||||
await runDailyDealCheck();
|
||||
|
||||
expect(mockedLogger.info).toHaveBeenCalledWith('[BackgroundJob] Starting daily deal check for all users...');
|
||||
expect(mockedLogger.info).toHaveBeenCalledWith('[BackgroundJob] No users found. Skipping deal check.');
|
||||
expect(mockedDb.getBestSalePricesForUser).not.toHaveBeenCalled();
|
||||
expect(mockedEmailService.sendDealNotificationEmail).not.toHaveBeenCalled();
|
||||
expect(mockedDb.createBulkNotifications).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should process users but not send notifications if no deals are found', async () => {
|
||||
mockedDb.getAllUsers.mockResolvedValue([mockUsers[0]]);
|
||||
mockedDb.getBestSalePricesForUser.mockResolvedValue([]);
|
||||
|
||||
await runDailyDealCheck();
|
||||
|
||||
expect(mockedDb.getBestSalePricesForUser).toHaveBeenCalledWith('user-1');
|
||||
expect(mockedEmailService.sendDealNotificationEmail).not.toHaveBeenCalled();
|
||||
expect(mockedDb.createBulkNotifications).not.toHaveBeenCalled();
|
||||
expect(mockedLogger.info).toHaveBeenCalledWith('[BackgroundJob] Daily deal check completed successfully.');
|
||||
});
|
||||
|
||||
it('should create notifications and send emails when deals are found', async () => {
|
||||
mockedDb.getAllUsers.mockResolvedValue(mockUsers);
|
||||
mockedDb.getBestSalePricesForUser.mockResolvedValue(mockDeals);
|
||||
|
||||
await runDailyDealCheck();
|
||||
|
||||
// Check that it processed both users
|
||||
expect(mockedDb.getBestSalePricesForUser).toHaveBeenCalledTimes(2);
|
||||
expect(mockedDb.getBestSalePricesForUser).toHaveBeenCalledWith('user-1');
|
||||
expect(mockedDb.getBestSalePricesForUser).toHaveBeenCalledWith('user-2');
|
||||
|
||||
// Check that emails were sent for both users
|
||||
expect(mockedEmailService.sendDealNotificationEmail).toHaveBeenCalledTimes(2);
|
||||
expect(mockedEmailService.sendDealNotificationEmail).toHaveBeenCalledWith('user1@test.com', 'User One', mockDeals);
|
||||
|
||||
// Check that in-app notifications were created for both users
|
||||
expect(mockedDb.createBulkNotifications).toHaveBeenCalledTimes(1);
|
||||
const notificationPayload = mockedDb.createBulkNotifications.mock.calls[0][0];
|
||||
expect(notificationPayload).toHaveLength(2);
|
||||
expect(notificationPayload[0]).toEqual({
|
||||
user_id: 'user-1',
|
||||
content: 'You have 1 new deal(s) on your watched items!',
|
||||
link_url: '/dashboard/deals',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle and log errors for individual users without stopping the process', async () => {
|
||||
mockedDb.getAllUsers.mockResolvedValue(mockUsers);
|
||||
// First user fails, second succeeds
|
||||
mockedDb.getBestSalePricesForUser
|
||||
.mockRejectedValueOnce(new Error('User 1 DB Error'))
|
||||
.mockResolvedValueOnce(mockDeals);
|
||||
|
||||
await runDailyDealCheck();
|
||||
|
||||
// Check that it logged the error for user 1
|
||||
expect(mockedLogger.error).toHaveBeenCalledWith(
|
||||
expect.stringContaining('Failed to process deals for user user-1'),
|
||||
expect.any(Object)
|
||||
);
|
||||
|
||||
// Check that it still processed user 2 successfully
|
||||
expect(mockedEmailService.sendDealNotificationEmail).toHaveBeenCalledTimes(1);
|
||||
expect(mockedEmailService.sendDealNotificationEmail).toHaveBeenCalledWith('user2@test.com', 'User Two', mockDeals);
|
||||
expect(mockedDb.createBulkNotifications).toHaveBeenCalledTimes(1);
|
||||
expect(mockedDb.createBulkNotifications.mock.calls[0][0]).toHaveLength(1); // Only one notification created
|
||||
});
|
||||
|
||||
it('should log a critical error if getAllUsers fails', async () => {
|
||||
mockedDb.getAllUsers.mockRejectedValue(new Error('Critical DB Failure'));
|
||||
|
||||
await runDailyDealCheck();
|
||||
|
||||
expect(mockedLogger.error).toHaveBeenCalledWith(
|
||||
'[BackgroundJob] A critical error occurred during the daily deal check:',
|
||||
expect.any(Object)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('startBackgroundJobs', () => {
|
||||
it('should schedule the cron job with the correct schedule and function', () => {
|
||||
startBackgroundJobs();
|
||||
|
||||
expect(mockedCron.schedule).toHaveBeenCalledTimes(1);
|
||||
// Check the schedule string
|
||||
expect(mockedCron.schedule).toHaveBeenCalledWith('0 2 * * *', expect.any(Function));
|
||||
// Check that the function passed is indeed runDailyDealCheck
|
||||
expect(mockedCron.schedule.mock.calls[0][1]).toBe(runDailyDealCheck);
|
||||
expect(mockedLogger.info).toHaveBeenCalledWith('[BackgroundJob] Cron job for daily deal checks has been scheduled.');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user