package calls
import (
"bytes"
"log/slog"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
)
// A panic inside the router is the 500 page with a reference and the
// viewer's chrome for a browser, and plain text for a fetch (spec §3.3).
func TestAPanicIsTheErrorPageWithAReference(t *testing.T) {
f := newHomeRouterFixture(t)
boom := f.app.recoverToErrorPage(http.HandlerFunc(func(http.ResponseWriter, *http.Request) { panic("boom") }))
r := httptest.NewRequest("GET", homeTestOrigin+"/m/x", nil)
r.Header.Set("Accept", "text/html")
w := httptest.NewRecorder()
boom.ServeHTTP(w, r)
body := w.Body.String()
if w.Code != 500 || !strings.Contains(body, "
Something went wrong · Calls") || !strings.Contains(body, "Reference:") ||
!strings.Contains(body, "") || !strings.Contains(body, `href="/m/x">Try again`) {
t.Fatalf("panic page: %d\n%s", w.Code, body)
}
r = httptest.NewRequest("GET", homeTestOrigin+"/m/x/info", nil)
w = httptest.NewRecorder()
boom.ServeHTTP(w, r)
if w.Code != 500 || !strings.HasPrefix(w.Header().Get("Content-Type"), "text/plain") {
t.Fatalf("panic for a fetch: %d %q", w.Code, w.Header().Get("Content-Type"))
}
}
// Astra on #24, finding 3 (Memoria #21's shape): the test above builds
// recoverToErrorPage by hand, so taking it out of the router left it
// green. This one panics inside a REAL route — the return cookie's clock,
// read by /signin/local?return_to= — through the production router,
// signed in and out, and checks the 500 page with a reference, the
// viewer's own chrome, and that the panic's value reaches neither the
// page nor the log.
func TestARoutePanicIsTheErrorPageThroughTheRouter(t *testing.T) {
const sentinel = "secret-sentinel-title"
for _, signed := range []bool{true, false} {
f := newHomeRouterFixture(t)
if signed {
f.local(t)
}
var logs bytes.Buffer
f.app.logger = slog.New(slog.NewTextHandler(&logs, nil))
f.app.store.clk.set(func() time.Time { panic(sentinel) })
r := httptest.NewRequest("GET", homeTestOrigin+"/signin/local?return_to=%2Fsettings", nil)
r.Header.Set("Accept", "text/html")
for _, c := range f.cookies {
r.AddCookie(c)
}
w := httptest.NewRecorder()
func() {
defer func() {
if v := recover(); v != nil {
t.Fatalf("signed in=%v: the panic escaped the router: %v", signed, v)
}
}()
f.handler.ServeHTTP(w, r)
}()
b := w.Body.String()
if w.Code != http.StatusInternalServerError || !strings.Contains(b, "Something went wrong · Calls") || !strings.Contains(b, "rst-error-ref") {
t.Errorf("signed in=%v: %d, want the 500 page with a reference:\n%s", signed, w.Code, b)
}
if strings.Contains(b, "estilo-account") != signed {
t.Errorf("signed in=%v: the 500 page's chrome is not the viewer's", signed)
}
if strings.Contains(b, sentinel) || strings.Contains(logs.String(), sentinel) {
t.Errorf("signed in=%v: the panic's value reached the page or the log", signed)
}
if !strings.Contains(logs.String(), "ref=") {
t.Errorf("signed in=%v: no reference in the log: %s", signed, logs.String())
}
}
}