reworked queue lib
This commit is contained in:
parent
629ba2b8d4
commit
c05a7413dc
34 changed files with 3887 additions and 861 deletions
221
libs/queue/test/queue-integration.test.ts
Normal file
221
libs/queue/test/queue-integration.test.ts
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
|
||||
import { QueueManager, handlerRegistry } from '../src';
|
||||
|
||||
// Suppress Redis connection errors in tests
|
||||
process.on('unhandledRejection', (reason, promise) => {
|
||||
if (reason && typeof reason === 'object' && 'message' in reason) {
|
||||
const message = (reason as Error).message;
|
||||
if (message.includes('Connection is closed') ||
|
||||
message.includes('Connection is in monitoring mode')) {
|
||||
// Suppress these specific Redis errors in tests
|
||||
return;
|
||||
}
|
||||
}
|
||||
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
||||
});
|
||||
|
||||
describe('QueueManager Integration Tests', () => {
|
||||
let queueManager: QueueManager;
|
||||
|
||||
// Use local Redis/Dragonfly
|
||||
const redisConfig = {
|
||||
host: 'localhost',
|
||||
port: 6379,
|
||||
password: '',
|
||||
db: 0,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
handlerRegistry.clear();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
if (queueManager) {
|
||||
try {
|
||||
await Promise.race([
|
||||
queueManager.shutdown(),
|
||||
new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('Shutdown timeout')), 3000)
|
||||
)
|
||||
]);
|
||||
} catch (error) {
|
||||
// Ignore shutdown errors in tests
|
||||
console.warn('Shutdown error:', error.message);
|
||||
} finally {
|
||||
queueManager = null as any;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear handler registry to prevent conflicts
|
||||
handlerRegistry.clear();
|
||||
|
||||
// Add delay to allow connections to close
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
});
|
||||
|
||||
test('should initialize queue manager', async () => {
|
||||
queueManager = new QueueManager({
|
||||
queueName: 'test-queue',
|
||||
redis: redisConfig,
|
||||
workers: 1,
|
||||
concurrency: 5,
|
||||
});
|
||||
|
||||
await queueManager.initialize();
|
||||
expect(queueManager.queueName).toBe('test-queue');
|
||||
});
|
||||
|
||||
test('should add and process a job', async () => {
|
||||
let processedPayload: any;
|
||||
|
||||
// Register handler
|
||||
handlerRegistry.register('test-handler', {
|
||||
'test-operation': async payload => {
|
||||
processedPayload = payload;
|
||||
return { success: true, data: payload };
|
||||
},
|
||||
});
|
||||
|
||||
queueManager = new QueueManager({
|
||||
queueName: 'test-queue',
|
||||
redis: redisConfig,
|
||||
workers: 1,
|
||||
});
|
||||
|
||||
await queueManager.initialize();
|
||||
|
||||
// Add job
|
||||
const job = await queueManager.add('test-job', {
|
||||
handler: 'test-handler',
|
||||
operation: 'test-operation',
|
||||
payload: { message: 'Hello, Queue!' },
|
||||
});
|
||||
|
||||
expect(job.name).toBe('test-job');
|
||||
|
||||
// Wait for processing
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
expect(processedPayload).toEqual({ message: 'Hello, Queue!' });
|
||||
});
|
||||
|
||||
test('should handle job errors with retries', async () => {
|
||||
let attemptCount = 0;
|
||||
|
||||
handlerRegistry.register('retry-handler', {
|
||||
'failing-operation': async () => {
|
||||
attemptCount++;
|
||||
if (attemptCount < 3) {
|
||||
throw new Error(`Attempt ${attemptCount} failed`);
|
||||
}
|
||||
return { success: true };
|
||||
},
|
||||
});
|
||||
|
||||
queueManager = new QueueManager({
|
||||
queueName: 'test-queue-retry',
|
||||
redis: redisConfig,
|
||||
workers: 1,
|
||||
defaultJobOptions: {
|
||||
attempts: 3,
|
||||
backoff: {
|
||||
type: 'fixed',
|
||||
delay: 50,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await queueManager.initialize();
|
||||
|
||||
const job = await queueManager.add('retry-job', {
|
||||
handler: 'retry-handler',
|
||||
operation: 'failing-operation',
|
||||
payload: {},
|
||||
});
|
||||
|
||||
// Wait for retries
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
|
||||
const completed = await job.isCompleted();
|
||||
expect(completed).toBe(true);
|
||||
expect(attemptCount).toBe(3);
|
||||
});
|
||||
|
||||
test('should collect metrics when enabled', async () => {
|
||||
queueManager = new QueueManager({
|
||||
queueName: 'test-queue-metrics',
|
||||
redis: redisConfig,
|
||||
workers: 0,
|
||||
enableMetrics: true,
|
||||
});
|
||||
|
||||
await queueManager.initialize();
|
||||
|
||||
// Add some jobs
|
||||
await queueManager.add('job1', {
|
||||
handler: 'test',
|
||||
operation: 'test',
|
||||
payload: { id: 1 },
|
||||
});
|
||||
|
||||
await queueManager.add('job2', {
|
||||
handler: 'test',
|
||||
operation: 'test',
|
||||
payload: { id: 2 },
|
||||
});
|
||||
|
||||
const metrics = await queueManager.getMetrics();
|
||||
expect(metrics).toBeDefined();
|
||||
expect(metrics.waiting).toBeDefined();
|
||||
expect(metrics.active).toBeDefined();
|
||||
expect(metrics.completed).toBeDefined();
|
||||
expect(metrics.failed).toBeDefined();
|
||||
expect(metrics.processingTime).toBeDefined();
|
||||
expect(metrics.throughput).toBeDefined();
|
||||
});
|
||||
|
||||
test('should handle rate limiting when enabled', async () => {
|
||||
let processedCount = 0;
|
||||
|
||||
handlerRegistry.register('rate-limited-handler', {
|
||||
'limited-op': async () => {
|
||||
processedCount++;
|
||||
return { processed: true };
|
||||
},
|
||||
});
|
||||
|
||||
queueManager = new QueueManager({
|
||||
queueName: 'test-queue-rate',
|
||||
redis: redisConfig,
|
||||
workers: 1,
|
||||
enableRateLimit: true,
|
||||
rateLimitRules: [
|
||||
{
|
||||
level: 'handler',
|
||||
handler: 'rate-limited-handler',
|
||||
config: {
|
||||
points: 2, // 2 requests
|
||||
duration: 1, // per 1 second
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await queueManager.initialize();
|
||||
|
||||
// Add 3 jobs quickly
|
||||
for (let i = 0; i < 3; i++) {
|
||||
await queueManager.add(`job${i}`, {
|
||||
handler: 'rate-limited-handler',
|
||||
operation: 'limited-op',
|
||||
payload: { id: i },
|
||||
});
|
||||
}
|
||||
|
||||
// Wait for processing
|
||||
await new Promise(resolve => setTimeout(resolve, 200));
|
||||
|
||||
// Only 2 should be processed due to rate limit
|
||||
expect(processedCount).toBe(2);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue