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,399 @@
|
|
|
1
|
+
import { DLQHandler } from '../DLQHandler';
|
|
2
|
+
import { ConnectionManager } from '../ConnectionManager';
|
|
3
|
+
import { ResourceCreator } from '../ResourceCreator';
|
|
4
|
+
import { IConfiguration } from '../../interfaces/IConfiguration';
|
|
5
|
+
|
|
6
|
+
// Mock dependencies
|
|
7
|
+
jest.mock('../ConnectionManager');
|
|
8
|
+
jest.mock('../ResourceCreator');
|
|
9
|
+
jest.mock('../Logger', () => ({
|
|
10
|
+
Logger: {
|
|
11
|
+
createComponentLogger: jest.fn().mockReturnValue({
|
|
12
|
+
logOperation: jest.fn(),
|
|
13
|
+
logError: jest.fn(),
|
|
14
|
+
info: jest.fn(),
|
|
15
|
+
error: jest.fn(),
|
|
16
|
+
warn: jest.fn(),
|
|
17
|
+
debug: jest.fn(),
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
describe('DLQHandler', () => {
|
|
23
|
+
let dlqHandler: DLQHandler;
|
|
24
|
+
let mockConnectionManager: jest.Mocked<ConnectionManager>;
|
|
25
|
+
let mockResourceCreator: jest.Mocked<ResourceCreator>;
|
|
26
|
+
let mockConfig: IConfiguration;
|
|
27
|
+
|
|
28
|
+
const mockChannel = {
|
|
29
|
+
publish: jest.fn(),
|
|
30
|
+
close: jest.fn(),
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const mockConnection = {
|
|
34
|
+
createChannel: jest.fn().mockResolvedValue(mockChannel),
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
// Reset all mocks
|
|
39
|
+
jest.clearAllMocks();
|
|
40
|
+
|
|
41
|
+
// Create mock instances using Object.create to avoid constructor issues
|
|
42
|
+
mockConnectionManager = Object.create(ConnectionManager.prototype);
|
|
43
|
+
mockResourceCreator = Object.create(ResourceCreator.prototype);
|
|
44
|
+
|
|
45
|
+
// Setup default mock implementations
|
|
46
|
+
mockConnectionManager.getConnection = jest.fn().mockReturnValue(mockConnection as any);
|
|
47
|
+
mockConnectionManager.executeOperation = jest.fn().mockImplementation(async (operation) => {
|
|
48
|
+
return await operation();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
mockResourceCreator.ensureExchange = jest.fn().mockResolvedValue(undefined);
|
|
52
|
+
mockResourceCreator.ensureQueue = jest.fn().mockResolvedValue(undefined);
|
|
53
|
+
mockResourceCreator.bindQueue = jest.fn().mockResolvedValue(undefined);
|
|
54
|
+
mockResourceCreator.unbindQueue = jest.fn().mockResolvedValue(undefined);
|
|
55
|
+
|
|
56
|
+
mockChannel.publish.mockReturnValue(true);
|
|
57
|
+
mockChannel.close.mockResolvedValue(undefined);
|
|
58
|
+
|
|
59
|
+
// Default config with DLQ enabled
|
|
60
|
+
mockConfig = {
|
|
61
|
+
url: 'amqp://localhost:5672',
|
|
62
|
+
dlq: {
|
|
63
|
+
active: true,
|
|
64
|
+
ttl: 300000,
|
|
65
|
+
maxRetries: 3,
|
|
66
|
+
retryDelay: 5000,
|
|
67
|
+
},
|
|
68
|
+
logging: {
|
|
69
|
+
level: 'info',
|
|
70
|
+
format: 'json',
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
dlqHandler = new DLQHandler(mockConfig, mockConnectionManager, mockResourceCreator);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('DLQ Configuration and Status', () => {
|
|
78
|
+
it('should return true when DLQ is enabled in configuration', () => {
|
|
79
|
+
expect(dlqHandler.isEnabled()).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should return false when DLQ is disabled in configuration', () => {
|
|
83
|
+
const disabledConfig = { ...mockConfig, dlq: { active: false } };
|
|
84
|
+
const disabledDlqHandler = new DLQHandler(disabledConfig, mockConnectionManager, mockResourceCreator);
|
|
85
|
+
|
|
86
|
+
expect(disabledDlqHandler.isEnabled()).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should return DLQ configuration', () => {
|
|
90
|
+
const dlqConfig = dlqHandler.getDLQConfig();
|
|
91
|
+
|
|
92
|
+
expect(dlqConfig).toEqual(mockConfig.dlq);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('DLQ Setup with Various Configurations', () => {
|
|
97
|
+
it('should setup DLQ with complete configuration', async () => {
|
|
98
|
+
const originalQueue = 'test-queue';
|
|
99
|
+
const expectedDlqExchange = 'test-queue.dlq';
|
|
100
|
+
const expectedDlqQueue = 'test-queue.dlq';
|
|
101
|
+
|
|
102
|
+
const result = await dlqHandler.setupDLQ(originalQueue);
|
|
103
|
+
|
|
104
|
+
expect(mockResourceCreator.ensureExchange).toHaveBeenCalledWith(
|
|
105
|
+
expectedDlqExchange,
|
|
106
|
+
'direct',
|
|
107
|
+
{ durable: true, autoDelete: false }
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
expect(mockResourceCreator.ensureQueue).toHaveBeenCalledWith(
|
|
111
|
+
expectedDlqQueue,
|
|
112
|
+
{
|
|
113
|
+
durable: true,
|
|
114
|
+
exclusive: false,
|
|
115
|
+
autoDelete: false,
|
|
116
|
+
arguments: { 'x-message-ttl': 300000 }
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
expect(mockResourceCreator.bindQueue).toHaveBeenCalledWith(
|
|
121
|
+
expectedDlqQueue,
|
|
122
|
+
expectedDlqExchange,
|
|
123
|
+
originalQueue
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
expect(result).toBe(expectedDlqQueue);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should setup DLQ without TTL when not configured', async () => {
|
|
130
|
+
const configWithoutTTL = {
|
|
131
|
+
...mockConfig,
|
|
132
|
+
dlq: { active: true, maxRetries: 3, retryDelay: 5000 }
|
|
133
|
+
};
|
|
134
|
+
const dlqHandlerWithoutTTL = new DLQHandler(configWithoutTTL, mockConnectionManager, mockResourceCreator);
|
|
135
|
+
|
|
136
|
+
await dlqHandlerWithoutTTL.setupDLQ('test-queue');
|
|
137
|
+
|
|
138
|
+
expect(mockResourceCreator.ensureQueue).toHaveBeenCalledWith(
|
|
139
|
+
'test-queue.dlq',
|
|
140
|
+
{
|
|
141
|
+
durable: true,
|
|
142
|
+
exclusive: false,
|
|
143
|
+
autoDelete: false
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should setup DLQ with zero TTL when configured', async () => {
|
|
149
|
+
const configWithZeroTTL = {
|
|
150
|
+
...mockConfig,
|
|
151
|
+
dlq: { active: true, ttl: 0, maxRetries: 3, retryDelay: 5000 }
|
|
152
|
+
};
|
|
153
|
+
const dlqHandlerWithZeroTTL = new DLQHandler(configWithZeroTTL, mockConnectionManager, mockResourceCreator);
|
|
154
|
+
|
|
155
|
+
await dlqHandlerWithZeroTTL.setupDLQ('test-queue');
|
|
156
|
+
|
|
157
|
+
expect(mockResourceCreator.ensureQueue).toHaveBeenCalledWith(
|
|
158
|
+
'test-queue.dlq',
|
|
159
|
+
{
|
|
160
|
+
durable: true,
|
|
161
|
+
exclusive: false,
|
|
162
|
+
autoDelete: false
|
|
163
|
+
}
|
|
164
|
+
);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('should use cache to avoid duplicate DLQ setup', async () => {
|
|
168
|
+
const originalQueue = 'test-queue';
|
|
169
|
+
|
|
170
|
+
// First setup
|
|
171
|
+
await dlqHandler.setupDLQ(originalQueue);
|
|
172
|
+
|
|
173
|
+
// Second setup should use cache
|
|
174
|
+
await dlqHandler.setupDLQ(originalQueue);
|
|
175
|
+
|
|
176
|
+
// Should only call resource creation once
|
|
177
|
+
expect(mockResourceCreator.ensureExchange).toHaveBeenCalledTimes(1);
|
|
178
|
+
expect(mockResourceCreator.ensureQueue).toHaveBeenCalledTimes(1);
|
|
179
|
+
expect(mockResourceCreator.bindQueue).toHaveBeenCalledTimes(1);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should throw error when original queue name is empty', async () => {
|
|
183
|
+
await expect(dlqHandler.setupDLQ('')).rejects.toThrow(
|
|
184
|
+
'Original queue name is required for DLQ setup'
|
|
185
|
+
);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should throw error when DLQ is disabled', async () => {
|
|
189
|
+
const disabledConfig = { ...mockConfig, dlq: { active: false } };
|
|
190
|
+
const disabledDlqHandler = new DLQHandler(disabledConfig, mockConnectionManager, mockResourceCreator);
|
|
191
|
+
|
|
192
|
+
await expect(disabledDlqHandler.setupDLQ('test-queue')).rejects.toThrow(
|
|
193
|
+
'DLQ is not enabled in configuration'
|
|
194
|
+
);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should handle resource creation failures', async () => {
|
|
198
|
+
const error = new Error('Exchange creation failed');
|
|
199
|
+
mockResourceCreator.ensureExchange.mockRejectedValue(error);
|
|
200
|
+
|
|
201
|
+
await expect(dlqHandler.setupDLQ('test-queue')).rejects.toThrow(error);
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
describe('Failed Message Routing to DLQ', () => {
|
|
206
|
+
it('should route failed message to DLQ with complete metadata', async () => {
|
|
207
|
+
const originalQueue = 'test-queue';
|
|
208
|
+
const message = {
|
|
209
|
+
content: Buffer.from('test message'),
|
|
210
|
+
properties: {
|
|
211
|
+
messageId: 'msg-123',
|
|
212
|
+
timestamp: 1234567890,
|
|
213
|
+
headers: { 'x-retry-count': 1 }
|
|
214
|
+
},
|
|
215
|
+
fields: {
|
|
216
|
+
exchange: 'original-exchange',
|
|
217
|
+
routingKey: 'original-key',
|
|
218
|
+
deliveryTag: 1,
|
|
219
|
+
redelivered: false
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
await dlqHandler.handleFailedMessage(message, originalQueue);
|
|
224
|
+
|
|
225
|
+
expect(mockChannel.publish).toHaveBeenCalledWith(
|
|
226
|
+
'test-queue.dlq',
|
|
227
|
+
originalQueue,
|
|
228
|
+
expect.any(Buffer),
|
|
229
|
+
{
|
|
230
|
+
persistent: true,
|
|
231
|
+
timestamp: expect.any(Number),
|
|
232
|
+
headers: {
|
|
233
|
+
'x-dlq-routed': true,
|
|
234
|
+
'x-original-queue': originalQueue
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
// Verify message content structure
|
|
240
|
+
const publishCall = mockChannel.publish.mock.calls[0];
|
|
241
|
+
const messageBuffer = publishCall[2] as Buffer;
|
|
242
|
+
const parsedMessage = JSON.parse(messageBuffer.toString());
|
|
243
|
+
|
|
244
|
+
expect(parsedMessage).toMatchObject({
|
|
245
|
+
originalMessage: expect.any(Object), // Buffer gets serialized as object
|
|
246
|
+
dlqMetadata: {
|
|
247
|
+
originalQueue: originalQueue,
|
|
248
|
+
originalExchange: 'original-exchange',
|
|
249
|
+
originalRoutingKey: 'original-key',
|
|
250
|
+
retryCount: 2, // Should increment from 1 to 2
|
|
251
|
+
maxRetries: 3,
|
|
252
|
+
retryDelay: 5000,
|
|
253
|
+
failedAt: expect.any(String)
|
|
254
|
+
},
|
|
255
|
+
originalProperties: message.properties,
|
|
256
|
+
originalFields: message.fields
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('should handle message without properties and fields', async () => {
|
|
261
|
+
const originalQueue = 'test-queue';
|
|
262
|
+
const simpleMessage = { content: 'simple message' };
|
|
263
|
+
|
|
264
|
+
await dlqHandler.handleFailedMessage(simpleMessage, originalQueue);
|
|
265
|
+
|
|
266
|
+
expect(mockChannel.publish).toHaveBeenCalled();
|
|
267
|
+
|
|
268
|
+
const publishCall = mockChannel.publish.mock.calls[0];
|
|
269
|
+
const messageBuffer = publishCall[2] as Buffer;
|
|
270
|
+
const parsedMessage = JSON.parse(messageBuffer.toString());
|
|
271
|
+
|
|
272
|
+
expect(parsedMessage.dlqMetadata).toMatchObject({
|
|
273
|
+
originalQueue: originalQueue,
|
|
274
|
+
originalExchange: '',
|
|
275
|
+
originalRoutingKey: '',
|
|
276
|
+
retryCount: 1, // Should start at 1 when no previous retry count
|
|
277
|
+
maxRetries: 3,
|
|
278
|
+
retryDelay: 5000
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('should handle message that is just content without wrapper', async () => {
|
|
283
|
+
const originalQueue = 'test-queue';
|
|
284
|
+
const directMessage = 'direct message content';
|
|
285
|
+
|
|
286
|
+
await dlqHandler.handleFailedMessage(directMessage, originalQueue);
|
|
287
|
+
|
|
288
|
+
expect(mockChannel.publish).toHaveBeenCalled();
|
|
289
|
+
|
|
290
|
+
const publishCall = mockChannel.publish.mock.calls[0];
|
|
291
|
+
const messageBuffer = publishCall[2] as Buffer;
|
|
292
|
+
const parsedMessage = JSON.parse(messageBuffer.toString());
|
|
293
|
+
|
|
294
|
+
expect(parsedMessage.originalMessage).toBe(directMessage);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('should throw error when original queue name is missing', async () => {
|
|
298
|
+
const message = { content: 'test' };
|
|
299
|
+
|
|
300
|
+
await expect(dlqHandler.handleFailedMessage(message, '')).rejects.toThrow(
|
|
301
|
+
'Original queue name is required for failed message handling'
|
|
302
|
+
);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('should throw error when message is missing', async () => {
|
|
306
|
+
await expect(dlqHandler.handleFailedMessage(null, 'test-queue')).rejects.toThrow(
|
|
307
|
+
'Message is required for failed message handling'
|
|
308
|
+
);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('should handle channel creation failure', async () => {
|
|
312
|
+
const error = new Error('Channel creation failed');
|
|
313
|
+
mockConnection.createChannel.mockRejectedValue(error);
|
|
314
|
+
|
|
315
|
+
await expect(dlqHandler.handleFailedMessage({ content: 'test' }, 'test-queue')).rejects.toThrow(
|
|
316
|
+
'Failed to route message to DLQ exchange \'test-queue.dlq\': Channel creation failed'
|
|
317
|
+
);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it('should handle publish failure', async () => {
|
|
321
|
+
// Reset the channel mock to ensure publish returns false
|
|
322
|
+
mockChannel.publish.mockReturnValue(false);
|
|
323
|
+
mockConnection.createChannel.mockResolvedValue(mockChannel);
|
|
324
|
+
|
|
325
|
+
await expect(dlqHandler.handleFailedMessage({ content: 'test' }, 'test-queue')).rejects.toThrow(
|
|
326
|
+
'Failed to publish message to DLQ - channel buffer full'
|
|
327
|
+
);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('should handle connection unavailable', async () => {
|
|
331
|
+
mockConnectionManager.getConnection.mockReturnValue(null);
|
|
332
|
+
|
|
333
|
+
await expect(dlqHandler.handleFailedMessage({ content: 'test' }, 'test-queue')).rejects.toThrow(
|
|
334
|
+
'No active connection to RabbitMQ'
|
|
335
|
+
);
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
describe('DLQ Disabled Behavior', () => {
|
|
340
|
+
let disabledDlqHandler: DLQHandler;
|
|
341
|
+
|
|
342
|
+
beforeEach(() => {
|
|
343
|
+
const disabledConfig = { ...mockConfig, dlq: { active: false } };
|
|
344
|
+
disabledDlqHandler = new DLQHandler(disabledConfig, mockConnectionManager, mockResourceCreator);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('should acknowledge failed message without routing when DLQ is disabled', async () => {
|
|
348
|
+
const message = { content: 'test message' };
|
|
349
|
+
const originalQueue = 'test-queue';
|
|
350
|
+
|
|
351
|
+
// Should not throw and should complete successfully
|
|
352
|
+
await expect(disabledDlqHandler.handleFailedMessage(message, originalQueue)).resolves.toBeUndefined();
|
|
353
|
+
|
|
354
|
+
// Should not attempt to setup DLQ or route message
|
|
355
|
+
expect(mockResourceCreator.ensureExchange).not.toHaveBeenCalled();
|
|
356
|
+
expect(mockResourceCreator.ensureQueue).not.toHaveBeenCalled();
|
|
357
|
+
expect(mockChannel.publish).not.toHaveBeenCalled();
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it('should not setup DLQ when disabled', async () => {
|
|
361
|
+
await expect(disabledDlqHandler.setupDLQ('test-queue')).rejects.toThrow(
|
|
362
|
+
'DLQ is not enabled in configuration'
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
expect(mockResourceCreator.ensureExchange).not.toHaveBeenCalled();
|
|
366
|
+
expect(mockResourceCreator.ensureQueue).not.toHaveBeenCalled();
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
describe('Cache Management', () => {
|
|
371
|
+
it('should clear cache and allow fresh setup', async () => {
|
|
372
|
+
const originalQueue = 'test-queue';
|
|
373
|
+
|
|
374
|
+
// First setup
|
|
375
|
+
await dlqHandler.setupDLQ(originalQueue);
|
|
376
|
+
expect(mockResourceCreator.ensureExchange).toHaveBeenCalledTimes(1);
|
|
377
|
+
|
|
378
|
+
// Clear cache
|
|
379
|
+
dlqHandler.clearCache();
|
|
380
|
+
|
|
381
|
+
// Second setup should not use cache
|
|
382
|
+
await dlqHandler.setupDLQ(originalQueue);
|
|
383
|
+
expect(mockResourceCreator.ensureExchange).toHaveBeenCalledTimes(2);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('should return cache statistics', () => {
|
|
387
|
+
const stats = dlqHandler.getCacheStats();
|
|
388
|
+
expect(stats).toEqual({ setupCache: 0 });
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
it('should update cache statistics after setup', async () => {
|
|
392
|
+
await dlqHandler.setupDLQ('queue1');
|
|
393
|
+
await dlqHandler.setupDLQ('queue2');
|
|
394
|
+
|
|
395
|
+
const stats = dlqHandler.getCacheStats();
|
|
396
|
+
expect(stats.setupCache).toBe(2);
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
});
|