- Add package comments to all packages (pkg/config, pkg/logger, internal/*, cmd/platform)
- Fix context key warnings by using custom ContextKey type
- Export ContextKey type to avoid unexported-return warnings
- Update all context value operations to use ContextKey instead of string
- Update RequestIDKey() and UserIDKey() to return ContextKey
- Fix error checking issues (errcheck)
- Properly handle os.Chdir errors in defer statements
- Properly handle os.Setenv/os.Unsetenv errors in tests
- Fix security warnings (gosec)
- Change directory permissions from 0755 to 0750 in tests
- Change file permissions from 0644 to 0600 in tests
- Fix unused parameter warnings (revive)
- Replace unused parameters with _ in:
* RegisterLifecycleHooks lifecycle functions
* Mock logger implementations
* noOpLogger methods
- Fix type assertion issues (staticcheck)
- Remove unnecessary type assertions in tests
- Use simpler compile-time checks
- Fix exported type stuttering warning
- Add nolint directive for ConfigProvider (standard interface pattern)
- Update golangci-lint configuration
- Add version: 2 field (required for newer versions)
- Remove unsupported linters (typecheck, gosimple)
- Move formatters (gofmt, goimports) to separate formatters section
- Simplify linter list to only well-supported linters
All linting issues resolved (0 issues reported by golangci-lint).
All tests pass and code compiles successfully.
369 lines
7.4 KiB
Go
369 lines
7.4 KiB
Go
package logger
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"git.dcentral.systems/toolz/goplt/pkg/logger"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func TestNewZapLogger_JSONFormat(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
log, err := NewZapLogger("info", "json")
|
|
if err != nil {
|
|
t.Fatalf("NewZapLogger failed: %v", err)
|
|
}
|
|
|
|
if log == nil {
|
|
t.Fatal("NewZapLogger returned nil")
|
|
}
|
|
|
|
// Verify it implements the interface (compile-time check)
|
|
_ = log
|
|
|
|
// Test that it can log
|
|
log.Info("test message")
|
|
}
|
|
|
|
func TestNewZapLogger_ConsoleFormat(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
log, err := NewZapLogger("info", "console")
|
|
if err != nil {
|
|
t.Fatalf("NewZapLogger failed: %v", err)
|
|
}
|
|
|
|
if log == nil {
|
|
t.Fatal("NewZapLogger returned nil")
|
|
}
|
|
|
|
// Test that it can log
|
|
log.Info("test message")
|
|
}
|
|
|
|
func TestNewZapLogger_InvalidLevel(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
log, err := NewZapLogger("invalid", "json")
|
|
if err != nil {
|
|
t.Fatalf("NewZapLogger failed: %v", err)
|
|
}
|
|
|
|
if log == nil {
|
|
t.Fatal("NewZapLogger returned nil")
|
|
}
|
|
|
|
// Should default to info level
|
|
log.Info("test message")
|
|
}
|
|
|
|
func TestNewZapLogger_AllLevels(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
levels := []string{"debug", "info", "warn", "error"}
|
|
|
|
for _, level := range levels {
|
|
t.Run(level, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
log, err := NewZapLogger(level, "json")
|
|
if err != nil {
|
|
t.Fatalf("NewZapLogger(%q) failed: %v", level, err)
|
|
}
|
|
|
|
if log == nil {
|
|
t.Fatalf("NewZapLogger(%q) returned nil", level)
|
|
}
|
|
|
|
// Test logging at each level
|
|
log.Debug("debug message")
|
|
log.Info("info message")
|
|
log.Warn("warn message")
|
|
log.Error("error message")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestZapLogger_With(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
log, err := NewZapLogger("info", "json")
|
|
if err != nil {
|
|
t.Fatalf("NewZapLogger failed: %v", err)
|
|
}
|
|
|
|
childLog := log.With(
|
|
logger.String("key", "value"),
|
|
logger.Int("number", 42),
|
|
)
|
|
|
|
if childLog == nil {
|
|
t.Fatal("With returned nil")
|
|
}
|
|
|
|
// Verify it's a different logger instance
|
|
if childLog == log {
|
|
t.Error("With should return a new logger instance")
|
|
}
|
|
|
|
// Test that child logger can log
|
|
childLog.Info("test message with fields")
|
|
}
|
|
|
|
func TestZapLogger_WithContext_RequestID(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
log, err := NewZapLogger("info", "json")
|
|
if err != nil {
|
|
t.Fatalf("NewZapLogger failed: %v", err)
|
|
}
|
|
|
|
ctx := context.WithValue(context.Background(), requestIDKey, "test-request-id")
|
|
contextLog := log.WithContext(ctx)
|
|
|
|
if contextLog == nil {
|
|
t.Fatal("WithContext returned nil")
|
|
}
|
|
|
|
// Test that context logger can log
|
|
contextLog.Info("test message with request ID")
|
|
}
|
|
|
|
func TestZapLogger_WithContext_UserID(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
log, err := NewZapLogger("info", "json")
|
|
if err != nil {
|
|
t.Fatalf("NewZapLogger failed: %v", err)
|
|
}
|
|
|
|
ctx := context.WithValue(context.Background(), userIDKey, "test-user-id")
|
|
contextLog := log.WithContext(ctx)
|
|
|
|
if contextLog == nil {
|
|
t.Fatal("WithContext returned nil")
|
|
}
|
|
|
|
// Test that context logger can log
|
|
contextLog.Info("test message with user ID")
|
|
}
|
|
|
|
func TestZapLogger_WithContext_Both(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
log, err := NewZapLogger("info", "json")
|
|
if err != nil {
|
|
t.Fatalf("NewZapLogger failed: %v", err)
|
|
}
|
|
|
|
ctx := context.WithValue(context.Background(), requestIDKey, "test-request-id")
|
|
ctx = context.WithValue(ctx, userIDKey, "test-user-id")
|
|
contextLog := log.WithContext(ctx)
|
|
|
|
if contextLog == nil {
|
|
t.Fatal("WithContext returned nil")
|
|
}
|
|
|
|
// Test that context logger can log
|
|
contextLog.Info("test message with both IDs")
|
|
}
|
|
|
|
func TestZapLogger_WithContext_EmptyContext(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
log, err := NewZapLogger("info", "json")
|
|
if err != nil {
|
|
t.Fatalf("NewZapLogger failed: %v", err)
|
|
}
|
|
|
|
ctx := context.Background()
|
|
contextLog := log.WithContext(ctx)
|
|
|
|
if contextLog == nil {
|
|
t.Fatal("WithContext returned nil")
|
|
}
|
|
|
|
// With empty context, should return the same logger
|
|
if contextLog != log {
|
|
t.Error("WithContext with empty context should return the same logger")
|
|
}
|
|
}
|
|
|
|
func TestZapLogger_LoggingMethods(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
log, err := NewZapLogger("debug", "json")
|
|
if err != nil {
|
|
t.Fatalf("NewZapLogger failed: %v", err)
|
|
}
|
|
|
|
fields := []logger.Field{
|
|
logger.String("key", "value"),
|
|
logger.Int("number", 42),
|
|
logger.Bool("flag", true),
|
|
}
|
|
|
|
// Test all logging methods
|
|
log.Debug("debug message", fields...)
|
|
log.Info("info message", fields...)
|
|
log.Warn("warn message", fields...)
|
|
log.Error("error message", fields...)
|
|
}
|
|
|
|
func TestConvertFields(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
fields []logger.Field
|
|
}{
|
|
{
|
|
name: "empty fields",
|
|
fields: []logger.Field{},
|
|
},
|
|
{
|
|
name: "valid zap fields",
|
|
fields: []logger.Field{
|
|
zap.String("key", "value"),
|
|
zap.Int("number", 42),
|
|
},
|
|
},
|
|
{
|
|
name: "mixed fields",
|
|
fields: []logger.Field{
|
|
logger.String("key", "value"),
|
|
zap.Int("number", 42),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
log, err := NewZapLogger("info", "json")
|
|
if err != nil {
|
|
t.Fatalf("NewZapLogger failed: %v", err)
|
|
}
|
|
|
|
// Test that convertFields works by logging
|
|
log.Info("test message", tt.fields...)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRequestIDKey(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
key := RequestIDKey()
|
|
if key == "" {
|
|
t.Error("RequestIDKey returned empty contextKey")
|
|
}
|
|
|
|
if key != requestIDKey {
|
|
t.Errorf("RequestIDKey() = %v, want %v", key, requestIDKey)
|
|
}
|
|
}
|
|
|
|
func TestUserIDKey(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
key := UserIDKey()
|
|
if key == "" {
|
|
t.Error("UserIDKey returned empty contextKey")
|
|
}
|
|
|
|
if key != userIDKey {
|
|
t.Errorf("UserIDKey() = %v, want %v", key, userIDKey)
|
|
}
|
|
}
|
|
|
|
func TestZapLogger_ChainedWith(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
log, err := NewZapLogger("info", "json")
|
|
if err != nil {
|
|
t.Fatalf("NewZapLogger failed: %v", err)
|
|
}
|
|
|
|
// Chain multiple With calls
|
|
childLog := log.With(
|
|
logger.String("parent", "value1"),
|
|
).With(
|
|
logger.String("child", "value2"),
|
|
)
|
|
|
|
if childLog == nil {
|
|
t.Fatal("Chained With returned nil")
|
|
}
|
|
|
|
childLog.Info("test message with chained fields")
|
|
}
|
|
|
|
func TestZapLogger_WithContext_ChainedWith(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
log, err := NewZapLogger("info", "json")
|
|
if err != nil {
|
|
t.Fatalf("NewZapLogger failed: %v", err)
|
|
}
|
|
|
|
ctx := context.WithValue(context.Background(), requestIDKey, "test-id")
|
|
contextLog := log.WithContext(ctx).With(
|
|
logger.String("additional", "field"),
|
|
)
|
|
|
|
if contextLog == nil {
|
|
t.Fatal("Chained WithContext and With returned nil")
|
|
}
|
|
|
|
contextLog.Info("test message with context and additional fields")
|
|
}
|
|
|
|
// Benchmark tests
|
|
func BenchmarkZapLogger_Info(b *testing.B) {
|
|
log, err := NewZapLogger("info", "json")
|
|
if err != nil {
|
|
b.Fatalf("NewZapLogger failed: %v", err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
log.Info("benchmark message")
|
|
}
|
|
}
|
|
|
|
func BenchmarkZapLogger_InfoWithFields(b *testing.B) {
|
|
log, err := NewZapLogger("info", "json")
|
|
if err != nil {
|
|
b.Fatalf("NewZapLogger failed: %v", err)
|
|
}
|
|
|
|
fields := []logger.Field{
|
|
logger.String("key1", "value1"),
|
|
logger.Int("key2", 42),
|
|
logger.Bool("key3", true),
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
log.Info("benchmark message", fields...)
|
|
}
|
|
}
|
|
|
|
func BenchmarkZapLogger_WithContext(b *testing.B) {
|
|
log, err := NewZapLogger("info", "json")
|
|
if err != nil {
|
|
b.Fatalf("NewZapLogger failed: %v", err)
|
|
}
|
|
|
|
ctx := context.WithValue(context.Background(), requestIDKey, "test-id")
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = log.WithContext(ctx)
|
|
}
|
|
}
|