import { describe, expect, it } from 'bun:test'; import { createJobHandler } from '../src/utils/create-job-handler'; describe('createJobHandler', () => { interface TestPayload { userId: string; action: string; data?: any; } interface TestResult { success: boolean; processedBy: string; timestamp: Date; } it('should create a type-safe job handler function', () => { const handler = createJobHandler(async job => { // Job should have correct payload type const { userId, action, data } = job.data; return { success: true, processedBy: userId, timestamp: new Date(), }; }); expect(typeof handler).toBe('function'); }); it('should execute handler with job data', async () => { const testPayload: TestPayload = { userId: 'user-123', action: 'process', data: { value: 42 }, }; const handler = createJobHandler(async job => { expect(job.data).toEqual(testPayload); expect(job.id).toBe('job-123'); expect(job.name).toBe('test-job'); return { success: true, processedBy: job.data.userId, timestamp: new Date(), }; }); // Create a mock job const mockJob = { id: 'job-123', name: 'test-job', data: testPayload, opts: {}, progress: () => {}, log: () => {}, updateProgress: async () => {}, }; const result = await handler(mockJob as any); expect(result.success).toBe(true); expect(result.processedBy).toBe('user-123'); expect(result.timestamp).toBeInstanceOf(Date); }); it('should handle errors in handler', async () => { const handler = createJobHandler(async job => { if (job.data.action === 'fail') { throw new Error('Handler error'); } return { success: true, processedBy: job.data.userId, timestamp: new Date(), }; }); const mockJob = { id: 'job-456', name: 'test-job', data: { userId: 'user-456', action: 'fail', }, opts: {}, progress: () => {}, log: () => {}, updateProgress: async () => {}, }; await expect(handler(mockJob as any)).rejects.toThrow('Handler error'); }); it('should support async operations', async () => { const handler = createJobHandler(async job => { // Simulate async operation await new Promise(resolve => setTimeout(resolve, 10)); return { success: true, processedBy: job.data.userId, timestamp: new Date(), }; }); const mockJob = { id: 'job-789', name: 'async-job', data: { userId: 'user-789', action: 'async-process', }, opts: {}, progress: () => {}, log: () => {}, updateProgress: async () => {}, }; const startTime = Date.now(); const result = await handler(mockJob as any); const endTime = Date.now(); expect(result.success).toBe(true); expect(endTime - startTime).toBeGreaterThanOrEqual(10); }); it('should maintain type safety for complex payloads', () => { interface ComplexPayload { user: { id: string; name: string; roles: string[]; }; request: { type: 'CREATE' | 'UPDATE' | 'DELETE'; resource: string; data: Record; }; metadata: { timestamp: Date; source: string; version: number; }; } interface ComplexResult { status: 'success' | 'failure'; changes: Array<{ field: string; oldValue: any; newValue: any; }>; audit: { performedBy: string; performedAt: Date; duration: number; }; } const handler = createJobHandler(async job => { const startTime = Date.now(); // Type-safe access to nested properties const userId = job.data.user.id; const requestType = job.data.request.type; const version = job.data.metadata.version; return { status: 'success', changes: [ { field: 'resource', oldValue: null, newValue: job.data.request.resource, }, ], audit: { performedBy: userId, performedAt: new Date(), duration: Date.now() - startTime, }, }; }); expect(typeof handler).toBe('function'); }); it('should work with job progress reporting', async () => { let progressValue = 0; const handler = createJobHandler(async job => { // Report progress await job.updateProgress(25); progressValue = 25; await new Promise(resolve => setTimeout(resolve, 10)); await job.updateProgress(50); progressValue = 50; await new Promise(resolve => setTimeout(resolve, 10)); await job.updateProgress(100); progressValue = 100; return { success: true, processedBy: job.data.userId, timestamp: new Date(), }; }); const mockJob = { id: 'job-progress', name: 'progress-job', data: { userId: 'user-progress', action: 'long-process', }, opts: {}, progress: () => progressValue, log: () => {}, updateProgress: async (value: number) => { progressValue = value; }, }; const result = await handler(mockJob as any); expect(result.success).toBe(true); expect(progressValue).toBe(100); }); });