textmachine/platform/internal/reqid/reqid_test.go

66 lines
2.2 KiB
Go

package reqid
import (
"bytes"
"context"
"encoding/json"
"log/slog"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
// The id is ours. An id echoed from the caller lets them stamp their own value on our lines and
// correlate — or collide with — someone else's. Mutation caught: reading the header from the
// request when it is present.
func TestRequestIDIsNeverTakenFromTheCaller(t *testing.T) {
var seen string
h := Middleware(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
seen = FromContext(r.Context())
}))
req := httptest.NewRequest(http.MethodGet, "/", nil)
req.Header.Set(Header, "attacker-supplied")
rec := httptest.NewRecorder()
h.ServeHTTP(rec, req)
if seen == "" {
t.Fatal("no id was stamped")
}
if seen == "attacker-supplied" || rec.Header().Get(Header) == "attacker-supplied" {
t.Fatal("the caller's id was adopted")
}
if rec.Header().Get(Header) != seen {
t.Fatalf("the echoed id %q is not the one in the context %q", rec.Header().Get(Header), seen)
}
}
// Every *Context log call inside a request carries the id without the call site saying so — which
// is the point: the error paths that need correlating are the ones nobody remembers to annotate.
func TestLogRecordsCarryTheRequestID(t *testing.T) {
var buf bytes.Buffer
log := slog.New(WithContext(slog.NewJSONHandler(&buf, nil)))
h := Middleware(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
log.With("layer", "test").ErrorContext(r.Context(), "something failed")
}))
h.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest(http.MethodGet, "/", nil))
var rec map[string]any
if err := json.Unmarshal(bytes.TrimSpace(buf.Bytes()), &rec); err != nil {
t.Fatalf("log line: %v (%s)", err, buf.String())
}
if id, _ := rec[Key].(string); id == "" {
t.Fatalf("no %s on the record: %s", Key, buf.String())
}
if rec["layer"] != "test" {
t.Fatalf("the wrapper dropped attributes added with With: %s", buf.String())
}
// Outside a request there is nothing to add, and the handler must not invent one.
buf.Reset()
log.ErrorContext(context.Background(), "background failure")
if strings.Contains(buf.String(), Key) {
t.Fatalf("an id appeared outside a request: %s", buf.String())
}
}