comq 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,264 +0,0 @@
1
- 'use strict'
2
-
3
- const { Watch } = require('../../source/shards/watch')
4
-
5
- /** @type {Record<string, comq.shards.Resolution | Error>} */
6
- let records
7
-
8
- /** @type {Watch} */
9
- let watch
10
-
11
- /** @type {jest.MockedFunction<Function>} */
12
- let change
13
-
14
- /** @type {jest.MockedFunction<Function>} */
15
- let error
16
-
17
- const INTERVAL = 1000
18
- const SETTLE = 5000
19
- const hosts = ['one', 'two']
20
-
21
- const tick = (ms = INTERVAL) => jest.advanceTimersByTimeAsync(ms)
22
-
23
- /**
24
- * @param {comq.shards.Timing} [timing]
25
- * @param {(host: string) => boolean} [urgent]
26
- */
27
- async function start (timing = { interval: INTERVAL, settle: SETTLE }, urgent) {
28
- watch = new Watch(hosts, timing, urgent)
29
- change = jest.fn()
30
- error = jest.fn()
31
-
32
- watch.on('change', change)
33
- watch.on('error', error)
34
-
35
- return await watch.start()
36
- }
37
-
38
- beforeEach(() => {
39
- jest.useFakeTimers()
40
-
41
- records = { one: { addresses: ['10.0.0.1'], ttl: 5 }, two: { addresses: ['10.0.0.2'], ttl: 5 } }
42
-
43
- global.COMQ_TESTING_LOOKUP = async (host) => {
44
- if (records[host] instanceof Error) throw records[host]
45
-
46
- return records[host]
47
- }
48
- })
49
-
50
- afterEach(() => {
51
- watch?.stop()
52
- jest.useRealTimers()
53
-
54
- delete global.COMQ_TESTING_LOOKUP
55
- })
56
-
57
- it('should tell the address of each name', async () => {
58
- const addresses = await start()
59
-
60
- expect(Object.fromEntries(addresses)).toStrictEqual({ one: '10.0.0.1', two: '10.0.0.2' })
61
- })
62
-
63
- it('should not tell of what has not changed', async () => {
64
- await start()
65
- await tick(SETTLE * 2)
66
-
67
- expect(change).not.toHaveBeenCalled()
68
- })
69
-
70
- it('should tell of a change once it has held for the settle time', async () => {
71
- await start()
72
-
73
- records.two = { addresses: ['10.0.0.3'], ttl: 5 }
74
-
75
- await tick(SETTLE)
76
-
77
- expect(change).not.toHaveBeenCalled()
78
-
79
- await tick()
80
-
81
- expect(change).toHaveBeenCalledTimes(1)
82
- expect(Object.fromEntries(change.mock.calls[0][0])).toStrictEqual({ one: '10.0.0.1', two: '10.0.0.3' })
83
- })
84
-
85
- it('should start the settle time anew whenever the answer changes', async () => {
86
- await start()
87
-
88
- records.two = { addresses: ['10.0.0.3'], ttl: 5 }
89
-
90
- await tick(SETTLE - INTERVAL)
91
-
92
- records.one = { addresses: ['10.0.0.3'], ttl: 5 }
93
-
94
- await tick(SETTLE - INTERVAL)
95
-
96
- expect(change).not.toHaveBeenCalled()
97
-
98
- await tick(INTERVAL * 2)
99
-
100
- expect(change).toHaveBeenCalledTimes(1)
101
- expect(Object.fromEntries(change.mock.calls[0][0])).toStrictEqual({ one: '10.0.0.3', two: '10.0.0.3' })
102
- })
103
-
104
- it('should not act on an answer that goes back and forth', async () => {
105
- await start()
106
-
107
- for (let i = 0; i < 20; i++) {
108
- records.two = { addresses: [i % 2 === 0 ? '10.0.0.3' : '10.0.0.2'], ttl: 5 }
109
-
110
- await tick()
111
- }
112
-
113
- expect(change).not.toHaveBeenCalled()
114
- })
115
-
116
- it('should forget a change that was taken back', async () => {
117
- await start()
118
-
119
- records.two = { addresses: ['10.0.0.3'], ttl: 5 }
120
-
121
- await tick(INTERVAL * 2)
122
-
123
- records.two = { addresses: ['10.0.0.2'], ttl: 5 }
124
-
125
- await tick(SETTLE * 2)
126
-
127
- expect(change).not.toHaveBeenCalled()
128
- })
129
-
130
- it('should keep the address of a name that fails to resolve', async () => {
131
- await start()
132
-
133
- const exception = new Error('ENOTFOUND')
134
-
135
- records.two = exception
136
-
137
- await tick(SETTLE * 2)
138
-
139
- expect(change).not.toHaveBeenCalled()
140
- expect(error).toHaveBeenCalledWith(exception)
141
- })
142
-
143
- it('should keep the address of a name that resolves to nothing', async () => {
144
- await start()
145
-
146
- records.two = { addresses: [] }
147
-
148
- await tick(SETTLE * 2)
149
-
150
- expect(change).not.toHaveBeenCalled()
151
- expect(error).toHaveBeenCalled()
152
- })
153
-
154
- it('should keep the address in use while it is listed', async () => {
155
- await start()
156
-
157
- records.two = { addresses: ['10.0.0.1', '10.0.0.2'], ttl: 5 }
158
-
159
- await tick(SETTLE * 2)
160
-
161
- expect(change).not.toHaveBeenCalled()
162
- })
163
-
164
- it('should take what an urgent name resolves to at once', async () => {
165
- await start(undefined, (host) => host === 'two')
166
-
167
- records.one = { addresses: ['10.0.0.4'], ttl: 5 }
168
- records.two = { addresses: ['10.0.0.3'], ttl: 5 }
169
-
170
- await tick()
171
-
172
- expect(change).toHaveBeenCalledTimes(1)
173
- expect(Object.fromEntries(change.mock.calls[0][0])).toStrictEqual({ one: '10.0.0.1', two: '10.0.0.3' })
174
- })
175
-
176
- it('should look at once when poked', async () => {
177
- await start(undefined, () => true)
178
-
179
- records.two = { addresses: ['10.0.0.3'], ttl: 5 }
180
-
181
- watch.poke()
182
-
183
- await tick(0)
184
-
185
- expect(change).toHaveBeenCalledTimes(1)
186
- })
187
-
188
- it('should resolve until every name is known', async () => {
189
- global.COMQ_TESTING_RESOLVE_BACKOFF = { base: 10, dispersion: 0 }
190
-
191
- records.two = new Error('ENOTFOUND')
192
-
193
- let addresses
194
-
195
- const starting = start().then((result) => { addresses = result })
196
-
197
- await tick(100)
198
-
199
- expect(addresses).toBeUndefined()
200
-
201
- records.two = { addresses: ['10.0.0.2'], ttl: 5 }
202
-
203
- await tick(100)
204
- await starting
205
-
206
- expect(addresses.get('two')).toStrictEqual('10.0.0.2')
207
-
208
- delete global.COMQ_TESTING_RESOLVE_BACKOFF
209
- })
210
-
211
- it('should stop', async () => {
212
- await start()
213
-
214
- records.two = { addresses: ['10.0.0.3'], ttl: 5 }
215
- watch.stop()
216
-
217
- await tick(SETTLE * 2)
218
-
219
- expect(change).not.toHaveBeenCalled()
220
- expect(jest.getTimerCount()).toStrictEqual(0)
221
- })
222
-
223
- describe('timing', () => {
224
- it('should settle and linger for the longest TTL seen', async () => {
225
- records.one = { addresses: ['10.0.0.1'], ttl: 300 }
226
- records.two = { addresses: ['10.0.0.2'], ttl: 120 }
227
-
228
- await start({})
229
-
230
- records.one = { addresses: ['10.0.0.1'], ttl: 10 }
231
-
232
- await tick(10_000)
233
-
234
- expect(watch.settle).toStrictEqual(300_000)
235
- expect(watch.linger).toStrictEqual(300_000)
236
- })
237
-
238
- it('should settle for no less than 30 seconds', async () => {
239
- await start({})
240
-
241
- expect(watch.settle).toStrictEqual(30_000)
242
- expect(watch.linger).toStrictEqual(30_000)
243
- })
244
-
245
- it('should give a name answered without a TTL 60 seconds', async () => {
246
- records.two = { addresses: ['10.0.0.2'] }
247
-
248
- await start({})
249
-
250
- expect(watch.settle).toStrictEqual(60_000)
251
- })
252
-
253
- it('should resolve every 10 seconds unless told otherwise', async () => {
254
- await start({})
255
-
256
- expect(watch.interval).toStrictEqual(10_000)
257
- })
258
-
259
- it('should take what it is told', async () => {
260
- await start({ interval: 1, settle: 2, linger: 3 })
261
-
262
- expect([watch.interval, watch.settle, watch.linger]).toStrictEqual([1, 2, 3])
263
- })
264
- })