Files
webhooker/internal/logger/logger_test.go
T
sneak c0f1331ec5
check / check (push) Waiting to run
Send fx's own events through the service's logger (closes #183)
fx printed its dependency graph and its start and stop hooks through
its console logger on standard error. fx.WithLogger now hands those
events to the service's logger: building the graph at DEBUG, the hooks,
the start and the stopping signal at INFO, failures at ERROR. The fx
logger's constructor takes the configuration, so the level DEBUG=true
sets is in force before fx replays the events it held back; a failure
before that, such as an invalid configuration value, still goes to
fx's console logger. FxLogger in internal/logger holds one SlogLogger
per level. fx moves from v1.20.1, which has no SlogLogger, to v1.24.0.
The README names the Go runtime and that early failure as writers
outside internal/logger.

Model: opus-5-5
2026-10-02 14:58:00 +00:00

114 lines
2.4 KiB
Go

package logger_test
import (
"bytes"
"encoding/json"
"errors"
"log/slog"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/fx"
"go.uber.org/fx/fxevent"
"go.uber.org/fx/fxtest"
"sneak.berlin/go/webhooker/internal/globals"
"sneak.berlin/go/webhooker/internal/logger"
)
var errStopHook = errors.New("stop hook failed on purpose")
func testGlobals() *globals.Globals {
return &globals.Globals{
Appname: "test-app",
Version: "1.0.0",
}
}
func TestNew(t *testing.T) {
t.Parallel()
lc := fxtest.NewLifecycle(t)
params := logger.LoggerParams{
Globals: testGlobals(),
}
l, err := logger.New(lc, params)
if err != nil {
t.Fatalf("New() error = %v", err)
}
if l.Get() == nil {
t.Error("Get() returned nil logger")
}
// Test that we can log without panic
l.Get().Info("test message", "key", "value")
}
func TestEnableDebugLogging(t *testing.T) {
t.Parallel()
lc := fxtest.NewLifecycle(t)
params := logger.LoggerParams{
Globals: testGlobals(),
}
l, err := logger.New(lc, params)
if err != nil {
t.Fatalf("New() error = %v", err)
}
// Enable debug logging should not panic
l.EnableDebugLogging()
// Test debug logging
l.Get().Debug("debug message", "test", true)
}
// TestFxLogger_Levels starts and stops an fx app that reports its own
// events through NewFxLogger, as cmd/webhooker does, and reads back
// what reached the handler: the graph at DEBUG, the start at INFO and
// a failed stop hook at ERROR, each as a structured record.
func TestFxLogger_Levels(t *testing.T) {
t.Parallel()
var out bytes.Buffer
log := slog.New(slog.NewJSONHandler(
&out, &slog.HandlerOptions{Level: slog.LevelDebug},
))
app := fx.New(
fx.WithLogger(func() fxevent.Logger {
return logger.NewFxLogger(log)
}),
fx.Invoke(func(lc fx.Lifecycle) {
lc.Append(fx.StopHook(func() error { return errStopHook }))
}),
)
require.NoError(t, app.Start(t.Context()))
require.ErrorIs(t, app.Stop(t.Context()), errStopHook)
levels := map[string]string{}
decoder := json.NewDecoder(&out)
for decoder.More() {
var record struct {
Level string `json:"level"`
Msg string `json:"msg"`
}
require.NoError(t, decoder.Decode(&record))
levels[record.Msg] = record.Level
}
assert.Equal(t, "DEBUG", levels["provided"])
assert.Equal(t, "INFO", levels["started"])
assert.Equal(t, "ERROR", levels["OnStop hook failed"])
}