blob: 84193fdae928e012367aae1d1abe985f0b4299bb [file] [log] [blame]
Akronfc281172025-06-12 15:14:05 +02001package logger
2
3import (
4 "bytes"
5 "encoding/json"
6 "path/filepath"
7 "strings"
8 "testing"
9
10 "github.com/korap/korap-mcp/config"
11 "github.com/rs/zerolog"
12 "github.com/stretchr/testify/assert"
13)
14
15func TestSetupLogger(t *testing.T) {
16 t.Run("should setup logger with default configuration", func(t *testing.T) {
17 cfg := &config.LoggingConfig{
18 Level: "info",
19 Format: "json",
20 }
21
22 logger, err := SetupLogger(cfg)
23 assert.NoError(t, err)
24 assert.NotNil(t, logger)
25 })
26
27 t.Run("should setup logger with text format", func(t *testing.T) {
28 cfg := &config.LoggingConfig{
29 Level: "debug",
30 Format: "text",
31 }
32
33 logger, err := SetupLogger(cfg)
34 assert.NoError(t, err)
35 assert.NotNil(t, logger)
36 })
37
38 t.Run("should setup logger with file output", func(t *testing.T) {
39 // Create temporary file
40 tmpDir := t.TempDir()
41 logFile := filepath.Join(tmpDir, "test.log")
42
43 cfg := &config.LoggingConfig{
44 Level: "warn",
45 Format: "json",
46 File: logFile,
47 }
48
49 logger, err := SetupLogger(cfg)
50 assert.NoError(t, err)
51 assert.NotNil(t, logger)
52
53 // Test logging to file
54 logger.Info().Msg("test message")
55
56 // Verify file was created
57 assert.FileExists(t, logFile)
58 })
59
60 t.Run("should return error for invalid log level", func(t *testing.T) {
61 cfg := &config.LoggingConfig{
62 Level: "invalid",
63 Format: "json",
64 }
65
66 _, err := SetupLogger(cfg)
67 assert.Error(t, err)
68 assert.Contains(t, strings.ToLower(err.Error()), "unknown level")
69 })
70
71 t.Run("should return error for invalid file path", func(t *testing.T) {
72 cfg := &config.LoggingConfig{
73 Level: "info",
74 Format: "json",
75 File: "/invalid/path/test.log",
76 }
77
78 _, err := SetupLogger(cfg)
79 assert.Error(t, err)
80 })
81}
82
83func TestGetLogger(t *testing.T) {
84 t.Run("should return configured logger", func(t *testing.T) {
85 cfg := &config.LoggingConfig{
86 Level: "debug",
87 Format: "json",
88 }
89
90 logger := GetLogger(cfg)
91 assert.NotNil(t, logger)
92 })
93
94 t.Run("should return fallback logger on configuration error", func(t *testing.T) {
95 cfg := &config.LoggingConfig{
96 Level: "invalid",
97 Format: "json",
98 }
99
100 logger := GetLogger(cfg)
101 assert.NotNil(t, logger)
102 })
103}
104
105func TestLoggerOutput(t *testing.T) {
106 t.Run("should produce structured JSON output", func(t *testing.T) {
107 var buf bytes.Buffer
108
109 // Create a logger that writes to our buffer
110 logger := zerolog.New(&buf).With().Timestamp().Logger()
111
112 // Log a message
113 logger.Info().Str("test", "value").Msg("test message")
114
115 // Parse the JSON output
Akron708f3912025-06-17 12:26:02 +0200116 var logEntry map[string]any
Akronfc281172025-06-12 15:14:05 +0200117 err := json.Unmarshal(buf.Bytes(), &logEntry)
118 assert.NoError(t, err)
119
120 // Verify log structure
121 assert.Equal(t, "info", logEntry["level"])
122 assert.Equal(t, "test message", logEntry["message"])
123 assert.Equal(t, "value", logEntry["test"])
124 assert.Contains(t, logEntry, "time")
125 })
126
127 t.Run("should respect log levels", func(t *testing.T) {
128 var buf bytes.Buffer
129
130 // Set global level to warn
131 zerolog.SetGlobalLevel(zerolog.WarnLevel)
132 logger := zerolog.New(&buf).With().Timestamp().Logger()
133
134 // Try to log at different levels
135 logger.Debug().Msg("debug message")
136 logger.Info().Msg("info message")
137 logger.Warn().Msg("warn message")
138
139 // Only warn message should be present
140 output := buf.String()
141 assert.NotContains(t, output, "debug message")
142 assert.NotContains(t, output, "info message")
143 assert.Contains(t, output, "warn message")
144
145 // Reset global level
146 zerolog.SetGlobalLevel(zerolog.InfoLevel)
147 })
148}
149
150func TestLoggerIntegration(t *testing.T) {
151 t.Run("should work with all supported log levels", func(t *testing.T) {
152 levels := []string{"trace", "debug", "info", "warn", "error", "fatal", "panic"}
153
154 for _, level := range levels {
155 cfg := &config.LoggingConfig{
156 Level: level,
157 Format: "json",
158 }
159
160 logger, err := SetupLogger(cfg)
161 if level == "panic" {
162 // panic level might not be supported in all contexts
163 continue
164 }
165 assert.NoError(t, err, "Failed to setup logger with level: %s", level)
166 assert.NotNil(t, logger, "Logger should not be nil for level: %s", level)
167 }
168 })
169
170 t.Run("should work with different output formats", func(t *testing.T) {
171 formats := []string{"json", "text"}
172
173 for _, format := range formats {
174 cfg := &config.LoggingConfig{
175 Level: "info",
176 Format: format,
177 }
178
179 logger, err := SetupLogger(cfg)
180 assert.NoError(t, err, "Failed to setup logger with format: %s", format)
181 assert.NotNil(t, logger, "Logger should not be nil for format: %s", format)
182 }
183 })
184}
185
186func TestLoggerErrorHandling(t *testing.T) {
187 t.Run("should handle errors gracefully in GetLogger", func(t *testing.T) {
188 // Test with various invalid configurations
189 invalidConfigs := []*config.LoggingConfig{
190 {Level: "invalid-level", Format: "json"},
191 {Level: "info", Format: "json", File: "/dev/null/invalid"},
192 }
193
194 for _, cfg := range invalidConfigs {
195 logger := GetLogger(cfg)
196 // Should not panic and should return a valid logger
197 assert.NotNil(t, logger)
198
199 // Should be able to log
200 logger.Info().Msg("test message")
201 }
202 })
203}