stock-bot/libs/core/handlers/test/create-job-handler.test.ts
2025-06-25 11:38:23 -04:00

237 lines
5.6 KiB
TypeScript

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<TestPayload, TestResult>(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<TestPayload, TestResult>(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<TestPayload, TestResult>(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<TestPayload, TestResult>(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<string, any>;
};
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<ComplexPayload, ComplexResult>(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<TestPayload, TestResult>(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);
});
});