package logger import ( "bytes" "errors" "strings" "testing" "time" ) func TestLevel_String(t *testing.T) { tests := []struct { level Level want string }{ {LevelDebug, "DEBUG"}, {LevelInfo, "INFO"}, {LevelWarn, "WARN"}, {LevelError, "ERROR"}, } for _, tt := range tests { if got := tt.level.String(); got != tt.want { t.Errorf("Level(%d).String() = %q, want %q", tt.level, got, tt.want) } } } func TestParseLevel(t *testing.T) { tests := []struct { input string want Level }{ {"debug", LevelDebug}, {"DEBUG", LevelDebug}, {"info", LevelInfo}, {"warn", LevelWarn}, {"error", LevelError}, {"invalid", LevelInfo}, // 默认 Info {"", LevelInfo}, } for _, tt := range tests { if got := ParseLevel(tt.input); got != tt.want { t.Errorf("ParseLevel(%q) = %v, want %v", tt.input, got, tt.want) } } } func TestFieldConstructors(t *testing.T) { sf := String("key", "value") if sf.Key != "key" || sf.Value != "value" { t.Errorf("String() = %+v", sf) } intF := Int("num", 42) if intF.Key != "num" || intF.Value != "42" { t.Errorf("Int() = %+v", intF) } int64F := Int64("big", 1<<40) if int64F.Key != "big" || !strings.Contains(int64F.Value, "1099511627776") { t.Errorf("Int64() = %+v", int64F) } floatF := Float("pi", 3.14) if floatF.Key != "pi" || !strings.HasPrefix(floatF.Value, "3.14") { t.Errorf("Float() = %+v", floatF) } durF := Duration("took", 500*time.Millisecond) if durF.Key != "took" || !strings.Contains(durF.Value, "ms") { t.Errorf("Duration() = %+v", durF) } boolF := Bool("ok", true) if boolF.Key != "ok" || boolF.Value != "true" { t.Errorf("Bool() = %+v", boolF) } errF := Err("cause", errors.New("boom")) if errF.Key != "cause" || errF.Value != "boom" { t.Errorf("Err() = %+v", errF) } // Nil error 的行为 nilErr := Err("none", nil) if nilErr.Value != "" && nilErr.Value != "" { t.Logf("Err(nil) = %q (可接受)", nilErr.Value) } } func TestSensitive(t *testing.T) { // Sensitive 字段在输出时才脱敏(标记在 Field 上,实际脱敏在 writeField) var buf bytes.Buffer log := New(&buf, LevelInfo) log.Info("auth", Sensitive("api_key", "sk-ant-api03-verylongsecret")) out := buf.String() if strings.Contains(out, "verylongsecret") { t.Errorf("Sensitive 字段未脱敏: %q", out) } if !strings.Contains(out, "[REDACTED]") { t.Errorf("应包含 [REDACTED]: %q", out) } if !strings.Contains(out, "api_key=") { t.Errorf("应保留 key 名: %q", out) } } func TestLogger_Info(t *testing.T) { var buf bytes.Buffer log := New(&buf, LevelInfo) log.Info("hello", String("user", "alice")) out := buf.String() if !strings.Contains(out, "hello") { t.Errorf("输出缺少 message: %q", out) } if !strings.Contains(out, "user=alice") { t.Errorf("输出缺少 field: %q", out) } if !strings.Contains(out, "INFO") { t.Errorf("输出缺少 level: %q", out) } } func TestLogger_LevelFilter(t *testing.T) { var buf bytes.Buffer log := New(&buf, LevelWarn) log.Debug("debug-msg") log.Info("info-msg") log.Warn("warn-msg") log.Error("error-msg") out := buf.String() if strings.Contains(out, "debug-msg") { t.Errorf("Debug 不应输出: %q", out) } if strings.Contains(out, "info-msg") { t.Errorf("Info 不应输出: %q", out) } if !strings.Contains(out, "warn-msg") { t.Errorf("Warn 应输出: %q", out) } if !strings.Contains(out, "error-msg") { t.Errorf("Error 应输出: %q", out) } } func TestLogger_SecretRedaction_KeyValueFormat(t *testing.T) { var buf bytes.Buffer log := New(&buf, LevelInfo) // sanitizeSecrets 只识别 key=value 格式(或 JSON 格式或 Authorization header) log.Info("env: api_key=sk-ant-api03-someverylongsecret") out := buf.String() if strings.Contains(out, "someverylongsecret") { t.Errorf("KEY=VALUE 格式的 secret 未脱敏: %q", out) } if !strings.Contains(out, "[REDACTED]") { t.Errorf("应含 [REDACTED]: %q", out) } } func TestLogger_SecretRedaction_HeaderFormat(t *testing.T) { var buf bytes.Buffer log := New(&buf, LevelInfo) log.Info("request: Authorization: Bearer sk-ant-api03-veryverysecret") out := buf.String() if strings.Contains(out, "veryverysecret") { t.Errorf("Authorization header 未脱敏: %q", out) } } func TestLogger_SecretRedaction_JSONFormat(t *testing.T) { var buf bytes.Buffer log := New(&buf, LevelInfo) log.Info(`body: {"api_key":"sk-ant-api03-jsonsecret","other":"ok"}`) out := buf.String() if strings.Contains(out, "jsonsecret") { t.Errorf("JSON 格式 secret 未脱敏: %q", out) } // 非敏感字段保留 if !strings.Contains(out, `"other"`) { t.Errorf("非敏感字段应保留: %q", out) } } func TestLogger_ErrorHandling(t *testing.T) { var buf bytes.Buffer log := New(&buf, LevelInfo) err := errors.New("connection refused") log.Error("request failed", Err("err", err)) out := buf.String() if !strings.Contains(out, "connection refused") { t.Errorf("错误信息缺失: %q", out) } if !strings.Contains(out, "ERROR") { t.Errorf("level 缺失: %q", out) } } func TestDefaultLogger(t *testing.T) { // Default() 应返回非 nil 的 logger def := Default() if def == nil { t.Fatal("Default() returned nil") } // 替换默认 logger var buf bytes.Buffer SetDefault(New(&buf, LevelInfo)) Default().Info("test") if !strings.Contains(buf.String(), "test") { t.Error("SetDefault 未生效") } } func TestConcurrentLogging(t *testing.T) { // 并发写入不应崩溃或交错损坏 var buf bytes.Buffer log := New(&buf, LevelInfo) done := make(chan struct{}) for i := 0; i < 10; i++ { go func(id int) { for j := 0; j < 100; j++ { log.Info("msg", Int("goroutine", id), Int("iter", j)) } done <- struct{}{} }(i) } for i := 0; i < 10; i++ { <-done } // 验证输出包含一些预期内容 out := buf.String() if !strings.Contains(out, "goroutine=") { t.Error("缺少 field 输出") } }