map(new GraphException('auth failed', 401), attempt: 1, maxAttempts: 3); expect($mapped['shouldRetry'])->toBeFalse(); expect($mapped['error_code'])->toBe(RunErrorMapper::ERROR_TOKEN_EXPIRED); }); it('marks 403 as permission missing without retry', function () { $mapper = app(RunErrorMapper::class); $mapped = $mapper->map(new GraphException('forbidden', 403), attempt: 1, maxAttempts: 3); expect($mapped['shouldRetry'])->toBeFalse(); expect($mapped['error_code'])->toBe(RunErrorMapper::ERROR_PERMISSION_MISSING); }); it('retries throttling with backoff', function () { $mapper = app(RunErrorMapper::class); $mapped = $mapper->map(new GraphException('throttled', 429), attempt: 1, maxAttempts: 3); expect($mapped['shouldRetry'])->toBeTrue(); expect($mapped['delay'])->toBe(60); expect($mapped['error_code'])->toBe(RunErrorMapper::ERROR_GRAPH_THROTTLE); }); it('retries service unavailable with backoff', function () { $mapper = app(RunErrorMapper::class); $mapped = $mapper->map(new GraphException('unavailable', 503), attempt: 2, maxAttempts: 3); expect($mapped['shouldRetry'])->toBeTrue(); expect($mapped['delay'])->toBe(300); expect($mapped['error_code'])->toBe(RunErrorMapper::ERROR_GRAPH_UNAVAILABLE); });