Files
golib/hlog/logger_test.go
2026-01-13 12:55:30 +11:00

377 lines
8.1 KiB
Go

package hlog
import (
"bytes"
"io"
"os"
"path/filepath"
"strings"
"testing"
"github.com/rs/zerolog"
)
func TestNewLogger(t *testing.T) {
tests := []struct {
name string
cfg *Config
writer io.Writer
wantErr bool
errMsg string
}{
{
name: "console output only",
cfg: &Config{
LogLevel: zerolog.InfoLevel,
LogOutput: "console",
LogDir: "",
LogFileName: "",
LogAppend: true,
},
writer: bytes.NewBuffer(nil),
wantErr: false,
},
{
name: "nil config",
cfg: nil,
writer: bytes.NewBuffer(nil),
wantErr: true,
errMsg: "No config provided",
},
{
name: "nil writer for both output",
cfg: &Config{
LogLevel: zerolog.InfoLevel,
LogOutput: "both",
},
writer: nil,
wantErr: true,
errMsg: "No Writer provided",
},
{
name: "file output without LogDir",
cfg: &Config{
LogLevel: zerolog.InfoLevel,
LogOutput: "file",
LogDir: "",
LogFileName: "test.log",
LogAppend: true,
},
writer: nil,
wantErr: true,
errMsg: "LOG_DIR must be set",
},
{
name: "file output without LogFileName",
cfg: &Config{
LogLevel: zerolog.InfoLevel,
LogOutput: "file",
LogDir: "/tmp",
LogFileName: "",
LogAppend: true,
},
writer: nil,
wantErr: true,
errMsg: "LOG_FILE_NAME must be set",
},
{
name: "both output without LogDir",
cfg: &Config{
LogLevel: zerolog.InfoLevel,
LogOutput: "both",
LogDir: "",
LogFileName: "test.log",
LogAppend: true,
},
writer: bytes.NewBuffer(nil),
wantErr: true,
errMsg: "LOG_DIR must be set",
},
{
name: "both output without LogFileName",
cfg: &Config{
LogLevel: zerolog.InfoLevel,
LogOutput: "both",
LogDir: "/tmp",
LogFileName: "",
LogAppend: true,
},
writer: bytes.NewBuffer(nil),
wantErr: true,
errMsg: "LOG_FILE_NAME must be set",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
logger, err := NewLogger(tt.cfg, tt.writer)
if tt.wantErr {
if err == nil {
t.Errorf("NewLogger() expected error but got nil")
return
}
if tt.errMsg != "" && !strings.Contains(err.Error(), tt.errMsg) {
t.Errorf("NewLogger() error = %v, should contain %v", err, tt.errMsg)
}
return
}
if err != nil {
t.Errorf("NewLogger() unexpected error = %v", err)
return
}
if logger == nil {
t.Errorf("NewLogger() returned nil logger")
return
}
if logger.Logger == nil {
t.Errorf("NewLogger() returned logger with nil zerolog.Logger")
}
})
}
}
func TestNewLogger_FileOutput(t *testing.T) {
// Create temporary directory for test logs
tempDir := t.TempDir()
tests := []struct {
name string
cfg *Config
writer io.Writer
wantErr bool
checkFile bool
logMessage string
}{
{
name: "file output with append",
cfg: &Config{
LogLevel: zerolog.InfoLevel,
LogOutput: "file",
LogDir: tempDir,
LogFileName: "append_test.log",
LogAppend: true,
},
writer: nil,
wantErr: false,
checkFile: true,
logMessage: "test append message",
},
{
name: "file output with overwrite",
cfg: &Config{
LogLevel: zerolog.InfoLevel,
LogOutput: "file",
LogDir: tempDir,
LogFileName: "overwrite_test.log",
LogAppend: false,
},
writer: nil,
wantErr: false,
checkFile: true,
logMessage: "test overwrite message",
},
{
name: "both output modes",
cfg: &Config{
LogLevel: zerolog.DebugLevel,
LogOutput: "both",
LogDir: tempDir,
LogFileName: "both_test.log",
LogAppend: true,
},
writer: bytes.NewBuffer(nil),
wantErr: false,
checkFile: true,
logMessage: "test both message",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
logger, err := NewLogger(tt.cfg, tt.writer)
if tt.wantErr {
if err == nil {
t.Errorf("NewLogger() expected error but got nil")
}
return
}
if err != nil {
t.Errorf("NewLogger() unexpected error = %v", err)
return
}
if logger == nil {
t.Errorf("NewLogger() returned nil logger")
return
}
// Log a test message
logger.Info().Msg(tt.logMessage)
// Close the log file to flush
err = logger.CloseLogFile()
if err != nil {
t.Errorf("CloseLogFile() error = %v", err)
}
// Check if file exists and contains message
if tt.checkFile {
logPath := filepath.Join(tt.cfg.LogDir, tt.cfg.LogFileName)
content, err := os.ReadFile(logPath)
if err != nil {
t.Errorf("Failed to read log file: %v", err)
return
}
if !strings.Contains(string(content), tt.logMessage) {
t.Errorf("Log file doesn't contain expected message. Got: %s", string(content))
}
}
// Check console output for "both" mode
if tt.cfg.LogOutput == "both" && tt.writer != nil {
if buf, ok := tt.writer.(*bytes.Buffer); ok {
consoleOutput := buf.String()
if !strings.Contains(consoleOutput, tt.logMessage) {
t.Errorf("Console output doesn't contain expected message. Got: %s", consoleOutput)
}
}
}
})
}
}
func TestNewLogger_AppendVsOverwrite(t *testing.T) {
tempDir := t.TempDir()
logFileName := "append_vs_overwrite.log"
// First logger - write initial content
cfg1 := &Config{
LogLevel: zerolog.InfoLevel,
LogOutput: "file",
LogDir: tempDir,
LogFileName: logFileName,
LogAppend: true,
}
logger1, err := NewLogger(cfg1, nil)
if err != nil {
t.Fatalf("NewLogger() error = %v", err)
}
logger1.Info().Msg("first message")
logger1.CloseLogFile()
// Second logger - append mode
cfg2 := &Config{
LogLevel: zerolog.InfoLevel,
LogOutput: "file",
LogDir: tempDir,
LogFileName: logFileName,
LogAppend: true,
}
logger2, err := NewLogger(cfg2, nil)
if err != nil {
t.Fatalf("NewLogger() error = %v", err)
}
logger2.Info().Msg("second message")
logger2.CloseLogFile()
// Check both messages exist
logPath := filepath.Join(tempDir, logFileName)
content, err := os.ReadFile(logPath)
if err != nil {
t.Fatalf("Failed to read log file: %v", err)
}
contentStr := string(content)
if !strings.Contains(contentStr, "first message") {
t.Errorf("Log file missing 'first message' after append")
}
if !strings.Contains(contentStr, "second message") {
t.Errorf("Log file missing 'second message' after append")
}
// Third logger - overwrite mode
cfg3 := &Config{
LogLevel: zerolog.InfoLevel,
LogOutput: "file",
LogDir: tempDir,
LogFileName: logFileName,
LogAppend: false,
}
logger3, err := NewLogger(cfg3, nil)
if err != nil {
t.Fatalf("NewLogger() error = %v", err)
}
logger3.Info().Msg("third message")
logger3.CloseLogFile()
// Check only third message exists
content, err = os.ReadFile(logPath)
if err != nil {
t.Fatalf("Failed to read log file: %v", err)
}
contentStr = string(content)
if strings.Contains(contentStr, "first message") {
t.Errorf("Log file still contains 'first message' after overwrite")
}
if strings.Contains(contentStr, "second message") {
t.Errorf("Log file still contains 'second message' after overwrite")
}
if !strings.Contains(contentStr, "third message") {
t.Errorf("Log file missing 'third message' after overwrite")
}
}
func TestLogger_CloseLogFile(t *testing.T) {
t.Run("close with file", func(t *testing.T) {
tempDir := t.TempDir()
cfg := &Config{
LogLevel: zerolog.InfoLevel,
LogOutput: "file",
LogDir: tempDir,
LogFileName: "close_test.log",
LogAppend: true,
}
logger, err := NewLogger(cfg, nil)
if err != nil {
t.Fatalf("NewLogger() error = %v", err)
}
err = logger.CloseLogFile()
if err != nil {
t.Errorf("CloseLogFile() error = %v", err)
}
})
t.Run("close without file", func(t *testing.T) {
cfg := &Config{
LogLevel: zerolog.InfoLevel,
LogOutput: "console",
}
logger, err := NewLogger(cfg, bytes.NewBuffer(nil))
if err != nil {
t.Fatalf("NewLogger() error = %v", err)
}
err = logger.CloseLogFile()
if err != nil {
t.Errorf("CloseLogFile() should not error when no file is open, got: %v", err)
}
})
}