Sync and async sinks, redaction, and a queryable HTTP service keep concurrent logs tied to a trace id. Co-authored-by: Cursor <cursoragent@cursor.com>
99 lines
2.5 KiB
Go
99 lines
2.5 KiB
Go
package server
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestIngestQueryTail(t *testing.T) {
|
|
srv, err := New(t.TempDir()+"/logs.jsonl", "tok", 100)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { _ = srv.Close() })
|
|
ts := httptest.NewServer(srv.Handler())
|
|
defer ts.Close()
|
|
|
|
body := []byte(`{"entries":[{"ts":"2026-09-29T00:00:00Z","level":"error","service":"agent-squad","msg":"provider down","trace_id":"trace-abc-1","code":"agent.provider_unavailable","fields":{"api_key":"sk-abcdefghijklmnopqrstuvwxyz"}}]}`)
|
|
req, err := http.NewRequest(http.MethodPost, ts.URL+"/v1/logs", bytes.NewReader(body))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
req.Header.Set("Authorization", "Bearer tok")
|
|
res, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
res.Body.Close()
|
|
if res.StatusCode != http.StatusOK {
|
|
t.Fatalf("ingest %d", res.StatusCode)
|
|
}
|
|
|
|
unauth, err := http.Get(ts.URL + "/v1/logs")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
unauth.Body.Close()
|
|
if unauth.StatusCode != http.StatusUnauthorized {
|
|
t.Fatalf("auth %d", unauth.StatusCode)
|
|
}
|
|
|
|
req, err = http.NewRequest(http.MethodGet, ts.URL+"/v1/logs?trace_id=trace-abc-1&level=error", nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
req.Header.Set("X-Log-Token", "tok")
|
|
res, err = http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer res.Body.Close()
|
|
var payload struct {
|
|
Entries []Record `json:"entries"`
|
|
Next int64 `json:"next_cursor"`
|
|
}
|
|
if err := json.NewDecoder(res.Body).Decode(&payload); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(payload.Entries) != 1 || payload.Entries[0].Code != "agent.provider_unavailable" {
|
|
t.Fatalf("%+v", payload.Entries)
|
|
}
|
|
raw, _ := json.Marshal(payload.Entries[0])
|
|
if strings.Contains(string(raw), "sk-abcdefghijklmnopqrstuvwxyz") {
|
|
t.Fatalf("secret leaked: %s", raw)
|
|
}
|
|
|
|
req, err = http.NewRequest(http.MethodGet, ts.URL+"/v1/logs/tail?cursor=0", nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
req.Header.Set("X-Log-Token", "tok")
|
|
tailRes, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer tailRes.Body.Close()
|
|
reader := bufio.NewReader(tailRes.Body)
|
|
line, err := reader.ReadString('\n')
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.HasPrefix(line, "data: ") || !strings.Contains(line, "provider down") {
|
|
t.Fatalf("sse %q", line)
|
|
}
|
|
|
|
health, err := http.Get(ts.URL + "/v1/health")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
health.Body.Close()
|
|
if health.StatusCode != http.StatusOK {
|
|
t.Fatal(health.StatusCode)
|
|
}
|
|
}
|