A container registry that uses the AT Protocol for manifest storage and S3 for blob storage.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at codeberg-source 397 lines 9.8 kB view raw
1package logging 2 3import ( 4 "bytes" 5 "log/slog" 6 "strings" 7 "testing" 8) 9 10// captureLogOutput runs a function and captures slog output 11func captureLogOutput(level string, logFunc func()) string { 12 var buf bytes.Buffer 13 14 // Save original logger 15 originalLogger := slog.Default() 16 defer slog.SetDefault(originalLogger) 17 18 // Parse level 19 var logLevel slog.Level 20 switch strings.ToLower(strings.TrimSpace(level)) { 21 case "debug": 22 logLevel = slog.LevelDebug 23 case "info", "": 24 logLevel = slog.LevelInfo 25 case "warn", "warning": 26 logLevel = slog.LevelWarn 27 case "error": 28 logLevel = slog.LevelError 29 default: 30 logLevel = slog.LevelInfo 31 } 32 33 // Create logger that writes to buffer 34 opts := &slog.HandlerOptions{ 35 Level: logLevel, 36 } 37 handler := slog.NewTextHandler(&buf, opts) 38 slog.SetDefault(slog.New(handler)) 39 40 // Run the function that generates logs 41 logFunc() 42 43 return buf.String() 44} 45 46func TestInitLogger(t *testing.T) { 47 // Save original logger to restore after all tests 48 originalLogger := slog.Default() 49 defer slog.SetDefault(originalLogger) 50 51 tests := []struct { 52 name string 53 level string 54 shouldLogDebug bool 55 shouldLogInfo bool 56 shouldLogWarn bool 57 shouldLogError bool 58 }{ 59 { 60 name: "debug level logs all", 61 level: "debug", 62 shouldLogDebug: true, 63 shouldLogInfo: true, 64 shouldLogWarn: true, 65 shouldLogError: true, 66 }, 67 { 68 name: "info level logs info and above", 69 level: "info", 70 shouldLogDebug: false, 71 shouldLogInfo: true, 72 shouldLogWarn: true, 73 shouldLogError: true, 74 }, 75 { 76 name: "warn level logs warn and above", 77 level: "warn", 78 shouldLogDebug: false, 79 shouldLogInfo: false, 80 shouldLogWarn: true, 81 shouldLogError: true, 82 }, 83 { 84 name: "error level logs only errors", 85 level: "error", 86 shouldLogDebug: false, 87 shouldLogInfo: false, 88 shouldLogWarn: false, 89 shouldLogError: true, 90 }, 91 { 92 name: "empty level defaults to info", 93 level: "", 94 shouldLogDebug: false, 95 shouldLogInfo: true, 96 shouldLogWarn: true, 97 shouldLogError: true, 98 }, 99 { 100 name: "invalid level defaults to info", 101 level: "invalid", 102 shouldLogDebug: false, 103 shouldLogInfo: true, 104 shouldLogWarn: true, 105 shouldLogError: true, 106 }, 107 { 108 name: "case insensitive - DEBUG", 109 level: "DEBUG", 110 shouldLogDebug: true, 111 shouldLogInfo: true, 112 shouldLogWarn: true, 113 shouldLogError: true, 114 }, 115 { 116 name: "case insensitive - WaRn", 117 level: "WaRn", 118 shouldLogDebug: false, 119 shouldLogInfo: false, 120 shouldLogWarn: true, 121 shouldLogError: true, 122 }, 123 { 124 name: "whitespace handling - ' info '", 125 level: " info ", 126 shouldLogDebug: false, 127 shouldLogInfo: true, 128 shouldLogWarn: true, 129 shouldLogError: true, 130 }, 131 { 132 name: "warning alias for warn", 133 level: "warning", 134 shouldLogDebug: false, 135 shouldLogInfo: false, 136 shouldLogWarn: true, 137 shouldLogError: true, 138 }, 139 } 140 141 for _, tt := range tests { 142 t.Run(tt.name, func(t *testing.T) { 143 output := captureLogOutput(tt.level, func() { 144 slog.Debug("debug message") 145 slog.Info("info message") 146 slog.Warn("warn message") 147 slog.Error("error message") 148 }) 149 150 // Check debug 151 if tt.shouldLogDebug { 152 if !strings.Contains(output, "debug message") { 153 t.Errorf("Expected debug message to be logged") 154 } 155 } else { 156 if strings.Contains(output, "debug message") { 157 t.Errorf("Did not expect debug message to be logged") 158 } 159 } 160 161 // Check info 162 if tt.shouldLogInfo { 163 if !strings.Contains(output, "info message") { 164 t.Errorf("Expected info message to be logged") 165 } 166 } else { 167 if strings.Contains(output, "info message") { 168 t.Errorf("Did not expect info message to be logged") 169 } 170 } 171 172 // Check warn 173 if tt.shouldLogWarn { 174 if !strings.Contains(output, "warn message") { 175 t.Errorf("Expected warn message to be logged") 176 } 177 } else { 178 if strings.Contains(output, "warn message") { 179 t.Errorf("Did not expect warn message to be logged") 180 } 181 } 182 183 // Check error 184 if tt.shouldLogError { 185 if !strings.Contains(output, "error message") { 186 t.Errorf("Expected error message to be logged") 187 } 188 } else { 189 if strings.Contains(output, "error message") { 190 t.Errorf("Did not expect error message to be logged") 191 } 192 } 193 }) 194 } 195} 196 197func TestInitLogger_LogLevels(t *testing.T) { 198 // Save original logger 199 originalLogger := slog.Default() 200 defer slog.SetDefault(originalLogger) 201 202 // Test that InitLogger actually calls slog.SetDefault 203 InitLogger("debug") 204 205 // Create a buffer to capture output 206 var buf bytes.Buffer 207 handler := slog.NewTextHandler(&buf, &slog.HandlerOptions{ 208 Level: slog.LevelDebug, 209 }) 210 slog.SetDefault(slog.New(handler)) 211 212 // Log at debug level 213 slog.Debug("test debug message") 214 215 // Verify output contains the message 216 if !strings.Contains(buf.String(), "test debug message") { 217 t.Error("Debug message not logged after InitLogger") 218 } 219} 220 221func TestSetupTestLogger(t *testing.T) { 222 // Save original logger 223 originalLogger := slog.Default() 224 defer slog.SetDefault(originalLogger) 225 226 // Test 1: SetupTestLogger suppresses INFO and DEBUG 227 cleanup := SetupTestLogger() 228 229 // Create a buffer to capture what SHOULD be discarded 230 // (but we can't really test io.Discard directly, so we'll test behavior) 231 232 // Log at different levels - since it's set to WARN, debug/info should be suppressed 233 // We can't capture io.Discard output, but we can verify the logger is configured correctly 234 logger := slog.Default() 235 236 // Verify handler is configured to discard 237 if logger == nil { 238 t.Error("Expected logger to be set") 239 } 240 241 // Test 2: Cleanup restores original logger 242 cleanup() 243 244 if slog.Default() != originalLogger { 245 t.Error("Expected cleanup to restore original logger") 246 } 247} 248 249func TestSetupTestLogger_LevelFiltering(t *testing.T) { 250 // Save original logger 251 originalLogger := slog.Default() 252 defer slog.SetDefault(originalLogger) 253 254 // Setup test logger (WARN level, io.Discard) 255 cleanup := SetupTestLogger() 256 defer cleanup() 257 258 // Replace the handler output with a buffer so we can test 259 // (This is a bit of a workaround since the real SetupTestLogger uses io.Discard) 260 var buf bytes.Buffer 261 handler := slog.NewTextHandler(&buf, &slog.HandlerOptions{ 262 Level: slog.LevelWarn, 263 }) 264 slog.SetDefault(slog.New(handler)) 265 266 // Log at different levels 267 slog.Debug("debug message") 268 slog.Info("info message") 269 slog.Warn("warn message") 270 slog.Error("error message") 271 272 output := buf.String() 273 274 // Debug and Info should NOT be in output (filtered by WARN level) 275 if strings.Contains(output, "debug message") { 276 t.Error("Debug message should be filtered out at WARN level") 277 } 278 if strings.Contains(output, "info message") { 279 t.Error("Info message should be filtered out at WARN level") 280 } 281 282 // Warn and Error SHOULD be in output 283 if !strings.Contains(output, "warn message") { 284 t.Error("Warn message should be logged at WARN level") 285 } 286 if !strings.Contains(output, "error message") { 287 t.Error("Error message should be logged at WARN level") 288 } 289} 290 291func TestSetupTestLogger_UsageWithTCleanup(t *testing.T) { 292 // This test demonstrates the intended usage pattern 293 originalLogger := slog.Default() 294 295 // Simulate using SetupTestLogger in a test 296 cleanup := SetupTestLogger() 297 t.Cleanup(cleanup) 298 299 // Logger should be different now 300 if slog.Default() == originalLogger { 301 t.Error("Expected logger to be changed after SetupTestLogger") 302 } 303 304 // When test ends, t.Cleanup will run and restore the logger 305 // We can't directly test this since it happens after the test function returns, 306 // but we're verifying the pattern works 307} 308 309func TestSetupTestLogger_MultipleCallsIndependent(t *testing.T) { 310 // Save original logger 311 originalLogger := slog.Default() 312 defer slog.SetDefault(originalLogger) 313 314 // First call 315 cleanup1 := SetupTestLogger() 316 logger1 := slog.Default() 317 318 // Second call 319 cleanup2 := SetupTestLogger() 320 logger2 := slog.Default() 321 322 // Loggers might be different instances 323 if logger1 == nil || logger2 == nil { 324 t.Error("Expected loggers to be set") 325 } 326 327 // Cleanup in reverse order (like defer) 328 cleanup2() 329 cleanup1() 330} 331 332func TestInitLogger_OutputFormat(t *testing.T) { 333 // Save original logger 334 originalLogger := slog.Default() 335 defer slog.SetDefault(originalLogger) 336 337 var buf bytes.Buffer 338 339 // Configure logger with buffer 340 opts := &slog.HandlerOptions{ 341 Level: slog.LevelInfo, 342 } 343 handler := slog.NewTextHandler(&buf, opts) 344 slog.SetDefault(slog.New(handler)) 345 346 // Log a message 347 slog.Info("test message", "key", "value") 348 349 output := buf.String() 350 351 // Verify text format (not JSON) 352 if !strings.Contains(output, "test message") { 353 t.Error("Expected message in output") 354 } 355 if !strings.Contains(output, "key=value") { 356 t.Error("Expected key=value in text format") 357 } 358 // Should NOT be JSON 359 if strings.HasPrefix(output, "{") { 360 t.Error("Expected text format, not JSON") 361 } 362} 363 364func BenchmarkInitLogger(b *testing.B) { 365 originalLogger := slog.Default() 366 defer slog.SetDefault(originalLogger) 367 368 b.ResetTimer() 369 for i := 0; i < b.N; i++ { 370 InitLogger("info") 371 } 372} 373 374func BenchmarkSetupTestLogger(b *testing.B) { 375 originalLogger := slog.Default() 376 defer slog.SetDefault(originalLogger) 377 378 b.ResetTimer() 379 for i := 0; i < b.N; i++ { 380 cleanup := SetupTestLogger() 381 cleanup() 382 } 383} 384 385// Example test showing how to use SetupTestLogger 386func ExampleSetupTestLogger() { 387 // In a test function: 388 cleanup := SetupTestLogger() 389 defer cleanup() 390 391 // Now logs at DEBUG and INFO are suppressed 392 slog.Debug("This won't show") 393 slog.Info("This won't show either") 394 slog.Warn("This WILL show") 395 396 // cleanup() will restore the original logger when defer runs 397}