package logging import ( "fmt" "sync" "testing" ) func newTestLogger() *Logger { l := NewLogger() var ts int64 l.now = func() int64 { ts++ return ts } return l } func TestLogger_ringBuffer_evictsOldest(t *testing.T) { l := newTestLogger() for i := 1; i <= 600; i++ { l.Info("src", fmt.Sprintf("msg%d", i)) } snap := l.Snapshot() if len(snap) != 500 { t.Fatalf("want 500 entries, got %d", len(snap)) } if snap[0].Message != "msg101" { t.Errorf("oldest entry: want msg101, got %q", snap[0].Message) } if snap[499].Message != "msg600" { t.Errorf("newest entry: want msg600, got %q", snap[499].Message) } } func TestLogger_levels(t *testing.T) { l := newTestLogger() l.Info("s", "info-msg") l.Warn("s", "warn-msg") l.Error("s", "error-msg") snap := l.Snapshot() if len(snap) != 3 { t.Fatalf("want 3 entries, got %d", len(snap)) } if snap[0].Level != LevelInfo { t.Errorf("entry 0: want LevelInfo, got %v", snap[0].Level) } if snap[1].Level != LevelWarn { t.Errorf("entry 1: want LevelWarn, got %v", snap[1].Level) } if snap[2].Level != LevelError { t.Errorf("entry 2: want LevelError, got %v", snap[2].Level) } } func TestLogger_snapshotOrder(t *testing.T) { l := newTestLogger() for i := 1; i <= 10; i++ { l.Info("s", fmt.Sprintf("msg%d", i)) } snap := l.Snapshot() for i, e := range snap { want := fmt.Sprintf("msg%d", i+1) if e.Message != want { t.Errorf("snap[%d].Message = %q, want %q", i, e.Message, want) } } } func TestLogger_detailsOptional(t *testing.T) { l := newTestLogger() l.Info("s", "no-details") l.Warn("s", "with-details", "extra info") snap := l.Snapshot() if snap[0].Details != "" { t.Errorf("Details without arg should be empty, got %q", snap[0].Details) } if snap[1].Details != "extra info" { t.Errorf("Details = %q, want %q", snap[1].Details, "extra info") } } func TestLogger_subscribe(t *testing.T) { l := newTestLogger() var received []Entry l.Subscribe(func(e Entry) { received = append(received, e) }) l.Info("s", "one") l.Error("s", "two") if len(received) != 2 { t.Fatalf("want 2 callbacks, got %d", len(received)) } if received[0].Message != "one" { t.Errorf("first callback message = %q, want 'one'", received[0].Message) } if received[1].Level != LevelError { t.Error("second callback should have LevelError") } } func TestLogger_concurrencySafe(t *testing.T) { l := NewLogger() var wg sync.WaitGroup for i := 0; i < 100; i++ { wg.Add(1) go func(n int) { defer wg.Done() l.Info("src", fmt.Sprintf("msg%d", n)) _ = l.Snapshot() }(i) } wg.Wait() } func TestLogger_sourceAndMessage(t *testing.T) { l := newTestLogger() l.Info("webhook", "connected") snap := l.Snapshot() if snap[0].Source != "webhook" { t.Errorf("Source = %q, want 'webhook'", snap[0].Source) } if snap[0].Message != "connected" { t.Errorf("Message = %q, want 'connected'", snap[0].Message) } } func TestLogger_snapshotIsACopy(t *testing.T) { l := newTestLogger() l.Info("s", "first") snap := l.Snapshot() l.Info("s", "second") if len(snap) != 1 { t.Errorf("snapshot should be a copy; len = %d, want 1", len(snap)) } } func TestDefault_exists(t *testing.T) { if Default == nil { t.Error("Default logger should not be nil") } }