This is an automated email from the ASF dual-hosted git repository.

jimin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-seata-go.git


The following commit(s) were added to refs/heads/master by this push:
     new 0747d09d test: improve test coverage for pkg/util/log (#964)
0747d09d is described below

commit 0747d09d6e290c8e29aa2d37a3561f7d008e889b
Author: EVERFID <[email protected]>
AuthorDate: Thu Nov 6 10:33:09 2025 +0800

    test: improve test coverage for pkg/util/log (#964)
---
 pkg/util/log/logging_test.go | 583 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 583 insertions(+)

diff --git a/pkg/util/log/logging_test.go b/pkg/util/log/logging_test.go
new file mode 100644
index 00000000..89a9231b
--- /dev/null
+++ b/pkg/util/log/logging_test.go
@@ -0,0 +1,583 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package log
+
+import (
+       "bytes"
+       "fmt"
+       "os"
+       "strings"
+       "sync"
+       "testing"
+       "time"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+       "go.uber.org/zap/zapcore"
+)
+
+var testLoggerMutex sync.Mutex
+
+func setupTest(t *testing.T) Logger {
+       t.Helper()
+
+       testLoggerMutex.Lock()
+
+       if log == nil {
+               Init()
+       }
+
+       originalLogger := log
+
+       t.Cleanup(func() {
+               log = originalLogger
+               testLoggerMutex.Unlock()
+       })
+
+       return originalLogger
+}
+
+// mockLogger implements Logger interface for testing
+type mockLogger struct {
+       debugCalls  []string
+       infoCalls   []string
+       warnCalls   []string
+       errorCalls  []string
+       panicCalls  []string
+       fatalCalls  []string
+       shouldPanic bool
+}
+
+func newMockLogger() *mockLogger {
+       return &mockLogger{
+               debugCalls: make([]string, 0),
+               infoCalls:  make([]string, 0),
+               warnCalls:  make([]string, 0),
+               errorCalls: make([]string, 0),
+               panicCalls: make([]string, 0),
+               fatalCalls: make([]string, 0),
+       }
+}
+
+func (m *mockLogger) Debug(v ...interface{}) {
+       m.debugCalls = append(m.debugCalls, fmt.Sprint(v...))
+}
+
+func (m *mockLogger) Debugf(format string, v ...interface{}) {
+       m.debugCalls = append(m.debugCalls, fmt.Sprintf(format, v...))
+}
+
+func (m *mockLogger) Info(v ...interface{}) {
+       m.infoCalls = append(m.infoCalls, fmt.Sprint(v...))
+}
+
+func (m *mockLogger) Infof(format string, v ...interface{}) {
+       m.infoCalls = append(m.infoCalls, fmt.Sprintf(format, v...))
+}
+
+func (m *mockLogger) Warn(v ...interface{}) {
+       m.warnCalls = append(m.warnCalls, fmt.Sprint(v...))
+}
+
+func (m *mockLogger) Warnf(format string, v ...interface{}) {
+       m.warnCalls = append(m.warnCalls, fmt.Sprintf(format, v...))
+}
+
+func (m *mockLogger) Error(v ...interface{}) {
+       m.errorCalls = append(m.errorCalls, fmt.Sprint(v...))
+}
+
+func (m *mockLogger) Errorf(format string, v ...interface{}) {
+       m.errorCalls = append(m.errorCalls, fmt.Sprintf(format, v...))
+}
+
+func (m *mockLogger) Panic(v ...interface{}) {
+       m.panicCalls = append(m.panicCalls, fmt.Sprint(v...))
+       if m.shouldPanic {
+               panic(fmt.Sprint(v...))
+       }
+}
+
+func (m *mockLogger) Panicf(format string, v ...interface{}) {
+       m.panicCalls = append(m.panicCalls, fmt.Sprintf(format, v...))
+       if m.shouldPanic {
+               panic(fmt.Sprintf(format, v...))
+       }
+}
+
+func (m *mockLogger) Fatal(v ...interface{}) {
+       m.fatalCalls = append(m.fatalCalls, fmt.Sprint(v...))
+}
+
+func (m *mockLogger) Fatalf(format string, v ...interface{}) {
+       m.fatalCalls = append(m.fatalCalls, fmt.Sprintf(format, v...))
+}
+
+// TestLogLevel tests the LogLevel type and its methods
+func TestLogLevel(t *testing.T) {
+       tests := []struct {
+               name     string
+               input    string
+               expected LogLevel
+               wantErr  bool
+       }{
+               {"debug lowercase", "debug", DebugLevel, false},
+               {"debug uppercase", "DEBUG", DebugLevel, false},
+               {"info lowercase", "info", InfoLevel, false},
+               {"info uppercase", "INFO", InfoLevel, false},
+               {"info empty", "", InfoLevel, false},
+               {"warn lowercase", "warn", WarnLevel, false},
+               {"warn uppercase", "WARN", WarnLevel, false},
+               {"error lowercase", "error", ErrorLevel, false},
+               {"error uppercase", "ERROR", ErrorLevel, false},
+               {"panic lowercase", "panic", PanicLevel, false},
+               {"panic uppercase", "PANIC", PanicLevel, false},
+               {"fatal lowercase", "fatal", FatalLevel, false},
+               {"fatal uppercase", "FATAL", FatalLevel, false},
+               {"invalid level", "invalid", 0, true},
+               {"mixed case", "DeBuG", DebugLevel, false},
+       }
+
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       var level LogLevel
+                       err := level.UnmarshalText([]byte(tt.input))
+
+                       if tt.wantErr {
+                               assert.Error(t, err, "UnmarshalText() should 
return error")
+                               assert.Contains(t, err.Error(), "unrecognized 
level")
+                       } else {
+                               assert.NoError(t, err, "UnmarshalText() should 
not return error")
+                               assert.Equal(t, tt.expected, level, "LogLevel 
should match expected value")
+                       }
+               })
+       }
+}
+
+func TestLogLevelUnmarshalTextNil(t *testing.T) {
+       var level *LogLevel
+       err := level.UnmarshalText([]byte("info"))
+
+       assert.Error(t, err, "UnmarshalText() on nil pointer should return 
error")
+       assert.Contains(t, err.Error(), "can't unmarshal a nil *Level")
+}
+
+func TestLogLevelValues(t *testing.T) {
+       // Test that LogLevel values match zapcore values
+       assert.Equal(t, LogLevel(zapcore.DebugLevel), DebugLevel, "DebugLevel 
value should match")
+       assert.Equal(t, LogLevel(zapcore.InfoLevel), InfoLevel, "InfoLevel 
value should match")
+       assert.Equal(t, LogLevel(zapcore.WarnLevel), WarnLevel, "WarnLevel 
value should match")
+       assert.Equal(t, LogLevel(zapcore.ErrorLevel), ErrorLevel, "ErrorLevel 
value should match")
+       assert.Equal(t, LogLevel(zapcore.PanicLevel), PanicLevel, "PanicLevel 
value should match")
+       assert.Equal(t, LogLevel(zapcore.FatalLevel), FatalLevel, "FatalLevel 
value should match")
+}
+
+func TestInit(t *testing.T) {
+       setupTest(t)
+
+       // Test Init
+       Init()
+
+       // Verify logger is initialized
+       assert.NotNil(t, log, "Init() should initialize logger")
+       assert.NotNil(t, zapLogger, "Init() should initialize zapLogger")
+}
+
+func TestSetLogger(t *testing.T) {
+       setupTest(t)
+
+       mock := newMockLogger()
+       SetLogger(mock)
+
+       assert.Equal(t, mock, log, "SetLogger() should set the logger")
+
+       // Test that the mock logger is used
+       Info("test")
+       assert.Len(t, mock.infoCalls, 1, "Mock logger should be called")
+}
+
+func TestGetLogger(t *testing.T) {
+       setupTest(t)
+
+       mock := newMockLogger()
+       SetLogger(mock)
+
+       retrieved := GetLogger()
+       assert.Equal(t, mock, retrieved, "GetLogger() should return the current 
logger")
+}
+
+func TestDebugFunctions(t *testing.T) {
+       setupTest(t)
+
+       mock := newMockLogger()
+       SetLogger(mock)
+
+       Debug("test", "message")
+       require.Len(t, mock.debugCalls, 1)
+       assert.Equal(t, "testmessage", mock.debugCalls[0])
+
+       Debugf("formatted %s %d", "test", 123)
+       require.Len(t, mock.debugCalls, 2)
+       assert.Equal(t, "formatted test 123", mock.debugCalls[1])
+}
+
+func TestInfoFunctions(t *testing.T) {
+       setupTest(t)
+
+       mock := newMockLogger()
+       SetLogger(mock)
+
+       Info("test", "message")
+       require.Len(t, mock.infoCalls, 1)
+       assert.Equal(t, "testmessage", mock.infoCalls[0])
+
+       Infof("formatted %s %d", "test", 123)
+       require.Len(t, mock.infoCalls, 2)
+       assert.Equal(t, "formatted test 123", mock.infoCalls[1])
+}
+
+func TestWarnFunctions(t *testing.T) {
+       setupTest(t)
+
+       mock := newMockLogger()
+       SetLogger(mock)
+
+       Warn("test", "message")
+       require.Len(t, mock.warnCalls, 1)
+       assert.Equal(t, "testmessage", mock.warnCalls[0])
+
+       Warnf("formatted %s %d", "test", 123)
+       require.Len(t, mock.warnCalls, 2)
+       assert.Equal(t, "formatted test 123", mock.warnCalls[1])
+}
+
+func TestErrorFunctions(t *testing.T) {
+       setupTest(t)
+
+       mock := newMockLogger()
+       SetLogger(mock)
+
+       Error("test", "message")
+       require.Len(t, mock.errorCalls, 1)
+       assert.Equal(t, "testmessage", mock.errorCalls[0])
+
+       Errorf("formatted %s %d", "test", 123)
+       require.Len(t, mock.errorCalls, 2)
+       assert.Equal(t, "formatted test 123", mock.errorCalls[1])
+}
+
+func TestErrorFunctionsWithNilLogger(t *testing.T) {
+       setupTest(t)
+
+       // Save stderr
+       oldStderr := os.Stderr
+       t.Cleanup(func() {
+               os.Stderr = oldStderr
+       })
+
+       // Redirect stderr to capture output
+       r, w, _ := os.Pipe()
+       os.Stderr = w
+
+       // Set logger to nil
+       log = nil
+
+       // Test Error
+       Error("test error")
+       w.Close()
+
+       var buf bytes.Buffer
+       buf.ReadFrom(r)
+       output := buf.String()
+
+       assert.Contains(t, output, "ERROR", "Error output should contain 
'ERROR'")
+       assert.Contains(t, output, "test error", "Error output should contain 
the message")
+}
+
+func TestErrorfWithNilLogger(t *testing.T) {
+       setupTest(t)
+
+       // Save stderr
+       oldStderr := os.Stderr
+       t.Cleanup(func() {
+               os.Stderr = oldStderr
+       })
+
+       // Redirect stderr to capture output
+       r, w, _ := os.Pipe()
+       os.Stderr = w
+
+       // Set logger to nil
+       log = nil
+
+       // Test Errorf
+       Errorf("formatted %s", "error")
+       w.Close()
+
+       var buf bytes.Buffer
+       buf.ReadFrom(r)
+       output := buf.String()
+
+       assert.Contains(t, output, "ERROR", "Errorf output should contain 
'ERROR'")
+       assert.Contains(t, output, "formatted error", "Errorf output should 
contain the formatted message")
+}
+
+func TestPanicFunctions(t *testing.T) {
+       setupTest(t)
+
+       mock := newMockLogger()
+       SetLogger(mock)
+
+       Panic("test", "panic")
+       require.Len(t, mock.panicCalls, 1)
+       assert.Equal(t, "testpanic", mock.panicCalls[0])
+
+       Panicf("formatted %s", "panic")
+       require.Len(t, mock.panicCalls, 2)
+       assert.Equal(t, "formatted panic", mock.panicCalls[1])
+}
+
+func TestPanicWithNilLogger(t *testing.T) {
+       setupTest(t)
+
+       // Set logger to nil
+       log = nil
+
+       // Test Panic with nil logger should panic
+       assert.Panics(t, func() {
+               Panic("test panic")
+       }, "Panic() with nil logger should panic")
+}
+
+func TestPanicfWithNilLogger(t *testing.T) {
+       setupTest(t)
+
+       // Set logger to nil
+       log = nil
+
+       // Test Panicf with nil logger should panic
+       assert.Panics(t, func() {
+               Panicf("test %s", "panic")
+       }, "Panicf() with nil logger should panic")
+}
+
+func TestFatalFunctions(t *testing.T) {
+       setupTest(t)
+
+       mock := newMockLogger()
+       SetLogger(mock)
+
+       Fatal("test", "fatal")
+       require.Len(t, mock.fatalCalls, 1)
+       assert.Equal(t, "testfatal", mock.fatalCalls[0])
+
+       Fatalf("formatted %s", "fatal")
+       require.Len(t, mock.fatalCalls, 2)
+       assert.Equal(t, "formatted fatal", mock.fatalCalls[1])
+}
+
+func TestLoggingWithNilLogger(t *testing.T) {
+       setupTest(t)
+
+       // Set logger to nil
+       log = nil
+
+       // Test that functions don't panic when logger is nil (except Panic 
functions)
+       assert.NotPanics(t, func() {
+               Debug("test")
+               Debugf("test %s", "debug")
+               Info("test")
+               Infof("test %s", "info")
+               Warn("test")
+               Warnf("test %s", "warn")
+       }, "Logging functions should not panic when logger is nil")
+}
+
+// mockArrayEncoder implements zapcore.PrimitiveArrayEncoder for testing
+type mockArrayEncoder struct {
+       elements []interface{}
+}
+
+func newMockArrayEncoder() *mockArrayEncoder {
+       return &mockArrayEncoder{
+               elements: make([]interface{}, 0),
+       }
+}
+
+func (m *mockArrayEncoder) AppendBool(v bool)              { m.elements = 
append(m.elements, v) }
+func (m *mockArrayEncoder) AppendByteString(v []byte)      { m.elements = 
append(m.elements, v) }
+func (m *mockArrayEncoder) AppendComplex128(v complex128)  { m.elements = 
append(m.elements, v) }
+func (m *mockArrayEncoder) AppendComplex64(v complex64)    { m.elements = 
append(m.elements, v) }
+func (m *mockArrayEncoder) AppendFloat64(v float64)        { m.elements = 
append(m.elements, v) }
+func (m *mockArrayEncoder) AppendFloat32(v float32)        { m.elements = 
append(m.elements, v) }
+func (m *mockArrayEncoder) AppendInt(v int)                { m.elements = 
append(m.elements, v) }
+func (m *mockArrayEncoder) AppendInt64(v int64)            { m.elements = 
append(m.elements, v) }
+func (m *mockArrayEncoder) AppendInt32(v int32)            { m.elements = 
append(m.elements, v) }
+func (m *mockArrayEncoder) AppendInt16(v int16)            { m.elements = 
append(m.elements, v) }
+func (m *mockArrayEncoder) AppendInt8(v int8)              { m.elements = 
append(m.elements, v) }
+func (m *mockArrayEncoder) AppendString(v string)          { m.elements = 
append(m.elements, v) }
+func (m *mockArrayEncoder) AppendUint(v uint)              { m.elements = 
append(m.elements, v) }
+func (m *mockArrayEncoder) AppendUint64(v uint64)          { m.elements = 
append(m.elements, v) }
+func (m *mockArrayEncoder) AppendUint32(v uint32)          { m.elements = 
append(m.elements, v) }
+func (m *mockArrayEncoder) AppendUint16(v uint16)          { m.elements = 
append(m.elements, v) }
+func (m *mockArrayEncoder) AppendUint8(v uint8)            { m.elements = 
append(m.elements, v) }
+func (m *mockArrayEncoder) AppendUintptr(v uintptr)        { m.elements = 
append(m.elements, v) }
+func (m *mockArrayEncoder) AppendDuration(v time.Duration) { m.elements = 
append(m.elements, v) }
+func (m *mockArrayEncoder) AppendTime(v time.Time)         { m.elements = 
append(m.elements, v) }
+
+func TestEncodeTime(t *testing.T) {
+       enc := newMockArrayEncoder()
+       testTime := time.Date(2024, 1, 1, 12, 30, 45, 123456789, time.UTC)
+
+       encodeTime(testTime, enc)
+
+       require.Len(t, enc.elements, 1, "Should append one element")
+       timeStr, ok := enc.elements[0].(string)
+       require.True(t, ok, "Encoded element should be a string")
+       assert.Equal(t, "2024-01-01 12:30:45.123 ", timeStr, "Time format 
should match expected format")
+}
+
+func TestEncodeCaller(t *testing.T) {
+       enc := newMockArrayEncoder()
+       caller := zapcore.EntryCaller{
+               Defined: true,
+               PC:      0,
+               File:    "/path/to/file.go",
+               Line:    42,
+       }
+
+       encodeCaller(caller, enc)
+
+       require.Len(t, enc.elements, 1, "Should append one element")
+       callerStr, ok := enc.elements[0].(string)
+       require.True(t, ok, "Encoded element should be a string")
+       assert.Len(t, callerStr, 45, "Caller should be formatted to 45 
characters")
+       assert.Contains(t, callerStr, caller.TrimmedPath(), "Should contain 
trimmed path")
+}
+
+func TestLoggerInterface(t *testing.T) {
+       // Verify that mockLogger implements Logger interface
+       var _ Logger = (*mockLogger)(nil)
+
+       // Test all interface methods
+       mock := newMockLogger()
+
+       mock.Debug("test")
+       mock.Debugf("test %s", "format")
+       mock.Info("test")
+       mock.Infof("test %s", "format")
+       mock.Warn("test")
+       mock.Warnf("test %s", "format")
+       mock.Error("test")
+       mock.Errorf("test %s", "format")
+       mock.Panic("test")
+       mock.Panicf("test %s", "format")
+       mock.Fatal("test")
+       mock.Fatalf("test %s", "format")
+
+       // Verify calls were recorded
+       assert.Len(t, mock.debugCalls, 2, "Debug methods should be called 
twice")
+       assert.Len(t, mock.infoCalls, 2, "Info methods should be called twice")
+       assert.Len(t, mock.warnCalls, 2, "Warn methods should be called twice")
+       assert.Len(t, mock.errorCalls, 2, "Error methods should be called 
twice")
+       assert.Len(t, mock.panicCalls, 2, "Panic methods should be called 
twice")
+       assert.Len(t, mock.fatalCalls, 2, "Fatal methods should be called 
twice")
+}
+
+func TestZapLoggerConfig(t *testing.T) {
+       // Test that zapLoggerConfig is properly initialized
+       assert.True(t, zapLoggerConfig.Development, "Development mode should be 
enabled")
+       assert.Equal(t, "console", zapLoggerConfig.Encoding, "Encoding should 
be 'console'")
+       assert.NotEmpty(t, zapLoggerConfig.OutputPaths, "OutputPaths should not 
be empty")
+       assert.NotEmpty(t, zapLoggerConfig.ErrorOutputPaths, "ErrorOutputPaths 
should not be empty")
+       assert.Contains(t, zapLoggerConfig.OutputPaths, "stderr", "OutputPaths 
should contain stderr")
+       assert.Contains(t, zapLoggerConfig.ErrorOutputPaths, "stderr", 
"ErrorOutputPaths should contain stderr")
+}
+
+func TestZapLoggerEncoderConfig(t *testing.T) {
+       // Test that zapLoggerEncoderConfig is properly initialized
+       assert.Equal(t, "time", zapLoggerEncoderConfig.TimeKey, "TimeKey should 
be 'time'")
+       assert.Equal(t, "level", zapLoggerEncoderConfig.LevelKey, "LevelKey 
should be 'level'")
+       assert.Equal(t, "message", zapLoggerEncoderConfig.MessageKey, 
"MessageKey should be 'message'")
+       assert.Equal(t, "logger", zapLoggerEncoderConfig.NameKey, "NameKey 
should be 'logger'")
+       assert.Equal(t, "caller", zapLoggerEncoderConfig.CallerKey, "CallerKey 
should be 'caller'")
+       assert.Equal(t, "stacktrace", zapLoggerEncoderConfig.StacktraceKey, 
"StacktraceKey should be 'stacktrace'")
+       assert.NotNil(t, zapLoggerEncoderConfig.EncodeTime, "EncodeTime should 
not be nil")
+       assert.NotNil(t, zapLoggerEncoderConfig.EncodeCaller, "EncodeCaller 
should not be nil")
+       assert.NotNil(t, zapLoggerEncoderConfig.EncodeLevel, "EncodeLevel 
should not be nil")
+       assert.NotNil(t, zapLoggerEncoderConfig.EncodeDuration, "EncodeDuration 
should not be nil")
+}
+
+func TestLogTimeFmt(t *testing.T) {
+       // Test that the time format constant is correct
+       assert.Equal(t, "2006-01-02 15:04:05.000 ", logTmFmt, "logTmFmt should 
match expected format")
+
+       // Test formatting with the constant
+       testTime := time.Date(2024, 12, 25, 13, 45, 30, 999999999, time.UTC)
+       formatted := testTime.Format(logTmFmt)
+
+       assert.True(t, strings.HasPrefix(formatted, "2024-12-25 13:45:30.999"), 
"Time format should be correct")
+}
+
+func TestErrorRecovery(t *testing.T) {
+       setupTest(t)
+
+       // Create a mock logger that panics
+       mock := newMockLogger()
+       mock.shouldPanic = true
+       SetLogger(mock)
+
+       // Test that Error function recovers from panic
+       assert.NotPanics(t, func() {
+               Error("test error")
+       }, "Error() should recover from panic")
+
+       // Test that Errorf function recovers from panic
+       assert.NotPanics(t, func() {
+               Errorf("test %s", "error")
+       }, "Errorf() should recover from panic")
+}
+
+func TestAllLogLevelsWithMock(t *testing.T) {
+       setupTest(t)
+
+       mock := newMockLogger()
+       SetLogger(mock)
+
+       // Test all log levels
+       Debug("debug msg")
+       Debugf("debug %s", "formatted")
+       Info("info msg")
+       Infof("info %s", "formatted")
+       Warn("warn msg")
+       Warnf("warn %s", "formatted")
+       Error("error msg")
+       Errorf("error %s", "formatted")
+       Panic("panic msg")
+       Panicf("panic %s", "formatted")
+       Fatal("fatal msg")
+       Fatalf("fatal %s", "formatted")
+
+       // Verify all calls
+       assert.Equal(t, []string{"debug msg", "debug formatted"}, 
mock.debugCalls)
+       assert.Equal(t, []string{"info msg", "info formatted"}, mock.infoCalls)
+       assert.Equal(t, []string{"warn msg", "warn formatted"}, mock.warnCalls)
+       assert.Equal(t, []string{"error msg", "error formatted"}, 
mock.errorCalls)
+       assert.Equal(t, []string{"panic msg", "panic formatted"}, 
mock.panicCalls)
+       assert.Equal(t, []string{"fatal msg", "fatal formatted"}, 
mock.fatalCalls)
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to