Files
NianAIGC/backend/internal/logging/service_test.go

124 lines
4.3 KiB
Go

package logging
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"testing"
"time"
)
func TestFixtureIsConsumedByGo(t *testing.T) {
data, err := os.ReadFile("../../../contracts/logs/runtime-v1.json")
if err != nil {
t.Fatal(err)
}
var fixture struct {
Version, DefaultLimit, MinimumLimit, MaximumLimit, MaximumTextLength, MaximumDepth int
FileName, RotatedSuffix string
}
if err := json.Unmarshal(data, &fixture); err != nil {
t.Fatal(err)
}
if fixture.Version != 1 || fixture.FileName != "server-events.jsonl" || fixture.MaximumLimit != 500 || fixture.MaximumDepth != 5 {
t.Fatalf("fixture = %#v", fixture)
}
}
func TestServiceAppendsListsFiltersAndClears(t *testing.T) {
path := filepath.Join(t.TempDir(), "server-events.jsonl")
now := time.Date(2026, 8, 13, 8, 0, 0, 0, time.UTC)
service := New(path, 1<<20, func() time.Time { now = now.Add(time.Second); return now }, func() string { return "fixed-id" })
first, err := service.Append(context.Background(), Input{Level: Error, Source: "api.test", Message: "Authorization: Bearer top-secret", Status: 500, Method: "POST", Path: "/api/test?token=abc", Stack: "password=hunter2", Details: map[string]any{"Authorization": "Bearer hidden", "nested": map[string]any{"api_key": "key", "prompt": "EvoLink"}}})
if err != nil {
t.Fatal(err)
}
if first.ID != "log_fixed-id" {
t.Fatalf("entry = %#v", first)
}
if _, err := service.Append(context.Background(), Input{Level: Warning, Source: "worker.test", Message: "retry scheduled"}); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, append(mustRead(t, path), []byte("not-json\n")...), 0o600); err != nil {
t.Fatal(err)
}
entries, err := service.List(context.Background(), Filters{Level: "error", Q: "evolink", Source: "API", Limit: 900})
if err != nil || len(entries) != 1 || entries[0].Source != "api.test" {
t.Fatalf("entries = %#v, err = %v", entries, err)
}
raw := string(mustRead(t, path))
for _, secret := range []string{"top-secret", "abc", "hunter2", "hidden", "\"key\""} {
if strings.Contains(raw, secret) {
t.Fatalf("raw log leaked %q: %s", secret, raw)
}
}
all, err := service.List(context.Background(), Filters{})
if err != nil || len(all) != 2 || all[0].Message != "retry scheduled" {
t.Fatalf("all = %#v, err = %v", all, err)
}
if err := service.Clear(context.Background()); err != nil {
t.Fatal(err)
}
if entries, err := service.List(context.Background(), Filters{}); err != nil || len(entries) != 0 {
t.Fatalf("after clear = %#v, %v", entries, err)
}
}
func TestServiceRotatesAndBoundsNestedDetails(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "server-events.jsonl")
service := New(path, 200, time.Now, func() string { return "id" })
if _, err := service.Append(context.Background(), Input{Source: "one", Message: strings.Repeat("x", 250)}); err != nil {
t.Fatal(err)
}
if _, err := service.Append(context.Background(), Input{Source: "two", Message: "next", Details: map[string]any{"jwt": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIn0.signature", "deep": nested(8)}}); err != nil {
t.Fatal(err)
}
if _, err := os.Stat(path + ".1"); err != nil {
t.Fatalf("rotated log: %v", err)
}
raw := string(mustRead(t, path))
if strings.Contains(raw, "eyJhbGci") || !strings.Contains(raw, "[truncated]") {
t.Fatalf("bounds/redaction missing: %s", raw)
}
}
func TestServiceSerializesConcurrentAppends(t *testing.T) {
path := filepath.Join(t.TempDir(), "server-events.jsonl")
service := New(path, 1<<20, time.Now, nil)
var wait sync.WaitGroup
for index := 0; index < 40; index++ {
wait.Add(1)
go func(index int) {
defer wait.Done()
if _, err := service.Append(context.Background(), Input{Source: "parallel", Message: fmt.Sprintf("entry-%d", index)}); err != nil {
t.Errorf("append %d: %v", index, err)
}
}(index)
}
wait.Wait()
entries, err := service.List(context.Background(), Filters{Limit: 100})
if err != nil || len(entries) != 40 {
t.Fatalf("entries = %d, err = %v", len(entries), err)
}
}
func nested(depth int) any {
if depth == 0 {
return "leaf"
}
return map[string]any{"child": nested(depth - 1)}
}
func mustRead(t *testing.T, path string) []byte {
t.Helper()
data, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
return data
}