181 lines
4.8 KiB
Go
181 lines
4.8 KiB
Go
package logger_test
|
||
|
||
import (
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
"testing"
|
||
|
||
"github.com/EthanCodeCraft/xlgo-core/config"
|
||
"github.com/EthanCodeCraft/xlgo-core/logger"
|
||
)
|
||
|
||
// 注意:logger 需要先初始化才能调用 Info/Debug/Warn/Error
|
||
// 这里只测试 Field 函数,不测试日志输出
|
||
|
||
func TestAPILog(t *testing.T) {
|
||
_ = logger.APILog()
|
||
// 未初始化时为 nop logger,调用安全
|
||
}
|
||
|
||
func TestDBLog(t *testing.T) {
|
||
_ = logger.DBLog()
|
||
// 未初始化时为 nop logger,调用安全
|
||
}
|
||
|
||
func TestFieldString(t *testing.T) {
|
||
field := logger.Field.String("key", "value")
|
||
if field.Key != "key" {
|
||
t.Error("Field.String key failed")
|
||
}
|
||
}
|
||
|
||
func TestFieldInt(t *testing.T) {
|
||
field := logger.Field.Int("count", 123)
|
||
if field.Key != "count" {
|
||
t.Error("Field.Int key failed")
|
||
}
|
||
}
|
||
|
||
func TestFieldInt64(t *testing.T) {
|
||
field := logger.Field.Int64("id", 1234567890)
|
||
if field.Key != "id" {
|
||
t.Error("Field.Int64 key failed")
|
||
}
|
||
}
|
||
|
||
func TestFieldBool(t *testing.T) {
|
||
field := logger.Field.Bool("enabled", true)
|
||
if field.Key != "enabled" {
|
||
t.Error("Field.Bool key failed")
|
||
}
|
||
}
|
||
|
||
func TestFieldUint(t *testing.T) {
|
||
field := logger.Field.Uint("uint_val", 100)
|
||
if field.Key != "uint_val" {
|
||
t.Error("Field.Uint key failed")
|
||
}
|
||
}
|
||
|
||
func TestFieldFloat64(t *testing.T) {
|
||
field := logger.Field.Float64("price", 99.99)
|
||
if field.Key != "price" {
|
||
t.Error("Field.Float64 key failed")
|
||
}
|
||
}
|
||
|
||
func TestFieldError(t *testing.T) {
|
||
_ = logger.Field.Error(nil)
|
||
// zap.Field 是结构体,仅验证函数调用正常
|
||
}
|
||
|
||
// initWithTempDir 使用临时目录初始化 logger,返回日志目录与读取文件内容的辅助函数。
|
||
// 验证修复 #3 之后,三个 logger 各自只写自己的文件,互不串扰。
|
||
//
|
||
// 测试退出时通过 t.Cleanup 调用 logger.Close 释放文件句柄,
|
||
// 避免 Windows 上 t.TempDir 因句柄占用而清理失败。
|
||
func initWithTempDir(t *testing.T) (dir string, read func(name string) string) {
|
||
t.Helper()
|
||
dir = t.TempDir()
|
||
cfg := &config.Config{
|
||
Server: config.ServerConfig{Mode: "production"}, // 走 InfoLevel,避免 Debug 噪音
|
||
Log: config.LogConfig{
|
||
Dir: dir,
|
||
MaxSize: 10,
|
||
MaxBackups: 1,
|
||
MaxAge: 1,
|
||
Compress: false,
|
||
},
|
||
}
|
||
if err := logger.Init(cfg); err != nil {
|
||
t.Fatalf("logger.Init failed: %v", err)
|
||
}
|
||
t.Cleanup(func() { _ = logger.Close() })
|
||
|
||
read = func(name string) string {
|
||
path := filepath.Join(dir, name)
|
||
data, err := os.ReadFile(path)
|
||
if err != nil {
|
||
if os.IsNotExist(err) {
|
||
return ""
|
||
}
|
||
t.Fatalf("read %s: %v", path, err)
|
||
}
|
||
return string(data)
|
||
}
|
||
return dir, read
|
||
}
|
||
|
||
// TestLoggerNoCrossWriting 验证 Logger / APILog / DBLog 不会重复写入彼此的日志文件。
|
||
//
|
||
// 这是 v1.0.3 修复的核心:旧实现 Logger = Tee(apiCore, dbCore, consoleCore),
|
||
// 导致 logger.Info(...) 同时写到 api.log + database.log + console 三份。
|
||
func TestLoggerNoCrossWriting(t *testing.T) {
|
||
_, read := initWithTempDir(t)
|
||
|
||
// 三个标记字符串足够独特,分别由三个 logger 写入
|
||
const (
|
||
appMark = "MARKER_APP_LOG_ONLY_xyz"
|
||
apiMark = "MARKER_API_LOG_ONLY_xyz"
|
||
dbMark = "MARKER_DB_LOG_ONLY_xyz"
|
||
)
|
||
|
||
logger.Info(appMark)
|
||
logger.APILog().Info(apiMark)
|
||
logger.DBLog().Info(dbMark)
|
||
|
||
if err := logger.Sync(); err != nil {
|
||
t.Fatalf("Sync: %v", err)
|
||
}
|
||
|
||
app := read("app.log")
|
||
api := read("api.log")
|
||
db := read("database.log")
|
||
|
||
// 各自的日志文件必须包含自己的标记
|
||
if !strings.Contains(app, appMark) {
|
||
t.Errorf("app.log missing %q, got: %s", appMark, app)
|
||
}
|
||
if !strings.Contains(api, apiMark) {
|
||
t.Errorf("api.log missing %q, got: %s", apiMark, api)
|
||
}
|
||
if !strings.Contains(db, dbMark) {
|
||
t.Errorf("database.log missing %q, got: %s", dbMark, db)
|
||
}
|
||
|
||
// 关键:禁止串写
|
||
if strings.Contains(api, appMark) {
|
||
t.Errorf("api.log should NOT contain %q (cross-writing bug regressed)", appMark)
|
||
}
|
||
if strings.Contains(db, appMark) {
|
||
t.Errorf("database.log should NOT contain %q (cross-writing bug regressed)", appMark)
|
||
}
|
||
if strings.Contains(app, apiMark) {
|
||
t.Errorf("app.log should NOT contain %q", apiMark)
|
||
}
|
||
if strings.Contains(db, apiMark) {
|
||
t.Errorf("database.log should NOT contain %q", apiMark)
|
||
}
|
||
if strings.Contains(app, dbMark) {
|
||
t.Errorf("app.log should NOT contain %q", dbMark)
|
||
}
|
||
if strings.Contains(api, dbMark) {
|
||
t.Errorf("api.log should NOT contain %q", dbMark)
|
||
}
|
||
}
|
||
|
||
// TestLoggerInitNilConfig 验证 Init 对 nil 配置返回错误,不再 panic。
|
||
func TestLoggerInitNilConfig(t *testing.T) {
|
||
if err := logger.Init(nil); err == nil {
|
||
t.Error("Init(nil) should return error, got nil")
|
||
}
|
||
}
|
||
|
||
// TestLoggerSyncBeforeInit 验证未初始化时 Sync 不 panic(globals 仍为 Nop)。
|
||
func TestLoggerSyncBeforeInit(t *testing.T) {
|
||
// Nop logger 的 Sync 永远返回 nil
|
||
if err := logger.Sync(); err != nil {
|
||
t.Errorf("Sync on nop logger should be nil, got %v", err)
|
||
}
|
||
} |