Files
logcorrelator/internal/observability/logger_test.go
Jacquin Antoine 24aa84bd9c
Some checks failed
Build and Test / test (push) Has been cancelled
Build and Test / build (push) Has been cancelled
Build and Test / docker (push) Has been cancelled
test: add comprehensive tests to improve coverage
- observability: added tests for LogLevel.String(), Warn(), Warnf(), Infof(),
  Debugf(), Error(), WithFields(), and concurrent access patterns
- file: added tests for Reopen(), Close(), empty/whitespace paths,
  validateFilePath allowed/rejected paths, concurrent writes, Flush(),
  and marshal errors
- config: added tests for TimeWindowConfig.GetDuration(),
  CorrelationConfig getters, validation scenarios (no inputs, no outputs,
  duplicate sockets, ClickHouse validation), and LogConfig.GetLevel()

Coverage improvements:
- observability: 57.7% → 79.5%
- file: 68.6% → 78.6%
- config: 69.8% → 97.7%
- total: 68.6% → 74.4%

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-03-02 22:52:09 +01:00

307 lines
6.6 KiB
Go

package observability
import (
"testing"
)
func TestNewLogger(t *testing.T) {
logger := NewLogger("test")
if logger == nil {
t.Fatal("expected non-nil logger")
}
if logger.prefix != "test" {
t.Errorf("expected prefix 'test', got %s", logger.prefix)
}
}
func TestLogger_Info(t *testing.T) {
logger := NewLoggerWithLevel("test", "INFO")
// INFO should be logged
if !logger.ShouldLog(INFO) {
t.Error("INFO should be enabled")
}
logger.Info("test message")
}
func TestLogger_Error(t *testing.T) {
logger := NewLoggerWithLevel("test", "ERROR")
// ERROR should be logged
if !logger.ShouldLog(ERROR) {
t.Error("ERROR should be enabled")
}
logger.Error("error message", nil)
}
func TestLogger_Debug(t *testing.T) {
logger := NewLogger("test")
// Debug should be disabled by default (INFO is default)
if logger.ShouldLog(DEBUG) {
t.Error("debug should be disabled by default")
}
// Enable debug level
logger.SetLevel("DEBUG")
if !logger.ShouldLog(DEBUG) {
t.Error("debug should be enabled after SetLevel(DEBUG)")
}
// Just verify ShouldLog works correctly
logger.Debug("test message") // Should not panic
}
func TestLogger_SetLevel(t *testing.T) {
logger := NewLogger("test")
// Default is INFO
if logger.minLevel != INFO {
t.Error("default level should be INFO")
}
// Test all levels
logger.SetLevel("DEBUG")
if !logger.ShouldLog(DEBUG) {
t.Error("DEBUG should be enabled after SetLevel(DEBUG)")
}
logger.SetLevel("INFO")
if logger.ShouldLog(DEBUG) {
t.Error("DEBUG should be disabled after SetLevel(INFO)")
}
if !logger.ShouldLog(INFO) {
t.Error("INFO should be enabled after SetLevel(INFO)")
}
logger.SetLevel("WARN")
if logger.ShouldLog(INFO) {
t.Error("INFO should be disabled after SetLevel(WARN)")
}
if !logger.ShouldLog(WARN) {
t.Error("WARN should be enabled after SetLevel(WARN)")
}
logger.SetLevel("ERROR")
if logger.ShouldLog(WARN) {
t.Error("WARN should be disabled after SetLevel(ERROR)")
}
if !logger.ShouldLog(ERROR) {
t.Error("ERROR should be enabled after SetLevel(ERROR)")
}
}
func TestParseLogLevel(t *testing.T) {
tests := []struct {
input string
expected LogLevel
}{
{"DEBUG", DEBUG},
{"debug", DEBUG},
{"INFO", INFO},
{"info", INFO},
{"WARN", WARN},
{"warn", WARN},
{"WARNING", WARN},
{"ERROR", ERROR},
{"error", ERROR},
{"", INFO}, // default
{"invalid", INFO}, // default
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
result := ParseLogLevel(tt.input)
if result != tt.expected {
t.Errorf("ParseLogLevel(%q) = %v, want %v", tt.input, result, tt.expected)
}
})
}
}
func TestLogger_WithFields(t *testing.T) {
logger := NewLogger("test")
fieldsLogger := logger.WithFields(map[string]any{
"key1": "value1",
"key2": 42,
})
if fieldsLogger == logger {
t.Error("expected different logger instance")
}
if len(fieldsLogger.fields) != 2 {
t.Errorf("expected 2 fields, got %d", len(fieldsLogger.fields))
}
}
func TestLogger_Name(t *testing.T) {
logger := NewLogger("myservice")
if logger.prefix != "myservice" {
t.Errorf("expected prefix 'myservice', got %s", logger.prefix)
}
}
func TestLogLevel_String(t *testing.T) {
tests := []struct {
level LogLevel
expected string
}{
{DEBUG, "DEBUG"},
{INFO, "INFO"},
{WARN, "WARN"},
{ERROR, "ERROR"},
{99, "INFO"}, // Unknown level defaults to INFO
}
for _, tt := range tests {
t.Run(tt.expected, func(t *testing.T) {
result := tt.level.String()
if result != tt.expected {
t.Errorf("LogLevel(%d).String() = %q, want %q", tt.level, result, tt.expected)
}
})
}
}
func TestLogger_Warn(t *testing.T) {
logger := NewLoggerWithLevel("test", "WARN")
// WARN should be logged
if !logger.ShouldLog(WARN) {
t.Error("WARN should be enabled")
}
logger.Warn("warning message") // Should not panic
}
func TestLogger_Warnf(t *testing.T) {
logger := NewLoggerWithLevel("test", "WARN")
logger.Warnf("formatted %s %d", "message", 42) // Should not panic
}
func TestLogger_Infof(t *testing.T) {
logger := NewLoggerWithLevel("test", "INFO")
logger.Infof("formatted %s %d", "message", 42) // Should not panic
}
func TestLogger_Debugf(t *testing.T) {
logger := NewLoggerWithLevel("test", "DEBUG")
logger.Debugf("formatted %s %d", "message", 42) // Should not panic
// Debug disabled
logger.SetLevel("INFO")
logger.Debugf("should not be logged") // Should not panic
}
func TestLogger_Error_WithError(t *testing.T) {
logger := NewLoggerWithLevel("test", "ERROR")
testErr := &testError{"test error"}
logger.Error("error occurred", testErr) // Should not panic
}
func TestLogger_Error_WithNilError(t *testing.T) {
logger := NewLoggerWithLevel("test", "ERROR")
logger.Error("error occurred", nil) // Should not panic
}
func TestLogger_WithFields_MergesFields(t *testing.T) {
logger := NewLogger("test")
logger.fields["existing"] = "value"
fieldsLogger := logger.WithFields(map[string]any{
"new": "field",
})
if len(fieldsLogger.fields) != 2 {
t.Errorf("expected 2 fields, got %d", len(fieldsLogger.fields))
}
if _, ok := fieldsLogger.fields["existing"]; !ok {
t.Error("expected existing field to be preserved")
}
if _, ok := fieldsLogger.fields["new"]; !ok {
t.Error("expected new field to be added")
}
}
func TestLogger_WithFields_EmptyFields(t *testing.T) {
logger := NewLogger("test")
fieldsLogger := logger.WithFields(map[string]any{})
if len(fieldsLogger.fields) != 0 {
t.Errorf("expected 0 fields, got %d", len(fieldsLogger.fields))
}
}
func TestLogger_ShouldLog_Concurrent(t *testing.T) {
logger := NewLoggerWithLevel("test", "DEBUG")
done := make(chan bool)
for i := 0; i < 10; i++ {
go func() {
_ = logger.ShouldLog(DEBUG)
done <- true
}()
}
for i := 0; i < 10; i++ {
<-done
}
}
func TestLogger_Log_Concurrent(t *testing.T) {
logger := NewLoggerWithLevel("test", "DEBUG")
done := make(chan bool)
for i := 0; i < 10; i++ {
go func(n int) {
logger.Debugf("message %d", n)
done <- true
}(i)
}
for i := 0; i < 10; i++ {
<-done
}
}
func TestLogger_WithFields_Concurrent(t *testing.T) {
logger := NewLogger("test")
done := make(chan bool)
for i := 0; i < 10; i++ {
go func(n int) {
_ = logger.WithFields(map[string]any{"key": n})
done <- true
}(i)
}
for i := 0; i < 10; i++ {
<-done
}
}
func TestLogger_SetLevel_Concurrent(t *testing.T) {
logger := NewLogger("test")
done := make(chan bool)
for i := 0; i < 10; i++ {
go func() {
logger.SetLevel("DEBUG")
logger.SetLevel("INFO")
done <- true
}()
}
for i := 0; i < 10; i++ {
<-done
}
}
// testError implements error for testing
type testError struct {
msg string
}
func (e *testError) Error() string {
return e.msg
}