-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathclient.retry.test.ts
More file actions
61 lines (56 loc) · 2.42 KB
/
client.retry.test.ts
File metadata and controls
61 lines (56 loc) · 2.42 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
import {describe, it, expect} from 'vitest';
import {
compute_backoff,
RETRY_BASE_MS,
RETRY_MAX_MS_DEFAULT,
} from '../../utils/client';
// PR-11: the per-request retry config + jitter is the mechanism that
// makes the AI-Flow concurrent-job cap (429) recoverable client-side.
// These tests cover the pure backoff math; the integration tests for
// the AI-trigger wiring live in commands/scraper.test.ts.
describe('utils/client.compute_backoff', ()=>{
it('grows exponentially with attempt', ()=>{
// With base=1000, max=large: attempt 0 → exp=1000 → range [500,1000]
// attempt 1 → exp=2000 → range [1000,2000]; etc. Take min of many
// samples so jitter doesn't flake the test.
const sample_min = (attempt: number)=>{
let m = Infinity;
for (let i = 0; i < 50; i++)
{
const d = compute_backoff(attempt, 1000, 1_000_000);
if (d < m) m = d;
}
return m;
};
expect(sample_min(0)).toBeGreaterThanOrEqual(500);
expect(sample_min(1)).toBeGreaterThanOrEqual(1000);
expect(sample_min(2)).toBeGreaterThanOrEqual(2000);
});
it('honors the max_ms ceiling', ()=>{
// At attempt 20, base * 2^20 would be huge; max_ms should cap it.
for (let i = 0; i < 50; i++)
{
const d = compute_backoff(20, 1000, 5000);
expect(d).toBeLessThanOrEqual(5000);
}
});
it('uses full-jitter (delay falls in [exp/2, exp])', ()=>{
// Sample many to ensure both halves of the range are reachable.
const samples: number[] = [];
for (let i = 0; i < 200; i++)
samples.push(compute_backoff(0, 1000, 1_000_000));
// exp = 1000, so range is [500, 1000].
expect(Math.min(...samples)).toBeGreaterThanOrEqual(500);
expect(Math.max(...samples)).toBeLessThanOrEqual(1000);
// With 200 samples we'd expect both halves to be hit.
const below_mid = samples.filter(d=>d < 750).length;
const above_mid = samples.filter(d=>d >= 750).length;
expect(below_mid).toBeGreaterThan(20);
expect(above_mid).toBeGreaterThan(20);
});
it('exported defaults match the documented short schedule', ()=>{
// Sanity: don't accidentally make every command wait minutes.
expect(RETRY_BASE_MS).toBe(500);
expect(RETRY_MAX_MS_DEFAULT).toBe(16_000);
});
});