@onlineapps/conn-base-hub 1.0.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.
@@ -0,0 +1,413 @@
1
+ 'use strict';
2
+
3
+ // Mock all external dependencies before requiring the module
4
+ jest.mock('@onlineapps/connector-mq-client', () => {
5
+ return jest.fn().mockImplementation(() => ({
6
+ connect: jest.fn().mockResolvedValue(),
7
+ publish: jest.fn().mockResolvedValue(),
8
+ subscribe: jest.fn().mockResolvedValue(),
9
+ close: jest.fn().mockResolvedValue()
10
+ }));
11
+ }, { virtual: true });
12
+
13
+ jest.mock('@onlineapps/connector-registry-client', () => ({
14
+ ServiceRegistryClient: jest.fn().mockImplementation(() => ({
15
+ register: jest.fn().mockResolvedValue(),
16
+ deregister: jest.fn().mockResolvedValue(),
17
+ discover: jest.fn().mockResolvedValue([]),
18
+ emit: jest.fn()
19
+ }))
20
+ }), { virtual: true });
21
+
22
+ jest.mock('@onlineapps/connector-cookbook', () => ({
23
+ validateCookbook: jest.fn(),
24
+ parseCookbook: jest.fn(),
25
+ executeCookbook: jest.fn()
26
+ }), { virtual: true });
27
+
28
+ jest.mock('@onlineapps/connector-storage', () => {
29
+ return jest.fn().mockImplementation(() => ({
30
+ upload: jest.fn().mockResolvedValue({ etag: '123' }),
31
+ download: jest.fn().mockResolvedValue(Buffer.from('test')),
32
+ delete: jest.fn().mockResolvedValue(),
33
+ list: jest.fn().mockResolvedValue([])
34
+ }));
35
+ }, { virtual: true });
36
+
37
+ jest.mock('@onlineapps/connector-logger', () => {
38
+ return jest.fn().mockImplementation((config) => ({
39
+ info: jest.fn(),
40
+ error: jest.fn(),
41
+ warn: jest.fn(),
42
+ debug: jest.fn(),
43
+ api: { info: jest.fn(), error: jest.fn() },
44
+ mq: { info: jest.fn(), error: jest.fn() },
45
+ workflow: { info: jest.fn(), error: jest.fn() },
46
+ registry: { info: jest.fn(), error: jest.fn() },
47
+ close: jest.fn().mockResolvedValue()
48
+ }));
49
+ }, { virtual: true });
50
+
51
+ describe('Hub Connector Component Tests', () => {
52
+ let hub;
53
+ let mockConfig;
54
+
55
+ beforeEach(() => {
56
+ jest.clearAllMocks();
57
+
58
+ // Clear require cache to get fresh module
59
+ delete require.cache[require.resolve('../../src/index.js')];
60
+
61
+ // Now require the hub
62
+ hub = require('../../src/index.js');
63
+
64
+ mockConfig = {
65
+ serviceName: 'test-service',
66
+ version: '1.0.0',
67
+ amqpUrl: 'amqp://localhost',
68
+ registry: {
69
+ registryUrl: 'http://registry:3000'
70
+ },
71
+ mq: {
72
+ queue: 'test-queue'
73
+ },
74
+ storage: {
75
+ endpoint: 'http://minio:9000',
76
+ accessKey: 'test',
77
+ secretKey: 'test'
78
+ },
79
+ logger: {
80
+ level: 'info'
81
+ }
82
+ };
83
+ });
84
+
85
+ afterEach(() => {
86
+ jest.restoreAllMocks();
87
+ });
88
+
89
+ describe('Module Exports', () => {
90
+ it('should export all required connectors', () => {
91
+ expect(hub.MQClient).toBeDefined();
92
+ expect(hub.ServiceRegistryClient).toBeDefined();
93
+ expect(hub.StorageConnector).toBeDefined();
94
+ expect(hub.createLogger).toBeDefined();
95
+ expect(hub.createMicroservice).toBeDefined();
96
+ });
97
+
98
+ it('should export cookbook functions', () => {
99
+ expect(hub.validateCookbook).toBeDefined();
100
+ expect(hub.parseCookbook).toBeDefined();
101
+ expect(hub.executeCookbook).toBeDefined();
102
+ });
103
+ });
104
+
105
+ describe('createMicroservice Factory', () => {
106
+ it('should create microservice with all components', () => {
107
+ const microservice = hub.createMicroservice(mockConfig);
108
+
109
+ expect(microservice).toHaveProperty('logger');
110
+ expect(microservice).toHaveProperty('registry');
111
+ expect(microservice).toHaveProperty('mq');
112
+ expect(microservice).toHaveProperty('storage');
113
+ // Note: cookbook is not a property on microservice, it's exported separately
114
+ });
115
+
116
+ it('should create microservice with partial config', () => {
117
+ const partialConfig = {
118
+ serviceName: 'partial-service',
119
+ version: '1.0.0',
120
+ mq: {
121
+ queue: 'partial-queue'
122
+ }
123
+ };
124
+
125
+ const microservice = hub.createMicroservice(partialConfig);
126
+
127
+ expect(microservice.logger).toBeDefined();
128
+ expect(microservice.mq).toBeDefined();
129
+ expect(microservice.registry).toBeNull();
130
+ expect(microservice.storage).toBeNull();
131
+ });
132
+
133
+ it('should use environment variables as fallback', () => {
134
+ process.env.RABBITMQ_URL = 'amqp://env-rabbit';
135
+ process.env.MINIO_ENDPOINT = 'http://env-minio:9000';
136
+ process.env.MINIO_ACCESS_KEY = 'env-access';
137
+ process.env.MINIO_SECRET_KEY = 'env-secret';
138
+
139
+ const microservice = hub.createMicroservice({
140
+ serviceName: 'env-service',
141
+ version: '1.0.0',
142
+ mq: {},
143
+ storage: {}
144
+ });
145
+
146
+ expect(microservice.mq).toBeDefined();
147
+ expect(microservice.storage).toBeDefined();
148
+
149
+ delete process.env.RABBITMQ_URL;
150
+ delete process.env.MINIO_ENDPOINT;
151
+ delete process.env.MINIO_ACCESS_KEY;
152
+ delete process.env.MINIO_SECRET_KEY;
153
+ });
154
+
155
+ it('should handle initialization errors gracefully', () => {
156
+ const invalidConfig = {
157
+ serviceName: null,
158
+ version: undefined
159
+ };
160
+
161
+ expect(() => {
162
+ hub.createMicroservice(invalidConfig);
163
+ }).not.toThrow();
164
+ });
165
+ });
166
+
167
+ describe('Logger Integration', () => {
168
+ it('should create logger with correct configuration', () => {
169
+ const microservice = hub.createMicroservice(mockConfig);
170
+
171
+ expect(microservice.logger).toBeDefined();
172
+ expect(microservice.logger.info).toBeDefined();
173
+ expect(microservice.logger.error).toBeDefined();
174
+ expect(microservice.logger.warn).toBeDefined();
175
+ expect(microservice.logger.debug).toBeDefined();
176
+ });
177
+
178
+ it('should handle logger operations', () => {
179
+ const microservice = hub.createMicroservice(mockConfig);
180
+
181
+ expect(() => {
182
+ microservice.logger.info('Test info');
183
+ microservice.logger.error('Test error');
184
+ microservice.logger.warn('Test warning');
185
+ microservice.logger.debug('Test debug');
186
+ }).not.toThrow();
187
+ });
188
+ });
189
+
190
+ describe('MQ Client Integration', () => {
191
+ it('should initialize MQ client with correct config', () => {
192
+ const microservice = hub.createMicroservice(mockConfig);
193
+
194
+ expect(microservice.mq).toBeDefined();
195
+ expect(microservice.mq.connect).toBeDefined();
196
+ expect(microservice.mq.publish).toBeDefined();
197
+ expect(microservice.mq.subscribe).toBeDefined();
198
+ });
199
+
200
+ it('should handle MQ operations', async () => {
201
+ const microservice = hub.createMicroservice(mockConfig);
202
+
203
+ await expect(microservice.mq.connect()).resolves.not.toThrow();
204
+ await expect(microservice.mq.publish('test-queue', { test: 'message' })).resolves.not.toThrow();
205
+
206
+ const handler = jest.fn();
207
+ await expect(microservice.mq.subscribe('test-queue', handler)).resolves.not.toThrow();
208
+ await expect(microservice.mq.close()).resolves.not.toThrow();
209
+ });
210
+ });
211
+
212
+ describe('Service Registry Integration', () => {
213
+ it('should initialize registry client with correct config', () => {
214
+ const microservice = hub.createMicroservice(mockConfig);
215
+
216
+ expect(microservice.registry).toBeDefined();
217
+ expect(microservice.registry.register).toBeDefined();
218
+ expect(microservice.registry.discover).toBeDefined();
219
+ expect(microservice.registry.deregister).toBeDefined();
220
+ });
221
+
222
+ it('should handle service registration', async () => {
223
+ const microservice = hub.createMicroservice(mockConfig);
224
+
225
+ await expect(
226
+ microservice.registry.register({
227
+ name: 'test-service',
228
+ version: '1.0.0',
229
+ endpoints: ['http://localhost:3000']
230
+ })
231
+ ).resolves.not.toThrow();
232
+
233
+ const services = await microservice.registry.discover('test-service');
234
+ expect(services).toBeDefined();
235
+ expect(Array.isArray(services)).toBe(true);
236
+
237
+ await expect(microservice.registry.deregister()).resolves.not.toThrow();
238
+ });
239
+ });
240
+
241
+ describe('Storage Integration', () => {
242
+ it('should initialize storage connector with correct config', () => {
243
+ const microservice = hub.createMicroservice(mockConfig);
244
+
245
+ expect(microservice.storage).toBeDefined();
246
+ expect(microservice.storage.upload).toBeDefined();
247
+ expect(microservice.storage.download).toBeDefined();
248
+ expect(microservice.storage.delete).toBeDefined();
249
+ expect(microservice.storage.list).toBeDefined();
250
+ });
251
+
252
+ it('should handle storage operations', async () => {
253
+ const microservice = hub.createMicroservice(mockConfig);
254
+
255
+ const buffer = Buffer.from('test data');
256
+ const result = await microservice.storage.upload('test-bucket', 'test-file', buffer);
257
+ expect(result).toBeDefined();
258
+ expect(result.etag).toBe('123');
259
+
260
+ const downloaded = await microservice.storage.download('test-bucket', 'test-file');
261
+ expect(downloaded).toBeDefined();
262
+ expect(Buffer.isBuffer(downloaded)).toBe(true);
263
+
264
+ await expect(microservice.storage.delete('test-bucket', 'test-file')).resolves.not.toThrow();
265
+
266
+ const list = await microservice.storage.list('test-bucket');
267
+ expect(Array.isArray(list)).toBe(true);
268
+ });
269
+ });
270
+
271
+ describe('Cookbook Integration', () => {
272
+ it('should provide cookbook functions', () => {
273
+ expect(hub.validateCookbook).toBeDefined();
274
+ expect(hub.parseCookbook).toBeDefined();
275
+ expect(hub.executeCookbook).toBeDefined();
276
+ });
277
+
278
+ it('should handle cookbook operations', () => {
279
+ const cookbook = {
280
+ version: '1.0',
281
+ name: 'test-cookbook',
282
+ steps: []
283
+ };
284
+
285
+ expect(() => hub.validateCookbook(cookbook)).not.toThrow();
286
+ expect(() => hub.parseCookbook('yaml content')).not.toThrow();
287
+ expect(() => hub.executeCookbook(cookbook, {})).not.toThrow();
288
+ });
289
+ });
290
+
291
+ describe('Complete Microservice Lifecycle', () => {
292
+ it('should initialize, operate and cleanup microservice', async () => {
293
+ const microservice = hub.createMicroservice(mockConfig);
294
+
295
+ // Initialize
296
+ await microservice.mq.connect();
297
+ await microservice.registry.register({
298
+ name: mockConfig.serviceName,
299
+ version: mockConfig.version
300
+ });
301
+
302
+ // Operate
303
+ microservice.logger.info('Service started');
304
+ await microservice.mq.publish('events', { type: 'started' });
305
+
306
+ // Cleanup
307
+ await microservice.registry.deregister();
308
+ await microservice.mq.close();
309
+
310
+ if (microservice.logger.close) {
311
+ await microservice.logger.close();
312
+ }
313
+
314
+ expect(true).toBe(true); // Test completed without errors
315
+ });
316
+
317
+ it('should handle errors during lifecycle', async () => {
318
+ const microservice = hub.createMicroservice(mockConfig);
319
+
320
+ // Simulate connection error
321
+ microservice.mq.connect = jest.fn().mockRejectedValue(new Error('Connection failed'));
322
+
323
+ await expect(microservice.mq.connect()).rejects.toThrow('Connection failed');
324
+
325
+ // Should still be able to use logger
326
+ expect(() => {
327
+ microservice.logger.error('Connection failed');
328
+ }).not.toThrow();
329
+ });
330
+ });
331
+
332
+ describe('Environment-specific Configuration', () => {
333
+ it('should adapt to development environment', () => {
334
+ const devConfig = {
335
+ ...mockConfig,
336
+ environment: 'development',
337
+ logger: {
338
+ level: 'debug',
339
+ pretty: true
340
+ }
341
+ };
342
+
343
+ const microservice = hub.createMicroservice(devConfig);
344
+
345
+ expect(microservice.logger).toBeDefined();
346
+ expect(microservice.logger.debug).toBeDefined();
347
+ });
348
+
349
+ it('should adapt to production environment', () => {
350
+ const prodConfig = {
351
+ ...mockConfig,
352
+ environment: 'production',
353
+ logger: {
354
+ level: 'error',
355
+ json: true
356
+ }
357
+ };
358
+
359
+ const microservice = hub.createMicroservice(prodConfig);
360
+
361
+ expect(microservice.logger).toBeDefined();
362
+ expect(microservice.logger.error).toBeDefined();
363
+ });
364
+ });
365
+
366
+ describe('Error Handling', () => {
367
+ it('should handle missing configuration gracefully', () => {
368
+ const invalidConfig = {
369
+ // Missing serviceName
370
+ version: '1.0.0'
371
+ };
372
+
373
+ const microservice = hub.createMicroservice(invalidConfig);
374
+
375
+ // Should still create microservice but with limited functionality
376
+ expect(microservice).toBeDefined();
377
+ expect(microservice.logger).toBeDefined();
378
+ });
379
+
380
+ it('should handle null configuration sections', () => {
381
+ const configWithNulls = {
382
+ serviceName: 'test',
383
+ version: '1.0.0',
384
+ registry: null,
385
+ mq: null,
386
+ storage: null
387
+ };
388
+
389
+ const microservice = hub.createMicroservice(configWithNulls);
390
+
391
+ expect(microservice.logger).toBeDefined();
392
+ expect(microservice.registry).toBeNull();
393
+ expect(microservice.mq).toBeNull();
394
+ expect(microservice.storage).toBeNull();
395
+ });
396
+ });
397
+
398
+ describe('Utility Functions', () => {
399
+ it('should provide convenience methods', async () => {
400
+ const microservice = hub.createMicroservice(mockConfig);
401
+
402
+ // Test start method if available
403
+ if (microservice.start) {
404
+ await expect(microservice.start()).resolves.not.toThrow();
405
+ }
406
+
407
+ // Test stop method if available
408
+ if (microservice.stop) {
409
+ await expect(microservice.stop()).resolves.not.toThrow();
410
+ }
411
+ });
412
+ });
413
+ });