Skip to main content
Glama
im47cn

Feishu Project MCP Server

by im47cn
logger.test.ts6.17 kB
import { Logger, LogLevel } from '../../utils/logger'; import * as fs from 'fs'; import * as path from 'path'; import winston from 'winston'; // Create mock types for winston interface MockWinstonTransport { filename?: string; maxFiles?: number; maxsize?: string; level?: string; } interface MockWinstonLogger { error: jest.Mock; warn: jest.Mock; info: jest.Mock; debug: jest.Mock; } // Mock winston module jest.mock('winston', () => { const mockLogger: MockWinstonLogger = { error: jest.fn(), warn: jest.fn(), info: jest.fn(), debug: jest.fn(), }; const mockFileTransport = jest.fn().mockImplementation((config: MockWinstonTransport) => ({ ...config, _type: 'file', })); const mockConsoleTransport = jest.fn().mockImplementation(() => ({ _type: 'console', })); return { createLogger: jest.fn().mockReturnValue(mockLogger), format: { combine: jest.fn(), timestamp: jest.fn(), json: jest.fn(), colorize: jest.fn(), simple: jest.fn(), }, transports: { File: mockFileTransport, Console: mockConsoleTransport, }, }; }); describe('Logger', () => { let logger: Logger; let tempDir: string; let logDir: string; let mockWinstonLogger: MockWinstonLogger; beforeEach(() => { // Create temporary test directory tempDir = fs.mkdtempSync('logger-test-'); logDir = path.join(tempDir, 'logs'); // Create logger instance logger = new Logger({ level: LogLevel.INFO, directory: logDir, }); // Get mock winston logger instance mockWinstonLogger = (winston.createLogger as jest.Mock).mock.results[0].value; }); afterEach(() => { // Clean up temporary directory fs.rmSync(tempDir, { recursive: true, force: true }); jest.clearAllMocks(); }); describe('initialization', () => { it('should create log directory if it does not exist', () => { expect(fs.existsSync(logDir)).toBe(true); }); it('should initialize with correct configuration', () => { expect(winston.createLogger).toHaveBeenCalled(); const fileTransportCalls = (winston.transports.File as unknown as jest.Mock).mock.calls; expect(fileTransportCalls.length).toBe(2); // Error and combined logs }); it('should add console transport in non-production environment', () => { process.env.NODE_ENV = 'development'; new Logger({ level: LogLevel.INFO, directory: logDir }); expect(winston.transports.Console).toHaveBeenCalled(); }); }); describe('logging methods', () => { const testMessage = 'Test message'; const testMeta = { key: 'value' }; it('should log error messages', () => { logger.error(testMessage, testMeta); expect(mockWinstonLogger.error).toHaveBeenCalledWith(testMessage, testMeta); }); it('should log warning messages', () => { logger.warn(testMessage, testMeta); expect(mockWinstonLogger.warn).toHaveBeenCalledWith(testMessage, testMeta); }); it('should log info messages', () => { logger.info(testMessage, testMeta); expect(mockWinstonLogger.info).toHaveBeenCalledWith(testMessage, testMeta); }); it('should log debug messages', () => { logger.debug(testMessage, testMeta); expect(mockWinstonLogger.debug).toHaveBeenCalledWith(testMessage, testMeta); }); }); describe('metric logging', () => { it('should log metrics in correct format', () => { const metricName = 'test_metric'; const metricValue = 42; const metricUnit = 'count'; logger.metric(metricName, metricValue, metricUnit); expect(mockWinstonLogger.info).toHaveBeenCalledWith('metric', { metric: metricName, value: metricValue, unit: metricUnit, timestamp: expect.any(String), }); }); }); describe('log file management', () => { it('should return correct log file path', () => { const expectedDate = new Date().toISOString().split('T')[0]; const expectedPath = path.join(logDir, `combined-${expectedDate}.log`); expect(logger.getCurrentLogFilePath()).toBe(expectedPath); }); it('should use correct file names for different log levels', () => { new Logger({ level: LogLevel.DEBUG, directory: logDir }); const fileTransportCalls = (winston.transports.File as unknown as jest.Mock).mock.calls; const fileConfigs = fileTransportCalls.map(call => call[0] as MockWinstonTransport); expect(fileConfigs.some(config => config.filename?.includes('error-'))).toBe(true); expect(fileConfigs.some(config => config.filename?.includes('combined-'))).toBe(true); }); }); describe('error handling', () => { it('should handle file system errors gracefully', () => { // Simulate file system error jest.spyOn(fs, 'mkdirSync').mockImplementationOnce(() => { throw new Error('File system error'); }); expect(() => { new Logger({ level: LogLevel.INFO, directory: '/invalid/path' }); }).toThrow('File system error'); }); }); describe('configuration validation', () => { it('should use default values for optional config', () => { const logger = new Logger({ level: LogLevel.INFO, directory: logDir }); const fileTransportCalls = (winston.transports.File as unknown as jest.Mock).mock.calls; const fileConfigs = fileTransportCalls.map(call => call[0] as MockWinstonTransport); fileConfigs.forEach(config => { expect(config.maxFiles).toBe(7); expect(config.maxsize).toBe('10m'); }); }); it('should override default values with provided config', () => { const logger = new Logger({ level: LogLevel.INFO, directory: logDir, maxFiles: 3, maxSize: '5m', }); const fileTransportCalls = (winston.transports.File as unknown as jest.Mock).mock.calls; const fileConfigs = fileTransportCalls.map(call => call[0] as MockWinstonTransport); fileConfigs.forEach(config => { expect(config.maxFiles).toBe(3); expect(config.maxsize).toBe('5m'); }); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/im47cn/feishu-project-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server