import { afterEach, beforeEach, describe, expect, test } from 'bun:test'; import { handlerRegistry, QueueManager } 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); }); });