package logging import ( "bytes" "context" "encoding/json" "strings" "testing" "time" ) func TestNewLogger(t *testing.T) { cfg := DefaultConfig() cfg.RedactEnabled = false // Simplify for this test logger := New(cfg) if logger == nil { t.Fatal("expected non-nil logger") } if logger.Slog() == nil { t.Fatal("expected non-nil slog.Logger") } } func TestLoggerWith(t *testing.T) { var buf bytes.Buffer cfg := DefaultConfig() cfg.RedactEnabled = false logger := NewWithWriter(cfg, &buf) // Test With method logger2 := logger.With("key", "value") logger2.Info("test message") output := buf.String() if !strings.Contains(output, `"key"`) || !strings.Contains(output, `"value"`) { t.Errorf("expected key/value in output, got: %s", output) } } func TestLoggerWithComponent(t *testing.T) { var buf bytes.Buffer cfg := DefaultConfig() cfg.RedactEnabled = false logger := NewWithWriter(cfg, &buf) logger.WithComponent("test_component").Info("test message") output := buf.String() if !strings.Contains(output, FieldComponent) || !strings.Contains(output, "test_component") { t.Errorf("expected component field in output, got: %s", output) } } func TestLoggerWithHandler(t *testing.T) { var buf bytes.Buffer cfg := DefaultConfig() cfg.RedactEnabled = false logger := NewWithWriter(cfg, &buf) logger.WithHandler("CreateProject").Info("test message") output := buf.String() if !strings.Contains(output, FieldHandler) || !strings.Contains(output, "CreateProject") { t.Errorf("expected handler field in output, got: %s", output) } } func TestLoggerWithError(t *testing.T) { var buf bytes.Buffer cfg := DefaultConfig() cfg.RedactEnabled = false logger := NewWithWriter(cfg, &buf) err := &testError{msg: "something went wrong"} logger.WithError(err).Error("operation failed") output := buf.String() // Verify we use "error" field, not "err" or "e" if !strings.Contains(output, `"error"`) || !strings.Contains(output, "something went wrong") { t.Errorf("expected error field in output, got: %s", output) } } type testError struct { msg string } func (e *testError) Error() string { return e.msg } func TestLoggerWithErrorNil(t *testing.T) { var buf bytes.Buffer cfg := DefaultConfig() cfg.RedactEnabled = false logger := NewWithWriter(cfg, &buf) // WithError(nil) should not add error field logger2 := logger.WithError(nil) if logger2 != logger { t.Error("WithError(nil) should return same logger") } } func TestLoggerTimed(t *testing.T) { var buf bytes.Buffer cfg := DefaultConfig() cfg.RedactEnabled = false logger := NewWithWriter(cfg, &buf) // Test Timed method done := logger.Timed("test_operation") time.Sleep(10 * time.Millisecond) done() output := buf.String() if !strings.Contains(output, "test_operation") { t.Errorf("expected operation in output, got: %s", output) } if !strings.Contains(output, FieldDuration) { t.Errorf("expected duration field in output, got: %s", output) } if !strings.Contains(output, "operation completed") { t.Errorf("expected completion message in output, got: %s", output) } } func TestLoggerJSONFormat(t *testing.T) { var buf bytes.Buffer cfg := DefaultConfig() cfg.Format = FormatJSON cfg.RedactEnabled = false logger := NewWithWriter(cfg, &buf) logger.Info("test message", "key", "value") var entry map[string]any if err := json.Unmarshal(buf.Bytes(), &entry); err != nil { t.Fatalf("expected valid JSON, got error: %v, output: %s", err, buf.String()) } if entry["msg"] != "test message" { t.Errorf("expected msg='test message', got: %v", entry["msg"]) } if entry["key"] != "value" { t.Errorf("expected key='value', got: %v", entry["key"]) } } func TestLoggerTextFormat(t *testing.T) { var buf bytes.Buffer cfg := DefaultConfig() cfg.Format = FormatText cfg.RedactEnabled = false logger := NewWithWriter(cfg, &buf) logger.Info("test message", "key", "value") output := buf.String() // Text format should contain the message and key=value if !strings.Contains(output, "test message") { t.Errorf("expected message in output, got: %s", output) } if !strings.Contains(output, "key=value") { t.Errorf("expected key=value in output, got: %s", output) } } func TestFromContext(t *testing.T) { cfg := DefaultConfig() cfg.RedactEnabled = false logger := New(cfg) SetDefault(logger) // No logger in context should return default ctx := context.Background() l := FromContext(ctx) if l == nil { t.Fatal("expected non-nil logger from empty context") } // Logger in context should be returned enriched := logger.With("test", "value") ctx = WithContext(ctx, enriched) l = FromContext(ctx) if l != enriched { t.Error("expected enriched logger from context") } } func TestFromContextNil(t *testing.T) { cfg := DefaultConfig() cfg.RedactEnabled = false logger := New(cfg) SetDefault(logger) // Test that FromContext handles nil context gracefully // This is intentionally testing nil context handling var nilCtx context.Context //nolint:staticcheck // testing nil context handling l := FromContext(nilCtx) if l == nil { t.Fatal("expected non-nil logger from nil context") } } func TestNop(t *testing.T) { logger := Nop() if logger == nil { t.Fatal("expected non-nil nop logger") } // Should not panic when called logger.Info("this should be discarded") logger.WithComponent("test").Error("this too") }