131 lines
3.4 KiB
Go
131 lines
3.4 KiB
Go
package observability
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestParseLogLevel(t *testing.T) {
|
|
tests := []struct {
|
|
input string
|
|
want LogLevel
|
|
}{
|
|
{"debug", LevelDebug}, {"info", LevelInfo},
|
|
{"warn", LevelWarn}, {"warning", LevelWarn},
|
|
{"error", LevelError}, {"invalid", LevelInfo},
|
|
}
|
|
for _, tt := range tests {
|
|
got := ParseLogLevel(tt.input)
|
|
if got != tt.want {
|
|
t.Errorf("ParseLogLevel(%q) = %d, want %d", tt.input, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLoggerMaskSensitive(t *testing.T) {
|
|
logger := &Logger{
|
|
level: LevelInfo,
|
|
maskFields: []string{"api_key", "secret", "password", "token"},
|
|
}
|
|
|
|
data := map[string]any{
|
|
"api_key": "sk-12345",
|
|
"message": "hello",
|
|
"nested": map[string]any{
|
|
"secret": "my-secret",
|
|
},
|
|
}
|
|
masked := logger.maskSensitive(data)
|
|
if masked["api_key"] != "***REDACTED***" {
|
|
t.Errorf("expected api_key redacted, got %v", masked["api_key"])
|
|
}
|
|
if masked["message"] != "hello" {
|
|
t.Errorf("expected message preserved, got %v", masked["message"])
|
|
}
|
|
nested, ok := masked["nested"].(map[string]any)
|
|
if !ok {
|
|
t.Fatal("expected nested map")
|
|
}
|
|
if nested["secret"] != "***REDACTED***" {
|
|
t.Errorf("expected nested secret redacted, got %v", nested["secret"])
|
|
}
|
|
}
|
|
|
|
func TestLogFieldsBuilder(t *testing.T) {
|
|
f := F().RequestID("req-1").TaskID("task-1").Event("test_event").Set("custom", "value")
|
|
if f.fields["request_id"] != "req-1" {
|
|
t.Error("request_id not set")
|
|
}
|
|
if f.fields["task_id"] != "task-1" {
|
|
t.Error("task_id not set")
|
|
}
|
|
if f.fields["event"] != "test_event" {
|
|
t.Error("event not set")
|
|
}
|
|
if f.fields["custom"] != "value" {
|
|
t.Error("custom not set")
|
|
}
|
|
}
|
|
|
|
func TestLoggerWrite(t *testing.T) {
|
|
// Use a temp file to capture output
|
|
tmpFile, err := os.CreateTemp("", "logtest*.json")
|
|
if err != nil {
|
|
t.Fatalf("create temp file: %v", err)
|
|
}
|
|
defer os.Remove(tmpFile.Name())
|
|
|
|
logger := NewLogger(LevelDebug, tmpFile, "metadata_only")
|
|
logger.Info("test message", F().RequestID("req-123").Event("unit_test"))
|
|
|
|
tmpFile.Close()
|
|
|
|
data, err := os.ReadFile(tmpFile.Name())
|
|
if err != nil {
|
|
t.Fatalf("read log file: %v", err)
|
|
}
|
|
|
|
var entry map[string]any
|
|
if err := json.Unmarshal(bytes.TrimSpace(data), &entry); err != nil {
|
|
t.Fatalf("parse log json: %v\nraw: %s", err, string(data))
|
|
}
|
|
|
|
if entry["level"] != "INFO" {
|
|
t.Errorf("expected level INFO, got %v", entry["level"])
|
|
}
|
|
if entry["message"] != "test message" {
|
|
t.Errorf("expected message 'test message', got %v", entry["message"])
|
|
}
|
|
if entry["request_id"] != "req-123" {
|
|
t.Errorf("expected request_id req-123, got %v", entry["request_id"])
|
|
}
|
|
if entry["event"] != "unit_test" {
|
|
t.Errorf("expected event unit_test, got %v", entry["event"])
|
|
}
|
|
}
|
|
|
|
func TestLoggerLevelFiltering(t *testing.T) {
|
|
// This test verifies that debug messages are not logged when level is INFO
|
|
tmpFile, err := os.CreateTemp("", "logtest*.json")
|
|
if err != nil {
|
|
t.Fatalf("create temp file: %v", err)
|
|
}
|
|
defer os.Remove(tmpFile.Name())
|
|
|
|
logger := NewLogger(LevelWarn, tmpFile, "metadata_only")
|
|
logger.Info("should not appear", F().Event("info_event"))
|
|
logger.Warn("should appear", F().Event("warn_event"))
|
|
|
|
tmpFile.Close()
|
|
data, _ := os.ReadFile(tmpFile.Name())
|
|
if strings.Contains(string(data), "should not appear") {
|
|
t.Error("INFO message was logged when level is WARN")
|
|
}
|
|
if !strings.Contains(string(data), "should appear") {
|
|
t.Error("WARN message was not logged")
|
|
}
|
|
}
|