@@ -0,0 +1,94 @@
10+ "github.com/charmbracelet/crush/internal/proto"
11+ "github.com/stretchr/testify/require"
14+// TestRecoverHandler_PanicReturns500 verifies that a panicking handler
15+// surfaces as a structured 500 to the client, rather than closing the
16+// connection silently and producing an opaque EOF.
17+func TestRecoverHandler_PanicReturns500(t *testing.T) {
21+ h := s.recoverHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
25+ rec := httptest.NewRecorder()
26+ req := httptest.NewRequest(http.MethodGet, "/test", nil)
27+ h.ServeHTTP(rec, req)
29+ require.Equal(t, http.StatusInternalServerError, rec.Code)
30+ body, err := io.ReadAll(rec.Body)
31+ require.NoError(t, err)
32+ require.NotEmpty(t, body)
35+ require.NoError(t, json.Unmarshal(body, &perr))
36+ require.NotEmpty(t, perr.Message)
39+// TestRecoverHandler_NoPanicPassthrough verifies that the middleware
40+// does not interfere with successful responses.
41+func TestRecoverHandler_NoPanicPassthrough(t *testing.T) {
45+ h := s.recoverHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
46+ w.WriteHeader(http.StatusTeapot)
47+ _, _ = w.Write([]byte("ok"))
50+ rec := httptest.NewRecorder()
51+ req := httptest.NewRequest(http.MethodGet, "/test", nil)
52+ h.ServeHTTP(rec, req)
54+ require.Equal(t, http.StatusTeapot, rec.Code)
55+ require.Equal(t, "ok", rec.Body.String())
58+// TestRecoverHandler_PanicAfterWriteHeader verifies that if a handler
59+// panics after it has already started writing the response, the
60+// middleware does not attempt to overwrite the status (which would
61+// trigger a superfluous WriteHeader warning) but still logs and
63+func TestRecoverHandler_PanicAfterWriteHeader(t *testing.T) {
67+ h := s.recoverHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
68+ w.WriteHeader(http.StatusOK)
69+ _, _ = w.Write([]byte("partial"))
73+ rec := httptest.NewRecorder()
74+ req := httptest.NewRequest(http.MethodGet, "/test", nil)
75+ require.NotPanics(t, func() { h.ServeHTTP(rec, req) })
76+ require.Equal(t, http.StatusOK, rec.Code)
77+ require.Equal(t, "partial", rec.Body.String())
80+// TestRecoverHandler_AbortHandlerPropagates verifies that the documented
81+// http.ErrAbortHandler sentinel is re-panicked so the net/http server
82+// can handle it normally (suppress logging, close connection).
83+func TestRecoverHandler_AbortHandlerPropagates(t *testing.T) {
87+ h := s.recoverHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
88+ panic(http.ErrAbortHandler)
91+ rec := httptest.NewRecorder()
92+ req := httptest.NewRequest(http.MethodGet, "/test", nil)
93+ require.PanicsWithValue(t, http.ErrAbortHandler, func() { h.ServeHTTP(rec, req) })