@onify/fake-amqplib 2.0.0 → 3.1.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.
- package/README.md +10 -10
- package/index.d.ts +8 -12
- package/index.js +309 -117
- package/main.cjs +309 -117
- package/package.json +9 -7
package/index.js
CHANGED
|
@@ -4,8 +4,26 @@ import { format as urlFormat } from 'url';
|
|
|
4
4
|
|
|
5
5
|
const kSmqp = Symbol.for('smqp');
|
|
6
6
|
const kClosed = Symbol.for('closed');
|
|
7
|
-
const
|
|
7
|
+
const kDeliveryTag = Symbol.for('channel delivery tag');
|
|
8
8
|
const kPrefetch = Symbol.for('prefetch');
|
|
9
|
+
const kChannelPrefetch = Symbol.for('channel prefetch');
|
|
10
|
+
|
|
11
|
+
class AmqplibBroker extends Broker {
|
|
12
|
+
constructor(...args) {
|
|
13
|
+
super(...args);
|
|
14
|
+
this[kDeliveryTag] = 0;
|
|
15
|
+
}
|
|
16
|
+
_getNextDeliveryTag() {
|
|
17
|
+
return ++this[kDeliveryTag];
|
|
18
|
+
}
|
|
19
|
+
_getMessageByDeliveryTag(queue, deliveryTag) {
|
|
20
|
+
const q = this.getQueue(queue);
|
|
21
|
+
return q.messages.find((m) => m.fields.deliveryTag === deliveryTag);
|
|
22
|
+
}
|
|
23
|
+
_getChannelConsumers(channelName) {
|
|
24
|
+
return this.getConsumers().filter((f) => f.options.channelName === channelName);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
9
27
|
|
|
10
28
|
class FakeAmqpError extends Error {
|
|
11
29
|
constructor(message, code, killChannel, killConnection) {
|
|
@@ -18,27 +36,55 @@ class FakeAmqpError extends Error {
|
|
|
18
36
|
|
|
19
37
|
class FakeAmqpNotFoundError extends FakeAmqpError {
|
|
20
38
|
constructor(type, name, vhost, killConnection = false) {
|
|
21
|
-
super(
|
|
39
|
+
super(
|
|
40
|
+
`Channel closed by server: 404 (NOT-FOUND) with message "NOT_FOUND - no ${type} '${name}' in vhost '${vhost || '/'}'`,
|
|
41
|
+
404,
|
|
42
|
+
true,
|
|
43
|
+
killConnection,
|
|
44
|
+
);
|
|
22
45
|
}
|
|
23
46
|
}
|
|
24
47
|
|
|
25
|
-
|
|
48
|
+
class FakeAmqpUnknownDeliveryTag extends FakeAmqpError {
|
|
49
|
+
constructor(deliveryTag) {
|
|
50
|
+
super(
|
|
51
|
+
`Channel closed by server: 406 (PRECONDITION-FAILED) with message "PRECONDITION_FAILED - unknown delivery tag ${deliveryTag}`,
|
|
52
|
+
406,
|
|
53
|
+
true,
|
|
54
|
+
false,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
get _emit() {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function Message(smqpMessage, deliveryTag) {
|
|
63
|
+
this[kSmqp] = smqpMessage;
|
|
64
|
+
this.fields = { ...smqpMessage.fields, deliveryTag };
|
|
65
|
+
this.content = Buffer.from(smqpMessage.content);
|
|
66
|
+
this.properties = { ...smqpMessage.properties };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export class FakeAmqplibChannel extends EventEmitter {
|
|
26
70
|
constructor(broker, connection) {
|
|
71
|
+
super();
|
|
27
72
|
this.connection = connection;
|
|
28
73
|
|
|
29
74
|
this[kPrefetch] = 10000;
|
|
75
|
+
this[kChannelPrefetch] = Infinity;
|
|
30
76
|
this[kClosed] = false;
|
|
31
|
-
this
|
|
32
|
-
this._channelName = `channel-${generateId()}`;
|
|
77
|
+
const channelName = (this._channelName = `channel-${generateId()}`);
|
|
33
78
|
this._version = connection._version;
|
|
34
79
|
this._broker = broker;
|
|
35
80
|
|
|
81
|
+
this._channelQueue = broker.assertQueue(`#${channelName}`);
|
|
36
82
|
this._emitReturn = this._emitReturn.bind(this);
|
|
37
83
|
|
|
38
84
|
broker.on('return', this._emitReturn);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
85
|
+
|
|
86
|
+
this._createChannelMessage = this._createChannelMessage.bind(this);
|
|
87
|
+
this._calculateChannelCapacity = this._calculateChannelCapacity.bind(this);
|
|
42
88
|
}
|
|
43
89
|
get _closed() {
|
|
44
90
|
return this[kClosed];
|
|
@@ -68,12 +114,12 @@ export class FakeAmqplibChannel {
|
|
|
68
114
|
}
|
|
69
115
|
bindExchange(destination, source, ...args) {
|
|
70
116
|
const broker = this._broker;
|
|
71
|
-
return Promise.all([
|
|
117
|
+
return Promise.all([this.checkExchange(source), this.checkExchange(destination)]).then(() => {
|
|
72
118
|
return this._callBroker(broker.bindExchange, source, destination, ...args);
|
|
73
119
|
});
|
|
74
120
|
}
|
|
75
121
|
bindQueue(queue, source, ...args) {
|
|
76
|
-
return Promise.all([
|
|
122
|
+
return Promise.all([this.checkQueue(queue), this.checkExchange(source)]).then(() => {
|
|
77
123
|
return this._callBroker(this._broker.bindQueue, queue, source, ...args);
|
|
78
124
|
});
|
|
79
125
|
}
|
|
@@ -104,14 +150,16 @@ export class FakeAmqplibChannel {
|
|
|
104
150
|
}
|
|
105
151
|
get(queue, ...args) {
|
|
106
152
|
const connPath = this.connection._url.pathname;
|
|
153
|
+
const createMessage = this._createChannelMessage;
|
|
107
154
|
return this._callBroker(getMessage, ...args);
|
|
108
155
|
|
|
109
156
|
function getMessage(...getargs) {
|
|
110
157
|
const q = this.getQueue(queue);
|
|
111
|
-
if (!q) throw new FakeAmqpNotFoundError('queue', queue, connPath
|
|
158
|
+
if (!q) throw new FakeAmqpNotFoundError('queue', queue, connPath);
|
|
112
159
|
const msg = q.get(...getargs) || false;
|
|
113
160
|
if (!msg) return msg;
|
|
114
|
-
|
|
161
|
+
|
|
162
|
+
return createMessage(msg, args[0]?.noAck);
|
|
115
163
|
}
|
|
116
164
|
}
|
|
117
165
|
deleteExchange(exchange, ...args) {
|
|
@@ -138,15 +186,17 @@ export class FakeAmqplibChannel {
|
|
|
138
186
|
if (!Buffer.isBuffer(content)) throw new TypeError('content is not a buffer');
|
|
139
187
|
if (exchange === '') return this.sendToQueue(routingKey, content, options, callback);
|
|
140
188
|
|
|
141
|
-
const args = [
|
|
189
|
+
const args = [this._broker.publish, exchange, routingKey, content];
|
|
142
190
|
|
|
143
191
|
args.push(options, callback);
|
|
144
192
|
|
|
145
|
-
this.checkExchange(exchange)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
193
|
+
this.checkExchange(exchange)
|
|
194
|
+
.then(() => {
|
|
195
|
+
return this._callBroker(...args);
|
|
196
|
+
})
|
|
197
|
+
.catch((err) => {
|
|
198
|
+
this.emit('error', err);
|
|
199
|
+
});
|
|
150
200
|
|
|
151
201
|
return true;
|
|
152
202
|
}
|
|
@@ -163,15 +213,17 @@ export class FakeAmqplibChannel {
|
|
|
163
213
|
sendToQueue(queue, content, options, callback) {
|
|
164
214
|
if (!Buffer.isBuffer(content)) throw new TypeError('content is not a buffer');
|
|
165
215
|
|
|
166
|
-
const args = [
|
|
216
|
+
const args = [this._broker.sendToQueue, queue, content];
|
|
167
217
|
|
|
168
218
|
args.push(options, callback);
|
|
169
219
|
|
|
170
|
-
this.checkQueue(queue)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
220
|
+
this.checkQueue(queue)
|
|
221
|
+
.then(() => {
|
|
222
|
+
return this._callBroker(...args);
|
|
223
|
+
})
|
|
224
|
+
.catch((err) => {
|
|
225
|
+
this.emit('error', err);
|
|
226
|
+
});
|
|
175
227
|
|
|
176
228
|
return true;
|
|
177
229
|
}
|
|
@@ -214,31 +266,47 @@ export class FakeAmqplibChannel {
|
|
|
214
266
|
}
|
|
215
267
|
consume(queue, onMessage, options = {}, callback) {
|
|
216
268
|
const { _id: connId, _url: connUrl } = this.connection;
|
|
269
|
+
const createMessage = this._createChannelMessage;
|
|
270
|
+
const calculateCapacity = this._calculateChannelCapacity;
|
|
217
271
|
const channelName = this._channelName;
|
|
218
272
|
const prefetch = this[kPrefetch];
|
|
219
273
|
|
|
220
|
-
return this._callBroker(
|
|
274
|
+
return this._callBroker(consume, callback);
|
|
221
275
|
|
|
222
|
-
function
|
|
276
|
+
function consume() {
|
|
223
277
|
const q = queue && this.getQueue(queue);
|
|
224
278
|
if (!q) {
|
|
225
279
|
throw new FakeAmqpNotFoundError('queue', queue, connUrl.pathname);
|
|
226
280
|
}
|
|
227
281
|
|
|
228
282
|
if (q.exclusive || (q.options.exclusive && q.options._connectionId !== connId)) {
|
|
229
|
-
throw new FakeAmqpError(
|
|
283
|
+
throw new FakeAmqpError(
|
|
284
|
+
`Channel closed by server: 403 (ACCESS-REFUSED) with message "ACCESS_REFUSED - queue '${queue}' in vhost '${connUrl.pathname}' in exclusive use"`,
|
|
285
|
+
403,
|
|
286
|
+
true,
|
|
287
|
+
true,
|
|
288
|
+
);
|
|
230
289
|
}
|
|
231
290
|
|
|
232
|
-
const
|
|
291
|
+
const consumer = this.consume(queue, onMessage && handler, {
|
|
233
292
|
...options,
|
|
234
293
|
channelName,
|
|
235
|
-
prefetch,
|
|
294
|
+
prefetch: calculateCapacity(prefetch),
|
|
295
|
+
_consumerPrefetch: prefetch,
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
const capacityProp = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(consumer), 'capacity');
|
|
299
|
+
Object.defineProperty(consumer, 'capacity', {
|
|
300
|
+
get() {
|
|
301
|
+
return calculateCapacity(capacityProp.get.call(this));
|
|
302
|
+
},
|
|
236
303
|
});
|
|
237
|
-
|
|
304
|
+
|
|
305
|
+
return { consumerTag: consumer.consumerTag };
|
|
238
306
|
}
|
|
239
307
|
|
|
240
308
|
function handler(_, msg) {
|
|
241
|
-
onMessage(
|
|
309
|
+
onMessage(createMessage(msg, options.noAck));
|
|
242
310
|
}
|
|
243
311
|
}
|
|
244
312
|
cancel(consumerTag, ...args) {
|
|
@@ -246,51 +314,137 @@ export class FakeAmqplibChannel {
|
|
|
246
314
|
}
|
|
247
315
|
close(callback) {
|
|
248
316
|
if (this[kClosed]) return;
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
broker.off('return', this._emitReturn);
|
|
253
|
-
const channelConsumers = broker.getConsumers().filter((f) => f.options.channelName === channelName);
|
|
254
|
-
channelConsumers.forEach((c) => broker.cancel(c.consumerTag));
|
|
255
|
-
this[kClosed] = true;
|
|
256
|
-
this[kEmitter].emit('close');
|
|
317
|
+
this._teardown();
|
|
318
|
+
this.emit('close');
|
|
257
319
|
return resolveOrCallback(callback);
|
|
258
320
|
}
|
|
259
|
-
ack(message,
|
|
260
|
-
|
|
321
|
+
ack(message, allUpTo) {
|
|
322
|
+
const deliveryTag = message.fields.deliveryTag;
|
|
323
|
+
const channelMessage = this._broker._getMessageByDeliveryTag(this._channelQueue.name, deliveryTag);
|
|
324
|
+
const channelQ = this._channelQueue;
|
|
325
|
+
|
|
326
|
+
if (!allUpTo) this._callBroker(ackMessage);
|
|
327
|
+
else this._callBroker(ackAllUpToMessage);
|
|
328
|
+
|
|
329
|
+
function ackMessage() {
|
|
330
|
+
const msg = message[kSmqp];
|
|
331
|
+
if (!channelMessage || !msg.pending) {
|
|
332
|
+
throw new FakeAmqpUnknownDeliveryTag(message.fields.deliveryTag);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
channelQ.ack(channelMessage, false);
|
|
336
|
+
this.ack(msg, false);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function ackAllUpToMessage() {
|
|
340
|
+
const msg = message[kSmqp];
|
|
341
|
+
if (!channelMessage || !msg.pending) {
|
|
342
|
+
throw new FakeAmqpUnknownDeliveryTag(message.fields.deliveryTag);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const brokerMessages = allUpToDeliveryTag(channelQ, deliveryTag, 'ack', false);
|
|
346
|
+
for (const brokerMessage of brokerMessages) {
|
|
347
|
+
brokerMessage.ack(false);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
channelQ.ack(channelMessage, false);
|
|
351
|
+
this.ack(msg, false);
|
|
352
|
+
}
|
|
261
353
|
}
|
|
262
354
|
ackAll() {
|
|
263
|
-
const
|
|
264
|
-
|
|
355
|
+
const channelQ = this._channelQueue;
|
|
356
|
+
let msg;
|
|
357
|
+
const brokerMessages = [];
|
|
358
|
+
while ((msg = channelQ.get())) {
|
|
359
|
+
brokerMessages.push(msg.content[kSmqp]);
|
|
360
|
+
msg.ack();
|
|
361
|
+
}
|
|
265
362
|
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
nack(message, ...args) {
|
|
270
|
-
if (this.connection._version >= 2.3) throw new Error(`Nack is not implemented in versions before 2.3 (${this.connection._version})`);
|
|
271
|
-
return this._broker.nack(message[kSmqp], ...args);
|
|
272
|
-
}
|
|
273
|
-
reject(message, ...args) {
|
|
274
|
-
this._broker.reject(message[kSmqp], ...args);
|
|
363
|
+
for (const brokerMessage of brokerMessages) {
|
|
364
|
+
brokerMessage.ack();
|
|
365
|
+
}
|
|
275
366
|
}
|
|
276
|
-
|
|
277
|
-
const
|
|
278
|
-
const
|
|
367
|
+
reject(message, requeue = false) {
|
|
368
|
+
const deliveryTag = message.fields.deliveryTag;
|
|
369
|
+
const channelMessage = this._broker._getMessageByDeliveryTag(this._channelQueue.name, deliveryTag);
|
|
370
|
+
const channelQ = this._channelQueue;
|
|
371
|
+
|
|
372
|
+
this._callBroker(rejectMessage);
|
|
279
373
|
|
|
280
|
-
|
|
281
|
-
|
|
374
|
+
function rejectMessage() {
|
|
375
|
+
const msg = message[kSmqp];
|
|
376
|
+
if (!channelMessage || !msg.pending) {
|
|
377
|
+
throw new FakeAmqpUnknownDeliveryTag(deliveryTag);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
channelQ.reject(channelMessage, false);
|
|
381
|
+
this.reject(msg, requeue);
|
|
382
|
+
}
|
|
282
383
|
}
|
|
283
|
-
|
|
284
|
-
this
|
|
384
|
+
nack(message, allUpTo = false, requeue = false) {
|
|
385
|
+
if (this.connection._version < 2.3) throw new Error(`Nack is not implemented in versions before 2.3 (${this.connection._version})`);
|
|
386
|
+
|
|
387
|
+
const deliveryTag = message.fields.deliveryTag;
|
|
388
|
+
const channelMessage = this._broker._getMessageByDeliveryTag(this._channelQueue.name, deliveryTag);
|
|
389
|
+
const channelQ = this._channelQueue;
|
|
390
|
+
|
|
391
|
+
if (!allUpTo) this._callBroker(nackMessage);
|
|
392
|
+
else this._callBroker(nackAllUpToMessage);
|
|
393
|
+
|
|
394
|
+
function nackMessage() {
|
|
395
|
+
const msg = message[kSmqp];
|
|
396
|
+
if (!channelMessage || !msg.pending) {
|
|
397
|
+
throw new FakeAmqpUnknownDeliveryTag(deliveryTag);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
channelQ.nack(channelMessage, false, false);
|
|
401
|
+
this.nack(msg, false, requeue);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function nackAllUpToMessage() {
|
|
405
|
+
const msg = message[kSmqp];
|
|
406
|
+
if (!channelMessage || !msg.pending) {
|
|
407
|
+
throw new FakeAmqpUnknownDeliveryTag(deliveryTag);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const brokerMessages = allUpToDeliveryTag(channelQ, deliveryTag, 'nack', false, false);
|
|
411
|
+
for (const brokerMessage of brokerMessages) {
|
|
412
|
+
brokerMessage.nack(false, requeue);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
channelMessage.nack(false, false);
|
|
416
|
+
this.nack(msg, false, requeue);
|
|
417
|
+
}
|
|
285
418
|
}
|
|
286
|
-
|
|
287
|
-
|
|
419
|
+
nackAll(requeue = true) {
|
|
420
|
+
const channelQ = this._channelQueue;
|
|
421
|
+
let msg;
|
|
422
|
+
const brokerMessages = [];
|
|
423
|
+
while ((msg = channelQ.get())) {
|
|
424
|
+
brokerMessages.push(msg.content[kSmqp]);
|
|
425
|
+
msg.reject(false);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
for (const brokerMessage of brokerMessages) {
|
|
429
|
+
brokerMessage.reject(requeue);
|
|
430
|
+
}
|
|
288
431
|
}
|
|
289
|
-
|
|
290
|
-
|
|
432
|
+
prefetch(val, isChannelPrefetch) {
|
|
433
|
+
if (this.connection._version < 3.3) {
|
|
434
|
+
if (isChannelPrefetch !== undefined) {
|
|
435
|
+
return this.connection.close();
|
|
436
|
+
}
|
|
437
|
+
this[kChannelPrefetch] = val;
|
|
438
|
+
} else {
|
|
439
|
+
if (isChannelPrefetch) {
|
|
440
|
+
this[kChannelPrefetch] = val;
|
|
441
|
+
} else {
|
|
442
|
+
this[kPrefetch] = val;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
291
445
|
}
|
|
292
446
|
_callBroker(fn, ...args) {
|
|
293
|
-
let [
|
|
447
|
+
let [poppedCb] = args.slice(-1);
|
|
294
448
|
if (typeof poppedCb === 'function') args.splice(-1);
|
|
295
449
|
else poppedCb = null;
|
|
296
450
|
|
|
@@ -304,7 +458,8 @@ export class FakeAmqplibChannel {
|
|
|
304
458
|
return resolve(result);
|
|
305
459
|
} catch (err) {
|
|
306
460
|
if (err._killConnection) this.connection.close();
|
|
307
|
-
else if (err._killChannel) this
|
|
461
|
+
else if (err._killChannel) this._teardown();
|
|
462
|
+
if (err._emit) this.emit('error', err);
|
|
308
463
|
if (!poppedCb) return reject(err);
|
|
309
464
|
poppedCb(err);
|
|
310
465
|
return resolve();
|
|
@@ -313,9 +468,44 @@ export class FakeAmqplibChannel {
|
|
|
313
468
|
}
|
|
314
469
|
_emitReturn({ fields, content, properties }) {
|
|
315
470
|
process.nextTick(() => {
|
|
316
|
-
this
|
|
471
|
+
this.emit('return', { fields, content, properties });
|
|
317
472
|
});
|
|
318
473
|
}
|
|
474
|
+
_createChannelMessage(smqpMessage, noAck) {
|
|
475
|
+
const deliveryTag = this._broker._getNextDeliveryTag();
|
|
476
|
+
const consumeMessage = new Message(smqpMessage, deliveryTag);
|
|
477
|
+
if (!noAck) {
|
|
478
|
+
const channelQ = this._channelQueue;
|
|
479
|
+
channelQ.queueMessage(consumeMessage.fields, consumeMessage);
|
|
480
|
+
}
|
|
481
|
+
return consumeMessage;
|
|
482
|
+
}
|
|
483
|
+
_teardown() {
|
|
484
|
+
this[kClosed] = true;
|
|
485
|
+
const channelName = this._channelName;
|
|
486
|
+
const broker = this._broker;
|
|
487
|
+
const channelConsumers = broker._getChannelConsumers(channelName);
|
|
488
|
+
channelConsumers.forEach((c) => broker.cancel(c.consumerTag));
|
|
489
|
+
|
|
490
|
+
let msg;
|
|
491
|
+
while ((msg = this._channelQueue.get())) {
|
|
492
|
+
msg.content[kSmqp].reject(true);
|
|
493
|
+
msg.reject(false);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
broker.off('return', this._emitReturn);
|
|
497
|
+
}
|
|
498
|
+
_calculateChannelCapacity(consumerCapacity) {
|
|
499
|
+
const channelPrefetch = this[kChannelPrefetch];
|
|
500
|
+
if (channelPrefetch === Infinity) return consumerCapacity;
|
|
501
|
+
|
|
502
|
+
const channelCapacity = channelPrefetch - this._channelQueue.messageCount;
|
|
503
|
+
|
|
504
|
+
let capacity = consumerCapacity;
|
|
505
|
+
if (channelCapacity <= 0) capacity = 0;
|
|
506
|
+
else if (channelCapacity < capacity) capacity = channelCapacity;
|
|
507
|
+
return capacity;
|
|
508
|
+
}
|
|
319
509
|
}
|
|
320
510
|
|
|
321
511
|
export class FakeAmqplibConfirmChannel extends FakeAmqplibChannel {
|
|
@@ -323,38 +513,42 @@ export class FakeAmqplibConfirmChannel extends FakeAmqplibChannel {
|
|
|
323
513
|
if (!Buffer.isBuffer(content)) throw new TypeError('content is not a buffer');
|
|
324
514
|
if (exchange === '') return this.sendToQueue(routingKey, content, options, callback);
|
|
325
515
|
|
|
326
|
-
const args = [
|
|
516
|
+
const args = [this._broker.publish, exchange, routingKey, content];
|
|
327
517
|
|
|
328
518
|
args.push(...addConfirmCallback(this._broker, options, callback));
|
|
329
519
|
|
|
330
|
-
this.checkExchange(exchange)
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
520
|
+
this.checkExchange(exchange)
|
|
521
|
+
.then(() => {
|
|
522
|
+
return this._callBroker(...args);
|
|
523
|
+
})
|
|
524
|
+
.catch((err) => {
|
|
525
|
+
this.emit('error', err);
|
|
526
|
+
});
|
|
335
527
|
|
|
336
528
|
return true;
|
|
337
529
|
}
|
|
338
530
|
sendToQueue(queue, content, options, callback) {
|
|
339
531
|
if (!Buffer.isBuffer(content)) throw new TypeError('content is not a buffer');
|
|
340
532
|
|
|
341
|
-
const args = [
|
|
533
|
+
const args = [this._broker.sendToQueue, queue, content];
|
|
342
534
|
|
|
343
535
|
args.push(...addConfirmCallback(this._broker, options, callback));
|
|
344
536
|
|
|
345
|
-
this.checkQueue(queue)
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
537
|
+
this.checkQueue(queue)
|
|
538
|
+
.then(() => {
|
|
539
|
+
return this._callBroker(...args);
|
|
540
|
+
})
|
|
541
|
+
.catch((err) => {
|
|
542
|
+
this.emit('error', err);
|
|
543
|
+
});
|
|
350
544
|
|
|
351
545
|
return true;
|
|
352
546
|
}
|
|
353
547
|
}
|
|
354
548
|
|
|
355
|
-
export class FakeAmqplibConnection {
|
|
549
|
+
export class FakeAmqplibConnection extends EventEmitter {
|
|
356
550
|
constructor(broker, version, amqpUrl) {
|
|
357
|
-
|
|
551
|
+
super();
|
|
358
552
|
this[kClosed] = false;
|
|
359
553
|
this._channels = [];
|
|
360
554
|
this._url = normalizeAmqpUrl(amqpUrl);
|
|
@@ -365,9 +559,6 @@ export class FakeAmqplibConnection {
|
|
|
365
559
|
get _closed() {
|
|
366
560
|
return this[kClosed];
|
|
367
561
|
}
|
|
368
|
-
get _emitter() {
|
|
369
|
-
return this[kEmitter];
|
|
370
|
-
}
|
|
371
562
|
get connection() {
|
|
372
563
|
return {
|
|
373
564
|
serverProperties: {
|
|
@@ -401,16 +592,10 @@ export class FakeAmqplibConnection {
|
|
|
401
592
|
|
|
402
593
|
this._channels.splice(0).forEach((channel) => channel.close());
|
|
403
594
|
|
|
404
|
-
this
|
|
595
|
+
this.emit('close');
|
|
405
596
|
|
|
406
597
|
return resolveOrCallback(args.slice(-1)[0]);
|
|
407
598
|
}
|
|
408
|
-
on(...args) {
|
|
409
|
-
return this[kEmitter].on(...args);
|
|
410
|
-
}
|
|
411
|
-
once(...args) {
|
|
412
|
-
return this[kEmitter].once(...args);
|
|
413
|
-
}
|
|
414
599
|
}
|
|
415
600
|
|
|
416
601
|
export function FakeAmqplib(minorVersion = '3.5') {
|
|
@@ -434,14 +619,14 @@ FakeAmqplib.prototype.connect = function fakeConnect(amqpUrl, ...args) {
|
|
|
434
619
|
|
|
435
620
|
FakeAmqplib.prototype.connectSync = function fakeConnectSync(amqpUrl, ...args) {
|
|
436
621
|
const { _broker } = this.connections.find((conn) => compareConnectionString(conn._url, amqpUrl)) || {};
|
|
437
|
-
const broker = _broker || new
|
|
622
|
+
const broker = _broker || new AmqplibBroker(this);
|
|
438
623
|
const connection = new FakeAmqplibConnection(broker, this.version, amqpUrl, ...args);
|
|
439
624
|
|
|
440
625
|
const connections = this.connections;
|
|
441
626
|
|
|
442
627
|
connections.push(connection);
|
|
443
628
|
|
|
444
|
-
connection.
|
|
629
|
+
connection.once('close', () => {
|
|
445
630
|
const idx = connections.indexOf(connection);
|
|
446
631
|
if (idx > -1) connections.splice(idx, 1);
|
|
447
632
|
});
|
|
@@ -477,39 +662,26 @@ function compareConnectionString(url1, url2) {
|
|
|
477
662
|
return parsedUrl1.host === parsedUrl2.host && parsedUrl1.pathname === parsedUrl2.pathname;
|
|
478
663
|
}
|
|
479
664
|
|
|
480
|
-
function Message(smqpMessage) {
|
|
481
|
-
this[kSmqp] = smqpMessage;
|
|
482
|
-
this.content = smqpMessage.content;
|
|
483
|
-
this.fields = smqpMessage.fields;
|
|
484
|
-
this.properties = smqpMessage.properties;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
665
|
function normalizeAmqpUrl(url) {
|
|
488
666
|
if (!url) return new URL('amqp://localhost:5672/');
|
|
489
667
|
if (typeof url === 'string') url = new URL(url);
|
|
490
668
|
|
|
491
669
|
if (!(url instanceof URL)) {
|
|
492
|
-
const {
|
|
493
|
-
protocol = 'amqp',
|
|
494
|
-
hostname = 'localhost',
|
|
495
|
-
port = 5672,
|
|
496
|
-
vhost = '/',
|
|
497
|
-
username,
|
|
498
|
-
password,
|
|
499
|
-
...rest
|
|
500
|
-
} = url;
|
|
670
|
+
const { protocol = 'amqp', hostname = 'localhost', port = 5672, vhost = '/', username, password, ...rest } = url;
|
|
501
671
|
let auth = username;
|
|
502
672
|
if (auth && password) {
|
|
503
673
|
auth += `:${password}`;
|
|
504
674
|
}
|
|
505
|
-
url = new URL(
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
675
|
+
url = new URL(
|
|
676
|
+
urlFormat({
|
|
677
|
+
protocol,
|
|
678
|
+
hostname,
|
|
679
|
+
port,
|
|
680
|
+
pathname: vhost,
|
|
681
|
+
slashes: true,
|
|
682
|
+
auth,
|
|
683
|
+
}),
|
|
684
|
+
);
|
|
513
685
|
|
|
514
686
|
for (const k in rest) {
|
|
515
687
|
switch (k) {
|
|
@@ -556,7 +728,27 @@ function addConfirmCallback(broker, options, callback) {
|
|
|
556
728
|
}
|
|
557
729
|
}
|
|
558
730
|
|
|
559
|
-
return [
|
|
731
|
+
return [options, confirmCallback];
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
function allUpToDeliveryTag(q, deliveryTag, op, ...args) {
|
|
735
|
+
const brokerMessages = [];
|
|
736
|
+
|
|
737
|
+
const consumer = q.consume(
|
|
738
|
+
(_, cmsg) => {
|
|
739
|
+
const msgDeliveryTag = cmsg.fields.deliveryTag;
|
|
740
|
+
if (msgDeliveryTag >= deliveryTag) {
|
|
741
|
+
return q.cancel(cmsg.fields.consumerTag);
|
|
742
|
+
}
|
|
743
|
+
brokerMessages.push(cmsg.content[kSmqp]);
|
|
744
|
+
cmsg[op](...args);
|
|
745
|
+
},
|
|
746
|
+
{ prefetch: Infinity },
|
|
747
|
+
);
|
|
748
|
+
|
|
749
|
+
consumer.cancel();
|
|
750
|
+
|
|
751
|
+
return brokerMessages;
|
|
560
752
|
}
|
|
561
753
|
|
|
562
754
|
const defaultFake = new FakeAmqplib('3.5');
|