Pulse/internal/logging/logging_test.go
rcourtman 6a8258be14 chore: remove dead code and unused exports
Remove ~900 lines of unused code identified by static analysis:

Go:
- internal/logging: Remove 10 unused functions (InitFromConfig, New,
  FromContext, WithLogger, etc.) that were built but never integrated
- cmd/pulse-sensor-proxy: Remove 7 dead validation functions for a
  removed command execution feature
- internal/metrics: Remove 8 unused notification metric functions and
  10 Prometheus metrics that were never wired up

Frontend:
- Delete ActivationBanner.tsx stub component
- Remove unused exports: stopMetricsSampler, getSamplerStatus,
  formatSpeedCompact, parseMetricKey, getResourceAlerts
2025-11-27 13:17:39 +00:00

294 lines
6 KiB
Go

package logging
import (
"bytes"
"os"
"reflect"
"sync"
"testing"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
func resetLoggingState() {
mu.Lock()
defer mu.Unlock()
baseWriter = os.Stderr
baseComponent = ""
baseLogger = zerolog.New(baseWriter).With().Timestamp().Logger()
log.Logger = baseLogger
zerolog.TimeFieldFormat = defaultTimeFmt
zerolog.SetGlobalLevel(zerolog.InfoLevel)
}
func TestInitJSONFormatSetsLevelAndComponent(t *testing.T) {
t.Cleanup(resetLoggingState)
Init(Config{
Format: "json",
Level: "debug",
Component: "apiserver",
})
mu.RLock()
defer mu.RUnlock()
if baseWriter != os.Stderr {
t.Fatalf("expected base writer to be os.Stderr, got %#v", baseWriter)
}
if zerolog.GlobalLevel() != zerolog.DebugLevel {
t.Fatalf("expected global level debug, got %s", zerolog.GlobalLevel())
}
if baseComponent != "apiserver" {
t.Fatalf("expected base component apiserver, got %s", baseComponent)
}
if !reflect.DeepEqual(log.Logger, baseLogger) {
t.Fatal("expected global log.Logger to match baseLogger")
}
}
func TestInitConsoleFormatUsesConsoleWriter(t *testing.T) {
t.Cleanup(resetLoggingState)
Init(Config{
Format: "console",
Level: "info",
})
mu.RLock()
defer mu.RUnlock()
if _, ok := baseWriter.(zerolog.ConsoleWriter); !ok {
t.Fatalf("expected console writer, got %#v", baseWriter)
}
}
func TestInitAutoFormatWithPipe(t *testing.T) {
t.Cleanup(resetLoggingState)
origStderr := os.Stderr
r, w, err := os.Pipe()
if err != nil {
t.Fatalf("failed to create pipe: %v", err)
}
os.Stderr = w
defer func() {
os.Stderr = origStderr
_ = r.Close()
_ = w.Close()
}()
Init(Config{
Format: "auto",
Level: "info",
})
mu.RLock()
defer mu.RUnlock()
if baseWriter != w {
t.Fatalf("expected base writer to use provided pipe, got %#v", baseWriter)
}
}
func TestWithRequestID(t *testing.T) {
t.Cleanup(resetLoggingState)
Init(Config{
Format: "json",
Level: "info",
})
ctx, generated := WithRequestID(nil, "")
if generated == "" {
t.Fatal("expected generated request id")
}
if ctx == nil {
t.Fatal("expected non-nil context")
}
ctx2, id := WithRequestID(nil, "custom-123")
if id != "custom-123" {
t.Fatalf("expected custom-123, got %s", id)
}
if ctx2 == nil {
t.Fatal("expected non-nil context")
}
}
func TestWithRequestIDTrimsWhitespace(t *testing.T) {
t.Cleanup(resetLoggingState)
Init(Config{})
_, id := WithRequestID(nil, " ")
if id == "" {
t.Fatal("expected generated id for whitespace input")
}
}
func TestInitThreadSafety(t *testing.T) {
t.Cleanup(resetLoggingState)
var wg sync.WaitGroup
configs := []Config{
{Format: "json", Level: "debug", Component: "worker"},
{Format: "json", Level: "warn", Component: "api"},
}
for i := 0; i < 50; i++ {
wg.Add(1)
go func(idx int) {
defer wg.Done()
Init(configs[idx%len(configs)])
}(i)
}
wg.Wait()
mu.RLock()
defer mu.RUnlock()
// Ensure baseLogger is valid and global logger matches it.
if reflect.DeepEqual(baseLogger, zerolog.Logger{}) {
t.Fatal("expected initialized base logger")
}
if !reflect.DeepEqual(log.Logger, baseLogger) {
t.Fatal("expected global log.Logger to match baseLogger after concurrent init")
}
}
func TestIsLevelEnabled(t *testing.T) {
t.Cleanup(resetLoggingState)
// Set global level to Info
zerolog.SetGlobalLevel(zerolog.InfoLevel)
if !IsLevelEnabled(zerolog.InfoLevel) {
t.Fatal("expected info level to be enabled")
}
if !IsLevelEnabled(zerolog.WarnLevel) {
t.Fatal("expected warn level to be enabled")
}
if !IsLevelEnabled(zerolog.ErrorLevel) {
t.Fatal("expected error level to be enabled")
}
if IsLevelEnabled(zerolog.DebugLevel) {
t.Fatal("expected debug level to be disabled")
}
// Change to debug level
zerolog.SetGlobalLevel(zerolog.DebugLevel)
if !IsLevelEnabled(zerolog.DebugLevel) {
t.Fatal("expected debug level to be enabled after setting global level")
}
}
func TestRollingFileWriter(t *testing.T) {
t.Cleanup(resetLoggingState)
dir := t.TempDir()
logFile := dir + "/test.log"
cfg := Config{
Format: "json",
Level: "info",
FilePath: logFile,
MaxSizeMB: 1,
MaxAgeDays: 7,
Compress: false,
}
Init(cfg)
// Write some log output
log.Info().Msg("test message")
// Check file exists (this confirms rolling file writer was created)
if _, err := os.Stat(logFile); os.IsNotExist(err) {
t.Fatal("expected log file to be created")
}
// Check file has content
data, err := os.ReadFile(logFile)
if err != nil {
t.Fatalf("failed to read log file: %v", err)
}
if len(data) == 0 {
t.Fatal("expected log file to have content")
}
}
func TestParseLevelDefaults(t *testing.T) {
tests := []struct {
input string
want zerolog.Level
}{
{"debug", zerolog.DebugLevel},
{"DEBUG", zerolog.DebugLevel},
{"info", zerolog.InfoLevel},
{"INFO", zerolog.InfoLevel},
{"warn", zerolog.WarnLevel},
{"WARN", zerolog.WarnLevel},
{"error", zerolog.ErrorLevel},
{"ERROR", zerolog.ErrorLevel},
{"unknown", zerolog.InfoLevel},
{"", zerolog.InfoLevel},
}
for _, tc := range tests {
got := parseLevel(tc.input)
if got != tc.want {
t.Errorf("parseLevel(%q) = %v, want %v", tc.input, got, tc.want)
}
}
}
func TestSelectWriter(t *testing.T) {
tests := []struct {
format string
isTTY bool
wantErr bool
}{
{"json", false, false},
{"console", false, false},
{"auto", false, false},
}
for _, tc := range tests {
w := selectWriter(tc.format)
if w == nil {
t.Errorf("selectWriter(%q) returned nil", tc.format)
}
}
}
// Test that the logging package doesn't panic under concurrent use
func TestConcurrentLogging(t *testing.T) {
t.Cleanup(resetLoggingState)
var buf bytes.Buffer
mu.Lock()
baseWriter = &buf
baseLogger = zerolog.New(&buf).With().Timestamp().Logger()
log.Logger = baseLogger
mu.Unlock()
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
log.Info().Int("iteration", n).Msg("concurrent log")
}(i)
}
wg.Wait()
if buf.Len() == 0 {
t.Fatal("expected log output from concurrent logging")
}
}