Mining/pkg/logging/logger_test.go
snider ddcf39b7ee feat: Add structured logging package
Create pkg/logging with:
- Log levels: Debug, Info, Warn, Error
- Structured fields support (key-value pairs)
- Component-based logging (WithComponent)
- Global logger convenience functions
- ParseLevel for configuration
- Full test coverage

The package provides a migration path from log.Printf to
structured logging without external dependencies.

Example usage:
  logging.Info("miner started", logging.Fields{"name": minerName})
  logger := logging.New(cfg).WithComponent("Manager")
  logger.Warn("connection lost", logging.Fields{"pool": pool})

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 11:18:22 +00:00

262 lines
5.5 KiB
Go

package logging
import (
"bytes"
"strings"
"testing"
)
func TestLoggerLevels(t *testing.T) {
var buf bytes.Buffer
logger := New(Config{
Output: &buf,
Level: LevelInfo,
})
// Debug should not appear at Info level
logger.Debug("debug message")
if buf.Len() > 0 {
t.Error("Debug message should not appear at Info level")
}
// Info should appear
logger.Info("info message")
if !strings.Contains(buf.String(), "[INFO]") {
t.Error("Info message should appear")
}
if !strings.Contains(buf.String(), "info message") {
t.Error("Info message content should appear")
}
buf.Reset()
// Warn should appear
logger.Warn("warn message")
if !strings.Contains(buf.String(), "[WARN]") {
t.Error("Warn message should appear")
}
buf.Reset()
// Error should appear
logger.Error("error message")
if !strings.Contains(buf.String(), "[ERROR]") {
t.Error("Error message should appear")
}
}
func TestLoggerDebugLevel(t *testing.T) {
var buf bytes.Buffer
logger := New(Config{
Output: &buf,
Level: LevelDebug,
})
logger.Debug("debug message")
if !strings.Contains(buf.String(), "[DEBUG]") {
t.Error("Debug message should appear at Debug level")
}
}
func TestLoggerWithFields(t *testing.T) {
var buf bytes.Buffer
logger := New(Config{
Output: &buf,
Level: LevelInfo,
})
logger.Info("test message", Fields{"key": "value", "num": 42})
output := buf.String()
if !strings.Contains(output, "key=value") {
t.Error("Field key=value should appear")
}
if !strings.Contains(output, "num=42") {
t.Error("Field num=42 should appear")
}
}
func TestLoggerWithComponent(t *testing.T) {
var buf bytes.Buffer
logger := New(Config{
Output: &buf,
Level: LevelInfo,
Component: "TestComponent",
})
logger.Info("test message")
output := buf.String()
if !strings.Contains(output, "[TestComponent]") {
t.Error("Component name should appear in log")
}
}
func TestLoggerDerivedComponent(t *testing.T) {
var buf bytes.Buffer
parent := New(Config{
Output: &buf,
Level: LevelInfo,
})
child := parent.WithComponent("ChildComponent")
child.Info("child message")
output := buf.String()
if !strings.Contains(output, "[ChildComponent]") {
t.Error("Derived component name should appear")
}
}
func TestLoggerFormatted(t *testing.T) {
var buf bytes.Buffer
logger := New(Config{
Output: &buf,
Level: LevelInfo,
})
logger.Infof("formatted %s %d", "string", 123)
output := buf.String()
if !strings.Contains(output, "formatted string 123") {
t.Errorf("Formatted message should appear, got: %s", output)
}
}
func TestSetLevel(t *testing.T) {
var buf bytes.Buffer
logger := New(Config{
Output: &buf,
Level: LevelError,
})
// Info should not appear at Error level
logger.Info("should not appear")
if buf.Len() > 0 {
t.Error("Info should not appear at Error level")
}
// Change to Info level
logger.SetLevel(LevelInfo)
logger.Info("should appear now")
if !strings.Contains(buf.String(), "should appear now") {
t.Error("Info should appear after level change")
}
// Verify GetLevel
if logger.GetLevel() != LevelInfo {
t.Error("GetLevel should return LevelInfo")
}
}
func TestParseLevel(t *testing.T) {
tests := []struct {
input string
expected Level
wantErr bool
}{
{"DEBUG", LevelDebug, false},
{"debug", LevelDebug, false},
{"INFO", LevelInfo, false},
{"info", LevelInfo, false},
{"WARN", LevelWarn, false},
{"WARNING", LevelWarn, false},
{"ERROR", LevelError, false},
{"error", LevelError, false},
{"invalid", LevelInfo, true},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
level, err := ParseLevel(tt.input)
if tt.wantErr && err == nil {
t.Error("Expected error but got none")
}
if !tt.wantErr && err != nil {
t.Errorf("Unexpected error: %v", err)
}
if !tt.wantErr && level != tt.expected {
t.Errorf("Expected %v, got %v", tt.expected, level)
}
})
}
}
func TestGlobalLogger(t *testing.T) {
var buf bytes.Buffer
logger := New(Config{
Output: &buf,
Level: LevelInfo,
})
SetGlobal(logger)
Info("global test")
if !strings.Contains(buf.String(), "global test") {
t.Error("Global logger should write message")
}
buf.Reset()
SetGlobalLevel(LevelError)
Info("should not appear")
if buf.Len() > 0 {
t.Error("Info should not appear at Error level")
}
// Reset to default for other tests
SetGlobal(New(DefaultConfig()))
}
func TestLevelString(t *testing.T) {
tests := []struct {
level Level
expected string
}{
{LevelDebug, "DEBUG"},
{LevelInfo, "INFO"},
{LevelWarn, "WARN"},
{LevelError, "ERROR"},
{Level(99), "UNKNOWN"},
}
for _, tt := range tests {
if got := tt.level.String(); got != tt.expected {
t.Errorf("Level(%d).String() = %s, want %s", tt.level, got, tt.expected)
}
}
}
func TestMergeFields(t *testing.T) {
// Empty fields
result := mergeFields(nil)
if result != nil {
t.Error("nil input should return nil")
}
result = mergeFields([]Fields{})
if result != nil {
t.Error("empty input should return nil")
}
// Single fields
result = mergeFields([]Fields{{"key": "value"}})
if result["key"] != "value" {
t.Error("Single field should be preserved")
}
// Multiple fields
result = mergeFields([]Fields{
{"key1": "value1"},
{"key2": "value2"},
})
if result["key1"] != "value1" || result["key2"] != "value2" {
t.Error("Multiple fields should be merged")
}
// Override
result = mergeFields([]Fields{
{"key": "value1"},
{"key": "value2"},
})
if result["key"] != "value2" {
t.Error("Later fields should override earlier ones")
}
}