From db66687f48c543fcc3fb387151b1d7ef2315907e Mon Sep 17 00:00:00 2001 From: Bojan Kucera Date: Sat, 7 Jun 2025 11:44:45 -0400 Subject: [PATCH] trying to fix tests --- libs/logger/src/logger.ts | 6 +- libs/logger/test/advanced.test.ts | 192 ++++++++++++++++++++++-------- libs/logger/test/setup.ts | 25 ++-- 3 files changed, 159 insertions(+), 64 deletions(-) diff --git a/libs/logger/src/logger.ts b/libs/logger/src/logger.ts index 24f256c..19df2e6 100644 --- a/libs/logger/src/logger.ts +++ b/libs/logger/src/logger.ts @@ -176,12 +176,14 @@ export class Logger { message: String(error) }; } - /** * Create child logger with additional context */ child(context: LogContext): Logger { - return new Logger((this.pino.bindings() as any).service, { ...this.context, ...context }); + const childLogger = new Logger((this.pino.bindings() as any).service, { ...this.context, ...context }); + // Use the pino child logger to properly propagate context + childLogger.pino = this.pino.child(context); + return childLogger; } } diff --git a/libs/logger/test/advanced.test.ts b/libs/logger/test/advanced.test.ts index 58f5cf5..2eff35d 100644 --- a/libs/logger/test/advanced.test.ts +++ b/libs/logger/test/advanced.test.ts @@ -1,102 +1,198 @@ /** * Advanced Logger Tests * - * Tests for advanced logger functionality. + * Tests for advanced logger functionality including complex metadata handling, + * child loggers, and advanced error scenarios. */ import { describe, it, expect, beforeEach, afterEach } from 'bun:test'; -import { Logger, getLogger } from '../src'; +import { Logger } from '../src'; import { loggerTestHelpers } from './setup'; -describe('Advanced Logger Tests', () => { +describe('Advanced Logger Features', () => { let logger: Logger; - beforeEach(() => { loggerTestHelpers.clearCapturedLogs(); - logger = loggerTestHelpers.createTestLogger('advanced-test'); + logger = loggerTestHelpers.createTestLogger('advanced-features'); }); afterEach(() => { loggerTestHelpers.clearCapturedLogs(); + // Force garbage collection to clean up any potential circular references + if (global.gc) { + global.gc(); + } }); - describe('Basic Logging', () => { - it('should create logger instances', () => { - expect(logger).toBeDefined(); - expect(typeof logger.info).toBe('function'); - expect(typeof logger.error).toBe('function'); - expect(typeof logger.warn).toBe('function'); - expect(typeof logger.debug).toBe('function'); - }); - - it('should log simple messages', () => { - logger.info('Test message'); + describe('Complex Metadata Handling', () => { + it('should handle nested metadata objects', () => { + const complexMetadata = { + user: { id: '123', name: 'John Doe' }, + session: { id: 'sess-456', timeout: 3600 }, + request: { method: 'POST', path: '/api/test' } + }; + + logger.info('Complex operation', complexMetadata); const logs = loggerTestHelpers.getCapturedLogs(); expect(logs.length).toBe(1); - expect(logs[0].msg).toBe('Test message'); - expect(logs[0].level).toBe('info'); - }); it('should include service name in logs', () => { - logger.info('Service test'); + expect(logs[0].user).toEqual({ id: '123', name: 'John Doe' }); + expect(logs[0].session).toEqual({ id: 'sess-456', timeout: 3600 }); + expect(logs[0].request).toEqual({ method: 'POST', path: '/api/test' }); + }); + + it('should handle arrays in metadata', () => { + const arrayMetadata = { + tags: ['user', 'authentication', 'success'], + ids: [1, 2, 3, 4] + }; + + logger.info('Array metadata test', arrayMetadata); const logs = loggerTestHelpers.getCapturedLogs(); expect(logs.length).toBe(1); - expect(logs[0].service).toBe('advanced-test'); + expect(logs[0].tags).toEqual(['user', 'authentication', 'success']); + expect(logs[0].ids).toEqual([1, 2, 3, 4]); }); - it('should handle multiple log levels', () => { - logger.debug('Debug level'); - logger.info('Info level'); - logger.warn('Warn level'); - logger.error('Error level'); + it('should handle null and undefined metadata values', () => { + const nullMetadata = { + nullValue: null, + undefinedValue: undefined, + emptyString: '', + zeroValue: 0 + }; + + logger.info('Null metadata test', nullMetadata); const logs = loggerTestHelpers.getCapturedLogs(); - expect(logs.length).toBe(4); - - const levels = logs.map(log => log.level); - expect(levels).toEqual(['debug', 'info', 'warn', 'error']); + expect(logs.length).toBe(1); + expect(logs[0].nullValue).toBe(null); + expect(logs[0].emptyString).toBe(''); + expect(logs[0].zeroValue).toBe(0); }); }); - describe('Context and Metadata', () => { - it('should include metadata in logs', () => { - const metadata = { userId: '123', action: 'test' }; - logger.info('User action', metadata); + describe('Child Logger Functionality', () => { + it('should create child logger with additional context', () => { + const childLogger = logger.child({ + component: 'auth-service', + version: '1.2.3' + }); + + childLogger.info('Child logger message'); const logs = loggerTestHelpers.getCapturedLogs(); expect(logs.length).toBe(1); - expect(logs[0].userId).toBe('123'); - expect(logs[0].action).toBe('test'); + expect(logs[0].component).toBe('auth-service'); + expect(logs[0].version).toBe('1.2.3'); + expect(logs[0].msg).toBe('Child logger message'); }); - it('should support child loggers', () => { - const childLogger = logger.child({ requestId: 'req-456' }); - childLogger.info('Child log message'); + it('should support nested child loggers', () => { + const childLogger = logger.child({ level1: 'parent' }); + const grandChildLogger = childLogger.child({ level2: 'child' }); + + grandChildLogger.warn('Nested child message'); const logs = loggerTestHelpers.getCapturedLogs(); expect(logs.length).toBe(1); - expect(logs[0].msg).toBe('Child log message'); + expect(logs[0].level1).toBe('parent'); + expect(logs[0].level2).toBe('child'); + expect(logs[0].level).toBe('warn'); + }); + + it('should merge child context with log metadata', () => { + const childLogger = logger.child({ service: 'api' }); + + childLogger.info('Request processed', { + requestId: 'req-789', + duration: 150 + }); + + const logs = loggerTestHelpers.getCapturedLogs(); + expect(logs.length).toBe(1); + expect(logs[0].service).toBe('api'); + expect(logs[0].requestId).toBe('req-789'); + expect(logs[0].duration).toBe(150); }); }); - describe('Error Logging', () => { - it('should log errors with error metadata', () => { - const error = new Error('Test error'); - logger.error('Error occurred', { error }); + describe('Advanced Error Handling', () => { + it('should handle Error objects with custom properties', () => { + const customError = new Error('Custom error message'); + (customError as any).code = 'ERR_CUSTOM'; + (customError as any).statusCode = 500; + + logger.error('Custom error occurred', { error: customError }); const logs = loggerTestHelpers.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].level).toBe('error'); - expect(logs[0].msg).toBe('Error occurred'); + expect(logs[0].msg).toBe('Custom error occurred'); }); - it('should handle error-like objects', () => { - const errorLike = { name: 'CustomError', message: 'Custom message' }; - logger.error('Custom error', { error: errorLike }); + it('should handle multiple errors in metadata', () => { + const error1 = new Error('First error'); + const error2 = new Error('Second error'); + + logger.error('Multiple errors', { + primaryError: error1, + secondaryError: error2, + context: 'batch processing' + }); + + const logs = loggerTestHelpers.getCapturedLogs(); + expect(logs.length).toBe(1); + expect(logs[0].context).toBe('batch processing'); + }); it('should handle error objects with circular references', () => { + const errorWithCircular: any = { name: 'CircularError', message: 'Circular reference error' }; + // Create a simple circular reference + errorWithCircular.self = errorWithCircular; + + // Should not throw when logging circular references + expect(() => { + logger.error('Circular error test', { error: errorWithCircular }); + }).not.toThrow(); const logs = loggerTestHelpers.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].level).toBe('error'); }); }); + describe('Performance and Edge Cases', () => { + it('should handle moderate metadata objects', () => { + const moderateMetadata: any = {}; + for (let i = 0; i < 10; i++) { + moderateMetadata[`key${i}`] = `value${i}`; + } + + logger.debug('Moderate metadata test', moderateMetadata); + + const logs = loggerTestHelpers.getCapturedLogs(); + expect(logs.length).toBe(1); + expect(logs[0].key0).toBe('value0'); + expect(logs[0].key9).toBe('value9'); + }); + + it('should handle special characters in messages', () => { + const specialMessage = 'Special chars: 🚀 ñ ü'; + + logger.info(specialMessage); + + const logs = loggerTestHelpers.getCapturedLogs(); + expect(logs.length).toBe(1); + expect(logs[0].msg).toBe(specialMessage); + }); + + it('should handle empty and whitespace-only messages', () => { + logger.info(''); + logger.info(' '); + + const logs = loggerTestHelpers.getCapturedLogs(); + expect(logs.length).toBe(2); + expect(logs[0].msg).toBe(''); + expect(logs[1].msg).toBe(' '); + }); + }); }); diff --git a/libs/logger/test/setup.ts b/libs/logger/test/setup.ts index 2aa646d..be79ee6 100644 --- a/libs/logger/test/setup.ts +++ b/libs/logger/test/setup.ts @@ -21,25 +21,22 @@ const originalConsole = { let capturedLogs: any[] = []; // Create a test logger helper -export const loggerTestHelpers = { - /** +export const loggerTestHelpers = { /** * Create a test logger instance that captures logs instead of outputting them - */ createTestLogger: (serviceName: string = 'test-service') => { + */ createTestLogger: (serviceName: string = 'test-service', context: any = {}) => { // Create a fully mocked Logger instance without using the real Logger class const logger = { serviceName, - context: {}, - debug: (msg: string, metadata?: any) => capturedLogs.push({ level: 'debug', msg, service: serviceName, ...metadata }), - info: (msg: string, metadata?: any) => capturedLogs.push({ level: 'info', msg, service: serviceName, ...metadata }), - warn: (msg: string, metadata?: any) => capturedLogs.push({ level: 'warn', msg, service: serviceName, ...metadata }), - error: (msg: string, metadata?: any) => capturedLogs.push({ level: 'error', msg, service: serviceName, ...metadata }), - http: (msg: string, metadata?: any) => capturedLogs.push({ level: 'http', msg, service: serviceName, ...metadata }), - verbose: (msg: string, metadata?: any) => capturedLogs.push({ level: 'verbose', msg, service: serviceName, ...metadata }), - silly: (msg: string, metadata?: any) => capturedLogs.push({ level: 'silly', msg, service: serviceName, ...metadata }), + context, + debug: (msg: string, metadata?: any) => capturedLogs.push({ level: 'debug', msg, service: serviceName, ...context, ...metadata }), + info: (msg: string, metadata?: any) => capturedLogs.push({ level: 'info', msg, service: serviceName, ...context, ...metadata }), + warn: (msg: string, metadata?: any) => capturedLogs.push({ level: 'warn', msg, service: serviceName, ...context, ...metadata }), + error: (msg: string, metadata?: any) => capturedLogs.push({ level: 'error', msg, service: serviceName, ...context, ...metadata }), + http: (msg: string, metadata?: any) => capturedLogs.push({ level: 'http', msg, service: serviceName, ...context, ...metadata }), + verbose: (msg: string, metadata?: any) => capturedLogs.push({ level: 'verbose', msg, service: serviceName, ...context, ...metadata }), + silly: (msg: string, metadata?: any) => capturedLogs.push({ level: 'silly', msg, service: serviceName, ...context, ...metadata }), child: (childContext: any) => { - const childLogger = loggerTestHelpers.createTestLogger(serviceName); - (childLogger as any).context = { ...(childLogger as any).context, ...childContext }; - return childLogger; + return loggerTestHelpers.createTestLogger(serviceName, { ...context, ...childContext }); } };