@onlineapps/conn-base-monitoring 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,460 @@
1
+ /**
2
+ * Component tests for conn-base-monitoring integration
3
+ */
4
+
5
+ // Mock monitoring-core for component tests
6
+ jest.mock('@onlineapps/monitoring-core', () => {
7
+ // Create a fresh Map for each test to avoid state pollution
8
+ let mockWorkflows;
9
+
10
+ const createMockCore = async (config) => {
11
+ mockWorkflows = new Map();
12
+
13
+ return {
14
+ config,
15
+ logger: {
16
+ info: jest.fn(),
17
+ error: jest.fn(),
18
+ warn: jest.fn(),
19
+ debug: jest.fn(),
20
+ log: jest.fn()
21
+ },
22
+ workflow: {
23
+ start: jest.fn((id, type) => {
24
+ mockWorkflows.set(id, { type, steps: [] });
25
+ return id;
26
+ }),
27
+ step: jest.fn((id, name) => {
28
+ const wf = mockWorkflows.get(id);
29
+ if (wf) wf.steps.push(name);
30
+ }),
31
+ end: jest.fn((id) => {
32
+ mockWorkflows.delete(id);
33
+ }),
34
+ error: jest.fn((id, error, attrs) => {
35
+ // Handle error workflow
36
+ }),
37
+ activeWorkflows: mockWorkflows,
38
+ getWorkflow: jest.fn((id) => mockWorkflows.get(id))
39
+ },
40
+ createCounter: jest.fn(() => ({ add: jest.fn() })),
41
+ createHistogram: jest.fn(() => ({ record: jest.fn() })),
42
+ createMetric: jest.fn(() => ({ add: jest.fn(), record: jest.fn() })),
43
+ shutdown: jest.fn()
44
+ };
45
+ };
46
+
47
+ return {
48
+ init: jest.fn(createMockCore),
49
+ getInstance: jest.fn(() => null),
50
+ createLogger: jest.fn((config) => ({
51
+ info: jest.fn(),
52
+ error: jest.fn(),
53
+ warn: jest.fn(),
54
+ debug: jest.fn()
55
+ })),
56
+ WorkflowTracker: jest.fn().mockImplementation((name) => ({
57
+ serviceName: name,
58
+ start: jest.fn(),
59
+ activeWorkflows: new Map()
60
+ }))
61
+ };
62
+ });
63
+
64
+ const { init } = require('../../src');
65
+
66
+ describe('conn-base-monitoring Component Tests', () => {
67
+ let monitoring;
68
+
69
+ beforeAll(() => {
70
+ process.env.MONITORING_MODE = 'off';
71
+ });
72
+
73
+ afterEach(async () => {
74
+ if (monitoring && monitoring.shutdown) {
75
+ await monitoring.shutdown();
76
+ }
77
+ });
78
+
79
+ describe('Full Workflow Lifecycle', () => {
80
+ beforeEach(async () => {
81
+ monitoring = await init({
82
+ serviceName: 'component-test',
83
+ serviceVersion: '1.0.0',
84
+ mode: 'off',
85
+ logLevel: 'DEBUG'
86
+ });
87
+ });
88
+
89
+ test('should handle complete workflow with logging', () => {
90
+ const workflowId = 'comp-wf-1';
91
+
92
+ // Start workflow
93
+ monitoring.info(`Starting workflow ${workflowId}`);
94
+ monitoring.startWorkflow(workflowId, 'data-processing', {
95
+ source: 'api',
96
+ userId: 'user-123'
97
+ });
98
+
99
+ // Process steps
100
+ monitoring.stepWorkflow(workflowId, 'validation', { status: 'validating' });
101
+ monitoring.debug('Validating input data', { workflowId, records: 100 });
102
+
103
+ monitoring.stepWorkflow(workflowId, 'transformation', { status: 'transforming' });
104
+ monitoring.info('Transforming data', { workflowId, format: 'json' });
105
+
106
+ monitoring.stepWorkflow(workflowId, 'persistence', { status: 'saving' });
107
+ monitoring.debug('Saving to database', { workflowId, table: 'results' });
108
+
109
+ // Complete workflow
110
+ monitoring.endWorkflow(workflowId, 'success', {
111
+ duration: 1500,
112
+ recordsProcessed: 100
113
+ });
114
+ monitoring.info(`Workflow ${workflowId} completed successfully`);
115
+
116
+ // Workflow completed successfully - we trust internal tracking
117
+ expect(monitoring.workflow.activeWorkflows.has(workflowId)).toBe(false);
118
+ });
119
+
120
+ test('should handle workflow with errors and recovery', () => {
121
+ const workflowId = 'error-wf-1';
122
+
123
+ monitoring.startWorkflow(workflowId, 'risky-operation');
124
+ monitoring.info('Starting risky operation', { workflowId });
125
+
126
+ // Simulate error
127
+ const error = new Error('Database connection failed');
128
+ monitoring.errorWorkflow(workflowId, error, {
129
+ retryCount: 1,
130
+ maxRetries: 3
131
+ });
132
+ monitoring.error('Operation failed, retrying', {
133
+ workflowId,
134
+ error: error.message
135
+ });
136
+
137
+ // Retry
138
+ monitoring.stepWorkflow(workflowId, 'retry-1', { attempt: 2 });
139
+ monitoring.warn('Retrying operation', { workflowId, attempt: 2 });
140
+
141
+ // Success after retry
142
+ monitoring.endWorkflow(workflowId, 'recovered', {
143
+ totalAttempts: 2
144
+ });
145
+ monitoring.info('Operation recovered after retry', { workflowId });
146
+
147
+ expect(monitoring.workflow.activeWorkflows.has(workflowId)).toBe(false);
148
+ });
149
+ });
150
+
151
+ describe('Metrics with Workflow Tracking', () => {
152
+ beforeEach(async () => {
153
+ monitoring = await init({
154
+ serviceName: 'metrics-workflow',
155
+ mode: 'light' // Enable metrics
156
+ });
157
+ });
158
+
159
+ test('should track metrics during workflow execution', () => {
160
+ const requestCounter = monitoring.createCounter('api.requests', {
161
+ description: 'API request count'
162
+ });
163
+
164
+ const latencyHistogram = monitoring.createHistogram('api.latency', {
165
+ description: 'API latency in ms',
166
+ unit: 'ms'
167
+ });
168
+
169
+ // Simulate multiple API calls
170
+ const workflows = [];
171
+ for (let i = 0; i < 5; i++) {
172
+ const wfId = `metric-wf-${i}`;
173
+ workflows.push(wfId);
174
+
175
+ monitoring.startWorkflow(wfId, 'api-call');
176
+ requestCounter.add(1, { endpoint: '/api/data', method: 'GET' });
177
+
178
+ // Simulate varying latencies
179
+ const latency = 100 + Math.random() * 200;
180
+ latencyHistogram.record(latency, { endpoint: '/api/data' });
181
+
182
+ monitoring.endWorkflow(wfId, 'success');
183
+ }
184
+
185
+ // All workflows should be completed
186
+ workflows.forEach(wfId => {
187
+ expect(monitoring.workflow.activeWorkflows.has(wfId)).toBe(false);
188
+ });
189
+ });
190
+
191
+ test('should handle error metrics', () => {
192
+ const errorCounter = monitoring.createCounter('errors.total', {
193
+ description: 'Total errors'
194
+ });
195
+
196
+ const errors = [
197
+ { type: 'ValidationError', code: 400 },
198
+ { type: 'DatabaseError', code: 500 },
199
+ { type: 'TimeoutError', code: 408 }
200
+ ];
201
+
202
+ errors.forEach((err, index) => {
203
+ const wfId = `error-metric-${index}`;
204
+ monitoring.startWorkflow(wfId, 'operation');
205
+
206
+ errorCounter.add(1, {
207
+ errorType: err.type,
208
+ statusCode: err.code
209
+ });
210
+
211
+ monitoring.error(`${err.type} occurred`, {
212
+ workflowId: wfId,
213
+ code: err.code
214
+ });
215
+
216
+ monitoring.errorWorkflow(wfId, new Error(err.type), {
217
+ statusCode: err.code
218
+ });
219
+
220
+ monitoring.endWorkflow(wfId, 'failed');
221
+ });
222
+
223
+ expect(monitoring.workflow.activeWorkflows.size).toBe(0);
224
+ });
225
+ });
226
+
227
+ describe('Concurrent Operations', () => {
228
+ beforeEach(async () => {
229
+ monitoring = await init({
230
+ serviceName: 'concurrent-test',
231
+ mode: 'off'
232
+ });
233
+ });
234
+
235
+ test('should handle multiple concurrent workflows', () => {
236
+ const workflows = [];
237
+ const concurrentCount = 10;
238
+
239
+ // Start multiple workflows
240
+ for (let i = 0; i < concurrentCount; i++) {
241
+ const wfId = `concurrent-${i}`;
242
+ workflows.push(wfId);
243
+ monitoring.startWorkflow(wfId, 'parallel-process');
244
+ }
245
+
246
+ expect(monitoring.workflow.activeWorkflows.size).toBe(concurrentCount);
247
+
248
+ // Process each workflow
249
+ workflows.forEach((wfId, index) => {
250
+ monitoring.stepWorkflow(wfId, 'processing', { index });
251
+ monitoring.info(`Processing workflow ${index}`, { workflowId: wfId });
252
+ });
253
+
254
+ // Complete half of them
255
+ const halfPoint = Math.floor(concurrentCount / 2);
256
+ workflows.slice(0, halfPoint).forEach(wfId => {
257
+ monitoring.endWorkflow(wfId, 'completed');
258
+ });
259
+
260
+ expect(monitoring.workflow.activeWorkflows.size).toBe(concurrentCount - halfPoint);
261
+
262
+ // Complete the rest
263
+ workflows.slice(halfPoint).forEach(wfId => {
264
+ monitoring.endWorkflow(wfId, 'completed');
265
+ });
266
+
267
+ expect(monitoring.workflow.activeWorkflows.size).toBe(0);
268
+ });
269
+
270
+ test('should handle rapid fire logging', () => {
271
+ const messageCount = 100;
272
+
273
+ expect(() => {
274
+ for (let i = 0; i < messageCount; i++) {
275
+ const level = ['debug', 'info', 'warn', 'error'][i % 4];
276
+ monitoring[level](`Message ${i}`, { index: i, level });
277
+ }
278
+ }).not.toThrow();
279
+ });
280
+ });
281
+
282
+ describe('Configuration Modes', () => {
283
+ test('should work in off mode (no external connections)', async () => {
284
+ const offMode = await init({
285
+ serviceName: 'off-mode-test',
286
+ mode: 'off'
287
+ });
288
+
289
+ expect(() => {
290
+ offMode.info('This should only console log');
291
+ offMode.startWorkflow('wf-off', 'test');
292
+ offMode.endWorkflow('wf-off', 'done');
293
+ }).not.toThrow();
294
+
295
+ await offMode.shutdown();
296
+ });
297
+
298
+ test('should work in light mode (basic telemetry)', async () => {
299
+ const lightMode = await init({
300
+ serviceName: 'light-mode-test',
301
+ mode: 'light'
302
+ });
303
+
304
+ const counter = lightMode.createCounter('light.counter');
305
+ expect(() => {
306
+ counter.add(1);
307
+ lightMode.info('Light mode active');
308
+ }).not.toThrow();
309
+
310
+ await lightMode.shutdown();
311
+ });
312
+
313
+ test('should work in full mode (complete telemetry)', async () => {
314
+ const fullMode = await init({
315
+ serviceName: 'full-mode-test',
316
+ mode: 'full'
317
+ });
318
+
319
+ expect(() => {
320
+ fullMode.startWorkflow('full-wf', 'complete');
321
+ fullMode.stepWorkflow('full-wf', 'step1');
322
+ fullMode.endWorkflow('full-wf', 'done');
323
+
324
+ const histogram = fullMode.createHistogram('full.histogram');
325
+ histogram.record(123.45);
326
+ }).not.toThrow();
327
+
328
+ await fullMode.shutdown();
329
+ });
330
+
331
+ test('should work in debug mode (verbose output)', async () => {
332
+ const debugMode = await init({
333
+ serviceName: 'debug-mode-test',
334
+ mode: 'debug',
335
+ logLevel: 'DEBUG'
336
+ });
337
+
338
+ expect(() => {
339
+ debugMode.debug('Debug details', { verbose: true });
340
+ debugMode.info('Info message');
341
+ debugMode.warn('Warning');
342
+ debugMode.error('Error');
343
+ }).not.toThrow();
344
+
345
+ await debugMode.shutdown();
346
+ });
347
+ });
348
+
349
+ describe('Error Handling and Edge Cases', () => {
350
+ beforeEach(async () => {
351
+ monitoring = await init({
352
+ serviceName: 'edge-cases',
353
+ mode: 'off'
354
+ });
355
+ });
356
+
357
+ test('should handle null and undefined values', () => {
358
+ expect(() => {
359
+ monitoring.info(null);
360
+ monitoring.info(undefined);
361
+ monitoring.info('Message', null);
362
+ monitoring.info('Message', undefined);
363
+ }).not.toThrow();
364
+ });
365
+
366
+ test('should handle circular references', () => {
367
+ const obj = { name: 'circular' };
368
+ obj.self = obj;
369
+
370
+ expect(() => {
371
+ monitoring.info('Circular ref', obj);
372
+ }).not.toThrow();
373
+ });
374
+
375
+ test('should handle very long messages', () => {
376
+ const longMessage = 'x'.repeat(10000);
377
+ const longData = {
378
+ field: 'y'.repeat(10000)
379
+ };
380
+
381
+ expect(() => {
382
+ monitoring.info(longMessage, longData);
383
+ }).not.toThrow();
384
+ });
385
+
386
+ test('should handle special characters', () => {
387
+ expect(() => {
388
+ monitoring.info('Unicode: 你好 مرحبا שלום', {
389
+ emoji: '🚀🎉😀',
390
+ special: '\n\t\r\0'
391
+ });
392
+ }).not.toThrow();
393
+ });
394
+
395
+ test('should handle workflow with missing steps', () => {
396
+ const wfId = 'incomplete-wf';
397
+
398
+ monitoring.startWorkflow(wfId, 'test');
399
+ // Skip steps, go directly to end
400
+ monitoring.endWorkflow(wfId, 'skipped');
401
+
402
+ expect(monitoring.workflow.activeWorkflows.has(wfId)).toBe(false);
403
+ });
404
+
405
+ test('should handle double workflow start', () => {
406
+ const wfId = 'double-start';
407
+
408
+ monitoring.startWorkflow(wfId, 'first');
409
+ monitoring.startWorkflow(wfId, 'second'); // Should replace
410
+
411
+ const workflow = monitoring.workflow.getWorkflow(wfId);
412
+ expect(workflow.type).toBe('second');
413
+
414
+ monitoring.endWorkflow(wfId, 'done');
415
+ });
416
+ });
417
+
418
+ describe('Memory and Resource Management', () => {
419
+ beforeEach(async () => {
420
+ // Create fresh monitoring instance for memory tests
421
+ monitoring = await init({
422
+ serviceName: 'memory-test',
423
+ mode: 'off'
424
+ });
425
+ });
426
+
427
+ test('should not leak memory with many workflows', async () => {
428
+ const iterations = 100;
429
+
430
+ for (let i = 0; i < iterations; i++) {
431
+ const wfId = `mem-test-${i}`;
432
+ monitoring.startWorkflow(wfId, 'memory-test');
433
+ monitoring.stepWorkflow(wfId, 'process');
434
+ monitoring.endWorkflow(wfId, 'done');
435
+ }
436
+
437
+ expect(monitoring.workflow.activeWorkflows.size).toBe(0);
438
+ });
439
+
440
+ test('should handle rapid instance creation', async () => {
441
+ const instances = [];
442
+
443
+ for (let i = 0; i < 10; i++) {
444
+ const instance = await init({
445
+ serviceName: `rapid-${i}`,
446
+ mode: 'off'
447
+ });
448
+ instances.push(instance);
449
+ }
450
+
451
+ // All instances should be independent
452
+ expect(instances[0]).not.toBe(instances[1]);
453
+
454
+ // Cleanup
455
+ for (const instance of instances) {
456
+ await instance.shutdown();
457
+ }
458
+ });
459
+ });
460
+ });