Files
webhooker/internal/server/flush_test.go
T
sneak e88192aa9a
check / check (push) Waiting to run
Let a handler's flush reach the client through the access log (closes #191)
The access log's response writer had no Unwrap method, so
http.ResponseController stopped at it and a handler's Flush returned
http.ErrNotSupported. It now has one.

With metrics on, adding Unwrap alone is not enough: go-http-metrics'
writer only passes a flush on when the writer inside it has a Flush
method, and it sat inside Logging, so the flush silently did nothing.
Metrics is now registered outside Logging, and the README's middleware
list follows.

A new test flushes through the production router, with the defaults
and with metrics and Sentry on, on a global route and inside an admin
page route group.

Model: opus-5-5
2026-10-02 08:46:08 +00:00

97 lines
2.5 KiB
Go

package server_test
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sneak.berlin/go/webhooker/internal/config"
"sneak.berlin/go/webhooker/internal/server"
)
// TestFlushThroughProductionRouter drives http.ResponseController.Flush
// through the shipped router and checks that the flush reaches the
// writer the server handed in.
//
// Every middleware that wraps the writer has to pass a flush through,
// with an Unwrap method or a Flush of its own. One that does neither
// makes Flush return http.ErrNotSupported, or do nothing at all when
// the wrapper outside it only looks for a Flush method, and the
// handler that trips over it is far from the cause.
//
// It runs once with the defaults and once with metrics and Sentry on,
// because those two add middleware to the chain, and through both the
// global middleware and an admin page route group, which adds its own.
func TestFlushThroughProductionRouter(t *testing.T) {
t.Parallel()
cases := []struct {
name string
cfg func(t *testing.T) *config.Config
sentryEnabled bool
}{
{
name: "defaults",
cfg: func(t *testing.T) *config.Config {
t.Helper()
return &config.Config{
DataDir: t.TempDir(),
Environment: config.EnvironmentDev,
}
},
},
{
name: "metrics and Sentry on",
cfg: func(t *testing.T) *config.Config {
t.Helper()
return metricsConfig(t, metricsUser, metricsAuthValue)
},
sentryEnabled: true,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
env := newTestEnvWithConfig(t, tc.cfg(t))
var flushErr error
probe := func(w http.ResponseWriter, _ *http.Request) {
flushErr = http.NewResponseController(w).Flush()
}
routers := map[string]http.Handler{
server.ProbePattern: server.NewRouterWithProbeForTest(
env.log.Get(), env.cfg, env.mw, env.hnd,
tc.sentryEnabled, probe,
),
server.PageProbePattern: server.NewRouterWithPageProbeForTest(
env.log.Get(), env.cfg, env.mw, env.hnd,
tc.sentryEnabled, probe,
),
}
for path, router := range routers {
flushErr = nil
w := httptest.NewRecorder()
router.ServeHTTP(w, httptest.NewRequestWithContext(
t.Context(), http.MethodGet, path, nil,
))
require.NoError(t, flushErr, path)
assert.True(
t, w.Flushed,
"%s: the flush must reach the server's writer", path,
)
}
})
}
}