forked from SmartDropLabs/smartdrop-backend
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwebhookRetryQueueHealth.test.js
More file actions
131 lines (98 loc) · 4.08 KB
/
Copy pathwebhookRetryQueueHealth.test.js
File metadata and controls
131 lines (98 loc) · 4.08 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
'use strict';
/**
* Webhook retry worker queue-depth reporting (issue #235).
*/
const { createCacheMock } = require('./helpers/cacheMock');
const mockHelper = createCacheMock();
const { reset, redis } = mockHelper;
jest.mock('../src/services/cache', () => mockHelper.cacheMock);
jest.mock('../src/logger', () => ({
info: jest.fn(), warn: jest.fn(), error: jest.fn(), debug: jest.fn(),
}));
const mockAttempt = jest.fn();
jest.mock('../src/services/webhookDispatcher', () => ({
attempt: mockAttempt,
}));
const mockPopDueRetries = jest.fn();
const mockCountPendingRetries = jest.fn();
jest.mock('../src/repositories/deliveryRepository', () => ({
popDueRetries: mockPopDueRetries,
countPendingRetries: mockCountPendingRetries,
}));
const worker = require('../src/jobs/webhookRetryWorker');
beforeEach(() => {
reset();
mockAttempt.mockReset().mockResolvedValue({});
mockPopDueRetries.mockReset().mockResolvedValue([]);
mockCountPendingRetries.mockReset().mockResolvedValue(0);
});
afterEach(() => {
worker.stop();
});
describe('webhook retry worker queue stats', () => {
test('reports the pending retry queue depth', async () => {
mockCountPendingRetries.mockResolvedValue(17);
const stats = await worker.getQueueStats();
expect(stats.pendingRetries).toBe(17);
});
test('distinguishes an unreadable queue from an empty one', async () => {
mockCountPendingRetries.mockResolvedValue(null);
const stats = await worker.getQueueStats();
expect(stats.pendingRetries).toBeNull();
});
test('records the size of the last claimed batch', async () => {
// totalRetriesProcessed is a lifetime counter on module state, so it is
// asserted as a delta rather than an absolute.
const before = (await worker.getQueueStats()).totalRetriesProcessed;
mockPopDueRetries.mockResolvedValue(['dlv_1', 'dlv_2', 'dlv_3']);
await worker.tick();
const stats = await worker.getQueueStats();
expect(stats.lastBatchSize).toBe(3);
expect(stats.totalRetriesProcessed - before).toBe(3);
});
test('an empty poll records a batch size of zero, not a stale previous size', async () => {
mockPopDueRetries.mockResolvedValueOnce(['dlv_1', 'dlv_2']);
await worker.tick();
mockPopDueRetries.mockResolvedValueOnce([]);
await worker.tick();
const stats = await worker.getQueueStats();
expect(stats.lastBatchSize).toBe(0);
});
test('reports an average delivery latency once retries have been attempted', async () => {
mockPopDueRetries.mockResolvedValue(['dlv_1', 'dlv_2']);
await worker.tick();
const stats = await worker.getQueueStats();
expect(stats.avgDeliveryLatencyMs).not.toBeNull();
expect(stats.avgDeliveryLatencyMs).toBeGreaterThanOrEqual(0);
});
test('reports a null average latency before any retry has been attempted', async () => {
// The worker keeps its counters in module state, so a freshly loaded
// copy is the only way to observe the pre-first-retry values regardless
// of the order tests run in.
let freshWorker;
jest.isolateModules(() => {
freshWorker = require('../src/jobs/webhookRetryWorker');
});
const stats = await freshWorker.getQueueStats();
expect(stats.avgDeliveryLatencyMs).toBeNull();
expect(stats.totalRetriesProcessed).toBe(0);
});
test('counts a retry that threw toward latency, since a timeout is the case that matters', async () => {
const before = (await worker.getQueueStats()).totalRetriesProcessed;
mockPopDueRetries.mockResolvedValue(['dlv_boom']);
mockAttempt.mockRejectedValue(new Error('delivery blew up'));
await worker.tick();
const stats = await worker.getQueueStats();
expect(stats.totalRetriesProcessed - before).toBe(1);
expect(stats.avgDeliveryLatencyMs).not.toBeNull();
});
test('getHealth carries the throughput fields alongside liveness', async () => {
mockPopDueRetries.mockResolvedValue(['dlv_1']);
await worker.tick();
const health = worker.getHealth();
expect(health).toEqual(expect.objectContaining({
lastBatchSize: 1,
avgDeliveryLatencyMs: expect.any(Number),
}));
});
});