From c6d115a8ff42f94d3deb91179a68abbc76edc317 Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Fri, 25 Sep 2026 06:59:19 -0700 Subject: [PATCH] v1 plan: reference copy of the one edited given file (recorder_test.go) Co-Authored-By: Claude Fable 5.1 --- docs/plans/v1/05-proxy.md | 3 +- .../v1/_files/internal/proxy/recorder_test.go | 66 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 docs/plans/v1/_files/internal/proxy/recorder_test.go diff --git a/docs/plans/v1/05-proxy.md b/docs/plans/v1/05-proxy.md index b35361f..4a0a4f1 100644 --- a/docs/plans/v1/05-proxy.md +++ b/docs/plans/v1/05-proxy.md @@ -28,7 +28,8 @@ real `store`, `lease.Table`, `limiter` and `health.Table`. call no longer compiles, so **this is the one given test you edit**: change that call to `proxy.New(cfg, h, nil, nil, nil, nil)` and nothing else; `New` must accept nils for `leases`, `lim`, `rec` and then behave like v0 (first healthy host, no queue, no recording). - Say so in Deviations. + Say so in Deviations. The edited file is the plan's reference copy at + `_files/internal/proxy/recorder_test.go` for the reviewer's byte-exact check. ## Interfaces diff --git a/docs/plans/v1/_files/internal/proxy/recorder_test.go b/docs/plans/v1/_files/internal/proxy/recorder_test.go new file mode 100644 index 0000000..8a22dff --- /dev/null +++ b/docs/plans/v1/_files/internal/proxy/recorder_test.go @@ -0,0 +1,66 @@ +package proxy_test + +import ( + "fmt" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "git.wntrmute.dev/kyle/crossbar/internal/config" + "git.wntrmute.dev/kyle/crossbar/internal/health" + "git.wntrmute.dev/kyle/crossbar/internal/proxy" +) + +// noFlush is a ResponseWriter that does not implement http.Flusher. Middleware and test +// recorders like this exist in the wild; the proxy must degrade to buffering, never panic. +type noFlush struct{ w http.ResponseWriter } + +func (n noFlush) Header() http.Header { return n.w.Header() } +func (n noFlush) Write(b []byte) (int, error) { return n.w.Write(b) } +func (n noFlush) WriteHeader(code int) { n.w.WriteHeader(code) } + +func TestStreamingWriterWithoutFlusherDoesNotPanic(t *testing.T) { + up := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/event-stream") + w.WriteHeader(200) + for i := 0; i < 3; i++ { + fmt.Fprintf(w, "data: chunk %d\n\n", i) + w.(http.Flusher).Flush() + } + })) + t.Cleanup(up.Close) + cfg, err := config.Parse(strings.NewReader(fmt.Sprintf(` +listen = "127.0.0.1:1" +[hosts.alpha] +base_url = %q +models = { "m" = { } } +[routes.r] +hosts = ["alpha"] +`, up.URL))) + if err != nil { + t.Fatal(err) + } + h := &fakeHealth{st: map[string]health.Status{"alpha": {Healthy: true, Loaded: []string{"m"}}}} + p := proxy.New(cfg, h, nil, nil, nil, nil) + + rec := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodPost, "/r/v1/chat/completions", strings.NewReader(`{"model":"m","stream":true}`)) + func() { + defer func() { + if r := recover(); r != nil { + t.Fatalf("ServeHTTP panicked on a writer without Flush: %v", r) + } + }() + p.ServeHTTP(noFlush{rec}, req) + }() + if rec.Code != 200 { + t.Fatalf("status %d", rec.Code) + } + if got := rec.Body.String(); !strings.Contains(got, "chunk 0") || !strings.Contains(got, "chunk 2") { + t.Errorf("body = %q, want all three chunks", got) + } + if rec.Header().Get(proxy.HostHeader) != "alpha" { + t.Errorf("host header %q", rec.Header().Get(proxy.HostHeader)) + } +}