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,357 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { ConfigurationManager } from '../ConfigurationManager';
|
|
4
|
+
import { ConfigurationError } from '../../interfaces/IErrors';
|
|
5
|
+
import { IConfiguration } from '../../interfaces/IConfiguration';
|
|
6
|
+
|
|
7
|
+
// Mock fs module
|
|
8
|
+
jest.mock('fs', () => ({
|
|
9
|
+
readFileSync: jest.fn(),
|
|
10
|
+
existsSync: jest.fn(),
|
|
11
|
+
}));
|
|
12
|
+
const mockedFs = fs as jest.Mocked<typeof fs>;
|
|
13
|
+
|
|
14
|
+
describe('ConfigurationManager', () => {
|
|
15
|
+
let configManager: ConfigurationManager;
|
|
16
|
+
const mockConfigPath = path.join(process.cwd(), 'config.json');
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
// Reset singleton instance before each test
|
|
20
|
+
(ConfigurationManager as any).instance = null;
|
|
21
|
+
configManager = ConfigurationManager.getInstance();
|
|
22
|
+
configManager.clearCache();
|
|
23
|
+
|
|
24
|
+
// Reset all mocks
|
|
25
|
+
jest.clearAllMocks();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('Singleton Behavior', () => {
|
|
29
|
+
it('should return the same instance when called multiple times', () => {
|
|
30
|
+
const instance1 = ConfigurationManager.getInstance();
|
|
31
|
+
const instance2 = ConfigurationManager.getInstance();
|
|
32
|
+
const instance3 = ConfigurationManager.getInstance();
|
|
33
|
+
|
|
34
|
+
expect(instance1).toBe(instance2);
|
|
35
|
+
expect(instance2).toBe(instance3);
|
|
36
|
+
expect(instance1).toBe(instance3);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('Valid Configuration Loading', () => {
|
|
41
|
+
it('should load valid configuration successfully', () => {
|
|
42
|
+
const validConfig: IConfiguration = {
|
|
43
|
+
url: 'amqp://localhost:5672',
|
|
44
|
+
dlq: {
|
|
45
|
+
active: true,
|
|
46
|
+
ttl: 300000,
|
|
47
|
+
maxRetries: 3,
|
|
48
|
+
retryDelay: 5000,
|
|
49
|
+
},
|
|
50
|
+
logging: {
|
|
51
|
+
level: 'info',
|
|
52
|
+
format: 'json',
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
mockedFs.existsSync.mockReturnValue(true);
|
|
57
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(validConfig));
|
|
58
|
+
|
|
59
|
+
const result = configManager.loadConfig();
|
|
60
|
+
|
|
61
|
+
expect(mockedFs.existsSync).toHaveBeenCalledWith(mockConfigPath);
|
|
62
|
+
expect(mockedFs.readFileSync).toHaveBeenCalledWith(mockConfigPath, 'utf-8');
|
|
63
|
+
expect(result).toEqual(validConfig);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should load minimal valid configuration', () => {
|
|
67
|
+
const minimalConfig: IConfiguration = {
|
|
68
|
+
url: 'amqp://localhost:5672',
|
|
69
|
+
dlq: {
|
|
70
|
+
active: false,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
mockedFs.existsSync.mockReturnValue(true);
|
|
75
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(minimalConfig));
|
|
76
|
+
|
|
77
|
+
const result = configManager.loadConfig();
|
|
78
|
+
|
|
79
|
+
expect(result).toEqual(minimalConfig);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should return cached configuration on subsequent calls', () => {
|
|
83
|
+
const validConfig: IConfiguration = {
|
|
84
|
+
url: 'amqp://localhost:5672',
|
|
85
|
+
dlq: {
|
|
86
|
+
active: true,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
mockedFs.existsSync.mockReturnValue(true);
|
|
91
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(validConfig));
|
|
92
|
+
|
|
93
|
+
// First call
|
|
94
|
+
const result1 = configManager.loadConfig();
|
|
95
|
+
// Second call
|
|
96
|
+
const result2 = configManager.loadConfig();
|
|
97
|
+
|
|
98
|
+
expect(mockedFs.readFileSync).toHaveBeenCalledTimes(1);
|
|
99
|
+
expect(result1).toEqual(result2);
|
|
100
|
+
expect(result1).toBe(result2); // Should be the same object reference
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should load configuration with optional logging properties', () => {
|
|
104
|
+
const configWithLogging: IConfiguration = {
|
|
105
|
+
url: 'amqp://test:5672',
|
|
106
|
+
dlq: {
|
|
107
|
+
active: true,
|
|
108
|
+
ttl: 600000,
|
|
109
|
+
maxRetries: 5,
|
|
110
|
+
retryDelay: 10000,
|
|
111
|
+
},
|
|
112
|
+
logging: {
|
|
113
|
+
level: 'debug',
|
|
114
|
+
format: 'text',
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
mockedFs.existsSync.mockReturnValue(true);
|
|
119
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(configWithLogging));
|
|
120
|
+
|
|
121
|
+
const result = configManager.loadConfig();
|
|
122
|
+
|
|
123
|
+
expect(result).toEqual(configWithLogging);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('Missing File Error Handling', () => {
|
|
128
|
+
it('should throw ConfigurationError when config file does not exist', () => {
|
|
129
|
+
mockedFs.existsSync.mockReturnValue(false);
|
|
130
|
+
|
|
131
|
+
expect(() => configManager.loadConfig()).toThrow(ConfigurationError);
|
|
132
|
+
expect(() => configManager.loadConfig()).toThrow(
|
|
133
|
+
`Configuration file not found at ${mockConfigPath}. Please create a config.json file in your project root.`
|
|
134
|
+
);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should include config path in error context when file is missing', () => {
|
|
138
|
+
mockedFs.existsSync.mockReturnValue(false);
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
configManager.loadConfig();
|
|
142
|
+
throw new Error('Expected ConfigurationError to be thrown');
|
|
143
|
+
} catch (error) {
|
|
144
|
+
expect(error).toBeInstanceOf(ConfigurationError);
|
|
145
|
+
expect((error as ConfigurationError).context).toEqual({
|
|
146
|
+
configPath: mockConfigPath,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should throw ConfigurationError when file read fails', () => {
|
|
152
|
+
mockedFs.existsSync.mockReturnValue(true);
|
|
153
|
+
mockedFs.readFileSync.mockImplementation(() => {
|
|
154
|
+
throw new Error('Permission denied');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
expect(() => configManager.loadConfig()).toThrow(ConfigurationError);
|
|
158
|
+
expect(() => configManager.loadConfig()).toThrow('Failed to load configuration: Permission denied');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should throw ConfigurationError when JSON parsing fails', () => {
|
|
162
|
+
mockedFs.existsSync.mockReturnValue(true);
|
|
163
|
+
mockedFs.readFileSync.mockReturnValue('{ invalid json }');
|
|
164
|
+
|
|
165
|
+
expect(() => configManager.loadConfig()).toThrow(ConfigurationError);
|
|
166
|
+
expect(() => configManager.loadConfig()).toThrow(/Invalid JSON in configuration file/);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe('Invalid Schema Validation', () => {
|
|
171
|
+
beforeEach(() => {
|
|
172
|
+
mockedFs.existsSync.mockReturnValue(true);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should throw ConfigurationError when config is not an object', () => {
|
|
176
|
+
mockedFs.readFileSync.mockReturnValue('"not an object"');
|
|
177
|
+
|
|
178
|
+
expect(() => configManager.loadConfig()).toThrow(ConfigurationError);
|
|
179
|
+
expect(() => configManager.loadConfig()).toThrow('Configuration must be a valid object');
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should throw ConfigurationError when url is missing', () => {
|
|
183
|
+
const invalidConfig = {
|
|
184
|
+
dlq: { active: true },
|
|
185
|
+
};
|
|
186
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(invalidConfig));
|
|
187
|
+
|
|
188
|
+
expect(() => configManager.loadConfig()).toThrow(ConfigurationError);
|
|
189
|
+
expect(() => configManager.loadConfig()).toThrow('Configuration must include a valid "url" string property');
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('should throw ConfigurationError when url is not a string', () => {
|
|
193
|
+
const invalidConfig = {
|
|
194
|
+
url: 12345,
|
|
195
|
+
dlq: { active: true },
|
|
196
|
+
};
|
|
197
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(invalidConfig));
|
|
198
|
+
|
|
199
|
+
expect(() => configManager.loadConfig()).toThrow(ConfigurationError);
|
|
200
|
+
expect(() => configManager.loadConfig()).toThrow('Configuration must include a valid "url" string property');
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should throw ConfigurationError when dlq is missing', () => {
|
|
204
|
+
const invalidConfig = {
|
|
205
|
+
url: 'amqp://localhost:5672',
|
|
206
|
+
};
|
|
207
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(invalidConfig));
|
|
208
|
+
|
|
209
|
+
expect(() => configManager.loadConfig()).toThrow(ConfigurationError);
|
|
210
|
+
expect(() => configManager.loadConfig()).toThrow('Configuration must include a valid "dlq" object property');
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('should throw ConfigurationError when dlq is not an object', () => {
|
|
214
|
+
const invalidConfig = {
|
|
215
|
+
url: 'amqp://localhost:5672',
|
|
216
|
+
dlq: 'not an object',
|
|
217
|
+
};
|
|
218
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(invalidConfig));
|
|
219
|
+
|
|
220
|
+
expect(() => configManager.loadConfig()).toThrow(ConfigurationError);
|
|
221
|
+
expect(() => configManager.loadConfig()).toThrow('Configuration must include a valid "dlq" object property');
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('should throw ConfigurationError when dlq.active is missing', () => {
|
|
225
|
+
const invalidConfig = {
|
|
226
|
+
url: 'amqp://localhost:5672',
|
|
227
|
+
dlq: {},
|
|
228
|
+
};
|
|
229
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(invalidConfig));
|
|
230
|
+
|
|
231
|
+
expect(() => configManager.loadConfig()).toThrow(ConfigurationError);
|
|
232
|
+
expect(() => configManager.loadConfig()).toThrow('Configuration dlq.active must be a boolean value');
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('should throw ConfigurationError when dlq.active is not a boolean', () => {
|
|
236
|
+
const invalidConfig = {
|
|
237
|
+
url: 'amqp://localhost:5672',
|
|
238
|
+
dlq: { active: 'yes' },
|
|
239
|
+
};
|
|
240
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(invalidConfig));
|
|
241
|
+
|
|
242
|
+
expect(() => configManager.loadConfig()).toThrow(ConfigurationError);
|
|
243
|
+
expect(() => configManager.loadConfig()).toThrow('Configuration dlq.active must be a boolean value');
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('should throw ConfigurationError when dlq.ttl is negative', () => {
|
|
247
|
+
const invalidConfig = {
|
|
248
|
+
url: 'amqp://localhost:5672',
|
|
249
|
+
dlq: { active: true, ttl: -1000 },
|
|
250
|
+
};
|
|
251
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(invalidConfig));
|
|
252
|
+
|
|
253
|
+
expect(() => configManager.loadConfig()).toThrow(ConfigurationError);
|
|
254
|
+
expect(() => configManager.loadConfig()).toThrow('Configuration dlq.ttl must be a positive number');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('should throw ConfigurationError when dlq.maxRetries is negative', () => {
|
|
258
|
+
const invalidConfig = {
|
|
259
|
+
url: 'amqp://localhost:5672',
|
|
260
|
+
dlq: { active: true, maxRetries: -5 },
|
|
261
|
+
};
|
|
262
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(invalidConfig));
|
|
263
|
+
|
|
264
|
+
expect(() => configManager.loadConfig()).toThrow(ConfigurationError);
|
|
265
|
+
expect(() => configManager.loadConfig()).toThrow('Configuration dlq.maxRetries must be a positive number');
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('should throw ConfigurationError when dlq.retryDelay is negative', () => {
|
|
269
|
+
const invalidConfig = {
|
|
270
|
+
url: 'amqp://localhost:5672',
|
|
271
|
+
dlq: { active: true, retryDelay: -2000 },
|
|
272
|
+
};
|
|
273
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(invalidConfig));
|
|
274
|
+
|
|
275
|
+
expect(() => configManager.loadConfig()).toThrow(ConfigurationError);
|
|
276
|
+
expect(() => configManager.loadConfig()).toThrow('Configuration dlq.retryDelay must be a positive number');
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('should throw ConfigurationError when logging is not an object', () => {
|
|
280
|
+
const invalidConfig = {
|
|
281
|
+
url: 'amqp://localhost:5672',
|
|
282
|
+
dlq: { active: true },
|
|
283
|
+
logging: 'invalid',
|
|
284
|
+
};
|
|
285
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(invalidConfig));
|
|
286
|
+
|
|
287
|
+
expect(() => configManager.loadConfig()).toThrow(ConfigurationError);
|
|
288
|
+
expect(() => configManager.loadConfig()).toThrow('Configuration logging must be an object');
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('should throw ConfigurationError when logging.level is invalid', () => {
|
|
292
|
+
const invalidConfig = {
|
|
293
|
+
url: 'amqp://localhost:5672',
|
|
294
|
+
dlq: { active: true },
|
|
295
|
+
logging: { level: 'invalid' },
|
|
296
|
+
};
|
|
297
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(invalidConfig));
|
|
298
|
+
|
|
299
|
+
expect(() => configManager.loadConfig()).toThrow(ConfigurationError);
|
|
300
|
+
expect(() => configManager.loadConfig()).toThrow('Configuration logging.level must be one of: error, warn, info, debug');
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('should throw ConfigurationError when logging.format is invalid', () => {
|
|
304
|
+
const invalidConfig = {
|
|
305
|
+
url: 'amqp://localhost:5672',
|
|
306
|
+
dlq: { active: true },
|
|
307
|
+
logging: { format: 'invalid' },
|
|
308
|
+
};
|
|
309
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(invalidConfig));
|
|
310
|
+
|
|
311
|
+
expect(() => configManager.loadConfig()).toThrow(ConfigurationError);
|
|
312
|
+
expect(() => configManager.loadConfig()).toThrow('Configuration logging.format must be one of: json, text');
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
describe('Utility Methods', () => {
|
|
317
|
+
it('should return correct config path', () => {
|
|
318
|
+
const configPath = configManager.getConfigPath();
|
|
319
|
+
expect(configPath).toBe(mockConfigPath);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('should clear cache properly', () => {
|
|
323
|
+
const validConfig: IConfiguration = {
|
|
324
|
+
url: 'amqp://localhost:5672',
|
|
325
|
+
dlq: { active: true },
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
mockedFs.existsSync.mockReturnValue(true);
|
|
329
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(validConfig));
|
|
330
|
+
|
|
331
|
+
// Load config to cache it
|
|
332
|
+
configManager.loadConfig();
|
|
333
|
+
expect(mockedFs.readFileSync).toHaveBeenCalledTimes(1);
|
|
334
|
+
|
|
335
|
+
// Clear cache and load again
|
|
336
|
+
configManager.clearCache();
|
|
337
|
+
configManager.loadConfig();
|
|
338
|
+
expect(mockedFs.readFileSync).toHaveBeenCalledTimes(2);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it('should validate config directly without loading from file', () => {
|
|
342
|
+
const validConfig: IConfiguration = {
|
|
343
|
+
url: 'amqp://localhost:5672',
|
|
344
|
+
dlq: { active: true },
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
expect(() => configManager.validateConfig(validConfig)).not.toThrow();
|
|
348
|
+
|
|
349
|
+
const invalidConfig = {
|
|
350
|
+
url: 'amqp://localhost:5672',
|
|
351
|
+
// missing dlq
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
expect(() => configManager.validateConfig(invalidConfig)).toThrow(ConfigurationError);
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
});
|