1package logging
2
3import (
4 "os"
5 "path/filepath"
6 "testing"
7
8 "github.com/rs/zerolog"
9 "github.com/stretchr/testify/assert"
10 "github.com/stretchr/testify/require"
11 "gordon/internal/config"
12)
13
14func TestSetup_LoggingDisabled(t *testing.T) {
15 cfg := &config.Config{
16 Logging: config.LoggingConfig{
17 Enabled: false,
18 },
19 }
20
21 err := Setup(cfg)
22 require.NoError(t, err)
23
24 // Should have console loggers set
25 assert.NotNil(t, MainLogger)
26 assert.NotNil(t, ProxyLogger)
27}
28
29func TestSetup_LoggingEnabled(t *testing.T) {
30 // Create temporary directory for test logs
31 tempDir := t.TempDir()
32
33 cfg := &config.Config{
34 Logging: config.LoggingConfig{
35 Enabled: true,
36 Dir: tempDir,
37 ContainerLogDir: "containers",
38 MainLogFile: "gordon.log",
39 ProxyLogFile: "proxy.log",
40 Level: "info",
41 MaxSize: 10,
42 MaxBackups: 3,
43 MaxAge: 7,
44 Compress: true,
45 },
46 }
47
48 err := Setup(cfg)
49 require.NoError(t, err)
50
51 // Check directories were created
52 assert.DirExists(t, tempDir)
53 assert.DirExists(t, filepath.Join(tempDir, "containers"))
54
55 // Check directory permissions (allow for different umask settings)
56 info, err := os.Stat(tempDir)
57 require.NoError(t, err)
58 assert.True(t, info.Mode().IsDir())
59
60 containerDirInfo, err := os.Stat(filepath.Join(tempDir, "containers"))
61 require.NoError(t, err)
62 assert.True(t, containerDirInfo.Mode().IsDir())
63
64 // Check loggers are initialized
65 assert.NotNil(t, MainLogger)
66 assert.NotNil(t, ProxyLogger)
67
68 // Check global log level was set
69 assert.Equal(t, zerolog.InfoLevel, zerolog.GlobalLevel())
70}
71
72func TestSetup_InvalidLogLevel(t *testing.T) {
73 tempDir := t.TempDir()
74
75 cfg := &config.Config{
76 Logging: config.LoggingConfig{
77 Enabled: true,
78 Dir: tempDir,
79 ContainerLogDir: "containers",
80 MainLogFile: "gordon.log",
81 ProxyLogFile: "proxy.log",
82 Level: "invalid_level",
83 MaxSize: 10,
84 MaxBackups: 3,
85 MaxAge: 7,
86 Compress: false,
87 },
88 }
89
90 err := Setup(cfg)
91 require.NoError(t, err)
92
93 // Should default to info level
94 assert.Equal(t, zerolog.InfoLevel, zerolog.GlobalLevel())
95}
96
97func TestSetup_CreateDirectoryError(t *testing.T) {
98 // Try to create logs in a path that doesn't exist and can't be created
99 cfg := &config.Config{
100 Logging: config.LoggingConfig{
101 Enabled: true,
102 Dir: "/invalid/path/that/cannot/be/created",
103 ContainerLogDir: "containers",
104 MainLogFile: "gordon.log",
105 ProxyLogFile: "proxy.log",
106 Level: "info",
107 MaxSize: 10,
108 MaxBackups: 3,
109 MaxAge: 7,
110 Compress: false,
111 },
112 }
113
114 err := Setup(cfg)
115 assert.Error(t, err)
116 assert.Contains(t, err.Error(), "failed to create logs directory")
117}
118
119func TestSetup_CreateContainerDirectoryError(t *testing.T) {
120 tempDir := t.TempDir()
121
122 // Create a file where the container directory should be
123 containerPath := filepath.Join(tempDir, "containers")
124 err := os.WriteFile(containerPath, []byte("blocking file"), 0644)
125 require.NoError(t, err)
126
127 cfg := &config.Config{
128 Logging: config.LoggingConfig{
129 Enabled: true,
130 Dir: tempDir,
131 ContainerLogDir: "containers",
132 MainLogFile: "gordon.log",
133 ProxyLogFile: "proxy.log",
134 Level: "info",
135 MaxSize: 10,
136 MaxBackups: 3,
137 MaxAge: 7,
138 Compress: false,
139 },
140 }
141
142 err = Setup(cfg)
143 assert.Error(t, err)
144 assert.Contains(t, err.Error(), "failed to create container logs directory")
145}
146
147func TestSetup_DifferentLogLevels(t *testing.T) {
148 testCases := []struct {
149 name string
150 level string
151 expected zerolog.Level
152 }{
153 {"debug level", "debug", zerolog.DebugLevel},
154 {"info level", "info", zerolog.InfoLevel},
155 {"warn level", "warn", zerolog.WarnLevel},
156 {"error level", "error", zerolog.ErrorLevel},
157 {"fatal level", "fatal", zerolog.FatalLevel},
158 {"panic level", "panic", zerolog.PanicLevel},
159 {"trace level", "trace", zerolog.TraceLevel},
160 {"disabled level", "disabled", zerolog.Disabled},
161 }
162
163 for _, tc := range testCases {
164 t.Run(tc.name, func(t *testing.T) {
165 tempDir := t.TempDir()
166
167 cfg := &config.Config{
168 Logging: config.LoggingConfig{
169 Enabled: true,
170 Dir: tempDir,
171 ContainerLogDir: "containers",
172 MainLogFile: "gordon.log",
173 ProxyLogFile: "proxy.log",
174 Level: tc.level,
175 MaxSize: 10,
176 MaxBackups: 3,
177 MaxAge: 7,
178 Compress: false,
179 },
180 }
181
182 err := Setup(cfg)
183 require.NoError(t, err)
184 assert.Equal(t, tc.expected, zerolog.GlobalLevel())
185 })
186 }
187}
188
189func TestGetContainerLogWriter_LoggingDisabled(t *testing.T) {
190 cfg := &config.Config{
191 Logging: config.LoggingConfig{
192 Enabled: false,
193 },
194 }
195
196 writer, err := GetContainerLogWriter(cfg, "container123", "test-container")
197 assert.Error(t, err)
198 assert.Nil(t, writer)
199 assert.Contains(t, err.Error(), "logging is disabled")
200}
201
202func TestGetContainerLogWriter_Success(t *testing.T) {
203 tempDir := t.TempDir()
204
205 cfg := &config.Config{
206 Logging: config.LoggingConfig{
207 Enabled: true,
208 Dir: tempDir,
209 ContainerLogDir: "containers",
210 MaxSize: 10,
211 MaxBackups: 3,
212 MaxAge: 7,
213 Compress: true,
214 },
215 }
216
217 containerID := "container123"
218 containerName := "test-container"
219
220 writer, err := GetContainerLogWriter(cfg, containerID, containerName)
221 require.NoError(t, err)
222 require.NotNil(t, writer)
223
224 // Check container log directory was created
225 containerLogDir := filepath.Join(tempDir, "containers")
226 assert.DirExists(t, containerLogDir)
227
228 // Check directory permissions
229 info, err := os.Stat(containerLogDir)
230 require.NoError(t, err)
231 assert.True(t, info.Mode().IsDir())
232
233 // Check writer configuration
234 expectedLogFile := filepath.Join(containerLogDir, "container123.log")
235 assert.Equal(t, expectedLogFile, writer.Filename)
236 assert.Equal(t, 10, writer.MaxSize)
237 assert.Equal(t, 3, writer.MaxBackups)
238 assert.Equal(t, 7, writer.MaxAge)
239 assert.True(t, writer.Compress)
240
241 // Check symlink was created
242 symlinkPath := filepath.Join(containerLogDir, "test-container.log")
243 _, err = os.Lstat(symlinkPath)
244 assert.NoError(t, err, "Symlink should exist")
245
246 // Verify symlink points to correct target
247 target, err := os.Readlink(symlinkPath)
248 require.NoError(t, err)
249 assert.Equal(t, "container123.log", target)
250}
251
252func TestGetContainerLogWriter_WithoutContainerName(t *testing.T) {
253 tempDir := t.TempDir()
254
255 cfg := &config.Config{
256 Logging: config.LoggingConfig{
257 Enabled: true,
258 Dir: tempDir,
259 ContainerLogDir: "containers",
260 MaxSize: 10,
261 MaxBackups: 3,
262 MaxAge: 7,
263 Compress: false,
264 },
265 }
266
267 containerID := "container456"
268
269 writer, err := GetContainerLogWriter(cfg, containerID, "")
270 require.NoError(t, err)
271 require.NotNil(t, writer)
272
273 // Check no symlink was created
274 containerLogDir := filepath.Join(tempDir, "containers")
275 symlinkPath := filepath.Join(containerLogDir, ".log")
276 _, err = os.Lstat(symlinkPath)
277 assert.Error(t, err, "No symlink should exist for empty container name")
278}
279
280func TestGetContainerLogWriter_ContainerNameSameAsID(t *testing.T) {
281 tempDir := t.TempDir()
282
283 cfg := &config.Config{
284 Logging: config.LoggingConfig{
285 Enabled: true,
286 Dir: tempDir,
287 ContainerLogDir: "containers",
288 MaxSize: 10,
289 MaxBackups: 3,
290 MaxAge: 7,
291 Compress: false,
292 },
293 }
294
295 containerID := "container789"
296
297 writer, err := GetContainerLogWriter(cfg, containerID, containerID)
298 require.NoError(t, err)
299 require.NotNil(t, writer)
300
301 // Check no symlink was created when name equals ID
302 containerLogDir := filepath.Join(tempDir, "containers")
303 symlinkPath := filepath.Join(containerLogDir, "container789.log")
304
305 // The symlink path would be the same as the actual log file, so no symlink should be created
306 info, err := os.Lstat(symlinkPath)
307 if err == nil {
308 // If it exists, it should be a regular file, not a symlink
309 assert.Equal(t, 0, info.Mode()&os.ModeSymlink, "Should not be a symlink")
310 }
311}
312
313func TestGetContainerLogWriter_ReplaceExistingSymlink(t *testing.T) {
314 tempDir := t.TempDir()
315 containerLogDir := filepath.Join(tempDir, "containers")
316 err := os.MkdirAll(containerLogDir, 0700)
317 require.NoError(t, err)
318
319 cfg := &config.Config{
320 Logging: config.LoggingConfig{
321 Enabled: true,
322 Dir: tempDir,
323 ContainerLogDir: "containers",
324 MaxSize: 10,
325 MaxBackups: 3,
326 MaxAge: 7,
327 Compress: false,
328 },
329 }
330
331 containerName := "test-container"
332 symlinkPath := filepath.Join(containerLogDir, "test-container.log")
333
334 // Create existing symlink
335 err = os.Symlink("old-target.log", symlinkPath)
336 require.NoError(t, err)
337
338 // Create new container log writer
339 containerID := "new-container123"
340 writer, err := GetContainerLogWriter(cfg, containerID, containerName)
341 require.NoError(t, err)
342 require.NotNil(t, writer)
343
344 // Check symlink was replaced
345 target, err := os.Readlink(symlinkPath)
346 require.NoError(t, err)
347 assert.Equal(t, "new-container123.log", target)
348}
349
350func TestGetContainerLogWriter_CreateDirectoryError(t *testing.T) {
351 // Create a file where the container directory should be
352 tempDir := t.TempDir()
353 containerPath := filepath.Join(tempDir, "containers")
354 err := os.WriteFile(containerPath, []byte("blocking file"), 0644)
355 require.NoError(t, err)
356
357 cfg := &config.Config{
358 Logging: config.LoggingConfig{
359 Enabled: true,
360 Dir: tempDir,
361 ContainerLogDir: "containers",
362 MaxSize: 10,
363 MaxBackups: 3,
364 MaxAge: 7,
365 Compress: false,
366 },
367 }
368
369 writer, err := GetContainerLogWriter(cfg, "container123", "test-container")
370 assert.Error(t, err)
371 assert.Nil(t, writer)
372 assert.Contains(t, err.Error(), "failed to create container log directory")
373}
374
375func TestClose(t *testing.T) {
376 // Test that Close() doesn't panic and can be called multiple times
377 assert.NotPanics(t, func() {
378 Close()
379 Close() // Should be safe to call multiple times
380 })
381}