Skip to content

Commit dc00de6

Browse files
committed
add tests
1 parent cfdf377 commit dc00de6

File tree

2 files changed

+306
-0
lines changed

2 files changed

+306
-0
lines changed
Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
package log
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"errors"
7+
"log/slog"
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
func TestNewSlogLogger(t *testing.T) {
15+
slogger := slog.New(slog.NewTextHandler(&bytes.Buffer{}, nil))
16+
logger := NewSlogLogger(slogger, InfoLevel)
17+
18+
assert.NotNil(t, logger)
19+
assert.Equal(t, InfoLevel, logger.Level())
20+
}
21+
22+
func TestSlogLogger_Level(t *testing.T) {
23+
tests := []struct {
24+
name string
25+
level Level
26+
}{
27+
{"debug", DebugLevel},
28+
{"info", InfoLevel},
29+
{"warn", WarnLevel},
30+
{"error", ErrorLevel},
31+
{"fatal", FatalLevel},
32+
}
33+
34+
for _, tt := range tests {
35+
t.Run(tt.name, func(t *testing.T) {
36+
buf := &bytes.Buffer{}
37+
slogger := slog.New(slog.NewTextHandler(buf, nil))
38+
logger := NewSlogLogger(slogger, tt.level)
39+
40+
assert.Equal(t, tt.level, logger.Level())
41+
})
42+
}
43+
}
44+
45+
func TestSlogLogger_ConvenienceMethods(t *testing.T) {
46+
buf := &bytes.Buffer{}
47+
slogger := slog.New(slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}))
48+
logger := NewSlogLogger(slogger, DebugLevel)
49+
50+
tests := []struct {
51+
name string
52+
logFunc func(string, ...slog.Attr)
53+
level string
54+
}{
55+
{"Debug", logger.Debug, "DEBUG"},
56+
{"Info", logger.Info, "INFO"},
57+
{"Warn", logger.Warn, "WARN"},
58+
{"Error", logger.Error, "ERROR"},
59+
}
60+
61+
for _, tt := range tests {
62+
t.Run(tt.name, func(t *testing.T) {
63+
buf.Reset()
64+
tt.logFunc("test message", slog.String("key", "value"))
65+
66+
output := buf.String()
67+
assert.Contains(t, output, "test message")
68+
assert.Contains(t, output, tt.level)
69+
assert.Contains(t, output, "key=value")
70+
})
71+
}
72+
}
73+
74+
func TestSlogLogger_Fatal(t *testing.T) {
75+
buf := &bytes.Buffer{}
76+
slogger := slog.New(slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}))
77+
logger := NewSlogLogger(slogger, DebugLevel)
78+
79+
// Fatal should panic after logging
80+
assert.Panics(t, func() {
81+
logger.Fatal("fatal message")
82+
})
83+
84+
// Verify the message was logged before panic
85+
assert.Contains(t, buf.String(), "fatal message")
86+
}
87+
88+
func TestSlogLogger_WithFields(t *testing.T) {
89+
buf := &bytes.Buffer{}
90+
slogger := slog.New(slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}))
91+
logger := NewSlogLogger(slogger, InfoLevel)
92+
93+
// Add fields and log
94+
loggerWithFields := logger.WithFields(
95+
slog.String("service", "test-service"),
96+
slog.Int("port", 8080),
97+
)
98+
99+
loggerWithFields.Info("message with fields")
100+
101+
output := buf.String()
102+
assert.Contains(t, output, "message with fields")
103+
assert.Contains(t, output, "service")
104+
assert.Contains(t, output, "test-service")
105+
assert.Contains(t, output, "port")
106+
assert.Contains(t, output, "8080")
107+
108+
// Original logger should not have the fields
109+
buf.Reset()
110+
logger.Info("message without fields")
111+
output = buf.String()
112+
assert.NotContains(t, output, "service")
113+
}
114+
115+
func TestSlogLogger_WithError(t *testing.T) {
116+
buf := &bytes.Buffer{}
117+
slogger := slog.New(slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}))
118+
logger := NewSlogLogger(slogger, InfoLevel)
119+
120+
testErr := errors.New("test error message")
121+
loggerWithError := logger.WithError(testErr)
122+
123+
loggerWithError.Error("operation failed")
124+
125+
output := buf.String()
126+
assert.Contains(t, output, "operation failed")
127+
assert.Contains(t, output, "error")
128+
assert.Contains(t, output, "test error message")
129+
}
130+
131+
func TestSlogLogger_Named(t *testing.T) {
132+
buf := &bytes.Buffer{}
133+
slogger := slog.New(slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}))
134+
logger := NewSlogLogger(slogger, InfoLevel)
135+
136+
namedLogger := logger.Named("my-component")
137+
namedLogger.Info("component message")
138+
139+
output := buf.String()
140+
assert.Contains(t, output, "component message")
141+
assert.Contains(t, output, "logger")
142+
assert.Contains(t, output, "my-component")
143+
}
144+
145+
func TestSlogLogger_WithLevel(t *testing.T) {
146+
buf := &bytes.Buffer{}
147+
slogger := slog.New(slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}))
148+
logger := NewSlogLogger(slogger, InfoLevel)
149+
150+
// New logger with debug level
151+
debugLogger := logger.WithLevel(DebugLevel)
152+
153+
// Verify levels are correct
154+
assert.Equal(t, InfoLevel, logger.Level())
155+
assert.Equal(t, DebugLevel, debugLogger.Level())
156+
}
157+
158+
func TestSlogLogger_Sync(t *testing.T) {
159+
buf := &bytes.Buffer{}
160+
slogger := slog.New(slog.NewTextHandler(buf, nil))
161+
logger := NewSlogLogger(slogger, InfoLevel)
162+
163+
// Sync should not error for slog (no-op)
164+
err := logger.Sync()
165+
assert.NoError(t, err)
166+
}
167+
168+
func TestSlogLogger_Chaining(t *testing.T) {
169+
buf := &bytes.Buffer{}
170+
slogger := slog.New(slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}))
171+
logger := NewSlogLogger(slogger, DebugLevel)
172+
173+
// Chain multiple operations
174+
chainedLogger := logger.
175+
Named("service").
176+
WithFields(slog.String("version", "1.0")).
177+
WithLevel(InfoLevel)
178+
179+
chainedLogger.Info("chained message")
180+
181+
output := buf.String()
182+
assert.Contains(t, output, "chained message")
183+
assert.Contains(t, output, "service")
184+
assert.Contains(t, output, "version")
185+
assert.Contains(t, output, "1.0")
186+
}
187+
188+
func TestConvertLevel(t *testing.T) {
189+
tests := []struct {
190+
name string
191+
input Level
192+
expected slog.Level
193+
}{
194+
{"debug to slog debug", DebugLevel, slog.LevelDebug},
195+
{"info to slog info", InfoLevel, slog.LevelInfo},
196+
{"warn to slog warn", WarnLevel, slog.LevelWarn},
197+
{"error to slog error", ErrorLevel, slog.LevelError},
198+
{"fatal to slog error", FatalLevel, slog.LevelError}, // slog has no fatal, use error
199+
}
200+
201+
for _, tt := range tests {
202+
t.Run(tt.name, func(t *testing.T) {
203+
result := convertLevel(tt.input)
204+
assert.Equal(t, tt.expected, result)
205+
})
206+
}
207+
}
208+
209+
func TestConvertLevel_Unknown(t *testing.T) {
210+
// Unknown level should default to info
211+
unknownLevel := Level{"unknown"}
212+
result := convertLevel(unknownLevel)
213+
assert.Equal(t, slog.LevelInfo, result)
214+
}
215+
216+
func TestSlogLogger_ImplementsInterface(_ *testing.T) {
217+
// Compile-time check that SlogLogger implements Logger
218+
var _ Logger = (*SlogLogger)(nil)
219+
}
220+
221+
func TestSlogLogger_LogWithContext(t *testing.T) {
222+
buf := &bytes.Buffer{}
223+
slogger := slog.New(slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}))
224+
logger := NewSlogLogger(slogger, DebugLevel)
225+
226+
ctx := context.Background()
227+
logger.Log(ctx, InfoLevel, "context message", slog.String("trace_id", "abc123"))
228+
229+
output := buf.String()
230+
assert.Contains(t, output, "context message")
231+
assert.Contains(t, output, "trace_id")
232+
assert.Contains(t, output, "abc123")
233+
}
234+
235+
func TestSlogLogger_WithFields_PreservesLevel(t *testing.T) {
236+
buf := &bytes.Buffer{}
237+
slogger := slog.New(slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}))
238+
logger := NewSlogLogger(slogger, WarnLevel)
239+
240+
withFields := logger.WithFields(slog.String("key", "value"))
241+
242+
// Level should be preserved
243+
assert.Equal(t, WarnLevel, withFields.Level())
244+
245+
// Should log with the added field
246+
withFields.Warn("should appear")
247+
assert.Contains(t, buf.String(), "should appear")
248+
assert.Contains(t, buf.String(), "key=value")
249+
}
250+
251+
func TestSlogLogger_WithError_NilSafe(t *testing.T) {
252+
buf := &bytes.Buffer{}
253+
slogger := slog.New(slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}))
254+
logger := NewSlogLogger(slogger, InfoLevel)
255+
256+
// WithError with nil should return the same logger (no error field added)
257+
require.NotPanics(t, func() {
258+
result := logger.WithError(nil)
259+
assert.NotNil(t, result)
260+
// Should return the same logger instance when error is nil
261+
assert.Equal(t, logger, result)
262+
})
263+
}
264+
265+
func TestSlogLogger_MultipleFields(t *testing.T) {
266+
buf := &bytes.Buffer{}
267+
slogger := slog.New(slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}))
268+
logger := NewSlogLogger(slogger, DebugLevel)
269+
270+
logger.Info("multi-field message",
271+
slog.String("string_field", "value"),
272+
slog.Int("int_field", 42),
273+
slog.Bool("bool_field", true),
274+
slog.Float64("float_field", 3.14),
275+
)
276+
277+
output := buf.String()
278+
assert.Contains(t, output, "multi-field message")
279+
assert.Contains(t, output, "string_field")
280+
assert.Contains(t, output, "int_field")
281+
assert.Contains(t, output, "bool_field")
282+
assert.Contains(t, output, "float_field")
283+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package observability
2+
3+
import (
4+
"testing"
5+
6+
"github.com/github/github-mcp-server/pkg/observability/log"
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestNewExporters(t *testing.T) {
11+
logger := log.NewNoopLogger()
12+
exp := NewExporters(logger)
13+
14+
assert.NotNil(t, exp)
15+
assert.Equal(t, logger, exp.Logger)
16+
}
17+
18+
func TestExporters_WithNilLogger(t *testing.T) {
19+
exp := NewExporters(nil)
20+
21+
assert.NotNil(t, exp)
22+
assert.Nil(t, exp.Logger)
23+
}

0 commit comments

Comments
 (0)