// Copyright (c) 2023 - 2025 IBM Corp. // All rights reserved. // // Licensed 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 logging import ( "bytes" "log" "strings" "testing" ) // TestLoggingCallbacks_NoLoggers tests the case when no loggers are provided. // It should return two callbacks using the default logger. func TestLoggingCallbacks_NoLoggers(t *testing.T) { infoLog, errLog := LoggingCallbacks() if infoLog == nil { t.Error("Expected infoLog to be non-nil") } if errLog == nil { t.Error("Expected errLog to be non-nil") } // Verify both callbacks work var buf bytes.Buffer log.SetOutput(&buf) defer log.SetOutput(nil) infoLog("test info: %s", "message") if !strings.Contains(buf.String(), "test info: message") { t.Errorf("Expected log to contain 'test info: message', got: %s", buf.String()) } buf.Reset() errLog("test error: %s", "message") if !strings.Contains(buf.String(), "test error: message") { t.Errorf("Expected log to contain 'test error: message', got: %s", buf.String()) } } // TestLoggingCallbacks_OneLogger tests the case when one logger is provided. // Both callbacks should use the same logger. func TestLoggingCallbacks_OneLogger(t *testing.T) { var buf bytes.Buffer logger := log.New(&buf, "TEST: ", 0) infoLog, errLog := LoggingCallbacks(logger) if infoLog == nil { t.Error("Expected infoLog to be non-nil") } if errLog == nil { t.Error("Expected errLog to be non-nil") } // Test info callback infoLog("info message: %d", 42) output := buf.String() if !strings.Contains(output, "TEST: info message: 42") { t.Errorf("Expected log to contain 'TEST: info message: 42', got: %s", output) } // Test error callback uses same logger buf.Reset() errLog("error message: %s", "failed") output = buf.String() if !strings.Contains(output, "TEST: error message: failed") { t.Errorf("Expected log to contain 'TEST: error message: failed', got: %s", output) } } // TestLoggingCallbacks_TwoLoggers tests the case when two loggers are provided. // First callback should use first logger, second callback should use second logger. func TestLoggingCallbacks_TwoLoggers(t *testing.T) { var infoBuf, errBuf bytes.Buffer infoLogger := log.New(&infoBuf, "INFO: ", 0) errorLogger := log.New(&errBuf, "ERROR: ", 0) infoLog, errLog := LoggingCallbacks(infoLogger, errorLogger) if infoLog == nil { t.Error("Expected infoLog to be non-nil") } if errLog == nil { t.Error("Expected errLog to be non-nil") } // Test info callback uses first logger infoLog("success: %s", "operation completed") infoOutput := infoBuf.String() if !strings.Contains(infoOutput, "INFO: success: operation completed") { t.Errorf("Expected info log to contain 'INFO: success: operation completed', got: %s", infoOutput) } if errBuf.Len() != 0 { t.Errorf("Expected error buffer to be empty, got: %s", errBuf.String()) } // Test error callback uses second logger errLog("failure: %s", "operation failed") errOutput := errBuf.String() if !strings.Contains(errOutput, "ERROR: failure: operation failed") { t.Errorf("Expected error log to contain 'ERROR: failure: operation failed', got: %s", errOutput) } } // TestLoggingCallbacks_MultipleLoggers tests the case when more than two loggers are provided. // Should use first two loggers and ignore the rest. func TestLoggingCallbacks_MultipleLoggers(t *testing.T) { var buf1, buf2, buf3 bytes.Buffer logger1 := log.New(&buf1, "LOG1: ", 0) logger2 := log.New(&buf2, "LOG2: ", 0) logger3 := log.New(&buf3, "LOG3: ", 0) infoLog, errLog := LoggingCallbacks(logger1, logger2, logger3) if infoLog == nil { t.Error("Expected infoLog to be non-nil") } if errLog == nil { t.Error("Expected errLog to be non-nil") } // Test that first logger is used for info infoLog("message 1") if !strings.Contains(buf1.String(), "LOG1: message 1") { t.Errorf("Expected first logger to be used, got: %s", buf1.String()) } // Test that second logger is used for error errLog("message 2") if !strings.Contains(buf2.String(), "LOG2: message 2") { t.Errorf("Expected second logger to be used, got: %s", buf2.String()) } // Test that third logger is not used if buf3.Len() != 0 { t.Errorf("Expected third logger to not be used, got: %s", buf3.String()) } } // TestLoggingCallbacks_FormattingWithMultipleArgs tests that formatting works correctly // with multiple arguments. func TestLoggingCallbacks_FormattingWithMultipleArgs(t *testing.T) { var buf bytes.Buffer logger := log.New(&buf, "", 0) infoLog, _ := LoggingCallbacks(logger) infoLog("test %s %d %v", "string", 123, true) output := buf.String() if !strings.Contains(output, "test string 123 true") { t.Errorf("Expected formatted output 'test string 123 true', got: %s", output) } } // TestLoggingCallbacks_NoFormatting tests logging without format specifiers. func TestLoggingCallbacks_NoFormatting(t *testing.T) { var buf bytes.Buffer logger := log.New(&buf, "PREFIX: ", 0) infoLog, _ := LoggingCallbacks(logger) infoLog("simple message") output := buf.String() if !strings.Contains(output, "PREFIX: simple message") { t.Errorf("Expected 'PREFIX: simple message', got: %s", output) } } // TestLoggingCallbacks_EmptyMessage tests logging with empty message. func TestLoggingCallbacks_EmptyMessage(t *testing.T) { var buf bytes.Buffer logger := log.New(&buf, "", 0) infoLog, _ := LoggingCallbacks(logger) infoLog("") output := buf.String() // Should still produce output (newline at minimum) if len(output) == 0 { t.Error("Expected some output even with empty message") } } // TestLoggingCallbacks_NilLogger tests behavior when nil logger is passed. // This tests edge case handling. func TestLoggingCallbacks_NilLogger(t *testing.T) { // This should not panic defer func() { if r := recover(); r != nil { t.Errorf("LoggingCallbacks panicked with nil logger: %v", r) } }() infoLog, errLog := LoggingCallbacks(nil) // The callbacks should still be created if infoLog == nil { t.Error("Expected infoLog to be non-nil even with nil logger") } if errLog == nil { t.Error("Expected errLog to be non-nil even with nil logger") } } // TestLoggingCallbacks_ConsecutiveCalls tests that callbacks can be called multiple times. func TestLoggingCallbacks_ConsecutiveCalls(t *testing.T) { var buf bytes.Buffer logger := log.New(&buf, "", 0) infoLog, errLog := LoggingCallbacks(logger) // Multiple calls to info infoLog("call 1") infoLog("call 2") infoLog("call 3") output := buf.String() if !strings.Contains(output, "call 1") || !strings.Contains(output, "call 2") || !strings.Contains(output, "call 3") { t.Errorf("Expected all three calls to be logged, got: %s", output) } buf.Reset() // Multiple calls to error errLog("error 1") errLog("error 2") output = buf.String() if !strings.Contains(output, "error 1") || !strings.Contains(output, "error 2") { t.Errorf("Expected both error calls to be logged, got: %s", output) } } // BenchmarkLoggingCallbacks_NoLoggers benchmarks the no-logger case. func BenchmarkLoggingCallbacks_NoLoggers(b *testing.B) { for i := 0; i < b.N; i++ { LoggingCallbacks() } } // BenchmarkLoggingCallbacks_OneLogger benchmarks the single-logger case. func BenchmarkLoggingCallbacks_OneLogger(b *testing.B) { var buf bytes.Buffer logger := log.New(&buf, "", 0) b.ResetTimer() for i := 0; i < b.N; i++ { LoggingCallbacks(logger) } } // BenchmarkLoggingCallbacks_TwoLoggers benchmarks the two-logger case. func BenchmarkLoggingCallbacks_TwoLoggers(b *testing.B) { var buf1, buf2 bytes.Buffer logger1 := log.New(&buf1, "", 0) logger2 := log.New(&buf2, "", 0) b.ResetTimer() for i := 0; i < b.N; i++ { LoggingCallbacks(logger1, logger2) } } // BenchmarkLoggingCallbacks_Logging benchmarks actual logging operations. func BenchmarkLoggingCallbacks_Logging(b *testing.B) { var buf bytes.Buffer logger := log.New(&buf, "", 0) infoLog, _ := LoggingCallbacks(logger) b.ResetTimer() for i := 0; i < b.N; i++ { infoLog("benchmark message %d", i) } }