mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
65b7658568
Follow-up to [review comment on #23025](https://github.com/coder/coder/pull/23025#discussion_r2930309487) from @mafredri. Extracts the repeated `logSink` / `fakeSink` test pattern into a shared `testutil.FakeSink` and migrates all existing call sites. > 🤖 This PR was created with the help of Coder Agents, and will be reviewed by my human. 🧑💻 --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
77 lines
1.8 KiB
Go
77 lines
1.8 KiB
Go
package testutil
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"testing"
|
|
|
|
"cdr.dev/slog/v3"
|
|
)
|
|
|
|
// FakeSink is a thread-safe slog.Sink that captures log entries so
|
|
// tests can assert on what was logged. It requires a testing.TB
|
|
// as it is only meant for use in tests.
|
|
type FakeSink struct {
|
|
mu sync.RWMutex
|
|
entries []slog.SinkEntry
|
|
}
|
|
|
|
// NewFakeSink returns a FakeSink ready for use.
|
|
func NewFakeSink(_ testing.TB) *FakeSink {
|
|
return &FakeSink{}
|
|
}
|
|
|
|
// LogEntry implements slog.Sink. It appends the entry to the
|
|
// internal slice.
|
|
func (s *FakeSink) LogEntry(_ context.Context, e slog.SinkEntry) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
s.entries = append(s.entries, e)
|
|
}
|
|
|
|
// Sync implements slog.Sink.
|
|
func (*FakeSink) Sync() {}
|
|
|
|
// Entries returns a copy of the captured entries. If filters are
|
|
// provided, only entries matching ALL filters are returned. This
|
|
// lets callers compose simple predicates instead of needing
|
|
// dedicated methods for each field.
|
|
func (s *FakeSink) Entries(filters ...func(slog.SinkEntry) bool) []slog.SinkEntry {
|
|
s.mu.RLock()
|
|
cpy := make([]slog.SinkEntry, len(s.entries))
|
|
copy(cpy, s.entries)
|
|
s.mu.RUnlock()
|
|
filtered := make([]slog.SinkEntry, 0)
|
|
for _, e := range cpy {
|
|
if !matchAll(e, filters) {
|
|
continue
|
|
}
|
|
filtered = append(filtered, e)
|
|
}
|
|
return filtered
|
|
}
|
|
|
|
// Logger returns a slog.Logger backed by this sink at the given
|
|
// level. If no level is provided it defaults to LevelDebug, which
|
|
// captures everything. If more than one level is provided, the
|
|
// first one wins.
|
|
func (s *FakeSink) Logger(level ...slog.Level) slog.Logger {
|
|
l := slog.LevelDebug
|
|
if len(level) > 0 {
|
|
l = level[0]
|
|
}
|
|
return slog.Make(s).Leveled(l)
|
|
}
|
|
|
|
func matchAll(e slog.SinkEntry, filters []func(slog.SinkEntry) bool) bool {
|
|
for _, f := range filters {
|
|
if f == nil {
|
|
continue
|
|
}
|
|
if !f(e) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|