Files

218 lines
7.1 KiB
TypeScript

import { LoggingService, LogLevel } from '../../../src/Application/Services/LoggingService';
import { logAuth, logError, logDatabase, logStartup } from '../../../src/Application/Services/Logger';
import fs from 'fs';
import path from 'path';
describe('LoggingService', () => {
let loggingService: LoggingService;
const testLogsDir = path.join(process.cwd(), 'test-logs');
beforeEach(() => {
// Clean up any existing test logs
if (fs.existsSync(testLogsDir)) {
fs.rmSync(testLogsDir, { recursive: true, force: true });
}
// Mock environment variables for testing
process.env.MAX_LOGS_PER_FILE = '10';
process.env.MINIO_ENDPOINT = '';
loggingService = LoggingService.getInstance();
});
afterEach(() => {
// Clean up test logs
if (fs.existsSync(testLogsDir)) {
fs.rmSync(testLogsDir, { recursive: true, force: true });
}
// Clean up environment variables
delete process.env.MAX_LOGS_PER_FILE;
delete process.env.MINIO_ENDPOINT;
});
describe('Log Level Functions', () => {
it('should log authentication events', () => {
const consoleSpy = jest.spyOn(console, 'info').mockImplementation();
logAuth('Test auth message', 'user123', { action: 'login' });
expect(consoleSpy).toHaveBeenCalled();
const logCall = consoleSpy.mock.calls[0][0];
expect(logCall).toContain('[AUTH]');
expect(logCall).toContain('Test auth message');
consoleSpy.mockRestore();
});
it('should log error events with stack trace', () => {
const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
const testError = new Error('Test error message');
logError('Test error occurred', testError);
expect(consoleSpy).toHaveBeenCalled();
const logCall = consoleSpy.mock.calls[0][0];
expect(logCall).toContain('[ERROR]');
expect(logCall).toContain('Test error occurred');
consoleSpy.mockRestore();
});
it('should log database operations with timing', () => {
const consoleSpy = jest.spyOn(console, 'info').mockImplementation();
logDatabase('Query executed', 'SELECT * FROM users', 45);
expect(consoleSpy).toHaveBeenCalled();
const logCall = consoleSpy.mock.calls[0][0];
expect(logCall).toContain('[DATABASE]');
expect(logCall).toContain('Query executed');
consoleSpy.mockRestore();
});
it('should log startup events', () => {
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
logStartup('Application started', { version: '1.0.0' });
expect(consoleSpy).toHaveBeenCalled();
const logCall = consoleSpy.mock.calls[0][0];
expect(logCall).toContain('[STARTUP]');
expect(logCall).toContain('Application started');
consoleSpy.mockRestore();
});
});
describe('Log Formatting', () => {
it('should include timestamp in log entries', () => {
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
logStartup('Test message');
expect(consoleSpy).toHaveBeenCalled();
const logCall = consoleSpy.mock.calls[0][0];
// Check if timestamp is in ISO format
const timestampRegex = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/;
expect(logCall).toMatch(timestampRegex);
consoleSpy.mockRestore();
});
it('should include metadata in log entries', () => {
const consoleSpy = jest.spyOn(console, 'info').mockImplementation();
const metadata = { userId: '123', action: 'test' };
logAuth('Test with metadata', 'user123', metadata);
expect(consoleSpy).toHaveBeenCalled();
const logCall = consoleSpy.mock.calls[0][0];
expect(logCall).toContain('Meta:');
expect(logCall).toContain('"userId":"123"');
expect(logCall).toContain('"action":"test"');
consoleSpy.mockRestore();
});
});
describe('Request Logging Middleware', () => {
it('should create request logging middleware', () => {
const middleware = loggingService.requestLoggingMiddleware();
expect(typeof middleware).toBe('function');
expect(middleware.length).toBe(3); // req, res, next
});
it('should create error logging middleware', () => {
const middleware = loggingService.errorLoggingMiddleware();
expect(typeof middleware).toBe('function');
expect(middleware.length).toBe(4); // error, req, res, next
});
});
describe('Log Levels', () => {
it('should have all required log levels defined', () => {
expect(LogLevel.REQUEST).toBe('REQUEST');
expect(LogLevel.ERROR).toBe('ERROR');
expect(LogLevel.WARNING).toBe('WARNING');
expect(LogLevel.AUTH).toBe('AUTH');
expect(LogLevel.DATABASE).toBe('DATABASE');
expect(LogLevel.STARTUP).toBe('STARTUP');
expect(LogLevel.CONNECTION).toBe('CONNECTION');
expect(LogLevel.OTHER).toBe('OTHER');
});
});
describe('Singleton Pattern', () => {
it('should return the same instance', () => {
const instance1 = LoggingService.getInstance();
const instance2 = LoggingService.getInstance();
expect(instance1).toBe(instance2);
});
});
describe('File Operations', () => {
it('should handle missing Minio configuration gracefully', () => {
// Test that the service starts without Minio config
expect(() => LoggingService.getInstance()).not.toThrow();
});
it('should generate monthly directory structure', () => {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const expectedPath = path.join('logs', `${year}-${month}`);
// This tests the internal logic through the public interface
logStartup('Test for directory creation');
// Since we can't directly test the private method, we verify the service doesn't crash
expect(loggingService).toBeDefined();
});
});
describe('Error Handling', () => {
it('should handle logging errors gracefully', () => {
// Mock fs.appendFileSync to throw an error
const originalAppendFileSync = fs.appendFileSync;
const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
fs.appendFileSync = jest.fn(() => {
throw new Error('Disk full');
});
expect(() => {
logStartup('This should not crash');
}).not.toThrow();
// Restore original function
fs.appendFileSync = originalAppendFileSync;
consoleSpy.mockRestore();
});
it('should continue logging to console even if file logging fails', () => {
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
// Mock file system to fail
const originalAppendFileSync = fs.appendFileSync;
fs.appendFileSync = jest.fn(() => {
throw new Error('File system error');
});
logStartup('Test message');
// Should still log to console
expect(consoleSpy).toHaveBeenCalled();
// Restore
fs.appendFileSync = originalAppendFileSync;
consoleSpy.mockRestore();
});
});
});