rabbitmq-sdk 0.0.1-security → 1.2.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.
Potentially problematic release.
This version of rabbitmq-sdk might be problematic. Click here for more details.
- package/.eslintrc.js +23 -0
- package/.kiro/specs/sdk-rabbitmq/design.md +369 -0
- package/.kiro/specs/sdk-rabbitmq/requirements.md +97 -0
- package/.kiro/specs/sdk-rabbitmq/tasks.md +248 -0
- package/README.md +273 -5
- package/bun.lock +790 -0
- package/config.example.json +13 -0
- package/dist/components/ConfigurationManager.d.ts +35 -0
- package/dist/components/ConfigurationManager.d.ts.map +1 -0
- package/dist/components/ConfigurationManager.js +118 -0
- package/dist/components/ConfigurationManager.js.map +1 -0
- package/dist/components/ConnectionManager.d.ts +93 -0
- package/dist/components/ConnectionManager.d.ts.map +1 -0
- package/dist/components/ConnectionManager.js +349 -0
- package/dist/components/ConnectionManager.js.map +1 -0
- package/dist/components/DLQHandler.d.ts +81 -0
- package/dist/components/DLQHandler.d.ts.map +1 -0
- package/dist/components/DLQHandler.js +228 -0
- package/dist/components/DLQHandler.js.map +1 -0
- package/dist/components/Logger.d.ts +77 -0
- package/dist/components/Logger.d.ts.map +1 -0
- package/dist/components/Logger.js +193 -0
- package/dist/components/Logger.js.map +1 -0
- package/dist/components/MessagePublisher.d.ts +49 -0
- package/dist/components/MessagePublisher.d.ts.map +1 -0
- package/dist/components/MessagePublisher.js +158 -0
- package/dist/components/MessagePublisher.js.map +1 -0
- package/dist/components/MessageSubscriber.d.ts +108 -0
- package/dist/components/MessageSubscriber.d.ts.map +1 -0
- package/dist/components/MessageSubscriber.js +503 -0
- package/dist/components/MessageSubscriber.js.map +1 -0
- package/dist/components/ResourceCreator.d.ts +89 -0
- package/dist/components/ResourceCreator.d.ts.map +1 -0
- package/dist/components/ResourceCreator.js +352 -0
- package/dist/components/ResourceCreator.js.map +1 -0
- package/dist/components/SdkRabbitmq.d.ts +103 -0
- package/dist/components/SdkRabbitmq.d.ts.map +1 -0
- package/dist/components/SdkRabbitmq.js +364 -0
- package/dist/components/SdkRabbitmq.js.map +1 -0
- package/dist/components/index.d.ts +9 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +20 -0
- package/dist/components/index.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/IConfiguration.d.ts +35 -0
- package/dist/interfaces/IConfiguration.d.ts.map +1 -0
- package/dist/interfaces/IConfiguration.js +3 -0
- package/dist/interfaces/IConfiguration.js.map +1 -0
- package/dist/interfaces/IConnection.d.ts +21 -0
- package/dist/interfaces/IConnection.d.ts.map +1 -0
- package/dist/interfaces/IConnection.js +3 -0
- package/dist/interfaces/IConnection.js.map +1 -0
- package/dist/interfaces/IDLQ.d.ts +12 -0
- package/dist/interfaces/IDLQ.d.ts.map +1 -0
- package/dist/interfaces/IDLQ.js +3 -0
- package/dist/interfaces/IDLQ.js.map +1 -0
- package/dist/interfaces/IErrors.d.ts +33 -0
- package/dist/interfaces/IErrors.d.ts.map +1 -0
- package/dist/interfaces/IErrors.js +56 -0
- package/dist/interfaces/IErrors.js.map +1 -0
- package/dist/interfaces/ILogger.d.ts +14 -0
- package/dist/interfaces/ILogger.d.ts.map +1 -0
- package/dist/interfaces/ILogger.js +3 -0
- package/dist/interfaces/ILogger.js.map +1 -0
- package/dist/interfaces/IMessage.d.ts +52 -0
- package/dist/interfaces/IMessage.d.ts.map +1 -0
- package/dist/interfaces/IMessage.js +3 -0
- package/dist/interfaces/IMessage.js.map +1 -0
- package/dist/interfaces/IResource.d.ts +31 -0
- package/dist/interfaces/IResource.d.ts.map +1 -0
- package/dist/interfaces/IResource.js +3 -0
- package/dist/interfaces/IResource.js.map +1 -0
- package/dist/interfaces/ISdkRabbitmq.d.ts +17 -0
- package/dist/interfaces/ISdkRabbitmq.d.ts.map +1 -0
- package/dist/interfaces/ISdkRabbitmq.js +3 -0
- package/dist/interfaces/ISdkRabbitmq.js.map +1 -0
- package/dist/interfaces/index.d.ts +9 -0
- package/dist/interfaces/index.d.ts.map +1 -0
- package/dist/interfaces/index.js +33 -0
- package/dist/interfaces/index.js.map +1 -0
- package/dist/utils/configSchema.d.ts +8 -0
- package/dist/utils/configSchema.d.ts.map +1 -0
- package/dist/utils/configSchema.js +51 -0
- package/dist/utils/configSchema.js.map +1 -0
- package/docker-compose.yml +24 -0
- package/example.ts +65 -0
- package/examples/README-dynamic-routing.md +155 -0
- package/examples/bind-unbind-example.js +56 -0
- package/examples/test-chatbot-exchange.ts +83 -0
- package/examples/test-dynamic-routing-flow.js +299 -0
- package/examples/test-dynamic-routing-flow.ts +355 -0
- package/examples/test-no-disconnect.ts +0 -0
- package/examples/test-raw-rabbitmq.js +68 -0
- package/examples/test-same-channel.ts +81 -0
- package/examples/test-schedule-flow.ts +713 -0
- package/examples/test-simple-greeting.ts +66 -0
- package/examples/test-simple-schedule.ts +76 -0
- package/examples/test-wildcard.ts +364 -0
- package/jest.config.js +17 -0
- package/package.json +42 -4
- package/preinstall.js +1 -0
- package/prompts/test-dynamic-routing-flow.md +46 -0
- package/run.js +4 -0
- package/scripts/run-dynamic-routing-test.ts +31 -0
- package/src/.gitkeep +1 -0
- package/src/components/.gitkeep +1 -0
- package/src/components/ConfigurationManager.ts +104 -0
- package/src/components/ConnectionManager.ts +357 -0
- package/src/components/DLQHandler.ts +271 -0
- package/src/components/Logger.ts +224 -0
- package/src/components/MessagePublisher.ts +180 -0
- package/src/components/MessageSubscriber.ts +597 -0
- package/src/components/ResourceCreator.ts +411 -0
- package/src/components/SdkRabbitmq.ts +443 -0
- package/src/components/__tests__/ConfigurationManager.test.ts +357 -0
- package/src/components/__tests__/ConnectionManager.test.ts +387 -0
- package/src/components/__tests__/DLQHandler.test.ts +399 -0
- package/src/components/__tests__/Logger.test.ts +354 -0
- package/src/components/__tests__/MessagePublisher.test.ts +337 -0
- package/src/components/__tests__/MessageSubscriber.test.ts +542 -0
- package/src/components/__tests__/ResourceCreator.test.ts +465 -0
- package/src/components/__tests__/SdkRabbitmq.integration.test.ts +433 -0
- package/src/components/index.ts +8 -0
- package/src/index.ts +11 -0
- package/src/interfaces/.gitkeep +1 -0
- package/src/interfaces/IConfiguration.ts +38 -0
- package/src/interfaces/IConnection.ts +27 -0
- package/src/interfaces/IDLQ.ts +13 -0
- package/src/interfaces/IErrors.ts +53 -0
- package/src/interfaces/ILogger.ts +16 -0
- package/src/interfaces/IMessage.ts +65 -0
- package/src/interfaces/IResource.ts +35 -0
- package/src/interfaces/ISdkRabbitmq.ts +26 -0
- package/src/interfaces/index.ts +23 -0
- package/src/utils/.gitkeep +1 -0
- package/src/utils/configSchema.ts +58 -0
- package/tsconfig.json +34 -0
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
import { ResourceCreator } from '../ResourceCreator';
|
|
2
|
+
import { ConnectionManager } from '../ConnectionManager';
|
|
3
|
+
|
|
4
|
+
// Mock amqplib
|
|
5
|
+
jest.mock('amqplib', () => ({}));
|
|
6
|
+
|
|
7
|
+
// Mock Logger
|
|
8
|
+
jest.mock('../Logger', () => ({
|
|
9
|
+
Logger: {
|
|
10
|
+
createComponentLogger: jest.fn(() => ({
|
|
11
|
+
info: jest.fn(),
|
|
12
|
+
warn: jest.fn(),
|
|
13
|
+
logOperation: jest.fn(),
|
|
14
|
+
logError: jest.fn()
|
|
15
|
+
}))
|
|
16
|
+
}
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
describe('ResourceCreator', () => {
|
|
20
|
+
let resourceCreator: ResourceCreator;
|
|
21
|
+
let mockConnectionManager: jest.Mocked<ConnectionManager>;
|
|
22
|
+
let mockChannel: any;
|
|
23
|
+
let mockConnection: any;
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
// Create mock channel
|
|
27
|
+
mockChannel = {
|
|
28
|
+
assertExchange: jest.fn().mockResolvedValue(undefined),
|
|
29
|
+
assertQueue: jest.fn().mockResolvedValue(undefined),
|
|
30
|
+
bindQueue: jest.fn().mockResolvedValue(undefined),
|
|
31
|
+
unbindQueue: jest.fn().mockResolvedValue(undefined),
|
|
32
|
+
checkExchange: jest.fn().mockRejectedValue(new Error('Exchange does not exist')),
|
|
33
|
+
checkQueue: jest.fn().mockRejectedValue(new Error('Queue does not exist')),
|
|
34
|
+
close: jest.fn().mockResolvedValue(undefined)
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Create mock connection
|
|
38
|
+
mockConnection = {
|
|
39
|
+
createChannel: jest.fn().mockResolvedValue(mockChannel)
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Create mock ConnectionManager
|
|
43
|
+
mockConnectionManager = {
|
|
44
|
+
getConnection: jest.fn().mockReturnValue(mockConnection),
|
|
45
|
+
executeOperation: jest.fn().mockImplementation((operation) => operation())
|
|
46
|
+
} as any;
|
|
47
|
+
|
|
48
|
+
resourceCreator = new ResourceCreator(mockConnectionManager);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
afterEach(() => {
|
|
52
|
+
jest.clearAllMocks();
|
|
53
|
+
resourceCreator.clearCache();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('ensureExchange', () => {
|
|
57
|
+
it('should create exchange with default type (direct)', async () => {
|
|
58
|
+
await resourceCreator.ensureExchange('test-exchange');
|
|
59
|
+
|
|
60
|
+
expect(mockConnectionManager.executeOperation).toHaveBeenCalled();
|
|
61
|
+
expect(mockConnection.createChannel).toHaveBeenCalled();
|
|
62
|
+
expect(mockChannel.assertExchange).toHaveBeenCalledWith(
|
|
63
|
+
'test-exchange',
|
|
64
|
+
'direct',
|
|
65
|
+
{ durable: true, autoDelete: false }
|
|
66
|
+
);
|
|
67
|
+
expect(mockChannel.close).toHaveBeenCalled();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should create exchange with topic type', async () => {
|
|
71
|
+
await resourceCreator.ensureExchange('test-exchange', 'topic');
|
|
72
|
+
|
|
73
|
+
expect(mockChannel.assertExchange).toHaveBeenCalledWith(
|
|
74
|
+
'test-exchange',
|
|
75
|
+
'topic',
|
|
76
|
+
{ durable: true, autoDelete: false }
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should create exchange with fanout type', async () => {
|
|
81
|
+
await resourceCreator.ensureExchange('test-exchange', 'fanout');
|
|
82
|
+
|
|
83
|
+
expect(mockChannel.assertExchange).toHaveBeenCalledWith(
|
|
84
|
+
'test-exchange',
|
|
85
|
+
'fanout',
|
|
86
|
+
{ durable: true, autoDelete: false }
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should create exchange with headers type', async () => {
|
|
91
|
+
await resourceCreator.ensureExchange('test-exchange', 'headers');
|
|
92
|
+
|
|
93
|
+
expect(mockChannel.assertExchange).toHaveBeenCalledWith(
|
|
94
|
+
'test-exchange',
|
|
95
|
+
'headers',
|
|
96
|
+
{ durable: true, autoDelete: false }
|
|
97
|
+
);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should create exchange with custom options', async () => {
|
|
101
|
+
const options = {
|
|
102
|
+
durable: false,
|
|
103
|
+
autoDelete: true,
|
|
104
|
+
arguments: { 'x-message-ttl': 60000 }
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
await resourceCreator.ensureExchange('test-exchange', 'direct', options);
|
|
108
|
+
|
|
109
|
+
expect(mockChannel.assertExchange).toHaveBeenCalledWith(
|
|
110
|
+
'test-exchange',
|
|
111
|
+
'direct',
|
|
112
|
+
options
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should not create exchange if already cached', async () => {
|
|
117
|
+
// First call
|
|
118
|
+
await resourceCreator.ensureExchange('test-exchange');
|
|
119
|
+
|
|
120
|
+
// Second call should use cache
|
|
121
|
+
await resourceCreator.ensureExchange('test-exchange');
|
|
122
|
+
|
|
123
|
+
expect(mockConnectionManager.executeOperation).toHaveBeenCalledTimes(2); // Once for existence check, once for creation
|
|
124
|
+
expect(mockChannel.assertExchange).toHaveBeenCalledTimes(1);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should throw error for empty exchange name', async () => {
|
|
128
|
+
await expect(resourceCreator.ensureExchange('')).rejects.toThrow('Exchange name is required');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should handle exchange creation failure', async () => {
|
|
132
|
+
const error = new Error('Exchange creation failed');
|
|
133
|
+
mockChannel.assertExchange.mockRejectedValue(error);
|
|
134
|
+
|
|
135
|
+
await expect(resourceCreator.ensureExchange('test-exchange')).rejects.toThrow(error);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should skip creation if exchange already exists', async () => {
|
|
139
|
+
// Mock that exchange exists (checkExchange succeeds)
|
|
140
|
+
mockChannel.checkExchange.mockResolvedValueOnce(undefined);
|
|
141
|
+
|
|
142
|
+
await resourceCreator.ensureExchange('existing-exchange');
|
|
143
|
+
|
|
144
|
+
expect(mockChannel.checkExchange).toHaveBeenCalledWith('existing-exchange');
|
|
145
|
+
expect(mockChannel.assertExchange).not.toHaveBeenCalled();
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe('ensureQueue', () => {
|
|
150
|
+
it('should create queue with default options', async () => {
|
|
151
|
+
await resourceCreator.ensureQueue('test-queue');
|
|
152
|
+
|
|
153
|
+
expect(mockConnectionManager.executeOperation).toHaveBeenCalled();
|
|
154
|
+
expect(mockConnection.createChannel).toHaveBeenCalled();
|
|
155
|
+
expect(mockChannel.assertQueue).toHaveBeenCalledWith(
|
|
156
|
+
'test-queue',
|
|
157
|
+
{ durable: true, exclusive: false, autoDelete: false }
|
|
158
|
+
);
|
|
159
|
+
expect(mockChannel.close).toHaveBeenCalled();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('should create queue with custom options', async () => {
|
|
163
|
+
const options = {
|
|
164
|
+
durable: false,
|
|
165
|
+
exclusive: true,
|
|
166
|
+
autoDelete: true,
|
|
167
|
+
arguments: { 'x-max-length': 1000 }
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
await resourceCreator.ensureQueue('test-queue', options);
|
|
171
|
+
|
|
172
|
+
expect(mockChannel.assertQueue).toHaveBeenCalledWith('test-queue', options);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should create queue with TTL argument', async () => {
|
|
176
|
+
const options = {
|
|
177
|
+
arguments: { 'x-message-ttl': 300000 }
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
await resourceCreator.ensureQueue('ttl-queue', options);
|
|
181
|
+
|
|
182
|
+
expect(mockChannel.assertQueue).toHaveBeenCalledWith(
|
|
183
|
+
'ttl-queue',
|
|
184
|
+
{ durable: true, exclusive: false, autoDelete: false, arguments: { 'x-message-ttl': 300000 } }
|
|
185
|
+
);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should create queue with dead letter exchange', async () => {
|
|
189
|
+
const options = {
|
|
190
|
+
arguments: {
|
|
191
|
+
'x-dead-letter-exchange': 'dlx-exchange',
|
|
192
|
+
'x-dead-letter-routing-key': 'failed'
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
await resourceCreator.ensureQueue('dlq-queue', options);
|
|
197
|
+
|
|
198
|
+
expect(mockChannel.assertQueue).toHaveBeenCalledWith(
|
|
199
|
+
'dlq-queue',
|
|
200
|
+
{
|
|
201
|
+
durable: true,
|
|
202
|
+
exclusive: false,
|
|
203
|
+
autoDelete: false,
|
|
204
|
+
arguments: {
|
|
205
|
+
'x-dead-letter-exchange': 'dlx-exchange',
|
|
206
|
+
'x-dead-letter-routing-key': 'failed'
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('should not create queue if already cached', async () => {
|
|
213
|
+
// First call
|
|
214
|
+
await resourceCreator.ensureQueue('test-queue');
|
|
215
|
+
|
|
216
|
+
// Second call should use cache
|
|
217
|
+
await resourceCreator.ensureQueue('test-queue');
|
|
218
|
+
|
|
219
|
+
expect(mockConnectionManager.executeOperation).toHaveBeenCalledTimes(2); // Once for existence check, once for creation
|
|
220
|
+
expect(mockChannel.assertQueue).toHaveBeenCalledTimes(1);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('should throw error for empty queue name', async () => {
|
|
224
|
+
await expect(resourceCreator.ensureQueue('')).rejects.toThrow('Queue name is required');
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('should handle queue creation failure', async () => {
|
|
228
|
+
const error = new Error('Queue creation failed');
|
|
229
|
+
mockChannel.assertQueue.mockRejectedValue(error);
|
|
230
|
+
|
|
231
|
+
await expect(resourceCreator.ensureQueue('test-queue')).rejects.toThrow(error);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('should skip creation if queue already exists', async () => {
|
|
235
|
+
// Mock that queue exists (checkQueue succeeds)
|
|
236
|
+
mockChannel.checkQueue.mockResolvedValueOnce(undefined);
|
|
237
|
+
|
|
238
|
+
await resourceCreator.ensureQueue('existing-queue');
|
|
239
|
+
|
|
240
|
+
expect(mockChannel.checkQueue).toHaveBeenCalledWith('existing-queue');
|
|
241
|
+
expect(mockChannel.assertQueue).not.toHaveBeenCalled();
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
describe('bindQueue', () => {
|
|
246
|
+
it('should bind queue to exchange with routing key', async () => {
|
|
247
|
+
await resourceCreator.bindQueue('test-queue', 'test-exchange', 'test.routing.key');
|
|
248
|
+
|
|
249
|
+
expect(mockConnectionManager.executeOperation).toHaveBeenCalled();
|
|
250
|
+
expect(mockConnection.createChannel).toHaveBeenCalled();
|
|
251
|
+
expect(mockChannel.bindQueue).toHaveBeenCalledWith(
|
|
252
|
+
'test-queue',
|
|
253
|
+
'test-exchange',
|
|
254
|
+
'test.routing.key'
|
|
255
|
+
);
|
|
256
|
+
expect(mockChannel.close).toHaveBeenCalled();
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('should bind queue with wildcard routing key', async () => {
|
|
260
|
+
await resourceCreator.bindQueue('test-queue', 'test-exchange', 'events.*');
|
|
261
|
+
|
|
262
|
+
expect(mockChannel.bindQueue).toHaveBeenCalledWith(
|
|
263
|
+
'test-queue',
|
|
264
|
+
'test-exchange',
|
|
265
|
+
'events.*'
|
|
266
|
+
);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('should bind queue with hash routing key for topic exchange', async () => {
|
|
270
|
+
await resourceCreator.bindQueue('test-queue', 'topic-exchange', 'user.#');
|
|
271
|
+
|
|
272
|
+
expect(mockChannel.bindQueue).toHaveBeenCalledWith(
|
|
273
|
+
'test-queue',
|
|
274
|
+
'topic-exchange',
|
|
275
|
+
'user.#'
|
|
276
|
+
);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('should not create binding if already cached', async () => {
|
|
280
|
+
// First call
|
|
281
|
+
await resourceCreator.bindQueue('test-queue', 'test-exchange', 'test.key');
|
|
282
|
+
|
|
283
|
+
// Second call should use cache
|
|
284
|
+
await resourceCreator.bindQueue('test-queue', 'test-exchange', 'test.key');
|
|
285
|
+
|
|
286
|
+
expect(mockConnectionManager.executeOperation).toHaveBeenCalledTimes(1);
|
|
287
|
+
expect(mockChannel.bindQueue).toHaveBeenCalledTimes(1);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('should throw error for empty queue name', async () => {
|
|
291
|
+
await expect(
|
|
292
|
+
resourceCreator.bindQueue('', 'test-exchange', 'test.key')
|
|
293
|
+
).rejects.toThrow('Queue name is required');
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it('should throw error for empty exchange name', async () => {
|
|
297
|
+
await expect(
|
|
298
|
+
resourceCreator.bindQueue('test-queue', '', 'test.key')
|
|
299
|
+
).rejects.toThrow('Exchange name is required');
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it('should throw error for empty routing key', async () => {
|
|
303
|
+
await expect(
|
|
304
|
+
resourceCreator.bindQueue('test-queue', 'test-exchange', '')
|
|
305
|
+
).rejects.toThrow('Routing key is required');
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('should handle binding failure', async () => {
|
|
309
|
+
const error = new Error('Binding failed');
|
|
310
|
+
mockChannel.bindQueue.mockRejectedValue(error);
|
|
311
|
+
|
|
312
|
+
await expect(
|
|
313
|
+
resourceCreator.bindQueue('test-queue', 'test-exchange', 'test.key')
|
|
314
|
+
).rejects.toThrow(error);
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
describe('unbindQueue', () => {
|
|
319
|
+
it('should unbind queue from exchange with routing key', async () => {
|
|
320
|
+
await resourceCreator.unbindQueue('test-queue', 'test-exchange', 'test.routing.key');
|
|
321
|
+
|
|
322
|
+
expect(mockConnectionManager.executeOperation).toHaveBeenCalled();
|
|
323
|
+
expect(mockConnection.createChannel).toHaveBeenCalled();
|
|
324
|
+
expect(mockChannel.unbindQueue).toHaveBeenCalledWith(
|
|
325
|
+
'test-queue',
|
|
326
|
+
'test-exchange',
|
|
327
|
+
'test.routing.key'
|
|
328
|
+
);
|
|
329
|
+
expect(mockChannel.close).toHaveBeenCalled();
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it('should remove binding from cache after unbinding', async () => {
|
|
333
|
+
// First bind
|
|
334
|
+
await resourceCreator.bindQueue('test-queue', 'test-exchange', 'test.key');
|
|
335
|
+
|
|
336
|
+
// Then unbind
|
|
337
|
+
await resourceCreator.unbindQueue('test-queue', 'test-exchange', 'test.key');
|
|
338
|
+
|
|
339
|
+
// Binding again should call the operation (not use cache)
|
|
340
|
+
await resourceCreator.bindQueue('test-queue', 'test-exchange', 'test.key');
|
|
341
|
+
|
|
342
|
+
expect(mockConnectionManager.executeOperation).toHaveBeenCalledTimes(3);
|
|
343
|
+
expect(mockChannel.bindQueue).toHaveBeenCalledTimes(2);
|
|
344
|
+
expect(mockChannel.unbindQueue).toHaveBeenCalledTimes(1);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('should throw error for empty queue name', async () => {
|
|
348
|
+
await expect(
|
|
349
|
+
resourceCreator.unbindQueue('', 'test-exchange', 'test.key')
|
|
350
|
+
).rejects.toThrow('Queue name is required');
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it('should throw error for empty exchange name', async () => {
|
|
354
|
+
await expect(
|
|
355
|
+
resourceCreator.unbindQueue('test-queue', '', 'test.key')
|
|
356
|
+
).rejects.toThrow('Exchange name is required');
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('should throw error for empty routing key', async () => {
|
|
360
|
+
await expect(
|
|
361
|
+
resourceCreator.unbindQueue('test-queue', 'test-exchange', '')
|
|
362
|
+
).rejects.toThrow('Routing key is required');
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('should handle unbinding failure', async () => {
|
|
366
|
+
const error = new Error('Unbinding failed');
|
|
367
|
+
mockChannel.unbindQueue.mockRejectedValue(error);
|
|
368
|
+
|
|
369
|
+
await expect(
|
|
370
|
+
resourceCreator.unbindQueue('test-queue', 'test-exchange', 'test.key')
|
|
371
|
+
).rejects.toThrow(error);
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
describe('validation methods', () => {
|
|
376
|
+
it('should validate exchange with valid type using ensureExchangeWithValidation', async () => {
|
|
377
|
+
await resourceCreator.ensureExchangeWithValidation('test-exchange', 'topic');
|
|
378
|
+
|
|
379
|
+
expect(mockChannel.assertExchange).toHaveBeenCalledWith(
|
|
380
|
+
'test-exchange',
|
|
381
|
+
'topic',
|
|
382
|
+
{ durable: true, autoDelete: false }
|
|
383
|
+
);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('should throw error for invalid exchange type', async () => {
|
|
387
|
+
await expect(
|
|
388
|
+
resourceCreator.ensureExchangeWithValidation('test-exchange', 'invalid-type')
|
|
389
|
+
).rejects.toThrow("Invalid exchange type 'invalid-type'");
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('should throw error for invalid exchange name characters', async () => {
|
|
393
|
+
await expect(
|
|
394
|
+
resourceCreator.ensureExchangeWithValidation('test@exchange')
|
|
395
|
+
).rejects.toThrow('Exchange name contains invalid characters');
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it('should throw error for exchange name too long', async () => {
|
|
399
|
+
const longName = 'a'.repeat(256);
|
|
400
|
+
await expect(
|
|
401
|
+
resourceCreator.ensureExchangeWithValidation(longName)
|
|
402
|
+
).rejects.toThrow('Exchange name cannot exceed 255 characters');
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it('should validate queue name using ensureQueueWithValidation', async () => {
|
|
406
|
+
await resourceCreator.ensureQueueWithValidation('test-queue');
|
|
407
|
+
|
|
408
|
+
expect(mockChannel.assertQueue).toHaveBeenCalledWith(
|
|
409
|
+
'test-queue',
|
|
410
|
+
{ durable: true, exclusive: false, autoDelete: false }
|
|
411
|
+
);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it('should throw error for invalid queue name characters', async () => {
|
|
415
|
+
await expect(
|
|
416
|
+
resourceCreator.ensureQueueWithValidation('test@queue')
|
|
417
|
+
).rejects.toThrow('Queue name contains invalid characters');
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
describe('cache management', () => {
|
|
422
|
+
it('should clear all caches', () => {
|
|
423
|
+
// Create some cached entries first
|
|
424
|
+
resourceCreator.ensureExchange('test-exchange');
|
|
425
|
+
resourceCreator.ensureQueue('test-queue');
|
|
426
|
+
|
|
427
|
+
resourceCreator.clearCache();
|
|
428
|
+
|
|
429
|
+
const stats = resourceCreator.getCacheStats();
|
|
430
|
+
expect(stats.created.exchanges).toBe(0);
|
|
431
|
+
expect(stats.created.queues).toBe(0);
|
|
432
|
+
expect(stats.created.bindings).toBe(0);
|
|
433
|
+
expect(stats.existing.exchanges).toBe(0);
|
|
434
|
+
expect(stats.existing.queues).toBe(0);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it('should return cache statistics', async () => {
|
|
438
|
+
await resourceCreator.ensureExchange('test-exchange');
|
|
439
|
+
await resourceCreator.ensureQueue('test-queue');
|
|
440
|
+
await resourceCreator.bindQueue('test-queue', 'test-exchange', 'test.key');
|
|
441
|
+
|
|
442
|
+
const stats = resourceCreator.getCacheStats();
|
|
443
|
+
expect(stats.created.exchanges).toBe(1);
|
|
444
|
+
expect(stats.created.queues).toBe(1);
|
|
445
|
+
expect(stats.created.bindings).toBe(1);
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
describe('error handling', () => {
|
|
450
|
+
it('should handle connection manager errors', async () => {
|
|
451
|
+
mockConnectionManager.getConnection.mockReturnValue(null);
|
|
452
|
+
|
|
453
|
+
await expect(resourceCreator.ensureExchange('test-exchange')).rejects.toThrow(
|
|
454
|
+
'No active connection to RabbitMQ'
|
|
455
|
+
);
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
it('should handle channel creation errors', async () => {
|
|
459
|
+
const error = new Error('Channel creation failed');
|
|
460
|
+
mockConnection.createChannel.mockRejectedValue(error);
|
|
461
|
+
|
|
462
|
+
await expect(resourceCreator.ensureExchange('test-exchange')).rejects.toThrow(error);
|
|
463
|
+
});
|
|
464
|
+
});
|
|
465
|
+
});
|