amqp-suite 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/package.json +5 -1
- package/src/{index.d.ts → amqp-client.d.ts} +1 -1
- package/src/amqp-client.js +10 -3
- package/src/index.js +1 -1
- package/vitest.config.js +1 -1
- package/docs/assets/repository_banner.png +0 -0
- package/tests/amqp.test.js +0 -172
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# amqp-suite
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/amqp-suite)
|
|
4
|
-
[](https://www.npmjs.com/package/amqp-suite)
|
|
3
|
+
[](https://www.npmjs.com/package/amqp-suite)
|
|
5
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://www.npmjs.com/package/amqp-suite)
|
|
6
6
|
[](https://github.com/iamcarlosdaniel/amqp-suite)
|
|
7
7
|
|
|
8
|
-

|
|
8
|
+

|
|
9
9
|
|
|
10
10
|
`amqp-suite` is a simple and efficient AMQP (Advanced Message Queuing Protocol) client wrapper for Node.js that handles connection management, message publishing, and consuming messages from queues with a topic exchange. This package abstracts complex connection handling and simplifies AMQP usage in applications by providing easy-to-use methods for connecting, publishing, consuming, and gracefully shutting down the connection.
|
|
11
11
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amqp-suite",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "A simple wrapper for AMQP 0-9-1 messaging.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
"email": "contact@iamcarlosdaniel.com",
|
|
9
9
|
"url": "http://iamcarlosdaniel.com"
|
|
10
10
|
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/iamcarlosdaniel/amqp-suite.git"
|
|
14
|
+
},
|
|
11
15
|
"type": "module",
|
|
12
16
|
"main": "src/index.js",
|
|
13
17
|
"exports": {
|
package/src/amqp-client.js
CHANGED
|
@@ -23,6 +23,8 @@ class AmqpClient {
|
|
|
23
23
|
this.channel = null;
|
|
24
24
|
/** @type {boolean} State flag to prevent multiple simultaneous connection attempts. */
|
|
25
25
|
this.isConnecting = false;
|
|
26
|
+
/** @type {boolean} State flag to indicate if the client is closing. */
|
|
27
|
+
this.isClosing = false;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
/**
|
|
@@ -51,11 +53,14 @@ class AmqpClient {
|
|
|
51
53
|
});
|
|
52
54
|
|
|
53
55
|
this.connection.on("close", () => {
|
|
54
|
-
this.isConnecting = false;
|
|
55
56
|
this.connection = null;
|
|
56
57
|
this.channel = null;
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
|
|
59
|
+
if (!this.isClosing) {
|
|
60
|
+
this.isConnecting = false;
|
|
61
|
+
console.warn(`AMQP connection lost. Retrying in ${delay}ms...`);
|
|
62
|
+
setTimeout(() => this.connect(), delay);
|
|
63
|
+
}
|
|
59
64
|
});
|
|
60
65
|
|
|
61
66
|
console.log("AMQP: Connection established successfully");
|
|
@@ -171,10 +176,12 @@ class AmqpClient {
|
|
|
171
176
|
*/
|
|
172
177
|
async close() {
|
|
173
178
|
try {
|
|
179
|
+
this.isClosing = true;
|
|
174
180
|
await this.channel?.close();
|
|
175
181
|
await this.connection?.close();
|
|
176
182
|
console.log("AMQP: Connection closed cleanly.");
|
|
177
183
|
} catch (err) {
|
|
184
|
+
this.isClosing = false;
|
|
178
185
|
console.error("AMQP: Error during shutdown:", err);
|
|
179
186
|
}
|
|
180
187
|
}
|
package/src/index.js
CHANGED
package/vitest.config.js
CHANGED
|
Binary file
|
package/tests/amqp.test.js
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Unit tests for the AMQP class using Vitest and amqplib mocks.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
6
|
-
import amqp from "amqplib";
|
|
7
|
-
import { AmqpClient } from "amqp-suite";
|
|
8
|
-
|
|
9
|
-
vi.mock("amqplib");
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Test suite for AMQP integration and message handling.
|
|
13
|
-
*/
|
|
14
|
-
describe("AMQP Class", () => {
|
|
15
|
-
/** @type {AmqpClient} */
|
|
16
|
-
let amqpInstance;
|
|
17
|
-
|
|
18
|
-
/** @type {string} */
|
|
19
|
-
const mockUrl = "amqp://localhost";
|
|
20
|
-
|
|
21
|
-
/** @type {string} */
|
|
22
|
-
const mockExchange = "test_exchange";
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Mocked AMQP Channel object with Vitest spy functions.
|
|
26
|
-
* @type {Object}
|
|
27
|
-
*/
|
|
28
|
-
const mockChannel = {
|
|
29
|
-
assertExchange: vi.fn().mockResolvedValue(undefined),
|
|
30
|
-
assertQueue: vi.fn().mockResolvedValue({}),
|
|
31
|
-
bindQueue: vi.fn().mockResolvedValue(undefined),
|
|
32
|
-
prefetch: vi.fn().mockResolvedValue(undefined),
|
|
33
|
-
publish: vi.fn().mockReturnValue(true),
|
|
34
|
-
consume: vi.fn().mockResolvedValue({ consumerTag: "abc" }),
|
|
35
|
-
ack: vi.fn(),
|
|
36
|
-
nack: vi.fn(),
|
|
37
|
-
close: vi.fn().mockResolvedValue(undefined),
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Mocked AMQP Connection object.
|
|
42
|
-
* @type {Object}
|
|
43
|
-
*/
|
|
44
|
-
const mockConnection = {
|
|
45
|
-
createChannel: vi.fn().mockResolvedValue(mockChannel),
|
|
46
|
-
on: vi.fn(),
|
|
47
|
-
close: vi.fn().mockResolvedValue(undefined),
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Setup hook to reset mocks and re-initialize the AMQP instance before each test.
|
|
52
|
-
*/
|
|
53
|
-
beforeEach(() => {
|
|
54
|
-
vi.clearAllMocks();
|
|
55
|
-
amqpInstance = new AmqpClient(mockUrl, mockExchange);
|
|
56
|
-
amqp.connect.mockResolvedValue(mockConnection);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Test case: Constructor property assignment.
|
|
61
|
-
*/
|
|
62
|
-
it("should correctly initialize values in the constructor", () => {
|
|
63
|
-
expect(amqpInstance.amqpUrl).toBe(mockUrl);
|
|
64
|
-
expect(amqpInstance.exchange).toBe(mockExchange);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Test case: Connection workflow and exchange assertion.
|
|
69
|
-
*/
|
|
70
|
-
it("should connect and create a channel successfully", async () => {
|
|
71
|
-
await amqpInstance.connect();
|
|
72
|
-
|
|
73
|
-
expect(amqp.connect).toHaveBeenCalledWith(mockUrl);
|
|
74
|
-
expect(mockConnection.createChannel).toHaveBeenCalled();
|
|
75
|
-
expect(mockChannel.assertExchange).toHaveBeenCalledWith(
|
|
76
|
-
mockExchange,
|
|
77
|
-
"topic",
|
|
78
|
-
{ durable: true }
|
|
79
|
-
);
|
|
80
|
-
expect(amqpInstance.connection).not.toBeNull();
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Test case: Message publishing logic and buffer conversion.
|
|
85
|
-
*/
|
|
86
|
-
it("should publish a message correctly", async () => {
|
|
87
|
-
await amqpInstance.connect();
|
|
88
|
-
const routingKey = "user.created";
|
|
89
|
-
const message = { id: 1, name: "Test" };
|
|
90
|
-
|
|
91
|
-
await amqpInstance.publish(routingKey, message);
|
|
92
|
-
|
|
93
|
-
expect(mockChannel.publish).toHaveBeenCalledWith(
|
|
94
|
-
mockExchange,
|
|
95
|
-
routingKey,
|
|
96
|
-
expect.any(Buffer),
|
|
97
|
-
expect.objectContaining({ persistent: true })
|
|
98
|
-
);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Test case: Consumer setup and successful message processing.
|
|
103
|
-
*/
|
|
104
|
-
it("should setup consumer and acknowledge messages on success", async () => {
|
|
105
|
-
await amqpInstance.connect();
|
|
106
|
-
const queueName = "test_queue";
|
|
107
|
-
const bindingKey = "test.key";
|
|
108
|
-
const mockPayload = { data: "hello world" };
|
|
109
|
-
const mockOnMessage = vi.fn().mockResolvedValue(undefined);
|
|
110
|
-
|
|
111
|
-
await amqpInstance.consume(
|
|
112
|
-
queueName,
|
|
113
|
-
mockOnMessage,
|
|
114
|
-
{ prefetch: 5 },
|
|
115
|
-
bindingKey
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
expect(mockChannel.assertQueue).toHaveBeenCalledWith(
|
|
119
|
-
queueName,
|
|
120
|
-
expect.objectContaining({ durable: true })
|
|
121
|
-
);
|
|
122
|
-
expect(mockChannel.bindQueue).toHaveBeenCalledWith(
|
|
123
|
-
queueName,
|
|
124
|
-
mockExchange,
|
|
125
|
-
bindingKey
|
|
126
|
-
);
|
|
127
|
-
expect(mockChannel.prefetch).toHaveBeenCalledWith(5);
|
|
128
|
-
|
|
129
|
-
const consumerCallback = mockChannel.consume.mock.calls[0][1];
|
|
130
|
-
const fakeMsg = {
|
|
131
|
-
content: Buffer.from(JSON.stringify(mockPayload)),
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
await consumerCallback(fakeMsg);
|
|
135
|
-
|
|
136
|
-
expect(mockOnMessage).toHaveBeenCalledWith(mockPayload, fakeMsg);
|
|
137
|
-
expect(mockChannel.ack).toHaveBeenCalledWith(fakeMsg);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Test case: Consumer error handling (nack).
|
|
142
|
-
*/
|
|
143
|
-
it("should nack messages if the processing fails", async () => {
|
|
144
|
-
await amqpInstance.connect();
|
|
145
|
-
const mockOnMessage = vi
|
|
146
|
-
.fn()
|
|
147
|
-
.mockRejectedValue(new Error("Processing failed"));
|
|
148
|
-
|
|
149
|
-
await amqpInstance.consume("error_queue", mockOnMessage);
|
|
150
|
-
|
|
151
|
-
const consumerCallback = mockChannel.consume.mock.calls[0][1];
|
|
152
|
-
const fakeMsg = {
|
|
153
|
-
content: Buffer.from(JSON.stringify({ some: "data" })),
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
await consumerCallback(fakeMsg);
|
|
157
|
-
|
|
158
|
-
expect(mockChannel.nack).toHaveBeenCalledWith(fakeMsg, false, false);
|
|
159
|
-
expect(mockChannel.ack).not.toHaveBeenCalled();
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Test case: Graceful shutdown of channel and connection.
|
|
164
|
-
*/
|
|
165
|
-
it("should close the connection cleanly", async () => {
|
|
166
|
-
await amqpInstance.connect();
|
|
167
|
-
await amqpInstance.close();
|
|
168
|
-
|
|
169
|
-
expect(mockChannel.close).toHaveBeenCalled();
|
|
170
|
-
expect(mockConnection.close).toHaveBeenCalled();
|
|
171
|
-
});
|
|
172
|
-
});
|