package logger import ( "os" "path/filepath" "testing" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) // TestInitLoggers 测试日志初始化(T026) func TestInitLoggers(t *testing.T) { // 创建临时目录用于日志文件 tempDir := t.TempDir() tests := []struct { name string level string development bool appLogConfig LogRotationConfig accessLogConfig LogRotationConfig wantErr bool validateFunc func(t *testing.T) }{ { name: "production mode with info level", level: "info", development: false, appLogConfig: LogRotationConfig{ Filename: filepath.Join(tempDir, "app-prod.log"), MaxSize: 10, MaxBackups: 3, MaxAge: 7, Compress: true, }, accessLogConfig: LogRotationConfig{ Filename: filepath.Join(tempDir, "access-prod.log"), MaxSize: 10, MaxBackups: 3, MaxAge: 7, Compress: true, }, wantErr: false, validateFunc: func(t *testing.T) { if appLogger == nil { t.Error("appLogger should not be nil") } if accessLogger == nil { t.Error("accessLogger should not be nil") } // 写入一条日志以触发文件创建 GetAppLogger().Info("test log creation") Sync() // 验证日志文件创建 if _, err := os.Stat(filepath.Join(tempDir, "app-prod.log")); os.IsNotExist(err) { t.Error("app log file should be created after writing") } }, }, { name: "development mode with debug level", level: "debug", development: true, appLogConfig: LogRotationConfig{ Filename: filepath.Join(tempDir, "app-dev.log"), MaxSize: 5, MaxBackups: 2, MaxAge: 3, Compress: false, }, accessLogConfig: LogRotationConfig{ Filename: filepath.Join(tempDir, "access-dev.log"), MaxSize: 5, MaxBackups: 2, MaxAge: 3, Compress: false, }, wantErr: false, validateFunc: func(t *testing.T) { if appLogger == nil { t.Error("appLogger should not be nil in dev mode") } if accessLogger == nil { t.Error("accessLogger should not be nil in dev mode") } }, }, { name: "warn level logging", level: "warn", development: false, appLogConfig: LogRotationConfig{ Filename: filepath.Join(tempDir, "app-warn.log"), MaxSize: 10, MaxBackups: 3, MaxAge: 7, Compress: true, }, accessLogConfig: LogRotationConfig{ Filename: filepath.Join(tempDir, "access-warn.log"), MaxSize: 10, MaxBackups: 3, MaxAge: 7, Compress: true, }, wantErr: false, validateFunc: func(t *testing.T) { if appLogger == nil { t.Error("appLogger should not be nil") } }, }, { name: "error level logging", level: "error", development: false, appLogConfig: LogRotationConfig{ Filename: filepath.Join(tempDir, "app-error.log"), MaxSize: 10, MaxBackups: 3, MaxAge: 7, Compress: true, }, accessLogConfig: LogRotationConfig{ Filename: filepath.Join(tempDir, "access-error.log"), MaxSize: 10, MaxBackups: 3, MaxAge: 7, Compress: true, }, wantErr: false, validateFunc: func(t *testing.T) { if appLogger == nil { t.Error("appLogger should not be nil") } }, }, { name: "invalid level defaults to info", level: "invalid", development: false, appLogConfig: LogRotationConfig{ Filename: filepath.Join(tempDir, "app-invalid.log"), MaxSize: 10, MaxBackups: 3, MaxAge: 7, Compress: true, }, accessLogConfig: LogRotationConfig{ Filename: filepath.Join(tempDir, "access-invalid.log"), MaxSize: 10, MaxBackups: 3, MaxAge: 7, Compress: true, }, wantErr: false, validateFunc: func(t *testing.T) { if appLogger == nil { t.Error("appLogger should not be nil even with invalid level") } }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := InitLoggers(tt.level, tt.development, tt.appLogConfig, tt.accessLogConfig) if (err != nil) != tt.wantErr { t.Errorf("InitLoggers() error = %v, wantErr %v", err, tt.wantErr) return } if tt.validateFunc != nil { tt.validateFunc(t) } }) } } // TestGetAppLogger 测试获取应用日志记录器(T026) func TestGetAppLogger(t *testing.T) { // 创建临时目录 tempDir := t.TempDir() tests := []struct { name string setupFunc func() wantNil bool }{ { name: "after initialization", setupFunc: func() { InitLoggers("info", false, LogRotationConfig{ Filename: filepath.Join(tempDir, "app-get.log"), MaxSize: 10, MaxBackups: 3, MaxAge: 7, Compress: true, }, LogRotationConfig{ Filename: filepath.Join(tempDir, "access-get.log"), MaxSize: 10, MaxBackups: 3, MaxAge: 7, Compress: true, }, ) }, wantNil: false, }, { name: "before initialization returns nop logger", setupFunc: func() { // 重置全局变量 appLogger = nil }, wantNil: false, // GetAppLogger 应该返回 nop logger,不是 nil }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tt.setupFunc() logger := GetAppLogger() if logger == nil { t.Error("GetAppLogger() should never return nil, should return nop logger instead") } }) } } // TestGetAccessLogger 测试获取访问日志记录器(T028) func TestGetAccessLogger(t *testing.T) { // 创建临时目录 tempDir := t.TempDir() tests := []struct { name string setupFunc func() wantNil bool }{ { name: "after initialization", setupFunc: func() { InitLoggers("info", false, LogRotationConfig{ Filename: filepath.Join(tempDir, "app-access.log"), MaxSize: 10, MaxBackups: 3, MaxAge: 7, Compress: true, }, LogRotationConfig{ Filename: filepath.Join(tempDir, "access-access.log"), MaxSize: 10, MaxBackups: 3, MaxAge: 7, Compress: true, }, ) }, wantNil: false, }, { name: "before initialization returns nop logger", setupFunc: func() { // 重置全局变量 accessLogger = nil }, wantNil: false, // GetAccessLogger 应该返回 nop logger,不是 nil }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tt.setupFunc() logger := GetAccessLogger() if logger == nil { t.Error("GetAccessLogger() should never return nil, should return nop logger instead") } }) } } // TestSync 测试日志缓冲区刷新(T028) func TestSync(t *testing.T) { // 创建临时目录 tempDir := t.TempDir() tests := []struct { name string setupFunc func() wantErr bool }{ { name: "sync after initialization", setupFunc: func() { InitLoggers("info", false, LogRotationConfig{ Filename: filepath.Join(tempDir, "app-sync.log"), MaxSize: 10, MaxBackups: 3, MaxAge: 7, Compress: true, }, LogRotationConfig{ Filename: filepath.Join(tempDir, "access-sync.log"), MaxSize: 10, MaxBackups: 3, MaxAge: 7, Compress: true, }, ) }, wantErr: false, }, { name: "sync before initialization", setupFunc: func() { appLogger = nil accessLogger = nil }, wantErr: false, // 应该优雅地处理 nil 情况 }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tt.setupFunc() err := Sync() if (err != nil) != tt.wantErr { t.Errorf("Sync() error = %v, wantErr %v", err, tt.wantErr) } }) } } // TestParseLevel 测试日志级别解析(T026) func TestParseLevel(t *testing.T) { tests := []struct { name string level string want zapcore.Level }{ { name: "debug level", level: "debug", want: zapcore.DebugLevel, }, { name: "info level", level: "info", want: zapcore.InfoLevel, }, { name: "warn level", level: "warn", want: zapcore.WarnLevel, }, { name: "error level", level: "error", want: zapcore.ErrorLevel, }, { name: "invalid level defaults to info", level: "invalid", want: zapcore.InfoLevel, }, { name: "empty level defaults to info", level: "", want: zapcore.InfoLevel, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := parseLevel(tt.level) if got != tt.want { t.Errorf("parseLevel() = %v, want %v", got, tt.want) } }) } } // TestDualLoggerSystem 测试双日志系统(T028) func TestDualLoggerSystem(t *testing.T) { // 创建临时目录 tempDir := t.TempDir() appLogFile := filepath.Join(tempDir, "app-dual.log") accessLogFile := filepath.Join(tempDir, "access-dual.log") // 初始化双日志系统 err := InitLoggers("info", false, LogRotationConfig{ Filename: appLogFile, MaxSize: 10, MaxBackups: 3, MaxAge: 7, Compress: false, // 不压缩以便检查内容 }, LogRotationConfig{ Filename: accessLogFile, MaxSize: 10, MaxBackups: 3, MaxAge: 7, Compress: false, }, ) if err != nil { t.Fatalf("InitLoggers failed: %v", err) } // 写入应用日志 appLog := GetAppLogger() appLog.Info("test app log message", zap.String("module", "test"), zap.Int("code", 200), ) // 写入访问日志 accessLog := GetAccessLogger() accessLog.Info("test access log message", zap.String("method", "GET"), zap.String("path", "/api/test"), zap.Int("status", 200), zap.Duration("latency", 100), ) // 刷新缓冲区 if err := Sync(); err != nil { t.Fatalf("Sync failed: %v", err) } // 验证应用日志文件存在并有内容 appLogContent, err := os.ReadFile(appLogFile) if err != nil { t.Fatalf("Failed to read app log file: %v", err) } if len(appLogContent) == 0 { t.Error("App log file should not be empty") } // 验证访问日志文件存在并有内容 accessLogContent, err := os.ReadFile(accessLogFile) if err != nil { t.Fatalf("Failed to read access log file: %v", err) } if len(accessLogContent) == 0 { t.Error("Access log file should not be empty") } // 验证两个日志文件是独立的 if string(appLogContent) == string(accessLogContent) { t.Error("App log and access log should have different content") } } // TestLoggerReinitialization 测试日志重新初始化(T026) func TestLoggerReinitialization(t *testing.T) { // 创建临时目录 tempDir := t.TempDir() // 第一次初始化 err := InitLoggers("info", false, LogRotationConfig{ Filename: filepath.Join(tempDir, "app-reinit1.log"), MaxSize: 10, MaxBackups: 3, MaxAge: 7, Compress: true, }, LogRotationConfig{ Filename: filepath.Join(tempDir, "access-reinit1.log"), MaxSize: 10, MaxBackups: 3, MaxAge: 7, Compress: true, }, ) if err != nil { t.Fatalf("First InitLoggers failed: %v", err) } firstAppLogger := GetAppLogger() // 第二次初始化(重新初始化) err = InitLoggers("debug", true, LogRotationConfig{ Filename: filepath.Join(tempDir, "app-reinit2.log"), MaxSize: 5, MaxBackups: 2, MaxAge: 3, Compress: false, }, LogRotationConfig{ Filename: filepath.Join(tempDir, "access-reinit2.log"), MaxSize: 5, MaxBackups: 2, MaxAge: 3, Compress: false, }, ) if err != nil { t.Fatalf("Second InitLoggers failed: %v", err) } secondAppLogger := GetAppLogger() // 验证重新初始化后日志记录器已更新 if firstAppLogger == secondAppLogger { t.Error("Logger should be replaced after reinitialization") } }