@adaas/a-utils 0.1.15 → 0.1.17

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,520 @@
1
+ /**
2
+ * A-Logger Component Tests
3
+ *
4
+ * Comprehensive test suite for the A_Logger component covering:
5
+ * - Basic logging functionality
6
+ * - Scope formatting and alignment
7
+ * - Error handling and formatting
8
+ * - Object and data type logging
9
+ * - Color support
10
+ * - Log level filtering
11
+ */
12
+
13
+ import { A_Scope, A_Error } from "@adaas/a-concept";
14
+ import { A_Logger } from "../src/lib/A-Logger/A-Logger.component";
15
+ import { A_Config } from "../src/lib/A-Config/A-Config.context";
16
+ import { A_LOGGER_COLORS } from "../src/lib/A-Logger/A-Logger.constants";
17
+
18
+ // Mock console methods for testing
19
+ const originalConsoleLog = console.log;
20
+ let capturedLogs: string[] = [];
21
+
22
+ function mockConsole() {
23
+ console.log = (...args: any[]) => {
24
+ capturedLogs.push(args.join(' '));
25
+ };
26
+ }
27
+
28
+ function restoreConsole() {
29
+ console.log = originalConsoleLog;
30
+ }
31
+
32
+ function getCapturedLogs(): string[] {
33
+ return capturedLogs;
34
+ }
35
+
36
+ function clearCapturedLogs() {
37
+ capturedLogs = [];
38
+ }
39
+
40
+ // =============================================
41
+ // Test Suite Setup
42
+ // =============================================
43
+
44
+ describe('A_Logger Component', () => {
45
+ let scope: A_Scope;
46
+ let logger: A_Logger;
47
+
48
+ beforeEach(() => {
49
+ scope = new A_Scope({ name: 'TestScope' });
50
+ logger = new A_Logger(scope);
51
+ clearCapturedLogs();
52
+ mockConsole();
53
+ });
54
+
55
+ afterEach(() => {
56
+ restoreConsole();
57
+ });
58
+
59
+ // =============================================
60
+ // Basic Functionality Tests
61
+ // =============================================
62
+
63
+ describe('Basic Logging', () => {
64
+ test('should log simple string messages', () => {
65
+ logger.log('Test message');
66
+
67
+ const logs = getCapturedLogs();
68
+ expect(logs).toHaveLength(1);
69
+ expect(logs[0]).toContain('TestScope');
70
+ expect(logs[0]).toContain('Test message');
71
+ });
72
+
73
+ test('should log with custom colors', () => {
74
+ logger.log('green', 'Success message');
75
+
76
+ const logs = getCapturedLogs();
77
+ expect(logs[0]).toContain('Success message');
78
+ // Colors might not be shown in test environment, just verify message content
79
+ });
80
+
81
+ test('should log warning messages with yellow color', () => {
82
+ logger.warning('Warning message');
83
+
84
+ const logs = getCapturedLogs();
85
+ expect(logs[0]).toContain('Warning message');
86
+ // Colors might not be shown in test environment, just verify message content
87
+ });
88
+
89
+ test('should log error messages with red color', () => {
90
+ logger.error('Error message');
91
+
92
+ const logs = getCapturedLogs();
93
+ expect(logs[0]).toContain('Error message');
94
+ // Colors might not be shown in test environment, just verify message content
95
+ });
96
+ });
97
+
98
+ // =============================================
99
+ // Scope Formatting Tests
100
+ // =============================================
101
+
102
+ describe('Scope Formatting', () => {
103
+ test('should pad short scope names to standard length', () => {
104
+ const shortScope = new A_Scope({ name: 'API' });
105
+ const shortLogger = new A_Logger(shortScope);
106
+
107
+ shortLogger.log('Test message');
108
+
109
+ const logs = getCapturedLogs();
110
+ expect(logs[0]).toContain('API'); // Should contain the scope name
111
+ // The formatting should ensure consistent alignment
112
+ });
113
+
114
+ test('should handle long scope names', () => {
115
+ const longScope = new A_Scope({ name: 'VeryLongServiceNameThatExceedsStandardLength' });
116
+ const longLogger = new A_Logger(longScope);
117
+
118
+ longLogger.log('Test message');
119
+
120
+ const logs = getCapturedLogs();
121
+ expect(logs[0]).toContain('VeryLongServiceNameThatExceedsStandardLength');
122
+ });
123
+
124
+ test('should maintain consistent alignment across different scope lengths', () => {
125
+ const scopes = [
126
+ new A_Scope({ name: 'A' }),
127
+ new A_Scope({ name: 'MediumLengthScope' }),
128
+ new A_Scope({ name: 'VeryVeryLongScopeName' })
129
+ ];
130
+
131
+ scopes.forEach(testScope => {
132
+ const testLogger = new A_Logger(testScope);
133
+ testLogger.log('Alignment test');
134
+ });
135
+
136
+ const logs = getCapturedLogs();
137
+ expect(logs).toHaveLength(3);
138
+
139
+ // All logs should have similar structure for alignment
140
+ logs.forEach(log => {
141
+ expect(log).toContain('Alignment test');
142
+ expect(log).toMatch(/\[.*\] \|.*\|/); // Pattern: [scope] |time|
143
+ });
144
+ });
145
+ });
146
+
147
+ // =============================================
148
+ // Object Logging Tests
149
+ // =============================================
150
+
151
+ describe('Object Logging', () => {
152
+ test('should format simple objects as JSON', () => {
153
+ const testObject = { id: 1, name: 'test', active: true };
154
+
155
+ logger.log('Object data:', testObject);
156
+
157
+ const logs = getCapturedLogs();
158
+ expect(logs[0]).toContain('"id": 1');
159
+ expect(logs[0]).toContain('"name": "test"');
160
+ expect(logs[0]).toContain('"active": true');
161
+ });
162
+
163
+ test('should handle nested objects', () => {
164
+ const nestedObject = {
165
+ user: {
166
+ id: 1,
167
+ profile: {
168
+ name: 'John',
169
+ settings: { theme: 'dark' }
170
+ }
171
+ }
172
+ };
173
+
174
+ logger.log('Nested object:', nestedObject);
175
+
176
+ const logs = getCapturedLogs();
177
+ expect(logs[0]).toContain('"theme": "dark"');
178
+ });
179
+
180
+ test('should handle arrays', () => {
181
+ const testArray = [1, 2, 3, 'test'];
182
+
183
+ logger.log('Array data:', testArray);
184
+
185
+ const logs = getCapturedLogs();
186
+ expect(logs[0]).toContain('[');
187
+ expect(logs[0]).toContain('1,');
188
+ expect(logs[0]).toContain('"test"');
189
+ });
190
+
191
+ test('should handle multiple arguments', () => {
192
+ logger.log('Processing:', 'User ID:', 123, 'Status:', { active: true });
193
+
194
+ const logs = getCapturedLogs();
195
+ expect(logs[0]).toContain('Processing:');
196
+ expect(logs[0]).toContain('User ID:');
197
+ expect(logs[0]).toContain('123');
198
+ expect(logs[0]).toContain('"active": true');
199
+ });
200
+ });
201
+
202
+ // =============================================
203
+ // Error Handling Tests
204
+ // =============================================
205
+
206
+ describe('Error Handling', () => {
207
+ test('should format standard JavaScript errors', () => {
208
+ const error = new Error('Test error message');
209
+
210
+ logger.error('Error occurred:', error);
211
+
212
+ const logs = getCapturedLogs();
213
+ expect(logs[0]).toContain('Test error message');
214
+ expect(logs[0]).toContain('"name": "Error"');
215
+ });
216
+
217
+ test('should format A_Error instances with special formatting', () => {
218
+ const aError = new A_Error('Custom error message');
219
+
220
+ logger.error('A_Error occurred:', aError);
221
+
222
+ const logs = getCapturedLogs();
223
+ expect(logs[0]).toContain('Custom error message');
224
+ expect(logs[0]).toContain('-------------------------------');
225
+ });
226
+
227
+ test('should handle errors with stack traces', () => {
228
+ const error = new Error('Stack trace test');
229
+ // Ensure error has stack
230
+ if (error.stack) {
231
+ logger.error(error);
232
+
233
+ const logs = getCapturedLogs();
234
+ expect(logs[0]).toContain('Stack trace test');
235
+ }
236
+ });
237
+ });
238
+
239
+ // =============================================
240
+ // Color Support Tests
241
+ // =============================================
242
+
243
+ describe('Color Support', () => {
244
+ test('should support all defined colors', () => {
245
+ const colors: Array<keyof typeof A_LOGGER_COLORS> = [
246
+ 'green', 'blue', 'red', 'yellow', 'gray',
247
+ 'magenta', 'cyan', 'white', 'pink'
248
+ ];
249
+
250
+ colors.forEach(color => {
251
+ clearCapturedLogs();
252
+ logger.log(color, `${color} message`);
253
+
254
+ const logs = getCapturedLogs();
255
+ expect(logs[0]).toContain(`${color} message`);
256
+ // Colors might not be shown in test environment, just verify message content
257
+ });
258
+ });
259
+
260
+ test('should default to blue color when no color specified', () => {
261
+ logger.log('Default color message');
262
+
263
+ const logs = getCapturedLogs();
264
+ expect(logs[0]).toContain('Default color message');
265
+ // Colors might not be shown in test environment, just verify message content
266
+ });
267
+
268
+ test('should treat non-color first argument as message', () => {
269
+ logger.log('not-a-color', 'Second argument');
270
+
271
+ const logs = getCapturedLogs();
272
+ expect(logs[0]).toContain('not-a-color');
273
+ expect(logs[0]).toContain('Second argument');
274
+ // Colors might not be shown in test environment, just verify message content
275
+ });
276
+ });
277
+
278
+ // =============================================
279
+ // Log Level Filtering Tests
280
+ // =============================================
281
+
282
+ describe('Log Level Filtering', () => {
283
+ test('should respect debug log level (show all)', () => {
284
+ const config = { get: () => 'debug' } as any;
285
+ const debugLogger = new A_Logger(scope, config);
286
+
287
+ debugLogger.log('Debug message');
288
+ debugLogger.warning('Warning message');
289
+ debugLogger.error('Error message');
290
+
291
+ const logs = getCapturedLogs();
292
+ expect(logs).toHaveLength(3);
293
+ });
294
+
295
+ test('should respect info log level (show log, warning, error)', () => {
296
+ const config = { get: () => 'info' } as any;
297
+ const infoLogger = new A_Logger(scope, config);
298
+
299
+ infoLogger.log('Info message');
300
+ infoLogger.warning('Warning message');
301
+ infoLogger.error('Error message');
302
+
303
+ const logs = getCapturedLogs();
304
+ expect(logs).toHaveLength(3);
305
+ });
306
+
307
+ test('should respect warn log level (show warning, error only)', () => {
308
+ const config = { get: () => 'warn' } as any;
309
+ const warnLogger = new A_Logger(scope, config);
310
+
311
+ warnLogger.log('Info message');
312
+ warnLogger.warning('Warning message');
313
+ warnLogger.error('Error message');
314
+
315
+ const logs = getCapturedLogs();
316
+ expect(logs).toHaveLength(2); // Only warning and error
317
+ expect(logs[0]).toContain('Warning message');
318
+ expect(logs[1]).toContain('Error message');
319
+ });
320
+
321
+ test('should respect error log level (show error only)', () => {
322
+ const config = { get: () => 'error' } as any;
323
+ const errorLogger = new A_Logger(scope, config);
324
+
325
+ errorLogger.log('Info message');
326
+ errorLogger.warning('Warning message');
327
+ errorLogger.error('Error message');
328
+
329
+ const logs = getCapturedLogs();
330
+ expect(logs).toHaveLength(1); // Only error
331
+ expect(logs[0]).toContain('Error message');
332
+ });
333
+
334
+ test('should handle unknown log level (default to no logging)', () => {
335
+ const config = { get: () => 'unknown' } as any;
336
+ const unknownLogger = new A_Logger(scope, config);
337
+
338
+ unknownLogger.log('Info message');
339
+ unknownLogger.warning('Warning message');
340
+ unknownLogger.error('Error message');
341
+
342
+ const logs = getCapturedLogs();
343
+ expect(logs).toHaveLength(0);
344
+ });
345
+ });
346
+
347
+ // =============================================
348
+ // Timestamp Tests
349
+ // =============================================
350
+
351
+ describe('Timestamp Formatting', () => {
352
+ test('should include timestamp in log messages', () => {
353
+ logger.log('Timestamp test');
354
+
355
+ const logs = getCapturedLogs();
356
+ // Should match pattern like |12:34:567|
357
+ expect(logs[0]).toMatch(/\|\d{2}:\d{2}:\d{3}\|/);
358
+ });
359
+
360
+ test('should use consistent timestamp format', () => {
361
+ logger.log('First message');
362
+ logger.log('Second message');
363
+
364
+ const logs = getCapturedLogs();
365
+
366
+ // Extract timestamps from both logs
367
+ const timestampRegex = /\|(\d{2}:\d{2}:\d{3})\|/;
368
+ const timestamp1 = logs[0].match(timestampRegex);
369
+ const timestamp2 = logs[1].match(timestampRegex);
370
+
371
+ expect(timestamp1).not.toBeNull();
372
+ expect(timestamp2).not.toBeNull();
373
+ if (timestamp1 && timestamp2) {
374
+ expect(timestamp1[1]).toMatch(/\d{2}:\d{2}:\d{3}/);
375
+ expect(timestamp2[1]).toMatch(/\d{2}:\d{2}:\d{3}/);
376
+ }
377
+ });
378
+ });
379
+
380
+ // =============================================
381
+ // Edge Cases and Error Conditions
382
+ // =============================================
383
+
384
+ describe('Edge Cases', () => {
385
+ test('should handle null and undefined values', () => {
386
+ logger.log('Null value:', null);
387
+ logger.log('Undefined value:', undefined);
388
+
389
+ const logs = getCapturedLogs();
390
+ expect(logs[0]).toContain('null');
391
+ expect(logs[1]).toContain('undefined');
392
+ });
393
+
394
+ test('should handle circular references in objects', () => {
395
+ const circularObj: any = { name: 'test' };
396
+ circularObj.self = circularObj;
397
+
398
+ // Should not throw an error and should handle circular reference gracefully
399
+ expect(() => {
400
+ logger.log('Circular object:', circularObj);
401
+ }).not.toThrow();
402
+
403
+ const logs = getCapturedLogs();
404
+ expect(logs[0]).toContain('Circular object:');
405
+ expect(logs[0]).toContain('[Circular Reference]');
406
+ });
407
+
408
+ test('should handle very long strings', () => {
409
+ const longString = 'A'.repeat(1000);
410
+
411
+ logger.log('Long string:', longString);
412
+
413
+ const logs = getCapturedLogs();
414
+ expect(logs[0]).toContain(longString);
415
+ });
416
+
417
+ test('should handle empty scope name', () => {
418
+ const emptyScope = new A_Scope({ name: '' });
419
+ const emptyLogger = new A_Logger(emptyScope);
420
+
421
+ emptyLogger.log('Empty scope test');
422
+
423
+ const logs = getCapturedLogs();
424
+ expect(logs).toHaveLength(1);
425
+ });
426
+ });
427
+
428
+ // =============================================
429
+ // Performance Tests
430
+ // =============================================
431
+
432
+ describe('Performance', () => {
433
+ test('should handle multiple rapid log calls', () => {
434
+ const startTime = Date.now();
435
+
436
+ for (let i = 0; i < 100; i++) {
437
+ logger.log(`Message ${i}`);
438
+ }
439
+
440
+ const endTime = Date.now();
441
+ const logs = getCapturedLogs();
442
+
443
+ expect(logs).toHaveLength(100);
444
+ expect(endTime - startTime).toBeLessThan(1000); // Should complete within 1 second
445
+ });
446
+
447
+ test('should handle large objects efficiently', () => {
448
+ const largeObject = {
449
+ data: Array.from({ length: 100 }, (_, i) => ({
450
+ id: i,
451
+ name: `Item ${i}`,
452
+ metadata: { timestamp: Date.now(), active: true }
453
+ }))
454
+ };
455
+
456
+ const startTime = Date.now();
457
+ logger.log('Large object:', largeObject);
458
+ const endTime = Date.now();
459
+
460
+ const logs = getCapturedLogs();
461
+ expect(logs).toHaveLength(1);
462
+ expect(endTime - startTime).toBeLessThan(500); // Should complete within 500ms
463
+ });
464
+ });
465
+ });
466
+
467
+ // =============================================
468
+ // Integration Tests
469
+ // =============================================
470
+
471
+ describe('A_Logger Integration', () => {
472
+ beforeEach(() => {
473
+ clearCapturedLogs();
474
+ mockConsole();
475
+ });
476
+
477
+ afterEach(() => {
478
+ restoreConsole();
479
+ });
480
+
481
+ test('should work with different scope configurations', () => {
482
+ const scopes = [
483
+ new A_Scope({ name: 'Service1' }),
484
+ new A_Scope({ name: 'Service2' }),
485
+ new A_Scope({ name: 'Service3' })
486
+ ];
487
+
488
+ scopes.forEach((scope, index) => {
489
+ const logger = new A_Logger(scope);
490
+ logger.log(`Message from ${scope.name}`);
491
+ });
492
+
493
+ const logs = getCapturedLogs();
494
+ expect(logs).toHaveLength(3);
495
+
496
+ logs.forEach((log, index) => {
497
+ expect(log).toContain(`Service${index + 1}`);
498
+ expect(log).toContain(`Message from Service${index + 1}`);
499
+ });
500
+ });
501
+
502
+ test('should maintain scope isolation', () => {
503
+ const scope1 = new A_Scope({ name: 'Scope1' });
504
+ const scope2 = new A_Scope({ name: 'Scope2' });
505
+
506
+ const logger1 = new A_Logger(scope1);
507
+ const logger2 = new A_Logger(scope2);
508
+
509
+ logger1.log('Message from logger 1');
510
+ logger2.log('Message from logger 2');
511
+
512
+ const logs = getCapturedLogs();
513
+ expect(logs[0]).toContain('Scope1');
514
+ expect(logs[0]).toContain('Message from logger 1');
515
+ expect(logs[1]).toContain('Scope2');
516
+ expect(logs[1]).toContain('Message from logger 2');
517
+ });
518
+ });
519
+
520
+ export { mockConsole, restoreConsole, getCapturedLogs, clearCapturedLogs };
@@ -104,18 +104,18 @@ describe('A-Memory tests', () => {
104
104
  }>();
105
105
 
106
106
  // No values set initially
107
- expect(await memory.verifyPrerequisites(['required1', 'required2'])).toBe(false);
107
+ expect(await memory.prerequisites(['required1', 'required2'])).toBe(false);
108
108
 
109
109
  // Set one required value
110
110
  await memory.set('required1', 'value1');
111
- expect(await memory.verifyPrerequisites(['required1', 'required2'])).toBe(false);
111
+ expect(await memory.prerequisites(['required1', 'required2'])).toBe(false);
112
112
 
113
113
  // Set both required values
114
114
  await memory.set('required2', 42);
115
- expect(await memory.verifyPrerequisites(['required1', 'required2'])).toBe(true);
116
-
115
+ expect(await memory.prerequisites(['required1', 'required2'])).toBe(true);
116
+
117
117
  // Test with empty requirements
118
- expect(await memory.verifyPrerequisites([])).toBe(true);
118
+ expect(await memory.prerequisites([])).toBe(true);
119
119
  });
120
120
 
121
121
  it('Should serialize to JSON correctly', async () => {
@@ -14,7 +14,7 @@ describe('A-Polyfill Tests', () => {
14
14
  testScope = new A_Scope({
15
15
  components: [A_Polyfill, A_Logger],
16
16
  });
17
- polyfill = testScope.resolve(A_Polyfill);
17
+ polyfill = testScope.resolve(A_Polyfill)!;
18
18
  await polyfill.load();
19
19
  });
20
20
 
@@ -190,6 +190,7 @@ describe('A-Polyfill Tests', () => {
190
190
  );
191
191
 
192
192
  req.on('error', (err) => {
193
+ console.log('Request error:', err);
193
194
  reject(err);
194
195
  });
195
196
 
@@ -429,7 +430,7 @@ describe('A-Polyfill Tests', () => {
429
430
  const freshScope = new A_Scope({
430
431
  components: [A_Polyfill, A_Logger]
431
432
  });
432
- const freshPolyfill = freshScope.resolve(A_Polyfill);
433
+ const freshPolyfill = freshScope.resolve(A_Polyfill)!;
433
434
 
434
435
  // Only initialize the needed polyfills
435
436
  const crypto = await freshPolyfill.crypto();