failover-error.test.js
4.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/**
* Unit Tests — engine/failover-error.js
*/
import { describe, it, expect } from 'vitest';
const { FailoverError, FailoverReason, classifyError, RETRYABLE_REASONS } = require('../../src/engine/failover-error');
describe('failover-error', () => {
describe('FailoverReason enum', () => {
it('should have all expected reasons', () => {
expect(FailoverReason.TIMEOUT).toBe('timeout');
expect(FailoverReason.RATE_LIMIT).toBe('rate-limit');
expect(FailoverReason.AUTH).toBe('auth');
expect(FailoverReason.NETWORK).toBe('network');
expect(FailoverReason.SPAWN_ERROR).toBe('spawn-error');
expect(FailoverReason.UNKNOWN).toBe('unknown');
});
it('should be frozen', () => {
expect(Object.isFrozen(FailoverReason)).toBe(true);
});
});
describe('RETRYABLE_REASONS', () => {
it('should include timeout', () => {
expect(RETRYABLE_REASONS.has('timeout')).toBe(true);
});
it('should include rate-limit', () => {
expect(RETRYABLE_REASONS.has('rate-limit')).toBe(true);
});
it('should not include auth', () => {
expect(RETRYABLE_REASONS.has('auth')).toBe(false);
});
it('should not include spawn-error', () => {
expect(RETRYABLE_REASONS.has('spawn-error')).toBe(false);
});
});
describe('classifyError', () => {
it('should classify timeout errors', () => {
const c = classifyError(new Error('Request timed out'));
expect(c.reason).toBe('timeout');
expect(c.retryable).toBe(true);
});
it('should classify overall-timeout', () => {
const c = classifyError(new Error('overall-timeout exceeded'));
expect(c.reason).toBe('timeout');
});
it('should classify no-output-timeout', () => {
const c = classifyError(new Error('no-output-timeout reached'));
expect(c.reason).toBe('no-output-timeout');
});
it('should classify rate limit errors', () => {
const c = classifyError(new Error('Rate limit exceeded, 429'));
expect(c.reason).toBe('rate-limit');
expect(c.retryable).toBe(true);
expect(c.delayMs).toBeGreaterThan(0);
});
it('should classify auth errors as non-retryable', () => {
const c = classifyError(new Error('401 Unauthorized access'));
expect(c.reason).toBe('auth');
expect(c.retryable).toBe(false);
});
it('should classify model not found', () => {
const c = classifyError(new Error('model not found'));
expect(c.reason).toBe('model-not-found');
expect(c.retryable).toBe(false);
});
it('should classify transport closed', () => {
const c = classifyError(new Error('transport is closed'));
expect(c.reason).toBe('transport-closed');
expect(c.retryable).toBe(true);
});
it('should classify spawn errors', () => {
const err = new Error('spawn ENOENT');
err.code = 'ENOENT';
const c = classifyError(err);
expect(c.reason).toBe('spawn-error');
expect(c.retryable).toBe(false);
});
it('should classify network errors', () => {
const err = new Error('connect ECONNREFUSED');
err.code = 'ECONNREFUSED';
const c = classifyError(err);
expect(c.reason).toBe('network');
expect(c.retryable).toBe(true);
});
it('should return unknown for unrecognized errors', () => {
const c = classifyError(new Error('Something completely unexpected'));
expect(c.reason).toBe('unknown');
expect(c.retryable).toBe(false);
});
it('should handle null/undefined error', () => {
const c = classifyError(null);
expect(c.reason).toBe('unknown');
});
});
describe('FailoverError class', () => {
it('should wrap an error with classification', () => {
const original = new Error('Request timed out');
const fe = FailoverError.from(original);
expect(fe).toBeInstanceOf(FailoverError);
expect(fe.reason).toBe('timeout');
expect(fe.retryable).toBe(true);
expect(fe.original).toBe(original);
expect(fe.message).toBe('Request timed out');
});
it('should not double-wrap', () => {
const original = new Error('rate limit');
const fe1 = FailoverError.from(original);
const fe2 = FailoverError.from(fe1);
expect(fe2).toBe(fe1); // same reference
});
it('should have name = FailoverError', () => {
const fe = FailoverError.from(new Error('test'));
expect(fe.name).toBe('FailoverError');
});
});
});