~bigbes/lethe

ref: 0555d5d52feb15256352adfa3f2a4d62835c7dac lethe/internal/platform/observability/logger_test.go -rw-r--r-- 3.2 KiB
0555d5d5 — Eugene Blikh chore: add BSD-2-Clause license 6 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package observability

import (
	"bytes"
	"context"
	"encoding/json"
	"log/slog"
	"testing"

	"sourcecraft.dev/bigbes/lethe/internal/config"
)

// TestLoggerInitTintAndJSON exercises Init for both supported formats and
// ensures the package-default logger is replaced.
func TestLoggerInitTintAndJSON(t *testing.T) {
	prev := slog.Default()
	t.Cleanup(func() { slog.SetDefault(prev) })

	for _, format := range []string{"tint", "json"} {
		l := &Logger{Cfg: config.LoggingConfig{Level: "info", Format: format}}
		if err := l.Init(context.Background()); err != nil {
			t.Fatalf("Init(%s): %v", format, err)
		}
		if l.L == nil {
			t.Fatalf("Init(%s): L is nil", format)
		}
		if slog.Default() != l.L {
			t.Fatalf("Init(%s): slog.Default not replaced", format)
		}
	}
}

func TestLoggerInitRejectsUnknownLevelAndFormat(t *testing.T) {
	l := &Logger{Cfg: config.LoggingConfig{Level: "verbose", Format: "json"}}
	if err := l.Init(context.Background()); err == nil {
		t.Fatalf("Init: expected error for unknown level")
	}
	l = &Logger{Cfg: config.LoggingConfig{Level: "info", Format: "yaml"}}
	if err := l.Init(context.Background()); err == nil {
		t.Fatalf("Init: expected error for unknown format")
	}
}

// TestContextHandlerAddsRequestIDAndUser proves the contextHandler stamp
// fires for both keys when present, and is silent when absent.
func TestContextHandlerAddsRequestIDAndUser(t *testing.T) {
	var buf bytes.Buffer
	inner := slog.NewJSONHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug})
	logger := slog.New(&contextHandler{inner: inner})

	ctx := WithUser(WithRequestID(context.Background(), "req-42"), "alice")
	logger.InfoContext(ctx, "hello")

	var rec map[string]any
	if err := json.Unmarshal(buf.Bytes(), &rec); err != nil {
		t.Fatalf("unmarshal log line: %v", err)
	}
	if rec["request_id"] != "req-42" {
		t.Fatalf("request_id = %v; want req-42", rec["request_id"])
	}
	if rec["user"] != "alice" {
		t.Fatalf("user = %v; want alice", rec["user"])
	}

	// Empty context: no request_id/user fields.
	buf.Reset()
	logger.InfoContext(context.Background(), "anon")
	rec = nil
	if err := json.Unmarshal(buf.Bytes(), &rec); err != nil {
		t.Fatalf("unmarshal anon log line: %v", err)
	}
	if _, ok := rec["request_id"]; ok {
		t.Errorf("anon: request_id present unexpectedly")
	}
	if _, ok := rec["user"]; ok {
		t.Errorf("anon: user present unexpectedly")
	}
}

// TestJSONMaskingRedactsSensitiveKeys covers the JSON ReplaceAttr path. The
// scribe tint path is library-tested by go.bigb.es/auxilia.
func TestJSONMaskingRedactsSensitiveKeys(t *testing.T) {
	var buf bytes.Buffer
	h := slog.NewJSONHandler(&buf, &slog.HandlerOptions{ReplaceAttr: maskAttrJSON})
	logger := slog.New(h)
	logger.Info("login", slog.String("password", "hunter2"), slog.String("authorization", "Bearer xyz"), slog.String("user", "alice"))

	var rec map[string]any
	if err := json.Unmarshal(buf.Bytes(), &rec); err != nil {
		t.Fatalf("unmarshal: %v", err)
	}
	if rec["password"] != "***" {
		t.Errorf("password = %v; want ***", rec["password"])
	}
	if rec["authorization"] != "***" {
		t.Errorf("authorization = %v; want ***", rec["authorization"])
	}
	if rec["user"] != "alice" {
		t.Errorf("user = %v; want alice (not masked)", rec["user"])
	}
}