153 lines
4.0 KiB
Go
153 lines
4.0 KiB
Go
package log
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestLevel verifies level string parsing, including case and whitespace handling.
|
|
func TestLevel(t *testing.T) {
|
|
tests := []struct {
|
|
input string
|
|
want int64
|
|
wantErr bool
|
|
}{
|
|
{"debug", LevelDebug, false},
|
|
{"DEBUG", LevelDebug, false},
|
|
{" info ", LevelInfo, false},
|
|
{"Warn", LevelWarn, false},
|
|
{"error", LevelError, false},
|
|
{"proc", LevelProc, false},
|
|
{"disk", LevelDisk, false},
|
|
{"sys", LevelSys, false},
|
|
{"invalid", 0, true},
|
|
{"", 0, true},
|
|
{" ", 0, true},
|
|
{"-4", 0, true}, // numeric forms are handled by applyConfigField, not Level
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.input, func(t *testing.T) {
|
|
level, err := Level(tt.input)
|
|
if tt.wantErr {
|
|
errContains(t, err, "invalid level string", "Level")
|
|
return
|
|
}
|
|
mustNoErr(t, err, "Level")
|
|
equal(t, level, tt.want, "level value")
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestParseKeyValue verifies key=value splitting and trimming.
|
|
func TestParseKeyValue(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
wantKey string
|
|
wantValue string
|
|
wantErr string
|
|
}{
|
|
{"simple", "key=value", "key", "value", ""},
|
|
{"trimmed", " key = value ", "key", "value", ""},
|
|
{"value with separators", "key=value=with=equals", "key", "value=with=equals", ""},
|
|
{"empty value", "key=", "key", "", ""},
|
|
{"no separator", "noequals", "", "", "expected key=value"},
|
|
{"empty key", "=value", "", "", "key cannot be empty"},
|
|
{"whitespace key", " =value", "", "", "key cannot be empty"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
key, value, err := parseKeyValue(tt.input)
|
|
if tt.wantErr != "" {
|
|
errContains(t, err, tt.wantErr, "parseKeyValue")
|
|
return
|
|
}
|
|
mustNoErr(t, err, "parseKeyValue")
|
|
equal(t, key, tt.wantKey, "key")
|
|
equal(t, value, tt.wantValue, "value")
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestFmtErrorf verifies prefixing is applied once.
|
|
func TestFmtErrorf(t *testing.T) {
|
|
err := fmtErrorf("test error: %s", "details")
|
|
mustErr(t, err, "fmtErrorf")
|
|
equal(t, err.Error(), "log: test error: details", "message")
|
|
|
|
err = fmtErrorf("log: already prefixed")
|
|
equal(t, err.Error(), "log: already prefixed", "message")
|
|
equal(t, strings.Count(err.Error(), "log: "), 1, "prefix occurrences")
|
|
}
|
|
|
|
// TestGetTrace verifies depth bounds and caller-to-callee ordering.
|
|
func TestGetTrace(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
depth int64
|
|
check func(t *testing.T, trace string)
|
|
}{
|
|
{"disabled", 0, func(t *testing.T, s string) {
|
|
if s != "" {
|
|
t.Errorf("depth 0 must produce no trace, got %q", s)
|
|
}
|
|
}},
|
|
{"negative", -1, func(t *testing.T, s string) {
|
|
if s != "" {
|
|
t.Errorf("negative depth must produce no trace, got %q", s)
|
|
}
|
|
}},
|
|
{"single frame", 1, func(t *testing.T, s string) {
|
|
if s == "" {
|
|
t.Error("depth 1 must produce a trace")
|
|
}
|
|
notContains(t, s, "->", "single frame must not contain a separator")
|
|
}},
|
|
{"multi frame", 3, func(t *testing.T, s string) {
|
|
if s == "" {
|
|
t.Fatal("depth 3 must produce a trace")
|
|
}
|
|
if s != "(unknown)" {
|
|
contains(t, s, "->", "multi-frame separator")
|
|
// Frames are reversed into caller -> callee order
|
|
parts := strings.Split(s, " -> ")
|
|
if len(parts) < 2 {
|
|
t.Errorf("expected multiple frames, got %q", s)
|
|
}
|
|
}
|
|
}},
|
|
{"over limit", 11, func(t *testing.T, s string) {
|
|
if s != "" {
|
|
t.Errorf("depth above 10 must produce no trace, got %q", s)
|
|
}
|
|
}},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tt.check(t, getTrace(tt.depth, 0))
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestGetTraceOrdering verifies the deepest caller appears first.
|
|
func TestGetTraceOrdering(t *testing.T) {
|
|
var trace string
|
|
outer := func() { trace = getTrace(3, 0) }
|
|
middle := func() { outer() }
|
|
middle()
|
|
|
|
if trace == "" || trace == "(unknown)" {
|
|
t.Skip("runtime frames unavailable under current inlining")
|
|
}
|
|
first := strings.Split(trace, " -> ")[0]
|
|
last := strings.Split(trace, " -> ")
|
|
// The innermost frame is getTrace itself; the caller chain precedes it
|
|
if first == last[len(last)-1] {
|
|
t.Errorf("frames not ordered: %q", trace)
|
|
}
|
|
}
|
|
|