//nolint:testpackage // needs the package logger; see TestWithAttributesReachTTYOutput package log //nolint:revive,nolintlint // stdlib log unused here; see #76 import ( "bytes" "log/slog" "regexp" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // withTestANSIEscape matches the SGR sequences TTYHandler emits. var withTestANSIEscape = regexp.MustCompile(`\x1b\[[0-9;]*m`) // TestWithAttributesReachTTYOutput exercises the exported package-level // With through a TTYHandler, which is the path the reported defect was // on: the handler is selected by TTY-ness, so on a terminal With's // attributes were silently dropped while the same code printed them // correctly in CI. // // This is an in-package test so it can point the package logger at a // buffer. Building an slog.Logger over a TTYHandler by hand would test // slog, not this package's With, and there is no injectable sink to // reach it from outside. The package logger is process-global, so this // test must not run in parallel. // //nolint:paralleltest // replaces the process-global package logger func TestWithAttributesReachTTYOutput(t *testing.T) { var buf bytes.Buffer previous := logger t.Cleanup(func() { logger = previous }) logger = slog.New(NewTTYHandler(&buf, &slog.HandlerOptions{ Level: slog.LevelDebug, })) With("key", "value").Info("hello") plain := withTestANSIEscape.ReplaceAllString(buf.String(), "") require.NotEmpty(t, plain) assert.Contains(t, plain, "hello") assert.Contains(t, plain, "key=value", "log.With attributes must reach TTYHandler output") } // TestWithoutInitializedLoggerFallsBack pins the documented behavior of // With before Initialize has run: it hands back the slog default rather // than a nil logger that would panic at the call site. // //nolint:paralleltest // replaces the process-global package logger func TestWithoutInitializedLoggerFallsBack(t *testing.T) { previous := logger t.Cleanup(func() { logger = previous }) logger = nil assert.NotNil(t, With("key", "value")) }