mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-09 19:32:24 +00:00
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
294 lines
6 KiB
Go
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")
|
|
}
|
|
}
|