From d836e766d5a1453df28a34c0d591dd458c500f29 Mon Sep 17 00:00:00 2001 From: Bojan Kucera Date: Sat, 7 Jun 2025 12:18:41 -0400 Subject: [PATCH] trying to figure out the issue with logger tests --- libs/config/src/logging.ts | 2 +- libs/logger/test/advanced.test.ts | 32 +++++----- libs/logger/test/basic.test.ts | 49 ++++++++------- libs/logger/test/integration.test.ts | 46 +++++++------- libs/logger/test/setup.ts | 91 +++++++++++++++++----------- 5 files changed, 127 insertions(+), 93 deletions(-) diff --git a/libs/config/src/logging.ts b/libs/config/src/logging.ts index 4c6bd94..cb8e0e3 100644 --- a/libs/config/src/logging.ts +++ b/libs/config/src/logging.ts @@ -11,7 +11,7 @@ export const loggingConfig = cleanEnv(process.env, { // Basic Logging Settings LOG_LEVEL: str({ default: 'info', - choices: ['error', 'warn', 'info', 'http', 'verbose', 'debug', 'silly'], + choices: ['debug' , 'info' , 'warn' , 'error'], desc: 'Logging level' }), LOG_FORMAT: str({ diff --git a/libs/logger/test/advanced.test.ts b/libs/logger/test/advanced.test.ts index d1e769b..e6e82bf 100644 --- a/libs/logger/test/advanced.test.ts +++ b/libs/logger/test/advanced.test.ts @@ -11,13 +11,15 @@ import { loggerTestHelpers } from './setup'; describe('Advanced Logger Features', () => { let logger: Logger; + let testLoggerInstance: ReturnType; + beforeEach(() => { - loggerTestHelpers.clearCapturedLogs(); - logger = loggerTestHelpers.createTestLogger('advanced-features'); + testLoggerInstance = loggerTestHelpers.createTestLogger('advanced-features'); + logger = testLoggerInstance.logger; }); afterEach(() => { - loggerTestHelpers.clearCapturedLogs(); + testLoggerInstance.clearCapturedLogs(); // Force garbage collection to clean up any potential circular references if (global.gc) { global.gc(); @@ -34,7 +36,7 @@ describe('Advanced Logger Features', () => { logger.info('Complex operation', complexMetadata); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].user).toEqual({ id: '123', name: 'John Doe' }); expect(logs[0].session).toEqual({ id: 'sess-456', timeout: 3600 }); @@ -49,7 +51,7 @@ describe('Advanced Logger Features', () => { logger.info('Array metadata test', arrayMetadata); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].tags).toEqual(['user', 'authentication', 'success']); expect(logs[0].ids).toEqual([1, 2, 3, 4]); @@ -65,7 +67,7 @@ describe('Advanced Logger Features', () => { logger.info('Null metadata test', nullMetadata); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].nullValue).toBe(null); expect(logs[0].emptyString).toBe(''); @@ -82,7 +84,7 @@ describe('Advanced Logger Features', () => { childLogger.info('Child logger message'); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].component).toBe('auth-service'); expect(logs[0].version).toBe('1.2.3'); @@ -95,7 +97,7 @@ describe('Advanced Logger Features', () => { grandChildLogger.warn('Nested child message'); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].level1).toBe('parent'); expect(logs[0].level2).toBe('child'); @@ -110,7 +112,7 @@ describe('Advanced Logger Features', () => { duration: 150 }); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].service).toBe('api'); expect(logs[0].requestId).toBe('req-789'); @@ -126,7 +128,7 @@ describe('Advanced Logger Features', () => { logger.error('Custom error occurred', { error: customError }); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].level).toBe('error'); expect(logs[0].msg).toBe('Custom error occurred'); @@ -142,7 +144,7 @@ describe('Advanced Logger Features', () => { context: 'batch processing' }); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].context).toBe('batch processing'); }); @@ -157,7 +159,7 @@ describe('Advanced Logger Features', () => { logger.error('Circular error test', { error: errorWithCircular }); }).not.toThrow(); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].level).toBe('error'); }); @@ -171,7 +173,7 @@ describe('Advanced Logger Features', () => { logger.debug('Moderate metadata test', moderateMetadata); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].key0).toBe('value0'); expect(logs[0].key9).toBe('value9'); @@ -182,7 +184,7 @@ describe('Advanced Logger Features', () => { logger.info(specialMessage); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].msg).toBe(specialMessage); }); @@ -191,7 +193,7 @@ describe('Advanced Logger Features', () => { logger.info(''); logger.info(' '); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(2); expect(logs[0].msg).toBe(''); expect(logs[1].msg).toBe(' '); diff --git a/libs/logger/test/basic.test.ts b/libs/logger/test/basic.test.ts index bbb13b6..824e68e 100644 --- a/libs/logger/test/basic.test.ts +++ b/libs/logger/test/basic.test.ts @@ -10,14 +10,15 @@ import { loggerTestHelpers } from './setup'; describe('Basic Logger Tests', () => { let logger: Logger; + let testLoggerInstance: ReturnType; beforeEach(() => { - loggerTestHelpers.clearCapturedLogs(); - logger = loggerTestHelpers.createTestLogger('utils-test'); + testLoggerInstance = loggerTestHelpers.createTestLogger('utils-test'); + logger = testLoggerInstance.logger; }); afterEach(() => { - loggerTestHelpers.clearCapturedLogs(); + testLoggerInstance.clearCapturedLogs(); }); describe('Logger Factory Functions', () => { @@ -26,8 +27,8 @@ describe('Basic Logger Tests', () => { // Test that getLogger doesn't throw expect(() => { - const testLogger = loggerTestHelpers.createTestLogger('factory-test'); - testLogger.info('Factory test'); + const anotherTestLoggerInstance = loggerTestHelpers.createTestLogger('factory-test'); + anotherTestLoggerInstance.logger.info('Factory test'); }).not.toThrow(); }); @@ -55,7 +56,7 @@ describe('Basic Logger Tests', () => { // Object message logger.info({ event: 'object_message', data: 'test' }); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(2); expect(logs[0].msg).toBe('String message'); expect(logs[1].level).toBe('info'); @@ -70,7 +71,7 @@ describe('Basic Logger Tests', () => { logger.info('Request processed', metadata); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].userId).toBe('user123'); expect(logs[0].sessionId).toBe('session456'); @@ -87,7 +88,7 @@ describe('Basic Logger Tests', () => { childLogger.info('Payment processed'); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].msg).toBe('Payment processed'); }); @@ -96,7 +97,7 @@ describe('Basic Logger Tests', () => { const childLogger = logger.child({ operation: 'test' }); childLogger.info('Child operation'); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].service).toBe('utils-test'); }); @@ -109,7 +110,7 @@ describe('Basic Logger Tests', () => { logger.error('Error test', error); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].level).toBe('error'); }); @@ -123,7 +124,7 @@ describe('Basic Logger Tests', () => { logger.error('Validation failed', { error: errorLike }); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].level).toBe('error'); }); @@ -131,7 +132,7 @@ describe('Basic Logger Tests', () => { it('should handle primitive error values', () => { logger.error('Simple error', { error: 'Error string' }); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].level).toBe('error'); }); @@ -144,7 +145,7 @@ describe('Basic Logger Tests', () => { logger.warn('Warn message'); logger.error('Error message'); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(4); logs.forEach(log => { @@ -153,16 +154,22 @@ describe('Basic Logger Tests', () => { }); it('should support different service names', () => { - const logger1 = loggerTestHelpers.createTestLogger('service-one'); - const logger2 = loggerTestHelpers.createTestLogger('service-two'); + const logger1Instance = loggerTestHelpers.createTestLogger('service-one'); + const logger2Instance = loggerTestHelpers.createTestLogger('service-two'); - logger1.info('Message from service one'); - logger2.info('Message from service two'); + logger1Instance.logger.info('Message from service one'); + logger2Instance.logger.info('Message from service two'); - const logs = loggerTestHelpers.getCapturedLogs(); - expect(logs.length).toBe(2); - expect(logs[0].service).toBe('service-one'); - expect(logs[1].service).toBe('service-two'); + // Since each logger instance has its own capture, we check them separately + // or combine them if that's the desired test logic. + // For this test, it seems we want to ensure they are separate. + const logs1 = logger1Instance.getCapturedLogs(); + expect(logs1.length).toBe(1); + expect(logs1[0].service).toBe('service-one'); + + const logs2 = logger2Instance.getCapturedLogs(); + expect(logs2.length).toBe(1); + expect(logs2[0].service).toBe('service-two'); }); }); }); diff --git a/libs/logger/test/integration.test.ts b/libs/logger/test/integration.test.ts index 7c669ac..ee4ad7c 100644 --- a/libs/logger/test/integration.test.ts +++ b/libs/logger/test/integration.test.ts @@ -14,15 +14,15 @@ import { loggerTestHelpers } from './setup'; describe('Logger Integration Tests', () => { let logger: Logger; + let testLoggerInstance: ReturnType; beforeEach(() => { - // Create a new test logger before each test - loggerTestHelpers.clearCapturedLogs(); - logger = loggerTestHelpers.createTestLogger('integration-test'); + testLoggerInstance = loggerTestHelpers.createTestLogger('integration-test'); + logger = testLoggerInstance.logger; }); afterEach(() => { - loggerTestHelpers.clearCapturedLogs(); + testLoggerInstance.clearCapturedLogs(); }); describe('Core Logger Functionality', () => { @@ -34,7 +34,7 @@ describe('Logger Integration Tests', () => { logger.error('Error message'); // Get captured logs - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); // Verify logs were captured expect(logs.length).toBe(4); @@ -53,7 +53,7 @@ describe('Logger Integration Tests', () => { logger.info('User logged in', { userId: '123', action: 'login' }); // Get captured logs - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); // Verify structured log expect(logs.length).toBe(1); @@ -69,7 +69,7 @@ describe('Logger Integration Tests', () => { logger.error('Something went wrong', { error: testError }); // Get captured logs - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); // Verify error was logged expect(logs.length).toBe(1); @@ -88,7 +88,7 @@ describe('Logger Integration Tests', () => { childLogger.info('Child logger test'); // Get captured logs - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); // Verify child logger logged something expect(logs.length).toBe(1); @@ -104,17 +104,19 @@ describe('Logger Integration Tests', () => { }); it('should create different logger instances', () => { - const logger1 = loggerTestHelpers.createTestLogger('service-1'); - const logger2 = loggerTestHelpers.createTestLogger('service-2'); + const logger1Instance = loggerTestHelpers.createTestLogger('service-1'); + const logger2Instance = loggerTestHelpers.createTestLogger('service-2'); - logger1.info('Message from service 1'); - logger2.info('Message from service 2'); + logger1Instance.logger.info('Message from service 1'); + logger2Instance.logger.info('Message from service 2'); - const logs = loggerTestHelpers.getCapturedLogs(); - - expect(logs.length).toBe(2); - expect(logs[0].service).toBe('service-1'); - expect(logs[1].service).toBe('service-2'); + const logs1 = logger1Instance.getCapturedLogs(); + expect(logs1.length).toBe(1); + expect(logs1[0].service).toBe('service-1'); + + const logs2 = logger2Instance.getCapturedLogs(); + expect(logs2.length).toBe(1); + expect(logs2[0].service).toBe('service-2'); }); }); @@ -125,7 +127,7 @@ describe('Logger Integration Tests', () => { logger.error('Error occurred', error); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].level).toBe('error'); expect(logs[0].msg).toBe('Error occurred'); @@ -140,7 +142,7 @@ describe('Logger Integration Tests', () => { logger.error('Custom error occurred', { error: errorLike }); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].level).toBe('error'); expect(logs[0].msg).toBe('Custom error occurred'); @@ -149,7 +151,7 @@ describe('Logger Integration Tests', () => { it('should handle primitive error values', () => { logger.error('String error occurred', { error: 'Simple string error' }); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].level).toBe('error'); expect(logs[0].msg).toBe('String error occurred'); @@ -166,7 +168,7 @@ describe('Logger Integration Tests', () => { logger.info('Operation completed', metadata); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].requestId).toBe('req-123'); expect(logs[0].userId).toBe('user-456'); @@ -182,7 +184,7 @@ describe('Logger Integration Tests', () => { logger.info(objectMessage); - const logs = loggerTestHelpers.getCapturedLogs(); + const logs = testLoggerInstance.getCapturedLogs(); expect(logs.length).toBe(1); expect(logs[0].level).toBe('info'); }); diff --git a/libs/logger/test/setup.ts b/libs/logger/test/setup.ts index be79ee6..055b802 100644 --- a/libs/logger/test/setup.ts +++ b/libs/logger/test/setup.ts @@ -5,7 +5,7 @@ * Provides utilities and mocks for testing logging operations. */ -import { Logger } from '../src'; +import { Logger, LogMetadata } from '../src'; import { afterAll, afterEach, beforeAll, beforeEach } from 'bun:test'; // Store original console methods @@ -17,43 +17,66 @@ const originalConsole = { debug: console.debug }; -// Storage for captured logs -let capturedLogs: any[] = []; - // Create a test logger helper -export const loggerTestHelpers = { /** - * Create a test logger instance that captures logs instead of outputting them - */ 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, ...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) => { - return loggerTestHelpers.createTestLogger(serviceName, { ...context, ...childContext }); +export const loggerTestHelpers = { + /** + * Create a test logger instance that captures logs locally. + */ + createTestLogger: (serviceName: string = 'test-service', initialContext: any = {}) => { + const capturedLogsArray: any[] = []; + + const createLoggerMockInstance = (currentContext: any): Logger => { + const buildLogEntry = (level: string, messageOrObject: string | object, metadata?: any) => { + const logObject: any = { level, service: serviceName, ...currentContext }; + + if (typeof messageOrObject === 'string') { + logObject.msg = messageOrObject; + if (metadata) { + Object.assign(logObject, metadata); + } + } else { // messageOrObject is an object + Object.assign(logObject, messageOrObject); // Merge fields from the message object + if (metadata) { // If metadata is also provided (e.g. logger.error({code: 1}, {extra: 'data'})) + Object.assign(logObject, metadata); + } + // Ensure 'msg' field exists, defaulting if necessary + if (typeof logObject.msg === 'undefined') { + // If the second arg was a string, it might have been intended as the message + if (typeof metadata === 'string') { + logObject.msg = metadata; + } else { + logObject.msg = 'Object logged'; + } + } + } + capturedLogsArray.push(logObject); + }; + + return { + // serviceName, // For inspection if needed, though logs capture it + context: currentContext, // For inspection + debug: (msgOrObj: string | object, meta?: any) => buildLogEntry('debug', msgOrObj, meta), + info: (msgOrObj: string | object, meta?: any) => buildLogEntry('info', msgOrObj, meta), + warn: (msgOrObj: string | object, meta?: any) => buildLogEntry('warn', msgOrObj, meta), + error: (msgOrObj: string | object, metaOrMsg?: any) => buildLogEntry('error', msgOrObj, metaOrMsg), + child: (childContext: any): Logger => { + // Children will log to the same capturedLogsArray + return createLoggerMockInstance({ ...currentContext, ...childContext }); + } + } as any; // Cast to Logger, assuming it fulfills the interface for testing purposes + }; + + const loggerInstance = createLoggerMockInstance(initialContext); + + return { + logger: loggerInstance, + getCapturedLogs: () => [...capturedLogsArray], + clearCapturedLogs: () => { + capturedLogsArray.length = 0; } }; - - return logger as unknown as Logger; }, - /** - * Get the captured logs - */ - getCapturedLogs: () => [...capturedLogs], - - /** - * Clear captured logs - */ - clearCapturedLogs: () => { - capturedLogs = []; - }, /** * Mock Loki transport */ @@ -156,7 +179,7 @@ beforeAll(() => { // Clean up after each test afterEach(() => { - loggerTestHelpers.clearCapturedLogs(); + // loggerTestHelpers.clearCapturedLogs(); // REMOVE this if it targeted a global array }); // Restore everything after tests