Files
ja4sentinel/internal/logging/service_logger_test.go
Jacquin Antoine fec500ba46
Some checks failed
Build RPM Package / Build RPM Packages (CentOS 7, Rocky 8/9/10) (push) Has been cancelled
fix: correction race conditions et amélioration robustesse
- Correction race condition dans tlsparse avec mutex par ConnectionFlow
- Fix fuite mémoire buffer HelloBuffer
- Ajout rotation de fichiers logs (100MB, 3 backups)
- Implémentation queue asynchrone avec reconnexion exponentielle (socket UNIX)
- Validation BPF (caractères, longueur, parenthèses)
- Augmentation snapLen pcap de 1600 à 65535 bytes
- Permissions fichiers sécurisées (0600)
- Ajout 46 tests unitaires (capture, output, logging)
- Passage go test -race sans erreur

Tests: go test -race ./... ✓
Build: go build ./... ✓
Lint: go vet ./... ✓

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-02-28 21:15:45 +01:00

239 lines
6.3 KiB
Go

package logging
import (
"bytes"
"encoding/json"
"log"
"strings"
"testing"
"ja4sentinel/api"
)
func TestIsLogLevelEnabled(t *testing.T) {
tests := []struct {
name string
loggerLevel string
messageLevel string
want bool
}{
{name: "debug logger accepts debug", loggerLevel: "debug", messageLevel: "debug", want: true},
{name: "debug logger accepts info", loggerLevel: "debug", messageLevel: "info", want: true},
{name: "info logger rejects debug", loggerLevel: "info", messageLevel: "debug", want: false},
{name: "info logger accepts info", loggerLevel: "info", messageLevel: "info", want: true},
{name: "warn logger rejects info", loggerLevel: "warn", messageLevel: "info", want: false},
{name: "warn logger accepts error", loggerLevel: "warn", messageLevel: "error", want: true},
{name: "error logger accepts only error", loggerLevel: "error", messageLevel: "error", want: true},
{name: "error logger rejects warn", loggerLevel: "error", messageLevel: "warn", want: false},
{name: "invalid level rejects all", loggerLevel: "invalid", messageLevel: "info", want: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
logger := NewServiceLogger(tt.loggerLevel)
if got := logger.isLogLevelEnabled(tt.messageLevel); got != tt.want {
t.Fatalf("isLogLevelEnabled(%q) = %v, want %v", tt.messageLevel, got, tt.want)
}
})
}
}
func TestDebug_NotEmittedWhenLoggerLevelInfo(t *testing.T) {
logger := NewServiceLogger("info")
var buf bytes.Buffer
logger.out = log.New(&buf, "", 0)
logger.Debug("service", "debug message", map[string]string{"k": "v"})
if buf.Len() != 0 {
t.Fatalf("expected no output for debug at info level, got: %s", buf.String())
}
}
func TestLog_UppercaseDebug_NotEmittedWhenLoggerLevelInfo(t *testing.T) {
logger := NewServiceLogger("info")
var buf bytes.Buffer
logger.out = log.New(&buf, "", 0)
logger.Log("service", "DEBUG", "debug message", nil)
if strings.TrimSpace(buf.String()) != "" {
t.Fatalf("expected no output for uppercase DEBUG at info level, got: %s", buf.String())
}
}
func TestInfo_EmitedWhenLoggerLevelInfo(t *testing.T) {
logger := NewServiceLogger("info")
var buf bytes.Buffer
logger.out = log.New(&buf, "", 0)
logger.Info("service", "info message", map[string]string{"key": "value"})
if buf.Len() == 0 {
t.Fatal("expected output for info at info level")
}
// Verify JSON format
var got map[string]interface{}
if err := json.Unmarshal(buf.Bytes(), &got); err != nil {
t.Fatalf("output is not valid JSON: %v", err)
}
if got["level"] != "INFO" {
t.Errorf("level = %v, want INFO", got["level"])
}
if got["component"] != "service" {
t.Errorf("component = %v, want service", got["component"])
}
if got["message"] != "info message" {
t.Errorf("message = %v, want info message", got["message"])
}
if got["key"] != "value" {
t.Errorf("key = %v, want value", got["key"])
}
}
func TestWarn_EmitedWhenLoggerLevelWarn(t *testing.T) {
logger := NewServiceLogger("warn")
var buf bytes.Buffer
logger.out = log.New(&buf, "", 0)
logger.Warn("service", "warn message", nil)
if buf.Len() == 0 {
t.Fatal("expected output for warn at warn level")
}
}
func TestError_AlwaysEmitted(t *testing.T) {
levels := []string{"debug", "info", "warn", "error"}
for _, level := range levels {
t.Run(level, func(t *testing.T) {
logger := NewServiceLogger(level)
var buf bytes.Buffer
logger.out = log.New(&buf, "", 0)
logger.Error("service", "error message", map[string]string{"error": "test"})
if buf.Len() == 0 {
t.Fatalf("expected output for error at %s level", level)
}
})
}
}
func TestLog_EmptyDetails(t *testing.T) {
logger := NewServiceLogger("debug")
var buf bytes.Buffer
logger.out = log.New(&buf, "", 0)
logger.Info("service", "test message", nil)
if buf.Len() == 0 {
t.Fatal("expected output")
}
var got map[string]interface{}
if err := json.Unmarshal(buf.Bytes(), &got); err != nil {
t.Fatalf("output is not valid JSON: %v", err)
}
// Details should not be present when nil/empty
if _, ok := got["details"]; ok {
t.Error("details should not be present when nil")
}
}
func TestLog_WithDetails(t *testing.T) {
logger := NewServiceLogger("debug")
var buf bytes.Buffer
logger.out = log.New(&buf, "", 0)
details := map[string]string{
"error": "test error",
"trace_id": "abc123",
}
logger.Info("service", "test message", details)
var got map[string]interface{}
if err := json.Unmarshal(buf.Bytes(), &got); err != nil {
t.Fatalf("output is not valid JSON: %v", err)
}
if got["error"] != "test error" {
t.Errorf("error = %v, want test error", got["error"])
}
if got["trace_id"] != "abc123" {
t.Errorf("trace_id = %v, want abc123", got["trace_id"])
}
}
func TestLog_TimestampPresent(t *testing.T) {
logger := NewServiceLogger("debug")
var buf bytes.Buffer
logger.out = log.New(&buf, "", 0)
logger.Info("service", "test", nil)
var got map[string]interface{}
if err := json.Unmarshal(buf.Bytes(), &got); err != nil {
t.Fatalf("output is not valid JSON: %v", err)
}
if _, ok := got["timestamp"]; !ok {
t.Error("timestamp should be present")
}
}
func TestLoggerFactory(t *testing.T) {
factory := &LoggerFactory{}
// Test NewLogger with different levels
levels := []string{"debug", "info", "warn", "error"}
for _, level := range levels {
t.Run(level, func(t *testing.T) {
logger := factory.NewLogger(level)
if logger == nil {
t.Fatalf("NewLogger(%q) returned nil", level)
}
})
}
// Test NewDefaultLogger
logger := factory.NewDefaultLogger()
if logger == nil {
t.Fatal("NewDefaultLogger() returned nil")
}
}
func TestServiceLogger_ImplementsApiLogger(t *testing.T) {
logger := NewServiceLogger("debug")
// Verify it implements the interface
var _ api.Logger = logger
}
func TestServiceLogger_ConcurrentLogging(t *testing.T) {
logger := NewServiceLogger("debug")
var buf bytes.Buffer
logger.out = log.New(&buf, "", 0)
done := make(chan bool)
for i := 0; i < 10; i++ {
go func(id int) {
logger.Info("service", "concurrent message", map[string]string{"id": string(rune(id))})
done <- true
}(i)
}
for i := 0; i < 10; i++ {
<-done
}
// Should have 10 lines
lines := strings.Split(strings.TrimSpace(buf.String()), "\n")
if len(lines) != 10 {
t.Errorf("expected 10 lines, got %d", len(lines))
}
}