@onify/fake-amqplib 0.8.2 → 0.9.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/CHANGELOG.md +61 -0
- package/index.js +125 -47
- package/package.json +5 -8
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Changelog
|
|
2
|
+
=========
|
|
3
|
+
|
|
4
|
+
# 0.9.0
|
|
5
|
+
|
|
6
|
+
- support url object
|
|
7
|
+
- smqp@6
|
|
8
|
+
|
|
9
|
+
# 0.8.5
|
|
10
|
+
|
|
11
|
+
- ack/nack all only cares about messages consumed by channel, previously everything was gone
|
|
12
|
+
|
|
13
|
+
# 0.8.4
|
|
14
|
+
|
|
15
|
+
- ack/nack all fix
|
|
16
|
+
|
|
17
|
+
# 0.8.3
|
|
18
|
+
|
|
19
|
+
- Call confirm channel callback when the message is queued, not when it is consumed!
|
|
20
|
+
- implement publish with empty string special case
|
|
21
|
+
- hide some internal props from message
|
|
22
|
+
|
|
23
|
+
# 0.8.2
|
|
24
|
+
|
|
25
|
+
- share behind the scenes broker if connection hosts and vhost are the same
|
|
26
|
+
- add new `connectSync` helper method to be able to get a connection synchronously to facilitate testing
|
|
27
|
+
|
|
28
|
+
# 0.8.1
|
|
29
|
+
|
|
30
|
+
- be a better mimic of amqplib, some stuff didn't work at all prior to this version
|
|
31
|
+
|
|
32
|
+
## Additions
|
|
33
|
+
|
|
34
|
+
- Handle different behaviours between RabbitMQ versions
|
|
35
|
+
|
|
36
|
+
# 0.8.0
|
|
37
|
+
|
|
38
|
+
- bump `smqp@5`
|
|
39
|
+
- stop building for node 10 (mocha's fault)
|
|
40
|
+
|
|
41
|
+
# 0.7.0
|
|
42
|
+
|
|
43
|
+
- bump `smqp@4`
|
|
44
|
+
|
|
45
|
+
# 0.6.0
|
|
46
|
+
|
|
47
|
+
- bump `smqp@3.2`
|
|
48
|
+
|
|
49
|
+
# 0.5.0
|
|
50
|
+
|
|
51
|
+
- support exclusive queue and its behaviour
|
|
52
|
+
- emit return on channel if mandatory message was not routed
|
|
53
|
+
|
|
54
|
+
# 0.4.0
|
|
55
|
+
|
|
56
|
+
- apparently connection is killed as well when trying to consume exclusive consumed queue
|
|
57
|
+
- try to mimic real behaviour and throw some errors with code
|
|
58
|
+
|
|
59
|
+
# 0.3.0
|
|
60
|
+
|
|
61
|
+
- kill channel if trying to consume exclusive consumed queue
|
package/index.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
const {Broker} = require('smqp');
|
|
4
4
|
const {EventEmitter} = require('events');
|
|
5
|
-
const {URL} = require('url');
|
|
5
|
+
const {URL, format: urlFormat} = require('url');
|
|
6
|
+
|
|
7
|
+
const smqpSymbol = Symbol.for('smqp');
|
|
6
8
|
|
|
7
9
|
class FakeAmqpError extends Error {
|
|
8
10
|
constructor(message, code, killChannel, killConnection) {
|
|
@@ -14,8 +16,8 @@ class FakeAmqpError extends Error {
|
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
class FakeAmqpNotFoundError extends FakeAmqpError {
|
|
17
|
-
constructor(type, name, killConnection = false) {
|
|
18
|
-
super(`Channel closed by server: 404 (NOT-FOUND) with message "NOT_FOUND - no ${type} '${name}' in vhost '/'`, 404, true, killConnection);
|
|
19
|
+
constructor(type, name, vhost, killConnection = false) {
|
|
20
|
+
super(`Channel closed by server: 404 (NOT-FOUND) with message "NOT_FOUND - no ${type} '${name}' in vhost '${vhost || '/'}'`, 404, true, killConnection);
|
|
19
21
|
}
|
|
20
22
|
}
|
|
21
23
|
|
|
@@ -42,7 +44,7 @@ function Fake(minorVersion) {
|
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
function connectSync(amqpUrl, ...args) {
|
|
45
|
-
const {_broker} = connections.find((conn) => compareConnectionString(conn.
|
|
47
|
+
const {_broker} = connections.find((conn) => compareConnectionString(conn._url, amqpUrl)) || {};
|
|
46
48
|
const broker = _broker || Broker();
|
|
47
49
|
const connection = Connection(broker, defaultVersion, amqpUrl, ...args);
|
|
48
50
|
connections.push(connection);
|
|
@@ -55,16 +57,18 @@ function Fake(minorVersion) {
|
|
|
55
57
|
}
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
function Connection(broker, version, ...connArgs) {
|
|
60
|
+
function Connection(broker, version, amqpUrl, ...connArgs) {
|
|
59
61
|
const emitter = new EventEmitter();
|
|
60
62
|
const options = connArgs.filter((a) => typeof a !== 'function');
|
|
61
63
|
let closed = false;
|
|
62
64
|
const channels = [];
|
|
65
|
+
const url = normalizeAmqpUrl(amqpUrl);
|
|
63
66
|
|
|
64
67
|
return {
|
|
65
68
|
_id: generateId(),
|
|
66
69
|
_broker: broker,
|
|
67
70
|
_version: version,
|
|
71
|
+
_url: url,
|
|
68
72
|
get _closed() {
|
|
69
73
|
return closed;
|
|
70
74
|
},
|
|
@@ -87,6 +91,7 @@ function Fake(minorVersion) {
|
|
|
87
91
|
return resolveOrCallback(args.slice(-1)[0], null, channel);
|
|
88
92
|
},
|
|
89
93
|
async close(...args) {
|
|
94
|
+
if (closed) return resolveOrCallback(args.slice(-1)[0]);
|
|
90
95
|
closed = true;
|
|
91
96
|
|
|
92
97
|
const idx = connections.indexOf(this);
|
|
@@ -165,7 +170,7 @@ function Fake(minorVersion) {
|
|
|
165
170
|
return callBroker(check, ...args);
|
|
166
171
|
|
|
167
172
|
function check() {
|
|
168
|
-
if (!broker.getExchange(name)) throw new
|
|
173
|
+
if (!broker.getExchange(name)) throw new FakeAmqpNotFoundError('exchange', name, connection._url.pathname);
|
|
169
174
|
return true;
|
|
170
175
|
}
|
|
171
176
|
},
|
|
@@ -175,7 +180,7 @@ function Fake(minorVersion) {
|
|
|
175
180
|
function check() {
|
|
176
181
|
let queue;
|
|
177
182
|
if (!(queue = broker.getQueue(name))) {
|
|
178
|
-
throw new FakeAmqpNotFoundError('queue', name);
|
|
183
|
+
throw new FakeAmqpNotFoundError('queue', name, connection._url.pathname);
|
|
179
184
|
}
|
|
180
185
|
|
|
181
186
|
return {
|
|
@@ -189,8 +194,10 @@ function Fake(minorVersion) {
|
|
|
189
194
|
|
|
190
195
|
function getMessage(...getargs) {
|
|
191
196
|
const q = broker.getQueue(queue);
|
|
192
|
-
if (!q) throw new FakeAmqpNotFoundError('queue');
|
|
193
|
-
|
|
197
|
+
if (!q) throw new FakeAmqpNotFoundError('queue', queue, connection._url.pathname);
|
|
198
|
+
const msg = q.get(...getargs) || false;
|
|
199
|
+
if (!msg) return msg;
|
|
200
|
+
return new Message(msg);
|
|
194
201
|
}
|
|
195
202
|
},
|
|
196
203
|
deleteExchange(exchange, ...args) {
|
|
@@ -198,7 +205,7 @@ function Fake(minorVersion) {
|
|
|
198
205
|
|
|
199
206
|
function check() {
|
|
200
207
|
const result = broker.deleteExchange(exchange, ...args);
|
|
201
|
-
if (!result && version < 3.2) throw new FakeAmqpNotFoundError('exchange', exchange);
|
|
208
|
+
if (!result && version < 3.2) throw new FakeAmqpNotFoundError('exchange', exchange, connection._url.pathname);
|
|
202
209
|
return result;
|
|
203
210
|
}
|
|
204
211
|
},
|
|
@@ -207,18 +214,21 @@ function Fake(minorVersion) {
|
|
|
207
214
|
|
|
208
215
|
function check() {
|
|
209
216
|
const result = broker.deleteQueue(queue, ...args);
|
|
210
|
-
if (!result && version < 3.2) throw new FakeAmqpNotFoundError('queue', queue);
|
|
217
|
+
if (!result && version < 3.2) throw new FakeAmqpNotFoundError('queue', queue, connection._url.pathname);
|
|
211
218
|
return result;
|
|
212
219
|
}
|
|
213
220
|
},
|
|
214
221
|
publish(exchange, routingKey, content, options, callback) {
|
|
215
222
|
if (!Buffer.isBuffer(content)) throw new TypeError('content is not a buffer');
|
|
216
|
-
if (
|
|
217
|
-
|
|
218
|
-
|
|
223
|
+
if (exchange === '') return this.sendToQueue(routingKey, content, options, callback);
|
|
224
|
+
|
|
225
|
+
const args = [broker.publish, exchange, routingKey, content];
|
|
226
|
+
|
|
227
|
+
if (confirmChannel) args.push(...addConfirmCallback(options, callback));
|
|
228
|
+
else args.push(options);
|
|
219
229
|
|
|
220
230
|
this.checkExchange(exchange).then(() => {
|
|
221
|
-
return callBroker(
|
|
231
|
+
return callBroker(...args);
|
|
222
232
|
}).catch((err) => {
|
|
223
233
|
emitter.emit('error', err);
|
|
224
234
|
});
|
|
@@ -230,18 +240,20 @@ function Fake(minorVersion) {
|
|
|
230
240
|
|
|
231
241
|
function check() {
|
|
232
242
|
const result = broker.purgeQueue(queue);
|
|
233
|
-
if (!result && version < 3.2) throw new FakeAmqpNotFoundError('queue', queue);
|
|
243
|
+
if (!result && version < 3.2) throw new FakeAmqpNotFoundError('queue', queue, connection._url.pathname);
|
|
234
244
|
return result === undefined ? undefined : {messageCount: result};
|
|
235
245
|
}
|
|
236
246
|
},
|
|
237
247
|
sendToQueue(queue, content, options, callback) {
|
|
238
248
|
if (!Buffer.isBuffer(content)) throw new TypeError('content is not a buffer');
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
249
|
+
|
|
250
|
+
const args = [broker.sendToQueue, queue, content];
|
|
251
|
+
|
|
252
|
+
if (confirmChannel) args.push(...addConfirmCallback(options, callback));
|
|
253
|
+
else args.push(options);
|
|
242
254
|
|
|
243
255
|
this.checkQueue(queue).then(() => {
|
|
244
|
-
return callBroker(
|
|
256
|
+
return callBroker(...args);
|
|
245
257
|
}).catch((err) => {
|
|
246
258
|
emitter.emit('error', err);
|
|
247
259
|
});
|
|
@@ -259,7 +271,7 @@ function Fake(minorVersion) {
|
|
|
259
271
|
if (!exchange) throw new FakeAmqpNotFoundError('exchange', source);
|
|
260
272
|
|
|
261
273
|
const result = broker.unbindExchange(source, destination, pattern);
|
|
262
|
-
if (!result && version <= 3.2) throw new FakeAmqpNotFoundError('binding', pattern);
|
|
274
|
+
if (!result && version <= 3.2) throw new FakeAmqpNotFoundError('binding', pattern, connection._url.pathname);
|
|
263
275
|
|
|
264
276
|
return true;
|
|
265
277
|
}
|
|
@@ -275,7 +287,7 @@ function Fake(minorVersion) {
|
|
|
275
287
|
if (!exchange) throw new FakeAmqpNotFoundError('exchange', source);
|
|
276
288
|
|
|
277
289
|
const binding = exchange.getBinding(queue, pattern);
|
|
278
|
-
if (!binding && version <= 3.2) throw new FakeAmqpNotFoundError('binding', pattern, version < 3.2);
|
|
290
|
+
if (!binding && version <= 3.2) throw new FakeAmqpNotFoundError('binding', pattern, connection._url.pathname, version < 3.2);
|
|
279
291
|
|
|
280
292
|
broker.unbindQueue(queue, source, pattern);
|
|
281
293
|
return true;
|
|
@@ -286,14 +298,12 @@ function Fake(minorVersion) {
|
|
|
286
298
|
|
|
287
299
|
function check() {
|
|
288
300
|
const q = queue && broker.getQueue(queue);
|
|
289
|
-
if (
|
|
290
|
-
throw new FakeAmqpNotFoundError('queue', queue);
|
|
301
|
+
if (!q) {
|
|
302
|
+
throw new FakeAmqpNotFoundError('queue', queue, connection._url.pathname);
|
|
291
303
|
}
|
|
292
304
|
|
|
293
|
-
if (q) {
|
|
294
|
-
|
|
295
|
-
throw new FakeAmqpError(`Channel closed by server: 403 (ACCESS-REFUSED) with message "ACCESS_REFUSED - queue '${queue}' in vhost '/' in exclusive use"`, 403, true, true);
|
|
296
|
-
}
|
|
305
|
+
if (q.exclusive || (q.options.exclusive && q.options._connectionId !== connection._id)) {
|
|
306
|
+
throw new FakeAmqpError(`Channel closed by server: 403 (ACCESS-REFUSED) with message "ACCESS_REFUSED - queue '${queue}' in vhost '${connection._url.pathname}' in exclusive use"`, 403, true, true);
|
|
297
307
|
}
|
|
298
308
|
|
|
299
309
|
const {consumerTag} = broker.consume(queue, onMessage && handler, {...options, channelName, prefetch});
|
|
@@ -301,7 +311,7 @@ function Fake(minorVersion) {
|
|
|
301
311
|
}
|
|
302
312
|
|
|
303
313
|
function handler(_, msg) {
|
|
304
|
-
onMessage(msg);
|
|
314
|
+
onMessage(new Message(msg));
|
|
305
315
|
}
|
|
306
316
|
},
|
|
307
317
|
cancel(consumerTag, ...args) {
|
|
@@ -316,15 +326,25 @@ function Fake(minorVersion) {
|
|
|
316
326
|
emitter.emit('close');
|
|
317
327
|
return resolveOrCallback(callback);
|
|
318
328
|
},
|
|
319
|
-
ack
|
|
320
|
-
|
|
329
|
+
ack(message, ...args) {
|
|
330
|
+
broker.ack(message[smqpSymbol], ...args);
|
|
331
|
+
},
|
|
332
|
+
ackAll() {
|
|
333
|
+
const consumers = broker.getConsumers().filter(({options}) => options.channelName === channelName);
|
|
334
|
+
consumers.forEach((c) => broker.getConsumer(c.consumerTag).ackAll());
|
|
335
|
+
},
|
|
321
336
|
...(version >= 2.3 ? {
|
|
322
337
|
nack(message, ...args) {
|
|
323
|
-
return broker.nack(message, ...args);
|
|
338
|
+
return broker.nack(message[smqpSymbol], ...args);
|
|
324
339
|
}
|
|
325
340
|
} : undefined),
|
|
326
|
-
reject
|
|
327
|
-
|
|
341
|
+
reject(message, ...args) {
|
|
342
|
+
broker.reject(message[smqpSymbol], ...args);
|
|
343
|
+
},
|
|
344
|
+
nackAll(requeue = false) {
|
|
345
|
+
const consumers = broker.getConsumers().filter(({options}) => options.channelName === channelName);
|
|
346
|
+
consumers.forEach((c) => broker.getConsumer(c.consumerTag).nackAll(requeue));
|
|
347
|
+
},
|
|
328
348
|
prefetch(val) {
|
|
329
349
|
prefetch = val;
|
|
330
350
|
},
|
|
@@ -336,30 +356,37 @@ function Fake(minorVersion) {
|
|
|
336
356
|
},
|
|
337
357
|
};
|
|
338
358
|
|
|
339
|
-
function
|
|
359
|
+
function addConfirmCallback(options, callback) {
|
|
340
360
|
const confirm = 'msg.' + generateId();
|
|
341
361
|
const consumerTag = 'ct-' + confirm;
|
|
362
|
+
options = {...options, confirm};
|
|
363
|
+
|
|
342
364
|
broker.on('message.*', onConsumeMessage, {consumerTag});
|
|
343
365
|
|
|
366
|
+
let undelivered;
|
|
344
367
|
function onConsumeMessage(event) {
|
|
345
|
-
if (event.properties.confirm !== confirm) return;
|
|
346
|
-
|
|
368
|
+
if (event.properties && event.properties.confirm !== confirm) return;
|
|
347
369
|
switch(event.name) {
|
|
348
370
|
case 'message.nack':
|
|
349
|
-
return confirmCallback(new Error('message nacked'));
|
|
350
371
|
case 'message.undelivered':
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
return confirmCallback(null, true);
|
|
372
|
+
undelivered = event.name;
|
|
373
|
+
break;
|
|
354
374
|
}
|
|
375
|
+
}
|
|
355
376
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
377
|
+
function confirmCallback() {
|
|
378
|
+
broker.off('message.*', consumerTag);
|
|
379
|
+
switch (undelivered) {
|
|
380
|
+
case 'message.nack':
|
|
381
|
+
return callback(new Error('message nacked'));
|
|
382
|
+
case 'message.undelivered':
|
|
383
|
+
throw callback(new Error('message undelivered'));
|
|
384
|
+
default:
|
|
385
|
+
return callback(null, true);
|
|
359
386
|
}
|
|
360
387
|
}
|
|
361
388
|
|
|
362
|
-
return
|
|
389
|
+
return [options, confirmCallback];
|
|
363
390
|
}
|
|
364
391
|
|
|
365
392
|
function callBroker(fn, ...args) {
|
|
@@ -404,7 +431,58 @@ function generateId() {
|
|
|
404
431
|
}
|
|
405
432
|
|
|
406
433
|
function compareConnectionString(url1, url2) {
|
|
407
|
-
const parsedUrl1 =
|
|
408
|
-
const parsedUrl2 =
|
|
434
|
+
const parsedUrl1 = normalizeAmqpUrl(url1);
|
|
435
|
+
const parsedUrl2 = normalizeAmqpUrl(url2);
|
|
436
|
+
|
|
409
437
|
return parsedUrl1.host === parsedUrl2.host && parsedUrl1.pathname === parsedUrl2.pathname;
|
|
410
438
|
}
|
|
439
|
+
|
|
440
|
+
function Message(smqpMessage) {
|
|
441
|
+
this[smqpSymbol] = smqpMessage;
|
|
442
|
+
this.content = smqpMessage.content;
|
|
443
|
+
this.fields = smqpMessage.fields;
|
|
444
|
+
this.properties = smqpMessage.properties;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function normalizeAmqpUrl(url) {
|
|
448
|
+
if (!url) return url = new URL('amqp://localhost:5672/');
|
|
449
|
+
if (typeof url === 'string') url = new URL(url);
|
|
450
|
+
|
|
451
|
+
if (!(url instanceof URL)) {
|
|
452
|
+
const {
|
|
453
|
+
protocol = 'amqp',
|
|
454
|
+
hostname = 'localhost',
|
|
455
|
+
port = 5672,
|
|
456
|
+
vhost = '/',
|
|
457
|
+
username,
|
|
458
|
+
password,
|
|
459
|
+
...rest
|
|
460
|
+
} = url;
|
|
461
|
+
let auth = username;
|
|
462
|
+
if (auth && password) {
|
|
463
|
+
auth += ':' + password;
|
|
464
|
+
}
|
|
465
|
+
url = new URL(urlFormat({
|
|
466
|
+
protocol,
|
|
467
|
+
hostname,
|
|
468
|
+
port,
|
|
469
|
+
pathname: vhost,
|
|
470
|
+
slashes: true,
|
|
471
|
+
auth,
|
|
472
|
+
}));
|
|
473
|
+
|
|
474
|
+
for (const k in rest) {
|
|
475
|
+
switch (k) {
|
|
476
|
+
case 'locale':
|
|
477
|
+
case 'frameMax':
|
|
478
|
+
case 'heartbeat':
|
|
479
|
+
url.searchParams.set(k, rest[k]);
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (!url.port) url.port = 5672;
|
|
486
|
+
if (!url.pathname) url.pathname = '/';
|
|
487
|
+
return url;
|
|
488
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onify/fake-amqplib",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Fake amqplib",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"smqp": "^
|
|
24
|
+
"smqp": "^6.0.0"
|
|
25
25
|
},
|
|
26
26
|
"keywords": [
|
|
27
27
|
"fake",
|
|
@@ -31,15 +31,12 @@
|
|
|
31
31
|
"rabbitmq"
|
|
32
32
|
],
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"chai": "^4.
|
|
34
|
+
"chai": "^4.3.6",
|
|
35
35
|
"eslint": "^7.32.0",
|
|
36
|
-
"mocha": "^9.
|
|
36
|
+
"mocha": "^9.2.0",
|
|
37
37
|
"nyc": "^15.1.0"
|
|
38
38
|
},
|
|
39
39
|
"files": [
|
|
40
40
|
"index.js"
|
|
41
|
-
]
|
|
42
|
-
"directories": {
|
|
43
|
-
"test": "test"
|
|
44
|
-
}
|
|
41
|
+
]
|
|
45
42
|
}
|