import { RetryStrategy } from '../src/index.js'; function createJob(retryCount = 0, maxAttempts = 4) { return { id: 'job-1', status: 'active' as const, data: {}, currentPhase: 'run', phases: [], phaseResults: {}, progress: 0, progressMessage: null, error: null, retryCount, maxAttempts, webhookUrl: null, webhookSent: false, createdAt: new Date().toISOString(), startedAt: null, completedAt: null, updatedAt: new Date().toISOString(), scheduledAt: null, cancelledAt: null, }; } describe('RetryStrategy', () => { it('uses exponential backoff for recoverable errors', async () => { const strategy = new RetryStrategy({ maxAttempts: 4, baseDelayMs: 100, classifyError: async () => 'recoverable', }); const decision = await strategy.shouldRetry(new Error('boom'), createJob(1)); expect(decision.retry).toBe(true); expect(decision.delayMs).toBe(200); expect(decision.disposition).toBe('recoverable'); }); it('uses fixed backoff when configured', async () => { const strategy = new RetryStrategy({ maxAttempts: 4, strategy: 'fixed', baseDelayMs: 75, classifyError: async () => 'recoverable', }); const decision = await strategy.shouldRetry(new Error('boom'), createJob(2)); expect(decision.retry).toBe(true); expect(decision.delayMs).toBe(75); }); it('uses linear backoff and caps to maxDelayMs', async () => { const strategy = new RetryStrategy({ maxAttempts: 6, strategy: 'linear', baseDelayMs: 100, maxDelayMs: 250, classifyError: async () => 'recoverable', }); const decision = await strategy.shouldRetry(new Error('boom'), createJob(4, 6)); expect(decision.retry).toBe(true); expect(decision.delayMs).toBe(250); }); it('stops retrying when max attempts are exhausted', async () => { const strategy = new RetryStrategy({ maxAttempts: 3, classifyError: async () => 'recoverable', }); const decision = await strategy.shouldRetry(new Error('boom'), createJob(2, 3)); expect(decision).toEqual({ retry: false, delayMs: 0, disposition: 'recoverable', }); }); it('does not retry fatal errors', async () => { const strategy = new RetryStrategy({ maxAttempts: 4, classifyError: async () => 'fatal', }); const decision = await strategy.shouldRetry(new Error('fatal'), createJob()); expect(decision).toEqual({ retry: false, delayMs: 0, disposition: 'fatal', }); }); });