amqplib 2.1.0 → 2.2.1

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,599 +0,0 @@
1
- const { describe, it } = require('node:test');
2
- const assert = require('node:assert');
3
- const api = require('../channel_api');
4
- const util = require('./lib/util');
5
- const schedule = util.schedule;
6
- const randomString = util.randomString;
7
- const promisify = require('node:util').promisify;
8
-
9
- const URL = process.env.URL || 'amqp://localhost';
10
- const QUEUE_OPTS = { durable: false };
11
- const EX_OPTS = { durable: false };
12
-
13
- function ignore() {}
14
-
15
- describe('connect', () => {
16
- it('at all', () => connect().then((c) => c.close()));
17
-
18
- it('create channel', () => withChannel(ignore));
19
- });
20
-
21
- describe('updateSecret', () => {
22
- it('updateSecret', () => {
23
- return connect().then((c) =>
24
- c.updateSecret(Buffer.from('new secret'), 'no reason')
25
- .finally(() => c.close())
26
- );
27
- });
28
-
29
- it('emits update-secret-ok event', () => {
30
- return connect().then((c) =>
31
- new Promise((resolve, reject) => {
32
- c.on('update-secret-ok', resolve);
33
- c.updateSecret(Buffer.from('new secret'), 'no reason').catch(reject);
34
- }).finally(() => c.close())
35
- );
36
- });
37
- });
38
-
39
- describe('assert, check, delete', () => {
40
- it('assert and check queue', () => {
41
- return withChannel((ch) => {
42
- return ch.assertQueue('test.check-queue', QUEUE_OPTS)
43
- .then((qok) => assert.strictEqual(qok.queue, 'test.check-queue'))
44
- .then(() => ch.checkQueue('test.check-queue'))
45
- .then((qok) => assert.strictEqual(qok.queue, 'test.check-queue'))
46
- });
47
- });
48
-
49
- it('assert and check exchange', () => {
50
- return withChannel((ch) => {
51
- return ch.assertExchange('test.check-exchange', 'direct', EX_OPTS)
52
- .then((eok) => assert.strictEqual(eok.exchange, 'test.check-exchange'))
53
- .then(() => ch.checkExchange('test.check-exchange'))
54
- .then((eok) => assert.ok(eok));
55
- });
56
- });
57
-
58
- it('fail on reasserting queue with different options', () => {
59
- return withChannel((ch) => {
60
- ch.once('error', ignore);
61
- return ch.assertQueue('test.reassert-queue', { durable: false, autoDelete: true })
62
- .then(() => assert.rejects(() => ch.assertQueue('test.reassert-queue', { durable: false, autoDelete: false }), (err) => {
63
- assert.match(err.message, /PRECONDITION_FAILED/);
64
- assert.strictEqual(406, err.code);
65
- assert.strictEqual(50, err.classId);
66
- assert.strictEqual(10, err.methodId);
67
- return true;
68
- }));
69
- });
70
- });
71
-
72
- it("fail on checking a queue that's not there", () => {
73
- return withChannel((ch) => {
74
- ch.once('error', ignore);
75
- return assert.rejects(() => ch.checkQueue(`test.random-${randomString()}`), (err) => {
76
- assert.match(err.message, /NOT_FOUND/);
77
- assert.strictEqual(404, err.code);
78
- assert.strictEqual(50, err.classId);
79
- assert.strictEqual(10, err.methodId);
80
- return true;
81
- });
82
- });
83
- });
84
-
85
- it("fail on checking an exchange that's not there", () => {
86
- return withChannel((ch) => {
87
- ch.once('error', ignore);
88
- return assert.rejects(() => ch.checkExchange(`test.random-${randomString()}`), (err) => {
89
- assert.match(err.message, /NOT_FOUND/);
90
- assert.strictEqual(404, err.code);
91
- return true;
92
- });
93
- });
94
- });
95
-
96
- it('fail on reasserting exchange with different type', () => {
97
- return withChannel((ch) => {
98
- ch.once('error', ignore);
99
- const ex = 'test.reassert-ex';
100
- return ch.assertExchange(ex, 'fanout', EX_OPTS)
101
- .then(() => assert.rejects(() => ch.assertExchange(ex, 'direct', EX_OPTS), (err) => {
102
- assert.match(err.message, /PRECONDITION_FAILED/);
103
- assert.strictEqual(406, err.code);
104
- return true;
105
- }));
106
- });
107
- });
108
-
109
- it('channel break on publishing to non-exchange', () => {
110
- return withChannel((ch) =>
111
- new Promise((resolve) => {
112
- ch.once('error', (err) => {
113
- assert.match(err.message, /NOT_FOUND/);
114
- assert.strictEqual(404, err.code);
115
- resolve();
116
- });
117
- ch.publish(randomString(), '', Buffer.from('foobar'));
118
- })
119
- );
120
- });
121
-
122
- it('delete queue', () => {
123
- return withChannel((ch) => {
124
- return ch.assertQueue('test.delete-queue', QUEUE_OPTS)
125
- .then(() => ch.deleteQueue('test.delete-queue'))
126
- .then(() => {
127
- ch.once('error', ignore);
128
- return assert.rejects(() => ch.checkQueue('test.delete-queue'), (err) => {
129
- assert.match(err.message, /NOT_FOUND/);
130
- assert.strictEqual(404, err.code);
131
- return true;
132
- });
133
- });
134
- });
135
- });
136
-
137
- it('delete exchange', () => {
138
- return withChannel((ch) => {
139
- return ch.assertExchange('test.delete-exchange', 'fanout', EX_OPTS)
140
- .then(() => ch.deleteExchange('test.delete-exchange'))
141
- .then(() => {
142
- ch.once('error', ignore);
143
- return assert.rejects(() => ch.checkExchange('test.delete-exchange'), (err) => {
144
- assert.match(err.message, /NOT_FOUND/);
145
- assert.strictEqual(404, err.code);
146
- return true;
147
- });
148
- });
149
- });
150
- });
151
- });
152
-
153
- describe('sendMessage', () => {
154
- it('send to queue and get from queue', () => {
155
- const msg = randomString();
156
- return withChannel((ch) =>
157
- ch.assertQueue('test.send-to-q', QUEUE_OPTS)
158
- .then(() => ch.purgeQueue('test.send-to-q'))
159
- .then(() => {
160
- ch.sendToQueue('test.send-to-q', Buffer.from(msg));
161
- return waitForMessages('test.send-to-q');
162
- })
163
- .then(() => ch.get('test.send-to-q', { noAck: true }))
164
- .then((m) => {
165
- assert(m);
166
- assert.equal(msg, m.content.toString());
167
- })
168
- );
169
- });
170
-
171
- it('send (and get) zero content to queue', () => {
172
- return withChannel((ch) =>
173
- ch.assertQueue('test.send-to-q', QUEUE_OPTS)
174
- .then(() => ch.purgeQueue('test.send-to-q'))
175
- .then(() => {
176
- ch.sendToQueue('test.send-to-q', Buffer.alloc(0));
177
- return waitForMessages('test.send-to-q');
178
- })
179
- .then(() => ch.get('test.send-to-q', { noAck: true }))
180
- .then((m) => {
181
- assert(m);
182
- assert.deepEqual(Buffer.alloc(0), m.content);
183
- })
184
- );
185
- });
186
- });
187
-
188
- describe('get', () => {
189
- it('returns false when queue is empty', () => {
190
- return withChannel((ch) =>
191
- ch.assertQueue('test.get-empty', QUEUE_OPTS)
192
- .then(() => ch.purgeQueue('test.get-empty'))
193
- .then(() => ch.get('test.get-empty', { noAck: true }))
194
- .then((m) => assert.strictEqual(false, m))
195
- );
196
- });
197
-
198
- it('returns a message when queue has messages', () => {
199
- const msg = randomString();
200
- return withChannel((ch) =>
201
- ch.assertQueue('test.get-message', QUEUE_OPTS)
202
- .then(() => ch.purgeQueue('test.get-message'))
203
- .then(() => {
204
- ch.sendToQueue('test.get-message', Buffer.from(msg));
205
- return waitForMessages('test.get-message');
206
- })
207
- .then(() => ch.get('test.get-message', { noAck: true }))
208
- .then((m) => {
209
- assert(m);
210
- assert.equal(msg, m.content.toString());
211
- })
212
- );
213
- });
214
-
215
- it('rejects with a proper error when queue does not exist', () => {
216
- return withChannel((ch) => {
217
- ch.once('error', ignore);
218
- return assert.rejects(() => ch.get('', {}), (err) => {
219
- assert(err instanceof Error);
220
- assert.match(err.message, /NOT_FOUND/);
221
- assert.strictEqual(404, err.code);
222
- assert.strictEqual(60, err.classId);
223
- assert.strictEqual(70, err.methodId);
224
- return true;
225
- });
226
- });
227
- });
228
- });
229
-
230
- describe('binding, consuming', () => {
231
- // bind, publish, get
232
- it('route message', () => {
233
- const msg = randomString();
234
- return withChannel((ch) =>
235
- ch.assertExchange('test.route-message', 'fanout', EX_OPTS)
236
- .then(() => ch.assertQueue('test.route-message-q', QUEUE_OPTS))
237
- .then(() => ch.purgeQueue('test.route-message-q'))
238
- .then(() => ch.bindQueue('test.route-message-q', 'test.route-message', '', {}))
239
- .then(() => {
240
- ch.publish('test.route-message', '', Buffer.from(msg));
241
- return waitForMessages('test.route-message-q');
242
- })
243
- .then(() => ch.get('test.route-message-q', { noAck: true }))
244
- .then((m) => {
245
- assert(m);
246
- assert.equal(msg, m.content.toString());
247
- })
248
- );
249
- });
250
-
251
- // send to queue, purge, get-empty
252
- it('purge queue', () => {
253
- return withChannel((ch) =>
254
- ch.assertQueue('test.purge-queue', QUEUE_OPTS)
255
- .then(() => {
256
- ch.sendToQueue('test.purge-queue', Buffer.from('foobar'));
257
- return waitForMessages('test.purge-queue');
258
- })
259
- .then(() => ch.purgeQueue('test.purge-queue'))
260
- .then(() => ch.get('test.purge-queue', { noAck: true }))
261
- .then((m) => {
262
- assert(!m); // get-empty
263
- })
264
- );
265
- });
266
-
267
- // bind again, unbind, publish, get-empty
268
- it('unbind queue', () => {
269
- const viabinding = randomString();
270
- const direct = randomString();
271
- return withChannel((ch) =>
272
- ch.assertExchange('test.unbind-queue-ex', 'fanout', EX_OPTS)
273
- .then(() => ch.assertQueue('test.unbind-queue', QUEUE_OPTS))
274
- .then(() => ch.purgeQueue('test.unbind-queue'))
275
- .then(() => ch.bindQueue('test.unbind-queue', 'test.unbind-queue-ex', '', {}))
276
- .then(() => {
277
- ch.publish('test.unbind-queue-ex', '', Buffer.from('foobar'));
278
- return waitForMessages('test.unbind-queue');
279
- })
280
- .then(() => ch.get('test.unbind-queue', { noAck: true }))
281
- .then((m) => assert(m)) // message got through!
282
- .then(() => ch.unbindQueue('test.unbind-queue', 'test.unbind-queue-ex', '', {}))
283
- .then(() => {
284
- // via the no-longer-existing binding
285
- ch.publish('test.unbind-queue-ex', '', Buffer.from(viabinding));
286
- // direct to the queue
287
- ch.sendToQueue('test.unbind-queue', Buffer.from(direct));
288
- return waitForMessages('test.unbind-queue');
289
- })
290
- .then(() => ch.get('test.unbind-queue'))
291
- .then((m) => {
292
- // the direct to queue message got through, the via-binding message (sent first) did not
293
- assert.equal(direct, m.content.toString());
294
- })
295
- );
296
- });
297
-
298
- // To some extent this is now just testing semantics of the server,
299
- // but we can at least try out a few settings, and consume.
300
- it('consume via exchange-exchange binding', () => {
301
- const msg = randomString();
302
- return withChannel((ch) =>
303
- ch.assertExchange('test.ex-ex-binding1', 'direct', EX_OPTS)
304
- .then(() => ch.assertExchange('test.ex-ex-binding2', 'fanout', { durable: false, internal: true }))
305
- .then(() => ch.assertQueue('test.ex-ex-binding-q', QUEUE_OPTS))
306
- .then(() => ch.purgeQueue('test.ex-ex-binding-q'))
307
- .then(() => ch.bindExchange('test.ex-ex-binding2', 'test.ex-ex-binding1', 'test.routing.key', {}))
308
- .then(() => ch.bindQueue('test.ex-ex-binding-q', 'test.ex-ex-binding2', '', {}))
309
- .then(() => new Promise((resolve, reject) => {
310
- ch.consume('test.ex-ex-binding-q', (m) => {
311
- if (m.content.toString() === msg) resolve();
312
- else reject(new Error('Wrong message'));
313
- }, { noAck: true }).then(() => {
314
- ch.publish('test.ex-ex-binding1', 'test.routing.key', Buffer.from(msg));
315
- });
316
- }))
317
- );
318
- });
319
-
320
- // bind again, unbind, publish, get-empty
321
- it('unbind exchange', () => {
322
- const viabinding = randomString();
323
- const direct = randomString();
324
- return withChannel((ch) =>
325
- ch.assertExchange('test.unbind-ex-source', 'fanout', EX_OPTS)
326
- .then(() => ch.assertExchange('test.unbind-ex-dest', 'fanout', EX_OPTS))
327
- .then(() => ch.assertQueue('test.unbind-ex-queue', QUEUE_OPTS))
328
- .then(() => ch.purgeQueue('test.unbind-ex-queue'))
329
- .then(() => ch.bindExchange('test.unbind-ex-dest', 'test.unbind-ex-source', '', {}))
330
- .then(() => ch.bindQueue('test.unbind-ex-queue', 'test.unbind-ex-dest', '', {}))
331
- .then(() => {
332
- ch.publish('test.unbind-ex-source', '', Buffer.from('foobar'));
333
- return waitForMessages('test.unbind-ex-queue');
334
- })
335
- .then(() => ch.get('test.unbind-ex-queue', { noAck: true }))
336
- .then((m) => assert(m)) // message got through!
337
- .then(() => ch.unbindExchange('test.unbind-ex-dest', 'test.unbind-ex-source', '', {}))
338
- .then(() => {
339
- // via the no-longer-existing binding
340
- ch.publish('test.unbind-ex-source', '', Buffer.from(viabinding));
341
- // direct to the queue
342
- ch.sendToQueue('test.unbind-ex-queue', Buffer.from(direct));
343
- return waitForMessages('test.unbind-ex-queue');
344
- })
345
- .then(() => ch.get('test.unbind-ex-queue'))
346
- .then((m) => {
347
- // the direct to queue message got through, the via-binding message (sent first) did not
348
- assert.equal(direct, m.content.toString());
349
- })
350
- );
351
- });
352
-
353
- it('cancel consumer', () => {
354
- let ctag;
355
- return withChannel((ch) =>
356
- ch.assertQueue('test.consumer-cancel', QUEUE_OPTS)
357
- .then(() => ch.purgeQueue('test.consumer-cancel'))
358
- .then(() => new Promise((resolve) => {
359
- ch.consume('test.consumer-cancel', resolve, { noAck: true })
360
- .then((ok) => {
361
- ctag = ok.consumerTag;
362
- ch.sendToQueue('test.consumer-cancel', Buffer.from('foo'));
363
- });
364
- }))
365
- // first message arrived via consumer
366
- .then(() => ch.cancel(ctag))
367
- .then(() => {
368
- ch.sendToQueue('test.consumer-cancel', Buffer.from('bar'));
369
- return waitForMessages('test.consumer-cancel');
370
- })
371
- .then(() => ch.get('test.consumer-cancel', { noAck: true }))
372
- .then((m) => {
373
- // message arrived in queue but NOT via the cancelled consumer
374
- assert.equal('bar', m.content.toString());
375
- })
376
- );
377
- });
378
-
379
- it('cancelled consumer', () => {
380
- return withChannel((ch) =>
381
- ch.assertQueue('test.cancelled-consumer')
382
- .then(() => ch.purgeQueue('test.cancelled-consumer'))
383
- .then(() => new Promise((resolve, reject) => {
384
- ch.consume('test.cancelled-consumer', (msg) => {
385
- if (msg === null) resolve();
386
- else reject(new Error('Message not expected'));
387
- }).then(() => ch.deleteQueue('test.cancelled-consumer'));
388
- }))
389
- );
390
- });
391
-
392
- // ack, by default, removes a single message from the queue
393
- it('ack', () => {
394
- const msg1 = randomString();
395
- const msg2 = randomString();
396
- return withChannel((ch) =>
397
- ch.assertQueue('test.ack', QUEUE_OPTS)
398
- .then(() => ch.purgeQueue('test.ack'))
399
- .then(() => {
400
- ch.sendToQueue('test.ack', Buffer.from(msg1));
401
- ch.sendToQueue('test.ack', Buffer.from(msg2));
402
- return waitForMessages('test.ack', 2);
403
- })
404
- .then(() => ch.get('test.ack', { noAck: false }))
405
- .then((m) => {
406
- assert.equal(msg1, m.content.toString());
407
- ch.ack(m);
408
- // %%% is there a race here? may depend on rabbitmq-specific semantics
409
- return ch.get('test.ack');
410
- })
411
- .then((m) => {
412
- assert(m);
413
- assert.equal(msg2, m.content.toString());
414
- })
415
- );
416
- });
417
-
418
- // Nack, by default, puts a message back on the queue (where in the queue is up to the server)
419
- it('nack', () => {
420
- const msg1 = randomString();
421
- return withChannel((ch) =>
422
- ch.assertQueue('test.nack', QUEUE_OPTS)
423
- .then(() => ch.purgeQueue('test.nack'))
424
- .then(() => {
425
- ch.sendToQueue('test.nack', Buffer.from(msg1));
426
- return waitForMessages('test.nack');
427
- })
428
- .then(() => ch.get('test.nack', { noAck: false }))
429
- .then((m) => {
430
- assert.equal(msg1, m.content.toString());
431
- ch.nack(m);
432
- return waitForMessages('test.nack');
433
- })
434
- .then(() => ch.get('test.nack'))
435
- .then((m) => {
436
- assert(m);
437
- assert.equal(msg1, m.content.toString());
438
- })
439
- );
440
- });
441
-
442
- // reject is a near-synonym for nack, the latter of which is not available in earlier RabbitMQ (or in AMQP proper).
443
- it('reject', () => {
444
- const msg1 = randomString();
445
- return withChannel((ch) =>
446
- ch.assertQueue('test.reject', QUEUE_OPTS)
447
- .then(() => ch.purgeQueue('test.reject'))
448
- .then(() => {
449
- ch.sendToQueue('test.reject', Buffer.from(msg1));
450
- return waitForMessages('test.reject');
451
- })
452
- .then(() => ch.get('test.reject', { noAck: false }))
453
- .then((m) => {
454
- assert.equal(msg1, m.content.toString());
455
- ch.reject(m);
456
- return waitForMessages('test.reject');
457
- })
458
- .then(() => ch.get('test.reject'))
459
- .then((m) => {
460
- assert(m);
461
- assert.equal(msg1, m.content.toString());
462
- })
463
- );
464
- });
465
-
466
- it('prefetch', () => {
467
- return withChannel((ch) =>
468
- ch.assertQueue('test.prefetch', QUEUE_OPTS)
469
- .then(() => ch.purgeQueue('test.prefetch'))
470
- .then(() => ch.prefetch(1))
471
- .then(() => {
472
- ch.sendToQueue('test.prefetch', Buffer.from('foobar'));
473
- ch.sendToQueue('test.prefetch', Buffer.from('foobar'));
474
- return waitForMessages('test.prefetch', 2);
475
- })
476
- .then(() => new Promise((resolve) => {
477
- let messageCount = 0;
478
- ch.consume('test.prefetch', (msg) => {
479
- ch.ack(msg);
480
- if (++messageCount > 1) resolve(messageCount);
481
- }, { noAck: false });
482
- }))
483
- .then((c) => assert.equal(2, c))
484
- );
485
- });
486
-
487
- it('close', () => {
488
- return withChannel((ch) => ch.close());
489
- });
490
- });
491
-
492
- describe('confirms', () => {
493
- it('message is confirmed', () => {
494
- return withConfirmChannel((ch) =>
495
- ch.assertQueue('test.confirm-message', QUEUE_OPTS)
496
- .then(() => ch.purgeQueue('test.confirm-message'))
497
- .then(() => ch.sendToQueue('test.confirm-message', Buffer.from('bleep')))
498
- );
499
- });
500
-
501
- // Usually one can provoke the server into confirming more than one
502
- // message in an ack by simply sending a few messages in quick
503
- // succession; a bit unscientific I know. Luckily we can eavesdrop on
504
- // the acknowledgements coming through to see if we really did get a
505
- // multi-ack.
506
- it('multiple confirms', () => {
507
- return withConfirmChannel((ch) =>
508
- ch.assertQueue('test.multiple-confirms', QUEUE_OPTS)
509
- .then(() => ch.purgeQueue('test.multiple-confirms'))
510
- .then(() => {
511
- let multipleRainbows = false;
512
- ch.on('ack', (a) => {
513
- if (a.multiple) multipleRainbows = true;
514
- });
515
-
516
- function prod(num) {
517
- const cs = [];
518
- for (let i = 0; i < num; i++) {
519
- cs.push(promisify((cb) => ch.sendToQueue('test.multiple-confirms', Buffer.from('bleep'), {}, cb))());
520
- }
521
- return Promise.all(cs).then(() => {
522
- if (multipleRainbows) return true;
523
- else if (num > 500) throw new Error(`Couldn't provoke the server into multi-acking with ${num} messages; giving up`);
524
- else return prod(num * 2);
525
- });
526
- }
527
- return prod(5);
528
- })
529
- );
530
- });
531
-
532
- it('wait for confirms', () => {
533
- return withConfirmChannel((ch) => {
534
- for (let i = 0; i < 1000; i++) {
535
- ch.publish('', '', Buffer.from('foobar'), {});
536
- }
537
- return ch.waitForConfirms();
538
- });
539
- });
540
-
541
- it('works when channel is closed', () => {
542
- return withConfirmChannel((ch) => {
543
- for (let i = 0; i < 1000; i++) {
544
- ch.publish('', '', Buffer.from('foobar'), {});
545
- }
546
- return ch.close()
547
- .then(() => assert.rejects(() => ch.waitForConfirms(), (e) => {
548
- assert.strictEqual(e.message, 'channel closed');
549
- return true;
550
- }));
551
- });
552
- });
553
-
554
- it('publish on closed channel does not leak callbacks', () => {
555
- return withConfirmChannel((ch) => {
556
- return ch.close().then(() => {
557
- for (let i = 0; i < 10; i++) {
558
- try { ch.publish('', '', Buffer.from('x'), {}, () => {}); } catch (_) {}
559
- }
560
- assert.strictEqual(ch.unconfirmed.length, 0);
561
- });
562
- });
563
- });
564
- });
565
-
566
- function connect() {
567
- return api.connect(URL);
568
- }
569
-
570
- function withChannel(cb) {
571
- return connect().then((c) => c.createChannel().then(cb).finally(() => c.close()));
572
- }
573
-
574
- function withConfirmChannel(cb) {
575
- return connect().then((c) => c.createConfirmChannel().then(cb).finally(() => c.close()));
576
- }
577
-
578
- function waitForQueue(q, condition) {
579
- return connect(URL).then((c) =>
580
- c.createChannel().then((ch) =>
581
- ch.checkQueue(q).then((_qok) => {
582
- function check() {
583
- return ch.checkQueue(q).then((qok) => {
584
- if (condition(qok)) {
585
- c.close();
586
- return qok;
587
- } else schedule(check);
588
- });
589
- }
590
- return check();
591
- }),
592
- ),
593
- );
594
- }
595
-
596
- function waitForMessages(q, num) {
597
- const min = num === undefined ? 1 : num;
598
- return waitForQueue(q, (qok) => qok.messageCount >= min);
599
- }