@onify/fake-amqplib 3.3.0 → 3.5.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 +76 -44
- package/index.d.ts +86 -55
- package/index.js +69 -3
- package/main.cjs +69 -3
- package/package.json +21 -10
package/README.md
CHANGED
|
@@ -4,6 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
Mocked version of https://www.npmjs.com/package/amqplib.
|
|
6
6
|
|
|
7
|
+
<!-- toc -->
|
|
8
|
+
|
|
9
|
+
- [Fake api](#fake-api)
|
|
10
|
+
- [RabbitMQ versions](#rabbitmq-versions)
|
|
11
|
+
- [Mocking amqplib](#mocking-amqplib)
|
|
12
|
+
- [ESM](#esm)
|
|
13
|
+
- [Node 20+ — `node:test` `mock.module` (recommended)](#node-20-nodetest-mockmodule-recommended)
|
|
14
|
+
- [Alternative — Quibble](#alternative-quibble)
|
|
15
|
+
- [CommonJS](#commonjs)
|
|
16
|
+
|
|
17
|
+
<!-- /toc -->
|
|
18
|
+
|
|
7
19
|
## Fake api
|
|
8
20
|
|
|
9
21
|
- `async connect(amqpurl[, ...otherOptions, callback])`: wait for a fake connection or expect one in the callback
|
|
@@ -38,46 +50,62 @@ var fakeAmqp = require('@onify/fake-amqplib');
|
|
|
38
50
|
|
|
39
51
|
You might want to override `amqplib` with `@onify/fake-amqplib` in tests. This can be done in a number of ways.
|
|
40
52
|
|
|
41
|
-
###
|
|
53
|
+
### ESM
|
|
42
54
|
|
|
43
|
-
Example on how to mock amqplib when working with
|
|
55
|
+
Example on how to mock the `amqplib` import when working with modules.
|
|
44
56
|
|
|
45
|
-
|
|
46
|
-
const amqplib = require('amqplib');
|
|
47
|
-
const fakeAmqp = require('@onify/fake-amqplib');
|
|
57
|
+
#### Node 20+ — `node:test` `mock.module` (recommended)
|
|
48
58
|
|
|
49
|
-
|
|
59
|
+
Node's built-in test runner ships an experimental module-mocking API that needs no extra dependency. Set it up at the top of the test file (or in a setup file), then dynamically import `amqplib`.
|
|
60
|
+
|
|
61
|
+
_.mocharc.json_
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"recursive": true,
|
|
66
|
+
"require": ["chai/register-expect.js"],
|
|
67
|
+
"node-option": ["experimental-test-module-mocks", "no-warnings"]
|
|
68
|
+
}
|
|
50
69
|
```
|
|
51
70
|
|
|
52
|
-
|
|
71
|
+
The `experimental-test-module-mocks` flag enables `mock.module`; `no-warnings` silences the "experimental feature" notice. Drop it if you'd rather see the warning.
|
|
53
72
|
|
|
54
|
-
|
|
55
|
-
const mock = require('mock-require');
|
|
56
|
-
const fakeAmqp = require('@onify/fake-amqplib');
|
|
73
|
+
_test/amqplib-connection-test.js_
|
|
57
74
|
|
|
58
|
-
|
|
59
|
-
|
|
75
|
+
```javascript
|
|
76
|
+
import { mock } from 'node:test';
|
|
77
|
+
import { connect as fakeConnect, resetMock } from '@onify/fake-amqplib';
|
|
60
78
|
|
|
61
|
-
|
|
79
|
+
describe('connection', () => {
|
|
80
|
+
let connect;
|
|
81
|
+
let ctx;
|
|
62
82
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
83
|
+
before(async () => {
|
|
84
|
+
ctx = mock.module('amqplib', { namedExports: { connect: fakeConnect } });
|
|
85
|
+
({ connect } = await import('amqplib'));
|
|
86
|
+
});
|
|
66
87
|
|
|
67
|
-
|
|
68
|
-
|
|
88
|
+
after(() => {
|
|
89
|
+
ctx.restore();
|
|
90
|
+
resetMock();
|
|
91
|
+
});
|
|
69
92
|
|
|
70
|
-
|
|
93
|
+
it('connects to the fake', async () => {
|
|
94
|
+
const connection = await connect('amqp://host');
|
|
95
|
+
expect(connection.connection.serverProperties).to.have.property('product', 'RabbitMQ');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
```
|
|
71
99
|
|
|
72
|
-
|
|
100
|
+
If you also use `mocha --parallel` or run tests via `node --test`, the same setup works — `mock.module` is process-global, so register it before the first dynamic import in each test file.
|
|
73
101
|
|
|
74
|
-
|
|
102
|
+
#### Alternative — Quibble
|
|
75
103
|
|
|
76
|
-
|
|
104
|
+
[Quibble](https://www.npmjs.com/package/quibble) is useful if you're on a Node version older than 20, or prefer not to rely on an experimental flag.
|
|
77
105
|
|
|
78
106
|
_test/setup.js_
|
|
79
107
|
|
|
80
|
-
```
|
|
108
|
+
```js
|
|
81
109
|
import * as fakeAmqpLib from '@onify/fake-amqplib';
|
|
82
110
|
import { connect as fakeConnect } from '@onify/fake-amqplib';
|
|
83
111
|
import quibble from 'quibble';
|
|
@@ -88,7 +116,7 @@ import quibble from 'quibble';
|
|
|
88
116
|
})();
|
|
89
117
|
```
|
|
90
118
|
|
|
91
|
-
_.mocharc.json_ (
|
|
119
|
+
_.mocharc.json_ (the `loader=quibble` option is only needed on Node < 20)
|
|
92
120
|
|
|
93
121
|
```json
|
|
94
122
|
{
|
|
@@ -98,29 +126,33 @@ _.mocharc.json_ (true for node version < 20)
|
|
|
98
126
|
}
|
|
99
127
|
```
|
|
100
128
|
|
|
101
|
-
|
|
129
|
+
Then import `amqplib` normally in your tests; quibble rewires the import.
|
|
102
130
|
|
|
103
|
-
|
|
104
|
-
import assert from 'node:assert';
|
|
105
|
-
import { connect } from 'amqplib';
|
|
106
|
-
import { connect as connectCb } from 'amqplib';
|
|
131
|
+
### CommonJS
|
|
107
132
|
|
|
108
|
-
|
|
133
|
+
Example on how to mock amqplib when working with commonjs.
|
|
109
134
|
|
|
110
|
-
|
|
111
|
-
|
|
135
|
+
```js
|
|
136
|
+
const amqplib = require('amqplib');
|
|
137
|
+
const fakeAmqp = require('@onify/fake-amqplib');
|
|
112
138
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
assert.equal(connection.connection.serverProperties.version, '3.5.0');
|
|
116
|
-
});
|
|
139
|
+
amqplib.connect = fakeAmqp.connect;
|
|
140
|
+
```
|
|
117
141
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
142
|
+
or:
|
|
143
|
+
|
|
144
|
+
```js
|
|
145
|
+
const mock = require('mock-require');
|
|
146
|
+
const fakeAmqp = require('@onify/fake-amqplib');
|
|
147
|
+
|
|
148
|
+
mock('amqplib/callback_api', fakeAmqp);
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
or just mock the entire amqplib with:
|
|
152
|
+
|
|
153
|
+
```js
|
|
154
|
+
const mock = require('mock-require');
|
|
155
|
+
const fakeAmqp = require('@onify/fake-amqplib');
|
|
156
|
+
|
|
157
|
+
mock('amqplib', fakeAmqp);
|
|
126
158
|
```
|
package/index.d.ts
CHANGED
|
@@ -1,59 +1,90 @@
|
|
|
1
1
|
/// <reference types="amqplib" />
|
|
2
2
|
/// <reference types="node" />
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
4
|
+
import { Options, Connection, Channel, ChannelModel, Replies } from 'amqplib';
|
|
5
|
+
import { Broker } from 'smqp';
|
|
6
|
+
|
|
7
|
+
export interface FakeAmqplibChannel extends Channel {
|
|
8
|
+
/** Channel name and identifier, for faking purposes */
|
|
9
|
+
_channelName: string;
|
|
10
|
+
_broker: Broker;
|
|
11
|
+
_version: number;
|
|
12
|
+
readonly _closed: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const FakeAmqplibChannel: {
|
|
16
|
+
new (broker: Broker, connection: FakeAmqplibConnection): FakeAmqplibChannel;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export interface FakeAmqplibConfirmChannel extends FakeAmqplibChannel {
|
|
20
|
+
publish(
|
|
21
|
+
exchange: string,
|
|
22
|
+
routingKey: string,
|
|
23
|
+
content: Buffer,
|
|
24
|
+
options?: Options.Publish,
|
|
25
|
+
callback?: (err: Error | null, ok: Replies.Empty) => void
|
|
26
|
+
): boolean;
|
|
27
|
+
sendToQueue(
|
|
28
|
+
queue: string,
|
|
29
|
+
content: Buffer,
|
|
30
|
+
options?: Options.Publish,
|
|
31
|
+
callback?: (err: Error | null, ok: Replies.Empty) => void
|
|
32
|
+
): boolean;
|
|
33
|
+
waitForConfirms(callback?: (err: Error | null) => void): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const FakeAmqplibConfirmChannel: {
|
|
37
|
+
new (broker: Broker, connection: FakeAmqplibConnection): FakeAmqplibConfirmChannel;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export interface FakeAmqplibConnection extends Connection, ChannelModel {
|
|
41
|
+
_channels: FakeAmqplibChannel[];
|
|
42
|
+
_url: URL;
|
|
43
|
+
/** Connection identifier, for faking purposes */
|
|
44
|
+
_id: string;
|
|
45
|
+
_broker: Broker;
|
|
46
|
+
_version: number;
|
|
47
|
+
readonly _closed: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const FakeAmqplibConnection: {
|
|
51
|
+
new (broker: Broker, version: number, amqpUrl: string, options?: SocketOptions): FakeAmqplibConnection;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export interface SocketOptions {
|
|
55
|
+
host?: string;
|
|
56
|
+
keepAlive?: boolean;
|
|
57
|
+
keepAliveDelay?: number;
|
|
58
|
+
noDelay?: boolean;
|
|
59
|
+
port?: number;
|
|
60
|
+
serverName?: string;
|
|
61
|
+
timeout?: number;
|
|
62
|
+
[x: string]: any;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type ConnectCallback = (err: Error | null, connection: FakeAmqplibConnection) => void;
|
|
66
|
+
|
|
67
|
+
export interface FakeAmqplib {
|
|
68
|
+
version: number;
|
|
69
|
+
connections: FakeAmqplibConnection[];
|
|
70
|
+
connect(url: string | Options.Connect, socketOptions?: SocketOptions): Promise<FakeAmqplibConnection>;
|
|
71
|
+
connect(url: string | Options.Connect, socketOptions: SocketOptions, callback: ConnectCallback): void;
|
|
72
|
+
connect(url: string | Options.Connect, callback: ConnectCallback): void;
|
|
73
|
+
connectSync(url: string | Options.Connect, socketOptions?: SocketOptions): FakeAmqplibConnection;
|
|
74
|
+
resetMock(): void;
|
|
75
|
+
setVersion(minorVersion: number | string): void;
|
|
59
76
|
}
|
|
77
|
+
|
|
78
|
+
export const FakeAmqplib: {
|
|
79
|
+
new (minorVersion?: number | string): FakeAmqplib;
|
|
80
|
+
(minorVersion?: number | string): FakeAmqplib;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const connections: FakeAmqplibConnection[];
|
|
84
|
+
|
|
85
|
+
export function connect(url: string | Options.Connect, socketOptions?: SocketOptions): Promise<FakeAmqplibConnection>;
|
|
86
|
+
export function connect(url: string | Options.Connect, socketOptions: SocketOptions, callback: ConnectCallback): void;
|
|
87
|
+
export function connect(url: string | Options.Connect, callback: ConnectCallback): void;
|
|
88
|
+
export function connectSync(url: string | Options.Connect, socketOptions?: SocketOptions): FakeAmqplibConnection;
|
|
89
|
+
export function resetMock(): void;
|
|
90
|
+
export function setVersion(minorVersion: number | string): void;
|
package/index.js
CHANGED
|
@@ -7,6 +7,9 @@ const kClosed = Symbol.for('closed');
|
|
|
7
7
|
const kDeliveryTag = Symbol.for('channel delivery tag');
|
|
8
8
|
const kPrefetch = Symbol.for('prefetch');
|
|
9
9
|
const kChannelPrefetch = Symbol.for('channel prefetch');
|
|
10
|
+
const kPendingConfirms = Symbol.for('pending confirms');
|
|
11
|
+
|
|
12
|
+
const CHANNEL_CLOSED_ERROR = 'Channel is closed';
|
|
10
13
|
|
|
11
14
|
class AmqplibBroker extends Broker {
|
|
12
15
|
constructor(...args) {
|
|
@@ -429,6 +432,19 @@ export class FakeAmqplibChannel extends EventEmitter {
|
|
|
429
432
|
brokerMessage.reject(requeue);
|
|
430
433
|
}
|
|
431
434
|
}
|
|
435
|
+
recover(...args) {
|
|
436
|
+
const channelQ = this._channelQueue;
|
|
437
|
+
return this._callBroker(recoverChannel, ...args);
|
|
438
|
+
|
|
439
|
+
function recoverChannel() {
|
|
440
|
+
let msg;
|
|
441
|
+
while ((msg = channelQ.get())) {
|
|
442
|
+
msg.content[kSmqp].reject(true);
|
|
443
|
+
msg.reject(false);
|
|
444
|
+
}
|
|
445
|
+
return {};
|
|
446
|
+
}
|
|
447
|
+
}
|
|
432
448
|
prefetch(val, isChannelPrefetch) {
|
|
433
449
|
if (this.connection._version < 3.3) {
|
|
434
450
|
if (isChannelPrefetch !== undefined) {
|
|
@@ -449,7 +465,7 @@ export class FakeAmqplibChannel extends EventEmitter {
|
|
|
449
465
|
else poppedCb = null;
|
|
450
466
|
|
|
451
467
|
if (this.connection._closed) throw new FakeAmqpError('Connection is closed', 504);
|
|
452
|
-
if (this[kClosed]) throw new Error(
|
|
468
|
+
if (this[kClosed]) throw new Error(CHANNEL_CLOSED_ERROR);
|
|
453
469
|
|
|
454
470
|
return new Promise((resolve, reject) => {
|
|
455
471
|
try {
|
|
@@ -509,19 +525,25 @@ export class FakeAmqplibChannel extends EventEmitter {
|
|
|
509
525
|
}
|
|
510
526
|
|
|
511
527
|
export class FakeAmqplibConfirmChannel extends FakeAmqplibChannel {
|
|
528
|
+
constructor(broker, connection) {
|
|
529
|
+
super(broker, connection);
|
|
530
|
+
this[kPendingConfirms] = new Set();
|
|
531
|
+
}
|
|
512
532
|
publish(exchange, routingKey, content, options, callback) {
|
|
513
533
|
if (!Buffer.isBuffer(content)) throw new TypeError('content is not a buffer');
|
|
514
534
|
if (exchange === '') return this.sendToQueue(routingKey, content, options, callback);
|
|
535
|
+
if (this[kClosed]) throw new Error(CHANNEL_CLOSED_ERROR);
|
|
515
536
|
|
|
516
537
|
const args = [this._broker.publish, exchange, routingKey, content];
|
|
517
538
|
|
|
518
|
-
args.push(...addConfirmCallback(this._broker, options, callback));
|
|
539
|
+
args.push(...addConfirmCallback(this._broker, options, this._trackConfirm(callback)));
|
|
519
540
|
|
|
520
541
|
this.checkExchange(exchange)
|
|
521
542
|
.then(() => {
|
|
522
543
|
return this._callBroker(...args);
|
|
523
544
|
})
|
|
524
545
|
.catch((err) => {
|
|
546
|
+
if (err.message === CHANNEL_CLOSED_ERROR) return;
|
|
525
547
|
this.emit('error', err);
|
|
526
548
|
});
|
|
527
549
|
|
|
@@ -529,21 +551,61 @@ export class FakeAmqplibConfirmChannel extends FakeAmqplibChannel {
|
|
|
529
551
|
}
|
|
530
552
|
sendToQueue(queue, content, options, callback) {
|
|
531
553
|
if (!Buffer.isBuffer(content)) throw new TypeError('content is not a buffer');
|
|
554
|
+
if (this[kClosed]) throw new Error(CHANNEL_CLOSED_ERROR);
|
|
532
555
|
|
|
533
556
|
const args = [this._broker.sendToQueue, queue, content];
|
|
534
557
|
|
|
535
|
-
args.push(...addConfirmCallback(this._broker, options, callback));
|
|
558
|
+
args.push(...addConfirmCallback(this._broker, options, this._trackConfirm(callback)));
|
|
536
559
|
|
|
537
560
|
this.checkQueue(queue)
|
|
538
561
|
.then(() => {
|
|
539
562
|
return this._callBroker(...args);
|
|
540
563
|
})
|
|
541
564
|
.catch((err) => {
|
|
565
|
+
if (err.message === CHANNEL_CLOSED_ERROR) return;
|
|
542
566
|
this.emit('error', err);
|
|
543
567
|
});
|
|
544
568
|
|
|
545
569
|
return true;
|
|
546
570
|
}
|
|
571
|
+
_trackConfirm(userCallback) {
|
|
572
|
+
let resolveSettled;
|
|
573
|
+
const settled = new Promise((resolve) => {
|
|
574
|
+
resolveSettled = resolve;
|
|
575
|
+
});
|
|
576
|
+
const entry = { settled, resolveSettled, userCallback };
|
|
577
|
+
this[kPendingConfirms].add(entry);
|
|
578
|
+
|
|
579
|
+
return (err, ok) => {
|
|
580
|
+
this[kPendingConfirms].delete(entry);
|
|
581
|
+
resolveSettled(err || null);
|
|
582
|
+
if (typeof userCallback === 'function') userCallback(err, ok);
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
waitForConfirms(callback) {
|
|
586
|
+
const snapshot = [...this[kPendingConfirms]].map((entry) => entry.settled);
|
|
587
|
+
const promise = Promise.all(snapshot).then((errs) => {
|
|
588
|
+
const firstErr = errs.find((e) => e !== null);
|
|
589
|
+
if (firstErr) throw firstErr;
|
|
590
|
+
});
|
|
591
|
+
if (typeof callback === 'function') {
|
|
592
|
+
promise.then(
|
|
593
|
+
() => callback(null),
|
|
594
|
+
(err) => callback(err)
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
return promise;
|
|
598
|
+
}
|
|
599
|
+
_teardown() {
|
|
600
|
+
super._teardown();
|
|
601
|
+
if (!this[kPendingConfirms] || this[kPendingConfirms].size === 0) return;
|
|
602
|
+
const err = new Error(CHANNEL_CLOSED_ERROR);
|
|
603
|
+
for (const entry of [...this[kPendingConfirms]]) {
|
|
604
|
+
this[kPendingConfirms].delete(entry);
|
|
605
|
+
entry.resolveSettled(err);
|
|
606
|
+
if (typeof entry.userCallback === 'function') entry.userCallback(err);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
547
609
|
}
|
|
548
610
|
|
|
549
611
|
export class FakeAmqplibConnection extends EventEmitter {
|
|
@@ -586,6 +648,10 @@ export class FakeAmqplibConnection extends EventEmitter {
|
|
|
586
648
|
this._channels.push(channel);
|
|
587
649
|
return resolveOrCallback(args.slice(-1)[0], null, channel);
|
|
588
650
|
}
|
|
651
|
+
updateSecret(...args) {
|
|
652
|
+
process.nextTick(() => this.emit('update-secret-ok'));
|
|
653
|
+
return resolveOrCallback(args.slice(-1)[0]);
|
|
654
|
+
}
|
|
589
655
|
close(...args) {
|
|
590
656
|
if (this[kClosed]) return resolveOrCallback(args.slice(-1)[0]);
|
|
591
657
|
this[kClosed] = true;
|
package/main.cjs
CHANGED
|
@@ -9,6 +9,9 @@ const kClosed = Symbol.for('closed');
|
|
|
9
9
|
const kDeliveryTag = Symbol.for('channel delivery tag');
|
|
10
10
|
const kPrefetch = Symbol.for('prefetch');
|
|
11
11
|
const kChannelPrefetch = Symbol.for('channel prefetch');
|
|
12
|
+
const kPendingConfirms = Symbol.for('pending confirms');
|
|
13
|
+
|
|
14
|
+
const CHANNEL_CLOSED_ERROR = 'Channel is closed';
|
|
12
15
|
|
|
13
16
|
class AmqplibBroker extends smqp.Broker {
|
|
14
17
|
constructor(...args) {
|
|
@@ -431,6 +434,19 @@ class FakeAmqplibChannel extends events.EventEmitter {
|
|
|
431
434
|
brokerMessage.reject(requeue);
|
|
432
435
|
}
|
|
433
436
|
}
|
|
437
|
+
recover(...args) {
|
|
438
|
+
const channelQ = this._channelQueue;
|
|
439
|
+
return this._callBroker(recoverChannel, ...args);
|
|
440
|
+
|
|
441
|
+
function recoverChannel() {
|
|
442
|
+
let msg;
|
|
443
|
+
while ((msg = channelQ.get())) {
|
|
444
|
+
msg.content[kSmqp].reject(true);
|
|
445
|
+
msg.reject(false);
|
|
446
|
+
}
|
|
447
|
+
return {};
|
|
448
|
+
}
|
|
449
|
+
}
|
|
434
450
|
prefetch(val, isChannelPrefetch) {
|
|
435
451
|
if (this.connection._version < 3.3) {
|
|
436
452
|
if (isChannelPrefetch !== undefined) {
|
|
@@ -451,7 +467,7 @@ class FakeAmqplibChannel extends events.EventEmitter {
|
|
|
451
467
|
else poppedCb = null;
|
|
452
468
|
|
|
453
469
|
if (this.connection._closed) throw new FakeAmqpError('Connection is closed', 504);
|
|
454
|
-
if (this[kClosed]) throw new Error(
|
|
470
|
+
if (this[kClosed]) throw new Error(CHANNEL_CLOSED_ERROR);
|
|
455
471
|
|
|
456
472
|
return new Promise((resolve, reject) => {
|
|
457
473
|
try {
|
|
@@ -511,19 +527,25 @@ class FakeAmqplibChannel extends events.EventEmitter {
|
|
|
511
527
|
}
|
|
512
528
|
|
|
513
529
|
class FakeAmqplibConfirmChannel extends FakeAmqplibChannel {
|
|
530
|
+
constructor(broker, connection) {
|
|
531
|
+
super(broker, connection);
|
|
532
|
+
this[kPendingConfirms] = new Set();
|
|
533
|
+
}
|
|
514
534
|
publish(exchange, routingKey, content, options, callback) {
|
|
515
535
|
if (!Buffer.isBuffer(content)) throw new TypeError('content is not a buffer');
|
|
516
536
|
if (exchange === '') return this.sendToQueue(routingKey, content, options, callback);
|
|
537
|
+
if (this[kClosed]) throw new Error(CHANNEL_CLOSED_ERROR);
|
|
517
538
|
|
|
518
539
|
const args = [this._broker.publish, exchange, routingKey, content];
|
|
519
540
|
|
|
520
|
-
args.push(...addConfirmCallback(this._broker, options, callback));
|
|
541
|
+
args.push(...addConfirmCallback(this._broker, options, this._trackConfirm(callback)));
|
|
521
542
|
|
|
522
543
|
this.checkExchange(exchange)
|
|
523
544
|
.then(() => {
|
|
524
545
|
return this._callBroker(...args);
|
|
525
546
|
})
|
|
526
547
|
.catch((err) => {
|
|
548
|
+
if (err.message === CHANNEL_CLOSED_ERROR) return;
|
|
527
549
|
this.emit('error', err);
|
|
528
550
|
});
|
|
529
551
|
|
|
@@ -531,21 +553,61 @@ class FakeAmqplibConfirmChannel extends FakeAmqplibChannel {
|
|
|
531
553
|
}
|
|
532
554
|
sendToQueue(queue, content, options, callback) {
|
|
533
555
|
if (!Buffer.isBuffer(content)) throw new TypeError('content is not a buffer');
|
|
556
|
+
if (this[kClosed]) throw new Error(CHANNEL_CLOSED_ERROR);
|
|
534
557
|
|
|
535
558
|
const args = [this._broker.sendToQueue, queue, content];
|
|
536
559
|
|
|
537
|
-
args.push(...addConfirmCallback(this._broker, options, callback));
|
|
560
|
+
args.push(...addConfirmCallback(this._broker, options, this._trackConfirm(callback)));
|
|
538
561
|
|
|
539
562
|
this.checkQueue(queue)
|
|
540
563
|
.then(() => {
|
|
541
564
|
return this._callBroker(...args);
|
|
542
565
|
})
|
|
543
566
|
.catch((err) => {
|
|
567
|
+
if (err.message === CHANNEL_CLOSED_ERROR) return;
|
|
544
568
|
this.emit('error', err);
|
|
545
569
|
});
|
|
546
570
|
|
|
547
571
|
return true;
|
|
548
572
|
}
|
|
573
|
+
_trackConfirm(userCallback) {
|
|
574
|
+
let resolveSettled;
|
|
575
|
+
const settled = new Promise((resolve) => {
|
|
576
|
+
resolveSettled = resolve;
|
|
577
|
+
});
|
|
578
|
+
const entry = { settled, resolveSettled, userCallback };
|
|
579
|
+
this[kPendingConfirms].add(entry);
|
|
580
|
+
|
|
581
|
+
return (err, ok) => {
|
|
582
|
+
this[kPendingConfirms].delete(entry);
|
|
583
|
+
resolveSettled(err || null);
|
|
584
|
+
if (typeof userCallback === 'function') userCallback(err, ok);
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
waitForConfirms(callback) {
|
|
588
|
+
const snapshot = [...this[kPendingConfirms]].map((entry) => entry.settled);
|
|
589
|
+
const promise = Promise.all(snapshot).then((errs) => {
|
|
590
|
+
const firstErr = errs.find((e) => e !== null);
|
|
591
|
+
if (firstErr) throw firstErr;
|
|
592
|
+
});
|
|
593
|
+
if (typeof callback === 'function') {
|
|
594
|
+
promise.then(
|
|
595
|
+
() => callback(null),
|
|
596
|
+
(err) => callback(err)
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
return promise;
|
|
600
|
+
}
|
|
601
|
+
_teardown() {
|
|
602
|
+
super._teardown();
|
|
603
|
+
if (!this[kPendingConfirms] || this[kPendingConfirms].size === 0) return;
|
|
604
|
+
const err = new Error(CHANNEL_CLOSED_ERROR);
|
|
605
|
+
for (const entry of [...this[kPendingConfirms]]) {
|
|
606
|
+
this[kPendingConfirms].delete(entry);
|
|
607
|
+
entry.resolveSettled(err);
|
|
608
|
+
if (typeof entry.userCallback === 'function') entry.userCallback(err);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
549
611
|
}
|
|
550
612
|
|
|
551
613
|
class FakeAmqplibConnection extends events.EventEmitter {
|
|
@@ -588,6 +650,10 @@ class FakeAmqplibConnection extends events.EventEmitter {
|
|
|
588
650
|
this._channels.push(channel);
|
|
589
651
|
return resolveOrCallback(args.slice(-1)[0], null, channel);
|
|
590
652
|
}
|
|
653
|
+
updateSecret(...args) {
|
|
654
|
+
process.nextTick(() => this.emit('update-secret-ok'));
|
|
655
|
+
return resolveOrCallback(args.slice(-1)[0]);
|
|
656
|
+
}
|
|
591
657
|
close(...args) {
|
|
592
658
|
if (this[kClosed]) return resolveOrCallback(args.slice(-1)[0]);
|
|
593
659
|
this[kClosed] = true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onify/fake-amqplib",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "Fake amqplib",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -13,19 +13,21 @@
|
|
|
13
13
|
"main": "./main.cjs",
|
|
14
14
|
"scripts": {
|
|
15
15
|
"test": "mocha",
|
|
16
|
-
"posttest": "npm run lint && npm run dist",
|
|
16
|
+
"posttest": "npm run lint && npm run dist && npm run test:md",
|
|
17
17
|
"lint": "eslint . --cache && prettier . -c --cache",
|
|
18
18
|
"dist": "rollup -c",
|
|
19
19
|
"prepack": "npm run dist",
|
|
20
20
|
"cov:html": "c8 -r html -r text mocha",
|
|
21
|
-
"test:lcov": "c8 -r lcov mocha && npm run lint"
|
|
21
|
+
"test:lcov": "c8 -r lcov mocha && npm run lint",
|
|
22
|
+
"test:md": "texample ./README.md -c ./.mocharc.json -r scripts/texample-mocha.js",
|
|
23
|
+
"toc": "node scripts/toc.js"
|
|
22
24
|
},
|
|
23
25
|
"author": {
|
|
24
26
|
"name": "Onify",
|
|
25
27
|
"url": "https://github.com/onify"
|
|
26
28
|
},
|
|
27
29
|
"engines": {
|
|
28
|
-
"node": ">=
|
|
30
|
+
"node": ">=18"
|
|
29
31
|
},
|
|
30
32
|
"repository": {
|
|
31
33
|
"type": "git",
|
|
@@ -33,21 +35,30 @@
|
|
|
33
35
|
},
|
|
34
36
|
"license": "MIT",
|
|
35
37
|
"dependencies": {
|
|
36
|
-
"smqp": "^
|
|
38
|
+
"smqp": "^12.0.0"
|
|
37
39
|
},
|
|
38
40
|
"devDependencies": {
|
|
41
|
+
"@eslint/js": "^10.0.1",
|
|
39
42
|
"@rollup/plugin-commonjs": "^29.0.0",
|
|
40
43
|
"@types/amqplib": "^0.10.2",
|
|
41
|
-
"amqplib": "^0.
|
|
42
|
-
"c8": "^
|
|
44
|
+
"amqplib": "^1.0.3",
|
|
45
|
+
"c8": "^11.0.0",
|
|
43
46
|
"chai": "^6.2.1",
|
|
44
|
-
"eslint": "^
|
|
45
|
-
"globals": "^
|
|
47
|
+
"eslint": "^10.3.0",
|
|
48
|
+
"globals": "^17.0.0",
|
|
46
49
|
"mocha": "^11.0.1",
|
|
47
50
|
"prettier": "^3.2.5",
|
|
48
51
|
"quibble": "^0.9.2",
|
|
49
52
|
"rollup": "^4.0.2",
|
|
50
|
-
"texample": "^
|
|
53
|
+
"texample": "^1.0.2"
|
|
54
|
+
},
|
|
55
|
+
"overrides": {
|
|
56
|
+
"c8": {
|
|
57
|
+
"yargs": "^18.0.0"
|
|
58
|
+
},
|
|
59
|
+
"mocha": {
|
|
60
|
+
"yargs": "^18.0.0"
|
|
61
|
+
}
|
|
51
62
|
},
|
|
52
63
|
"keywords": [
|
|
53
64
|
"fake",
|