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 }