Major changes: - Add internal/logging package with field constants, context propagation, sensitive data auto-redaction, and per-component log levels - Add worker timeout constants (TimeoutQuickOp, TimeoutHealthCheck, etc.) - Extend SDLC with callback handlers, generate endpoints, and executor - Add new cookbook trees for aeries and slackpath progression - Add skeleton templates for queue, realtime, and microservices - Add worker component template with async job processing - Refactor services and handlers to use new logging infrastructure - Split component.go into component_infra.go and component_listing.go Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
216 lines
5.3 KiB
Go
216 lines
5.3 KiB
Go
package logging
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestNewLogger(t *testing.T) {
|
|
cfg := DefaultConfig()
|
|
cfg.RedactEnabled = false // Simplify for this test
|
|
logger := New(cfg)
|
|
|
|
if logger == nil {
|
|
t.Fatal("expected non-nil logger")
|
|
}
|
|
if logger.Slog() == nil {
|
|
t.Fatal("expected non-nil slog.Logger")
|
|
}
|
|
}
|
|
|
|
func TestLoggerWith(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
cfg := DefaultConfig()
|
|
cfg.RedactEnabled = false
|
|
logger := NewWithWriter(cfg, &buf)
|
|
|
|
// Test With method
|
|
logger2 := logger.With("key", "value")
|
|
logger2.Info("test message")
|
|
|
|
output := buf.String()
|
|
if !strings.Contains(output, `"key"`) || !strings.Contains(output, `"value"`) {
|
|
t.Errorf("expected key/value in output, got: %s", output)
|
|
}
|
|
}
|
|
|
|
func TestLoggerWithComponent(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
cfg := DefaultConfig()
|
|
cfg.RedactEnabled = false
|
|
logger := NewWithWriter(cfg, &buf)
|
|
|
|
logger.WithComponent("test_component").Info("test message")
|
|
|
|
output := buf.String()
|
|
if !strings.Contains(output, FieldComponent) || !strings.Contains(output, "test_component") {
|
|
t.Errorf("expected component field in output, got: %s", output)
|
|
}
|
|
}
|
|
|
|
func TestLoggerWithHandler(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
cfg := DefaultConfig()
|
|
cfg.RedactEnabled = false
|
|
logger := NewWithWriter(cfg, &buf)
|
|
|
|
logger.WithHandler("CreateProject").Info("test message")
|
|
|
|
output := buf.String()
|
|
if !strings.Contains(output, FieldHandler) || !strings.Contains(output, "CreateProject") {
|
|
t.Errorf("expected handler field in output, got: %s", output)
|
|
}
|
|
}
|
|
|
|
func TestLoggerWithError(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
cfg := DefaultConfig()
|
|
cfg.RedactEnabled = false
|
|
logger := NewWithWriter(cfg, &buf)
|
|
|
|
err := &testError{msg: "something went wrong"}
|
|
logger.WithError(err).Error("operation failed")
|
|
|
|
output := buf.String()
|
|
// Verify we use "error" field, not "err" or "e"
|
|
if !strings.Contains(output, `"error"`) || !strings.Contains(output, "something went wrong") {
|
|
t.Errorf("expected error field in output, got: %s", output)
|
|
}
|
|
}
|
|
|
|
type testError struct {
|
|
msg string
|
|
}
|
|
|
|
func (e *testError) Error() string {
|
|
return e.msg
|
|
}
|
|
|
|
func TestLoggerWithErrorNil(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
cfg := DefaultConfig()
|
|
cfg.RedactEnabled = false
|
|
logger := NewWithWriter(cfg, &buf)
|
|
|
|
// WithError(nil) should not add error field
|
|
logger2 := logger.WithError(nil)
|
|
if logger2 != logger {
|
|
t.Error("WithError(nil) should return same logger")
|
|
}
|
|
}
|
|
|
|
func TestLoggerTimed(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
cfg := DefaultConfig()
|
|
cfg.RedactEnabled = false
|
|
logger := NewWithWriter(cfg, &buf)
|
|
|
|
// Test Timed method
|
|
done := logger.Timed("test_operation")
|
|
time.Sleep(10 * time.Millisecond)
|
|
done()
|
|
|
|
output := buf.String()
|
|
if !strings.Contains(output, "test_operation") {
|
|
t.Errorf("expected operation in output, got: %s", output)
|
|
}
|
|
if !strings.Contains(output, FieldDuration) {
|
|
t.Errorf("expected duration field in output, got: %s", output)
|
|
}
|
|
if !strings.Contains(output, "operation completed") {
|
|
t.Errorf("expected completion message in output, got: %s", output)
|
|
}
|
|
}
|
|
|
|
func TestLoggerJSONFormat(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
cfg := DefaultConfig()
|
|
cfg.Format = FormatJSON
|
|
cfg.RedactEnabled = false
|
|
logger := NewWithWriter(cfg, &buf)
|
|
|
|
logger.Info("test message", "key", "value")
|
|
|
|
var entry map[string]any
|
|
if err := json.Unmarshal(buf.Bytes(), &entry); err != nil {
|
|
t.Fatalf("expected valid JSON, got error: %v, output: %s", err, buf.String())
|
|
}
|
|
if entry["msg"] != "test message" {
|
|
t.Errorf("expected msg='test message', got: %v", entry["msg"])
|
|
}
|
|
if entry["key"] != "value" {
|
|
t.Errorf("expected key='value', got: %v", entry["key"])
|
|
}
|
|
}
|
|
|
|
func TestLoggerTextFormat(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
cfg := DefaultConfig()
|
|
cfg.Format = FormatText
|
|
cfg.RedactEnabled = false
|
|
logger := NewWithWriter(cfg, &buf)
|
|
|
|
logger.Info("test message", "key", "value")
|
|
|
|
output := buf.String()
|
|
// Text format should contain the message and key=value
|
|
if !strings.Contains(output, "test message") {
|
|
t.Errorf("expected message in output, got: %s", output)
|
|
}
|
|
if !strings.Contains(output, "key=value") {
|
|
t.Errorf("expected key=value in output, got: %s", output)
|
|
}
|
|
}
|
|
|
|
func TestFromContext(t *testing.T) {
|
|
cfg := DefaultConfig()
|
|
cfg.RedactEnabled = false
|
|
logger := New(cfg)
|
|
SetDefault(logger)
|
|
|
|
// No logger in context should return default
|
|
ctx := context.Background()
|
|
l := FromContext(ctx)
|
|
if l == nil {
|
|
t.Fatal("expected non-nil logger from empty context")
|
|
}
|
|
|
|
// Logger in context should be returned
|
|
enriched := logger.With("test", "value")
|
|
ctx = WithContext(ctx, enriched)
|
|
l = FromContext(ctx)
|
|
if l != enriched {
|
|
t.Error("expected enriched logger from context")
|
|
}
|
|
}
|
|
|
|
func TestFromContextNil(t *testing.T) {
|
|
cfg := DefaultConfig()
|
|
cfg.RedactEnabled = false
|
|
logger := New(cfg)
|
|
SetDefault(logger)
|
|
|
|
// Test that FromContext handles nil context gracefully
|
|
// This is intentionally testing nil context handling
|
|
var nilCtx context.Context //nolint:staticcheck // testing nil context handling
|
|
l := FromContext(nilCtx)
|
|
if l == nil {
|
|
t.Fatal("expected non-nil logger from nil context")
|
|
}
|
|
}
|
|
|
|
func TestNop(t *testing.T) {
|
|
logger := Nop()
|
|
if logger == nil {
|
|
t.Fatal("expected non-nil nop logger")
|
|
}
|
|
|
|
// Should not panic when called
|
|
logger.Info("this should be discarded")
|
|
logger.WithComponent("test").Error("this too")
|
|
}
|