@onify/fake-amqplib 2.0.0 → 3.0.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 +4 -4
- package/index.d.ts +4 -4
- package/index.js +246 -74
- package/main.cjs +246 -74
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Mocked version of https://www.npmjs.com/package/amqplib.
|
|
|
14
14
|
|
|
15
15
|
## RabbitMQ versions
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
RabbitMQ behaviour differs between versions. To specify your version of RabbitMQ you can call `setVersion(minorVersionFloatOrString)`. Default version is 3.5.
|
|
18
18
|
|
|
19
19
|
Example:
|
|
20
20
|
```js
|
|
@@ -26,7 +26,7 @@ var fakeAmqp = require('@onify/fake-amqplib');
|
|
|
26
26
|
const conn2 = await fakeAmqp.connect('amqp://rabbit2-2');
|
|
27
27
|
|
|
28
28
|
fakeAmqp.setVersion('3.2');
|
|
29
|
-
const conn3 = await fakeAmqp.connect('amqp://rabbit3-
|
|
29
|
+
const conn3 = await fakeAmqp.connect('amqp://rabbit3-2');
|
|
30
30
|
|
|
31
31
|
fakeAmqp.setVersion('3.7');
|
|
32
32
|
const conn37 = await fakeAmqp.connect('amqp://rabbit3-7');
|
|
@@ -35,7 +35,7 @@ var fakeAmqp = require('@onify/fake-amqplib');
|
|
|
35
35
|
|
|
36
36
|
## Mocking amqplib
|
|
37
37
|
|
|
38
|
-
You might want to override `amqplib` with `@onify/fake-amqplib` in tests. This can be done
|
|
38
|
+
You might want to override `amqplib` with `@onify/fake-amqplib` in tests. This can be done in a number of ways.
|
|
39
39
|
|
|
40
40
|
### CommonJS
|
|
41
41
|
|
|
@@ -72,7 +72,7 @@ Example on how to mock amqplib import when working with modules.
|
|
|
72
72
|
|
|
73
73
|
**[Quibble](https://www.npmjs.com/package/quibble) mocha example**
|
|
74
74
|
|
|
75
|
-
Both amqplib and fake-amqplib have to be
|
|
75
|
+
Both amqplib and fake-amqplib have to be quibbled if reset mock is used during testing.
|
|
76
76
|
|
|
77
77
|
_test/setup.js_
|
|
78
78
|
```js
|
package/index.d.ts
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
/// <reference types="amqplib" />
|
|
2
2
|
/// <reference types="node" />
|
|
3
3
|
|
|
4
|
-
import { Options, Connection, Channel
|
|
4
|
+
import { Options, Connection, Channel } from "amqplib";
|
|
5
5
|
import { EventEmitter } from "events";
|
|
6
6
|
import { Broker } from 'smqp';
|
|
7
7
|
|
|
8
8
|
export interface FakeAmqplibChannel extends Channel {
|
|
9
|
+
/** Channel name and identifier, for faking purposes */
|
|
9
10
|
_channelName: string;
|
|
10
|
-
_version: number;
|
|
11
11
|
_broker: Broker;
|
|
12
|
+
_version: number;
|
|
12
13
|
new(broker: Broker, connection: FakeAmqplibConnection): FakeAmqplibChannel;
|
|
13
14
|
get _closed(): boolean;
|
|
14
|
-
get _emitter(): EventEmitter;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export interface FakeAmqplibConnection extends Connection {
|
|
18
18
|
_channels: FakeAmqplibChannel[];
|
|
19
19
|
_url: URL;
|
|
20
|
+
/** Connection identifier, for faking purposes */
|
|
20
21
|
_id: string;
|
|
21
22
|
_broker: Broker;
|
|
22
23
|
_version: number;
|
|
23
24
|
new(broker: Broker, version: number, amqpUrl: string, options?: any): FakeAmqplibConnection;
|
|
24
25
|
get _closed(): boolean;
|
|
25
|
-
get _emitter(): EventEmitter;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
interface SocketOptions {
|
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) {
|
|
@@ -22,23 +40,41 @@ class FakeAmqpNotFoundError extends FakeAmqpError {
|
|
|
22
40
|
}
|
|
23
41
|
}
|
|
24
42
|
|
|
25
|
-
|
|
43
|
+
class FakeAmqpUnknownDeliveryTag extends FakeAmqpError {
|
|
44
|
+
constructor(deliveryTag) {
|
|
45
|
+
super(`Channel closed by server: 406 (PRECONDITION-FAILED) with message "PRECONDITION_FAILED - unknown delivery tag ${deliveryTag}`, 406, true, false);
|
|
46
|
+
}
|
|
47
|
+
get _emit() {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function Message(smqpMessage, deliveryTag) {
|
|
53
|
+
this[kSmqp] = smqpMessage;
|
|
54
|
+
this.fields = { ...smqpMessage.fields, deliveryTag };
|
|
55
|
+
this.content = Buffer.from(smqpMessage.content);
|
|
56
|
+
this.properties = { ...smqpMessage.properties };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export class FakeAmqplibChannel extends EventEmitter {
|
|
26
60
|
constructor(broker, connection) {
|
|
61
|
+
super();
|
|
27
62
|
this.connection = connection;
|
|
28
63
|
|
|
29
64
|
this[kPrefetch] = 10000;
|
|
65
|
+
this[kChannelPrefetch] = Infinity;
|
|
30
66
|
this[kClosed] = false;
|
|
31
|
-
this
|
|
32
|
-
this._channelName = `channel-${generateId()}`;
|
|
67
|
+
const channelName = this._channelName = `channel-${generateId()}`;
|
|
33
68
|
this._version = connection._version;
|
|
34
69
|
this._broker = broker;
|
|
35
70
|
|
|
71
|
+
this._channelQueue = broker.assertQueue(`#${channelName}`);
|
|
36
72
|
this._emitReturn = this._emitReturn.bind(this);
|
|
37
73
|
|
|
38
74
|
broker.on('return', this._emitReturn);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
75
|
+
|
|
76
|
+
this._createChannelMessage = this._createChannelMessage.bind(this);
|
|
77
|
+
this._calculateChannelCapacity = this._calculateChannelCapacity.bind(this);
|
|
42
78
|
}
|
|
43
79
|
get _closed() {
|
|
44
80
|
return this[kClosed];
|
|
@@ -104,14 +140,16 @@ export class FakeAmqplibChannel {
|
|
|
104
140
|
}
|
|
105
141
|
get(queue, ...args) {
|
|
106
142
|
const connPath = this.connection._url.pathname;
|
|
143
|
+
const createMessage = this._createChannelMessage;
|
|
107
144
|
return this._callBroker(getMessage, ...args);
|
|
108
145
|
|
|
109
146
|
function getMessage(...getargs) {
|
|
110
147
|
const q = this.getQueue(queue);
|
|
111
|
-
if (!q) throw new FakeAmqpNotFoundError('queue', queue, connPath
|
|
148
|
+
if (!q) throw new FakeAmqpNotFoundError('queue', queue, connPath);
|
|
112
149
|
const msg = q.get(...getargs) || false;
|
|
113
150
|
if (!msg) return msg;
|
|
114
|
-
|
|
151
|
+
|
|
152
|
+
return createMessage(msg, args[0]?.noAck);
|
|
115
153
|
}
|
|
116
154
|
}
|
|
117
155
|
deleteExchange(exchange, ...args) {
|
|
@@ -145,7 +183,7 @@ export class FakeAmqplibChannel {
|
|
|
145
183
|
this.checkExchange(exchange).then(() => {
|
|
146
184
|
return this._callBroker(...args);
|
|
147
185
|
}).catch((err) => {
|
|
148
|
-
this
|
|
186
|
+
this.emit('error', err);
|
|
149
187
|
});
|
|
150
188
|
|
|
151
189
|
return true;
|
|
@@ -170,7 +208,7 @@ export class FakeAmqplibChannel {
|
|
|
170
208
|
this.checkQueue(queue).then(() => {
|
|
171
209
|
return this._callBroker(...args);
|
|
172
210
|
}).catch((err) => {
|
|
173
|
-
this
|
|
211
|
+
this.emit('error', err);
|
|
174
212
|
});
|
|
175
213
|
|
|
176
214
|
return true;
|
|
@@ -214,12 +252,14 @@ export class FakeAmqplibChannel {
|
|
|
214
252
|
}
|
|
215
253
|
consume(queue, onMessage, options = {}, callback) {
|
|
216
254
|
const { _id: connId, _url: connUrl } = this.connection;
|
|
255
|
+
const createMessage = this._createChannelMessage;
|
|
256
|
+
const calculateCapacity = this._calculateChannelCapacity;
|
|
217
257
|
const channelName = this._channelName;
|
|
218
258
|
const prefetch = this[kPrefetch];
|
|
219
259
|
|
|
220
|
-
return this._callBroker(
|
|
260
|
+
return this._callBroker(consume, callback);
|
|
221
261
|
|
|
222
|
-
function
|
|
262
|
+
function consume() {
|
|
223
263
|
const q = queue && this.getQueue(queue);
|
|
224
264
|
if (!q) {
|
|
225
265
|
throw new FakeAmqpNotFoundError('queue', queue, connUrl.pathname);
|
|
@@ -229,16 +269,25 @@ export class FakeAmqplibChannel {
|
|
|
229
269
|
throw new FakeAmqpError(`Channel closed by server: 403 (ACCESS-REFUSED) with message "ACCESS_REFUSED - queue '${queue}' in vhost '${connUrl.pathname}' in exclusive use"`, 403, true, true);
|
|
230
270
|
}
|
|
231
271
|
|
|
232
|
-
const
|
|
272
|
+
const consumer = this.consume(queue, onMessage && handler, {
|
|
233
273
|
...options,
|
|
234
274
|
channelName,
|
|
235
|
-
prefetch,
|
|
275
|
+
prefetch: calculateCapacity(prefetch),
|
|
276
|
+
_consumerPrefetch: prefetch,
|
|
236
277
|
});
|
|
237
|
-
|
|
278
|
+
|
|
279
|
+
const capacityProp = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(consumer), 'capacity');
|
|
280
|
+
Object.defineProperty(consumer, 'capacity', {
|
|
281
|
+
get() {
|
|
282
|
+
return calculateCapacity(capacityProp.get.call(this));
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
return { consumerTag: consumer.consumerTag };
|
|
238
287
|
}
|
|
239
288
|
|
|
240
289
|
function handler(_, msg) {
|
|
241
|
-
onMessage(
|
|
290
|
+
onMessage(createMessage(msg, options.noAck));
|
|
242
291
|
}
|
|
243
292
|
}
|
|
244
293
|
cancel(consumerTag, ...args) {
|
|
@@ -246,48 +295,134 @@ export class FakeAmqplibChannel {
|
|
|
246
295
|
}
|
|
247
296
|
close(callback) {
|
|
248
297
|
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');
|
|
298
|
+
this._teardown();
|
|
299
|
+
this.emit('close');
|
|
257
300
|
return resolveOrCallback(callback);
|
|
258
301
|
}
|
|
259
|
-
ack(message,
|
|
260
|
-
|
|
302
|
+
ack(message, allUpTo) {
|
|
303
|
+
const deliveryTag = message.fields.deliveryTag;
|
|
304
|
+
const channelMessage = this._broker._getMessageByDeliveryTag(this._channelQueue.name, deliveryTag);
|
|
305
|
+
const channelQ = this._channelQueue;
|
|
306
|
+
|
|
307
|
+
if (!allUpTo) this._callBroker(ackMessage);
|
|
308
|
+
else this._callBroker(ackAllUpToMessage);
|
|
309
|
+
|
|
310
|
+
function ackMessage() {
|
|
311
|
+
const msg = message[kSmqp];
|
|
312
|
+
if (!channelMessage || !msg.pending) {
|
|
313
|
+
throw new FakeAmqpUnknownDeliveryTag(message.fields.deliveryTag);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
channelQ.ack(channelMessage, false);
|
|
317
|
+
this.ack(msg, false);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function ackAllUpToMessage() {
|
|
321
|
+
const msg = message[kSmqp];
|
|
322
|
+
if (!channelMessage || !msg.pending) {
|
|
323
|
+
throw new FakeAmqpUnknownDeliveryTag(message.fields.deliveryTag);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const brokerMessages = allUpToDeliveryTag(channelQ, deliveryTag, 'ack', false);
|
|
327
|
+
for (const brokerMessage of brokerMessages) {
|
|
328
|
+
brokerMessage.ack(false);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
channelQ.ack(channelMessage, false);
|
|
332
|
+
this.ack(msg, false);
|
|
333
|
+
}
|
|
261
334
|
}
|
|
262
335
|
ackAll() {
|
|
263
|
-
const
|
|
264
|
-
|
|
336
|
+
const channelQ = this._channelQueue;
|
|
337
|
+
let msg;
|
|
338
|
+
const brokerMessages = [];
|
|
339
|
+
while ((msg = channelQ.get())) {
|
|
340
|
+
brokerMessages.push(msg.content[kSmqp]);
|
|
341
|
+
msg.ack();
|
|
342
|
+
}
|
|
265
343
|
|
|
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);
|
|
344
|
+
for (const brokerMessage of brokerMessages) {
|
|
345
|
+
brokerMessage.ack();
|
|
346
|
+
}
|
|
275
347
|
}
|
|
276
|
-
|
|
277
|
-
const
|
|
278
|
-
const
|
|
348
|
+
reject(message, requeue = false) {
|
|
349
|
+
const deliveryTag = message.fields.deliveryTag;
|
|
350
|
+
const channelMessage = this._broker._getMessageByDeliveryTag(this._channelQueue.name, deliveryTag);
|
|
351
|
+
const channelQ = this._channelQueue;
|
|
352
|
+
|
|
353
|
+
this._callBroker(rejectMessage);
|
|
279
354
|
|
|
280
|
-
|
|
281
|
-
|
|
355
|
+
function rejectMessage() {
|
|
356
|
+
const msg = message[kSmqp];
|
|
357
|
+
if (!channelMessage || !msg.pending) {
|
|
358
|
+
throw new FakeAmqpUnknownDeliveryTag(deliveryTag);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
channelQ.reject(channelMessage, false);
|
|
362
|
+
this.reject(msg, requeue);
|
|
363
|
+
}
|
|
282
364
|
}
|
|
283
|
-
|
|
284
|
-
this
|
|
365
|
+
nack(message, allUpTo = false, requeue = false) {
|
|
366
|
+
if (this.connection._version < 2.3) throw new Error(`Nack is not implemented in versions before 2.3 (${this.connection._version})`);
|
|
367
|
+
|
|
368
|
+
const deliveryTag = message.fields.deliveryTag;
|
|
369
|
+
const channelMessage = this._broker._getMessageByDeliveryTag(this._channelQueue.name, deliveryTag);
|
|
370
|
+
const channelQ = this._channelQueue;
|
|
371
|
+
|
|
372
|
+
if (!allUpTo) this._callBroker(nackMessage);
|
|
373
|
+
else this._callBroker(nackAllUpToMessage);
|
|
374
|
+
|
|
375
|
+
function nackMessage() {
|
|
376
|
+
const msg = message[kSmqp];
|
|
377
|
+
if (!channelMessage || !msg.pending) {
|
|
378
|
+
throw new FakeAmqpUnknownDeliveryTag(deliveryTag);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
channelQ.nack(channelMessage, false, false);
|
|
382
|
+
this.nack(msg, false, requeue);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function nackAllUpToMessage() {
|
|
386
|
+
const msg = message[kSmqp];
|
|
387
|
+
if (!channelMessage || !msg.pending) {
|
|
388
|
+
throw new FakeAmqpUnknownDeliveryTag(deliveryTag);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const brokerMessages = allUpToDeliveryTag(channelQ, deliveryTag, 'nack', false, false);
|
|
392
|
+
for (const brokerMessage of brokerMessages) {
|
|
393
|
+
brokerMessage.nack(false, requeue);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
channelMessage.nack(false, false);
|
|
397
|
+
this.nack(msg, false, requeue);
|
|
398
|
+
}
|
|
285
399
|
}
|
|
286
|
-
|
|
287
|
-
|
|
400
|
+
nackAll(requeue = true) {
|
|
401
|
+
const channelQ = this._channelQueue;
|
|
402
|
+
let msg;
|
|
403
|
+
const brokerMessages = [];
|
|
404
|
+
while ((msg = channelQ.get())) {
|
|
405
|
+
brokerMessages.push(msg.content[kSmqp]);
|
|
406
|
+
msg.reject(false);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
for (const brokerMessage of brokerMessages) {
|
|
410
|
+
brokerMessage.reject(requeue);
|
|
411
|
+
}
|
|
288
412
|
}
|
|
289
|
-
|
|
290
|
-
|
|
413
|
+
prefetch(val, isChannelPrefetch) {
|
|
414
|
+
if (this.connection._version < 3.3) {
|
|
415
|
+
if (isChannelPrefetch !== undefined) {
|
|
416
|
+
return this.connection.close();
|
|
417
|
+
}
|
|
418
|
+
this[kChannelPrefetch] = val;
|
|
419
|
+
} else {
|
|
420
|
+
if (isChannelPrefetch) {
|
|
421
|
+
this[kChannelPrefetch] = val;
|
|
422
|
+
} else {
|
|
423
|
+
this[kPrefetch] = val;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
291
426
|
}
|
|
292
427
|
_callBroker(fn, ...args) {
|
|
293
428
|
let [ poppedCb ] = args.slice(-1);
|
|
@@ -304,7 +439,8 @@ export class FakeAmqplibChannel {
|
|
|
304
439
|
return resolve(result);
|
|
305
440
|
} catch (err) {
|
|
306
441
|
if (err._killConnection) this.connection.close();
|
|
307
|
-
else if (err._killChannel) this
|
|
442
|
+
else if (err._killChannel) this._teardown();
|
|
443
|
+
if (err._emit) this.emit('error', err);
|
|
308
444
|
if (!poppedCb) return reject(err);
|
|
309
445
|
poppedCb(err);
|
|
310
446
|
return resolve();
|
|
@@ -313,9 +449,44 @@ export class FakeAmqplibChannel {
|
|
|
313
449
|
}
|
|
314
450
|
_emitReturn({ fields, content, properties }) {
|
|
315
451
|
process.nextTick(() => {
|
|
316
|
-
this
|
|
452
|
+
this.emit('return', { fields, content, properties });
|
|
317
453
|
});
|
|
318
454
|
}
|
|
455
|
+
_createChannelMessage(smqpMessage, noAck) {
|
|
456
|
+
const deliveryTag = this._broker._getNextDeliveryTag();
|
|
457
|
+
const consumeMessage = new Message(smqpMessage, deliveryTag);
|
|
458
|
+
if (!noAck) {
|
|
459
|
+
const channelQ = this._channelQueue;
|
|
460
|
+
channelQ.queueMessage(consumeMessage.fields, consumeMessage);
|
|
461
|
+
}
|
|
462
|
+
return consumeMessage;
|
|
463
|
+
}
|
|
464
|
+
_teardown() {
|
|
465
|
+
this[kClosed] = true;
|
|
466
|
+
const channelName = this._channelName;
|
|
467
|
+
const broker = this._broker;
|
|
468
|
+
const channelConsumers = broker._getChannelConsumers(channelName);
|
|
469
|
+
channelConsumers.forEach((c) => broker.cancel(c.consumerTag));
|
|
470
|
+
|
|
471
|
+
let msg;
|
|
472
|
+
while ((msg = this._channelQueue.get())) {
|
|
473
|
+
msg.content[kSmqp].reject(true);
|
|
474
|
+
msg.reject(false);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
broker.off('return', this._emitReturn);
|
|
478
|
+
}
|
|
479
|
+
_calculateChannelCapacity(consumerCapacity) {
|
|
480
|
+
const channelPrefetch = this[kChannelPrefetch];
|
|
481
|
+
if (channelPrefetch === Infinity) return consumerCapacity;
|
|
482
|
+
|
|
483
|
+
const channelCapacity = channelPrefetch - this._channelQueue.messageCount;
|
|
484
|
+
|
|
485
|
+
let capacity = consumerCapacity;
|
|
486
|
+
if (channelCapacity <= 0) capacity = 0;
|
|
487
|
+
else if (channelCapacity < capacity) capacity = channelCapacity;
|
|
488
|
+
return capacity;
|
|
489
|
+
}
|
|
319
490
|
}
|
|
320
491
|
|
|
321
492
|
export class FakeAmqplibConfirmChannel extends FakeAmqplibChannel {
|
|
@@ -330,7 +501,7 @@ export class FakeAmqplibConfirmChannel extends FakeAmqplibChannel {
|
|
|
330
501
|
this.checkExchange(exchange).then(() => {
|
|
331
502
|
return this._callBroker(...args);
|
|
332
503
|
}).catch((err) => {
|
|
333
|
-
this
|
|
504
|
+
this.emit('error', err);
|
|
334
505
|
});
|
|
335
506
|
|
|
336
507
|
return true;
|
|
@@ -345,16 +516,16 @@ export class FakeAmqplibConfirmChannel extends FakeAmqplibChannel {
|
|
|
345
516
|
this.checkQueue(queue).then(() => {
|
|
346
517
|
return this._callBroker(...args);
|
|
347
518
|
}).catch((err) => {
|
|
348
|
-
this
|
|
519
|
+
this.emit('error', err);
|
|
349
520
|
});
|
|
350
521
|
|
|
351
522
|
return true;
|
|
352
523
|
}
|
|
353
524
|
}
|
|
354
525
|
|
|
355
|
-
export class FakeAmqplibConnection {
|
|
526
|
+
export class FakeAmqplibConnection extends EventEmitter {
|
|
356
527
|
constructor(broker, version, amqpUrl) {
|
|
357
|
-
|
|
528
|
+
super();
|
|
358
529
|
this[kClosed] = false;
|
|
359
530
|
this._channels = [];
|
|
360
531
|
this._url = normalizeAmqpUrl(amqpUrl);
|
|
@@ -365,9 +536,6 @@ export class FakeAmqplibConnection {
|
|
|
365
536
|
get _closed() {
|
|
366
537
|
return this[kClosed];
|
|
367
538
|
}
|
|
368
|
-
get _emitter() {
|
|
369
|
-
return this[kEmitter];
|
|
370
|
-
}
|
|
371
539
|
get connection() {
|
|
372
540
|
return {
|
|
373
541
|
serverProperties: {
|
|
@@ -401,16 +569,10 @@ export class FakeAmqplibConnection {
|
|
|
401
569
|
|
|
402
570
|
this._channels.splice(0).forEach((channel) => channel.close());
|
|
403
571
|
|
|
404
|
-
this
|
|
572
|
+
this.emit('close');
|
|
405
573
|
|
|
406
574
|
return resolveOrCallback(args.slice(-1)[0]);
|
|
407
575
|
}
|
|
408
|
-
on(...args) {
|
|
409
|
-
return this[kEmitter].on(...args);
|
|
410
|
-
}
|
|
411
|
-
once(...args) {
|
|
412
|
-
return this[kEmitter].once(...args);
|
|
413
|
-
}
|
|
414
576
|
}
|
|
415
577
|
|
|
416
578
|
export function FakeAmqplib(minorVersion = '3.5') {
|
|
@@ -434,14 +596,14 @@ FakeAmqplib.prototype.connect = function fakeConnect(amqpUrl, ...args) {
|
|
|
434
596
|
|
|
435
597
|
FakeAmqplib.prototype.connectSync = function fakeConnectSync(amqpUrl, ...args) {
|
|
436
598
|
const { _broker } = this.connections.find((conn) => compareConnectionString(conn._url, amqpUrl)) || {};
|
|
437
|
-
const broker = _broker || new
|
|
599
|
+
const broker = _broker || new AmqplibBroker(this);
|
|
438
600
|
const connection = new FakeAmqplibConnection(broker, this.version, amqpUrl, ...args);
|
|
439
601
|
|
|
440
602
|
const connections = this.connections;
|
|
441
603
|
|
|
442
604
|
connections.push(connection);
|
|
443
605
|
|
|
444
|
-
connection.
|
|
606
|
+
connection.once('close', () => {
|
|
445
607
|
const idx = connections.indexOf(connection);
|
|
446
608
|
if (idx > -1) connections.splice(idx, 1);
|
|
447
609
|
});
|
|
@@ -477,13 +639,6 @@ function compareConnectionString(url1, url2) {
|
|
|
477
639
|
return parsedUrl1.host === parsedUrl2.host && parsedUrl1.pathname === parsedUrl2.pathname;
|
|
478
640
|
}
|
|
479
641
|
|
|
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
642
|
function normalizeAmqpUrl(url) {
|
|
488
643
|
if (!url) return new URL('amqp://localhost:5672/');
|
|
489
644
|
if (typeof url === 'string') url = new URL(url);
|
|
@@ -559,6 +714,23 @@ function addConfirmCallback(broker, options, callback) {
|
|
|
559
714
|
return [ options, confirmCallback ];
|
|
560
715
|
}
|
|
561
716
|
|
|
717
|
+
function allUpToDeliveryTag(q, deliveryTag, op, ...args) {
|
|
718
|
+
const brokerMessages = [];
|
|
719
|
+
|
|
720
|
+
const consumer = q.consume((_, cmsg) => {
|
|
721
|
+
const msgDeliveryTag = cmsg.fields.deliveryTag;
|
|
722
|
+
if (msgDeliveryTag >= deliveryTag) {
|
|
723
|
+
return q.cancel(cmsg.fields.consumerTag);
|
|
724
|
+
}
|
|
725
|
+
brokerMessages.push(cmsg.content[kSmqp]);
|
|
726
|
+
cmsg[op](...args);
|
|
727
|
+
}, { prefetch: Infinity });
|
|
728
|
+
|
|
729
|
+
consumer.cancel();
|
|
730
|
+
|
|
731
|
+
return brokerMessages;
|
|
732
|
+
}
|
|
733
|
+
|
|
562
734
|
const defaultFake = new FakeAmqplib('3.5');
|
|
563
735
|
export const connections = defaultFake.connections;
|
|
564
736
|
|
package/main.cjs
CHANGED
|
@@ -6,8 +6,26 @@ var url = require('url');
|
|
|
6
6
|
|
|
7
7
|
const kSmqp = Symbol.for('smqp');
|
|
8
8
|
const kClosed = Symbol.for('closed');
|
|
9
|
-
const
|
|
9
|
+
const kDeliveryTag = Symbol.for('channel delivery tag');
|
|
10
10
|
const kPrefetch = Symbol.for('prefetch');
|
|
11
|
+
const kChannelPrefetch = Symbol.for('channel prefetch');
|
|
12
|
+
|
|
13
|
+
class AmqplibBroker extends smqp.Broker {
|
|
14
|
+
constructor(...args) {
|
|
15
|
+
super(...args);
|
|
16
|
+
this[kDeliveryTag] = 0;
|
|
17
|
+
}
|
|
18
|
+
_getNextDeliveryTag() {
|
|
19
|
+
return ++this[kDeliveryTag];
|
|
20
|
+
}
|
|
21
|
+
_getMessageByDeliveryTag(queue, deliveryTag) {
|
|
22
|
+
const q = this.getQueue(queue);
|
|
23
|
+
return q.messages.find((m) => m.fields.deliveryTag === deliveryTag);
|
|
24
|
+
}
|
|
25
|
+
_getChannelConsumers(channelName) {
|
|
26
|
+
return this.getConsumers().filter((f) => f.options.channelName === channelName);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
11
29
|
|
|
12
30
|
class FakeAmqpError extends Error {
|
|
13
31
|
constructor(message, code, killChannel, killConnection) {
|
|
@@ -24,23 +42,41 @@ class FakeAmqpNotFoundError extends FakeAmqpError {
|
|
|
24
42
|
}
|
|
25
43
|
}
|
|
26
44
|
|
|
27
|
-
class
|
|
45
|
+
class FakeAmqpUnknownDeliveryTag extends FakeAmqpError {
|
|
46
|
+
constructor(deliveryTag) {
|
|
47
|
+
super(`Channel closed by server: 406 (PRECONDITION-FAILED) with message "PRECONDITION_FAILED - unknown delivery tag ${deliveryTag}`, 406, true, false);
|
|
48
|
+
}
|
|
49
|
+
get _emit() {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function Message(smqpMessage, deliveryTag) {
|
|
55
|
+
this[kSmqp] = smqpMessage;
|
|
56
|
+
this.fields = { ...smqpMessage.fields, deliveryTag };
|
|
57
|
+
this.content = Buffer.from(smqpMessage.content);
|
|
58
|
+
this.properties = { ...smqpMessage.properties };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
class FakeAmqplibChannel extends events.EventEmitter {
|
|
28
62
|
constructor(broker, connection) {
|
|
63
|
+
super();
|
|
29
64
|
this.connection = connection;
|
|
30
65
|
|
|
31
66
|
this[kPrefetch] = 10000;
|
|
67
|
+
this[kChannelPrefetch] = Infinity;
|
|
32
68
|
this[kClosed] = false;
|
|
33
|
-
this
|
|
34
|
-
this._channelName = `channel-${generateId()}`;
|
|
69
|
+
const channelName = this._channelName = `channel-${generateId()}`;
|
|
35
70
|
this._version = connection._version;
|
|
36
71
|
this._broker = broker;
|
|
37
72
|
|
|
73
|
+
this._channelQueue = broker.assertQueue(`#${channelName}`);
|
|
38
74
|
this._emitReturn = this._emitReturn.bind(this);
|
|
39
75
|
|
|
40
76
|
broker.on('return', this._emitReturn);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
77
|
+
|
|
78
|
+
this._createChannelMessage = this._createChannelMessage.bind(this);
|
|
79
|
+
this._calculateChannelCapacity = this._calculateChannelCapacity.bind(this);
|
|
44
80
|
}
|
|
45
81
|
get _closed() {
|
|
46
82
|
return this[kClosed];
|
|
@@ -106,14 +142,16 @@ class FakeAmqplibChannel {
|
|
|
106
142
|
}
|
|
107
143
|
get(queue, ...args) {
|
|
108
144
|
const connPath = this.connection._url.pathname;
|
|
145
|
+
const createMessage = this._createChannelMessage;
|
|
109
146
|
return this._callBroker(getMessage, ...args);
|
|
110
147
|
|
|
111
148
|
function getMessage(...getargs) {
|
|
112
149
|
const q = this.getQueue(queue);
|
|
113
|
-
if (!q) throw new FakeAmqpNotFoundError('queue', queue, connPath
|
|
150
|
+
if (!q) throw new FakeAmqpNotFoundError('queue', queue, connPath);
|
|
114
151
|
const msg = q.get(...getargs) || false;
|
|
115
152
|
if (!msg) return msg;
|
|
116
|
-
|
|
153
|
+
|
|
154
|
+
return createMessage(msg, args[0]?.noAck);
|
|
117
155
|
}
|
|
118
156
|
}
|
|
119
157
|
deleteExchange(exchange, ...args) {
|
|
@@ -147,7 +185,7 @@ class FakeAmqplibChannel {
|
|
|
147
185
|
this.checkExchange(exchange).then(() => {
|
|
148
186
|
return this._callBroker(...args);
|
|
149
187
|
}).catch((err) => {
|
|
150
|
-
this
|
|
188
|
+
this.emit('error', err);
|
|
151
189
|
});
|
|
152
190
|
|
|
153
191
|
return true;
|
|
@@ -172,7 +210,7 @@ class FakeAmqplibChannel {
|
|
|
172
210
|
this.checkQueue(queue).then(() => {
|
|
173
211
|
return this._callBroker(...args);
|
|
174
212
|
}).catch((err) => {
|
|
175
|
-
this
|
|
213
|
+
this.emit('error', err);
|
|
176
214
|
});
|
|
177
215
|
|
|
178
216
|
return true;
|
|
@@ -216,12 +254,14 @@ class FakeAmqplibChannel {
|
|
|
216
254
|
}
|
|
217
255
|
consume(queue, onMessage, options = {}, callback) {
|
|
218
256
|
const { _id: connId, _url: connUrl } = this.connection;
|
|
257
|
+
const createMessage = this._createChannelMessage;
|
|
258
|
+
const calculateCapacity = this._calculateChannelCapacity;
|
|
219
259
|
const channelName = this._channelName;
|
|
220
260
|
const prefetch = this[kPrefetch];
|
|
221
261
|
|
|
222
|
-
return this._callBroker(
|
|
262
|
+
return this._callBroker(consume, callback);
|
|
223
263
|
|
|
224
|
-
function
|
|
264
|
+
function consume() {
|
|
225
265
|
const q = queue && this.getQueue(queue);
|
|
226
266
|
if (!q) {
|
|
227
267
|
throw new FakeAmqpNotFoundError('queue', queue, connUrl.pathname);
|
|
@@ -231,16 +271,25 @@ class FakeAmqplibChannel {
|
|
|
231
271
|
throw new FakeAmqpError(`Channel closed by server: 403 (ACCESS-REFUSED) with message "ACCESS_REFUSED - queue '${queue}' in vhost '${connUrl.pathname}' in exclusive use"`, 403, true, true);
|
|
232
272
|
}
|
|
233
273
|
|
|
234
|
-
const
|
|
274
|
+
const consumer = this.consume(queue, onMessage && handler, {
|
|
235
275
|
...options,
|
|
236
276
|
channelName,
|
|
237
|
-
prefetch,
|
|
277
|
+
prefetch: calculateCapacity(prefetch),
|
|
278
|
+
_consumerPrefetch: prefetch,
|
|
238
279
|
});
|
|
239
|
-
|
|
280
|
+
|
|
281
|
+
const capacityProp = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(consumer), 'capacity');
|
|
282
|
+
Object.defineProperty(consumer, 'capacity', {
|
|
283
|
+
get() {
|
|
284
|
+
return calculateCapacity(capacityProp.get.call(this));
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
return { consumerTag: consumer.consumerTag };
|
|
240
289
|
}
|
|
241
290
|
|
|
242
291
|
function handler(_, msg) {
|
|
243
|
-
onMessage(
|
|
292
|
+
onMessage(createMessage(msg, options.noAck));
|
|
244
293
|
}
|
|
245
294
|
}
|
|
246
295
|
cancel(consumerTag, ...args) {
|
|
@@ -248,48 +297,134 @@ class FakeAmqplibChannel {
|
|
|
248
297
|
}
|
|
249
298
|
close(callback) {
|
|
250
299
|
if (this[kClosed]) return;
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
broker.off('return', this._emitReturn);
|
|
255
|
-
const channelConsumers = broker.getConsumers().filter((f) => f.options.channelName === channelName);
|
|
256
|
-
channelConsumers.forEach((c) => broker.cancel(c.consumerTag));
|
|
257
|
-
this[kClosed] = true;
|
|
258
|
-
this[kEmitter].emit('close');
|
|
300
|
+
this._teardown();
|
|
301
|
+
this.emit('close');
|
|
259
302
|
return resolveOrCallback(callback);
|
|
260
303
|
}
|
|
261
|
-
ack(message,
|
|
262
|
-
|
|
304
|
+
ack(message, allUpTo) {
|
|
305
|
+
const deliveryTag = message.fields.deliveryTag;
|
|
306
|
+
const channelMessage = this._broker._getMessageByDeliveryTag(this._channelQueue.name, deliveryTag);
|
|
307
|
+
const channelQ = this._channelQueue;
|
|
308
|
+
|
|
309
|
+
if (!allUpTo) this._callBroker(ackMessage);
|
|
310
|
+
else this._callBroker(ackAllUpToMessage);
|
|
311
|
+
|
|
312
|
+
function ackMessage() {
|
|
313
|
+
const msg = message[kSmqp];
|
|
314
|
+
if (!channelMessage || !msg.pending) {
|
|
315
|
+
throw new FakeAmqpUnknownDeliveryTag(message.fields.deliveryTag);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
channelQ.ack(channelMessage, false);
|
|
319
|
+
this.ack(msg, false);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function ackAllUpToMessage() {
|
|
323
|
+
const msg = message[kSmqp];
|
|
324
|
+
if (!channelMessage || !msg.pending) {
|
|
325
|
+
throw new FakeAmqpUnknownDeliveryTag(message.fields.deliveryTag);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const brokerMessages = allUpToDeliveryTag(channelQ, deliveryTag, 'ack', false);
|
|
329
|
+
for (const brokerMessage of brokerMessages) {
|
|
330
|
+
brokerMessage.ack(false);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
channelQ.ack(channelMessage, false);
|
|
334
|
+
this.ack(msg, false);
|
|
335
|
+
}
|
|
263
336
|
}
|
|
264
337
|
ackAll() {
|
|
265
|
-
const
|
|
266
|
-
|
|
338
|
+
const channelQ = this._channelQueue;
|
|
339
|
+
let msg;
|
|
340
|
+
const brokerMessages = [];
|
|
341
|
+
while ((msg = channelQ.get())) {
|
|
342
|
+
brokerMessages.push(msg.content[kSmqp]);
|
|
343
|
+
msg.ack();
|
|
344
|
+
}
|
|
267
345
|
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
nack(message, ...args) {
|
|
272
|
-
if (this.connection._version >= 2.3) throw new Error(`Nack is not implemented in versions before 2.3 (${this.connection._version})`);
|
|
273
|
-
return this._broker.nack(message[kSmqp], ...args);
|
|
274
|
-
}
|
|
275
|
-
reject(message, ...args) {
|
|
276
|
-
this._broker.reject(message[kSmqp], ...args);
|
|
346
|
+
for (const brokerMessage of brokerMessages) {
|
|
347
|
+
brokerMessage.ack();
|
|
348
|
+
}
|
|
277
349
|
}
|
|
278
|
-
|
|
279
|
-
const
|
|
280
|
-
const
|
|
350
|
+
reject(message, requeue = false) {
|
|
351
|
+
const deliveryTag = message.fields.deliveryTag;
|
|
352
|
+
const channelMessage = this._broker._getMessageByDeliveryTag(this._channelQueue.name, deliveryTag);
|
|
353
|
+
const channelQ = this._channelQueue;
|
|
354
|
+
|
|
355
|
+
this._callBroker(rejectMessage);
|
|
281
356
|
|
|
282
|
-
|
|
283
|
-
|
|
357
|
+
function rejectMessage() {
|
|
358
|
+
const msg = message[kSmqp];
|
|
359
|
+
if (!channelMessage || !msg.pending) {
|
|
360
|
+
throw new FakeAmqpUnknownDeliveryTag(deliveryTag);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
channelQ.reject(channelMessage, false);
|
|
364
|
+
this.reject(msg, requeue);
|
|
365
|
+
}
|
|
284
366
|
}
|
|
285
|
-
|
|
286
|
-
this
|
|
367
|
+
nack(message, allUpTo = false, requeue = false) {
|
|
368
|
+
if (this.connection._version < 2.3) throw new Error(`Nack is not implemented in versions before 2.3 (${this.connection._version})`);
|
|
369
|
+
|
|
370
|
+
const deliveryTag = message.fields.deliveryTag;
|
|
371
|
+
const channelMessage = this._broker._getMessageByDeliveryTag(this._channelQueue.name, deliveryTag);
|
|
372
|
+
const channelQ = this._channelQueue;
|
|
373
|
+
|
|
374
|
+
if (!allUpTo) this._callBroker(nackMessage);
|
|
375
|
+
else this._callBroker(nackAllUpToMessage);
|
|
376
|
+
|
|
377
|
+
function nackMessage() {
|
|
378
|
+
const msg = message[kSmqp];
|
|
379
|
+
if (!channelMessage || !msg.pending) {
|
|
380
|
+
throw new FakeAmqpUnknownDeliveryTag(deliveryTag);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
channelQ.nack(channelMessage, false, false);
|
|
384
|
+
this.nack(msg, false, requeue);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function nackAllUpToMessage() {
|
|
388
|
+
const msg = message[kSmqp];
|
|
389
|
+
if (!channelMessage || !msg.pending) {
|
|
390
|
+
throw new FakeAmqpUnknownDeliveryTag(deliveryTag);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const brokerMessages = allUpToDeliveryTag(channelQ, deliveryTag, 'nack', false, false);
|
|
394
|
+
for (const brokerMessage of brokerMessages) {
|
|
395
|
+
brokerMessage.nack(false, requeue);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
channelMessage.nack(false, false);
|
|
399
|
+
this.nack(msg, false, requeue);
|
|
400
|
+
}
|
|
287
401
|
}
|
|
288
|
-
|
|
289
|
-
|
|
402
|
+
nackAll(requeue = true) {
|
|
403
|
+
const channelQ = this._channelQueue;
|
|
404
|
+
let msg;
|
|
405
|
+
const brokerMessages = [];
|
|
406
|
+
while ((msg = channelQ.get())) {
|
|
407
|
+
brokerMessages.push(msg.content[kSmqp]);
|
|
408
|
+
msg.reject(false);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
for (const brokerMessage of brokerMessages) {
|
|
412
|
+
brokerMessage.reject(requeue);
|
|
413
|
+
}
|
|
290
414
|
}
|
|
291
|
-
|
|
292
|
-
|
|
415
|
+
prefetch(val, isChannelPrefetch) {
|
|
416
|
+
if (this.connection._version < 3.3) {
|
|
417
|
+
if (isChannelPrefetch !== undefined) {
|
|
418
|
+
return this.connection.close();
|
|
419
|
+
}
|
|
420
|
+
this[kChannelPrefetch] = val;
|
|
421
|
+
} else {
|
|
422
|
+
if (isChannelPrefetch) {
|
|
423
|
+
this[kChannelPrefetch] = val;
|
|
424
|
+
} else {
|
|
425
|
+
this[kPrefetch] = val;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
293
428
|
}
|
|
294
429
|
_callBroker(fn, ...args) {
|
|
295
430
|
let [ poppedCb ] = args.slice(-1);
|
|
@@ -306,7 +441,8 @@ class FakeAmqplibChannel {
|
|
|
306
441
|
return resolve(result);
|
|
307
442
|
} catch (err) {
|
|
308
443
|
if (err._killConnection) this.connection.close();
|
|
309
|
-
else if (err._killChannel) this
|
|
444
|
+
else if (err._killChannel) this._teardown();
|
|
445
|
+
if (err._emit) this.emit('error', err);
|
|
310
446
|
if (!poppedCb) return reject(err);
|
|
311
447
|
poppedCb(err);
|
|
312
448
|
return resolve();
|
|
@@ -315,9 +451,44 @@ class FakeAmqplibChannel {
|
|
|
315
451
|
}
|
|
316
452
|
_emitReturn({ fields, content, properties }) {
|
|
317
453
|
process.nextTick(() => {
|
|
318
|
-
this
|
|
454
|
+
this.emit('return', { fields, content, properties });
|
|
319
455
|
});
|
|
320
456
|
}
|
|
457
|
+
_createChannelMessage(smqpMessage, noAck) {
|
|
458
|
+
const deliveryTag = this._broker._getNextDeliveryTag();
|
|
459
|
+
const consumeMessage = new Message(smqpMessage, deliveryTag);
|
|
460
|
+
if (!noAck) {
|
|
461
|
+
const channelQ = this._channelQueue;
|
|
462
|
+
channelQ.queueMessage(consumeMessage.fields, consumeMessage);
|
|
463
|
+
}
|
|
464
|
+
return consumeMessage;
|
|
465
|
+
}
|
|
466
|
+
_teardown() {
|
|
467
|
+
this[kClosed] = true;
|
|
468
|
+
const channelName = this._channelName;
|
|
469
|
+
const broker = this._broker;
|
|
470
|
+
const channelConsumers = broker._getChannelConsumers(channelName);
|
|
471
|
+
channelConsumers.forEach((c) => broker.cancel(c.consumerTag));
|
|
472
|
+
|
|
473
|
+
let msg;
|
|
474
|
+
while ((msg = this._channelQueue.get())) {
|
|
475
|
+
msg.content[kSmqp].reject(true);
|
|
476
|
+
msg.reject(false);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
broker.off('return', this._emitReturn);
|
|
480
|
+
}
|
|
481
|
+
_calculateChannelCapacity(consumerCapacity) {
|
|
482
|
+
const channelPrefetch = this[kChannelPrefetch];
|
|
483
|
+
if (channelPrefetch === Infinity) return consumerCapacity;
|
|
484
|
+
|
|
485
|
+
const channelCapacity = channelPrefetch - this._channelQueue.messageCount;
|
|
486
|
+
|
|
487
|
+
let capacity = consumerCapacity;
|
|
488
|
+
if (channelCapacity <= 0) capacity = 0;
|
|
489
|
+
else if (channelCapacity < capacity) capacity = channelCapacity;
|
|
490
|
+
return capacity;
|
|
491
|
+
}
|
|
321
492
|
}
|
|
322
493
|
|
|
323
494
|
class FakeAmqplibConfirmChannel extends FakeAmqplibChannel {
|
|
@@ -332,7 +503,7 @@ class FakeAmqplibConfirmChannel extends FakeAmqplibChannel {
|
|
|
332
503
|
this.checkExchange(exchange).then(() => {
|
|
333
504
|
return this._callBroker(...args);
|
|
334
505
|
}).catch((err) => {
|
|
335
|
-
this
|
|
506
|
+
this.emit('error', err);
|
|
336
507
|
});
|
|
337
508
|
|
|
338
509
|
return true;
|
|
@@ -347,16 +518,16 @@ class FakeAmqplibConfirmChannel extends FakeAmqplibChannel {
|
|
|
347
518
|
this.checkQueue(queue).then(() => {
|
|
348
519
|
return this._callBroker(...args);
|
|
349
520
|
}).catch((err) => {
|
|
350
|
-
this
|
|
521
|
+
this.emit('error', err);
|
|
351
522
|
});
|
|
352
523
|
|
|
353
524
|
return true;
|
|
354
525
|
}
|
|
355
526
|
}
|
|
356
527
|
|
|
357
|
-
class FakeAmqplibConnection {
|
|
528
|
+
class FakeAmqplibConnection extends events.EventEmitter {
|
|
358
529
|
constructor(broker, version, amqpUrl) {
|
|
359
|
-
|
|
530
|
+
super();
|
|
360
531
|
this[kClosed] = false;
|
|
361
532
|
this._channels = [];
|
|
362
533
|
this._url = normalizeAmqpUrl(amqpUrl);
|
|
@@ -367,9 +538,6 @@ class FakeAmqplibConnection {
|
|
|
367
538
|
get _closed() {
|
|
368
539
|
return this[kClosed];
|
|
369
540
|
}
|
|
370
|
-
get _emitter() {
|
|
371
|
-
return this[kEmitter];
|
|
372
|
-
}
|
|
373
541
|
get connection() {
|
|
374
542
|
return {
|
|
375
543
|
serverProperties: {
|
|
@@ -403,16 +571,10 @@ class FakeAmqplibConnection {
|
|
|
403
571
|
|
|
404
572
|
this._channels.splice(0).forEach((channel) => channel.close());
|
|
405
573
|
|
|
406
|
-
this
|
|
574
|
+
this.emit('close');
|
|
407
575
|
|
|
408
576
|
return resolveOrCallback(args.slice(-1)[0]);
|
|
409
577
|
}
|
|
410
|
-
on(...args) {
|
|
411
|
-
return this[kEmitter].on(...args);
|
|
412
|
-
}
|
|
413
|
-
once(...args) {
|
|
414
|
-
return this[kEmitter].once(...args);
|
|
415
|
-
}
|
|
416
578
|
}
|
|
417
579
|
|
|
418
580
|
function FakeAmqplib(minorVersion = '3.5') {
|
|
@@ -436,14 +598,14 @@ FakeAmqplib.prototype.connect = function fakeConnect(amqpUrl, ...args) {
|
|
|
436
598
|
|
|
437
599
|
FakeAmqplib.prototype.connectSync = function fakeConnectSync(amqpUrl, ...args) {
|
|
438
600
|
const { _broker } = this.connections.find((conn) => compareConnectionString(conn._url, amqpUrl)) || {};
|
|
439
|
-
const broker = _broker || new
|
|
601
|
+
const broker = _broker || new AmqplibBroker(this);
|
|
440
602
|
const connection = new FakeAmqplibConnection(broker, this.version, amqpUrl, ...args);
|
|
441
603
|
|
|
442
604
|
const connections = this.connections;
|
|
443
605
|
|
|
444
606
|
connections.push(connection);
|
|
445
607
|
|
|
446
|
-
connection.
|
|
608
|
+
connection.once('close', () => {
|
|
447
609
|
const idx = connections.indexOf(connection);
|
|
448
610
|
if (idx > -1) connections.splice(idx, 1);
|
|
449
611
|
});
|
|
@@ -479,13 +641,6 @@ function compareConnectionString(url1, url2) {
|
|
|
479
641
|
return parsedUrl1.host === parsedUrl2.host && parsedUrl1.pathname === parsedUrl2.pathname;
|
|
480
642
|
}
|
|
481
643
|
|
|
482
|
-
function Message(smqpMessage) {
|
|
483
|
-
this[kSmqp] = smqpMessage;
|
|
484
|
-
this.content = smqpMessage.content;
|
|
485
|
-
this.fields = smqpMessage.fields;
|
|
486
|
-
this.properties = smqpMessage.properties;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
644
|
function normalizeAmqpUrl(url$1) {
|
|
490
645
|
if (!url$1) return new URL('amqp://localhost:5672/');
|
|
491
646
|
if (typeof url$1 === 'string') url$1 = new URL(url$1);
|
|
@@ -561,6 +716,23 @@ function addConfirmCallback(broker, options, callback) {
|
|
|
561
716
|
return [ options, confirmCallback ];
|
|
562
717
|
}
|
|
563
718
|
|
|
719
|
+
function allUpToDeliveryTag(q, deliveryTag, op, ...args) {
|
|
720
|
+
const brokerMessages = [];
|
|
721
|
+
|
|
722
|
+
const consumer = q.consume((_, cmsg) => {
|
|
723
|
+
const msgDeliveryTag = cmsg.fields.deliveryTag;
|
|
724
|
+
if (msgDeliveryTag >= deliveryTag) {
|
|
725
|
+
return q.cancel(cmsg.fields.consumerTag);
|
|
726
|
+
}
|
|
727
|
+
brokerMessages.push(cmsg.content[kSmqp]);
|
|
728
|
+
cmsg[op](...args);
|
|
729
|
+
}, { prefetch: Infinity });
|
|
730
|
+
|
|
731
|
+
consumer.cancel();
|
|
732
|
+
|
|
733
|
+
return brokerMessages;
|
|
734
|
+
}
|
|
735
|
+
|
|
564
736
|
const defaultFake = new FakeAmqplib('3.5');
|
|
565
737
|
const connections = defaultFake.connections;
|
|
566
738
|
|
package/package.json
CHANGED