Render unmatched Calendar routes with existing error page
Co-Authored-By: Codex <noreply@openai.com>
2 files changed,
+57
−0
internal/calendar/routes.go+4 −0internal/calendartest/notfound_test.go+53 −0
CI failed — run details
diff --git a/internal/calendar/routes.go b/internal/calendar/routes.go| index 7e128d9..2c60129 100644 |
| --- a/internal/calendar/routes.go |
| +++ b/internal/calendar/routes.go |
| @@ -26,6 +26,10 @@ func (a *App) routes(h *handlers, authPlugin *rauth.Auth, list allowlist, origin |
| // covered. Origin-checking, not tokens — nothing to mint or forget |
| // in a form. |
| r.Use(csrf.Protect(origin)) |
| + r.NotFound(func(w http.ResponseWriter, r *http.Request) { |
| + w.Header().Set("Content-Type", "text/html; charset=utf-8") |
| + ErrorPage(w, r, http.StatusNotFound, "") |
| + }) |
| // Signed-out. |
| r.Get("/signin", h.signin) |
diff --git a/internal/calendartest/notfound_test.go b/internal/calendartest/notfound_test.go| new file mode 100644 |
| index 0000000..ca17d51 |
| --- /dev/null |
| +++ b/internal/calendartest/notfound_test.go |
| @@ -0,0 +1,53 @@ |
| +package calendartest |
| + |
| +import ( |
| + "io" |
| + "net/http" |
| + "net/http/httptest" |
| + "strings" |
| + "testing" |
| +) |
| + |
| +func TestUnknownRouteRendersSharedErrorPage(t *testing.T) { |
| + server := httptest.NewServer(newApp(t).Handler()) |
| + defer server.Close() |
| + response, err := server.Client().Get(server.URL + "/not-a-real-route") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + defer response.Body.Close() |
| + body, err := io.ReadAll(response.Body) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + if response.StatusCode != http.StatusNotFound { |
| + t.Fatalf("status %d", response.StatusCode) |
| + } |
| + if got := response.Header.Get("Content-Type"); got != "text/html; charset=utf-8" { |
| + t.Errorf("content type %q", got) |
| + } |
| + for _, marker := range []string{"<html", "<main", "rst-error-status>404", "rst-error-title", "/static/theme.", "href=\"/\""} { |
| + if !strings.Contains(string(body), marker) { |
| + t.Errorf("missing shared error marker %q", marker) |
| + } |
| + } |
| +} |
| + |
| +func TestNotFoundLeavesMatchedRoutesAlone(t *testing.T) { |
| + h := newApp(t).Handler() |
| + for _, path := range []string{"/static/not-a-real-file.css", "/feed/not-a-real-token.ics"} { |
| + r := get(t, h, path) |
| + if r.Code != http.StatusNotFound || r.Body.String() != "404 page not found\n" { |
| + t.Errorf("matched refusal changed for %s: status %d", path, r.Code) |
| + } |
| + if r.Header().Get("Content-Type") != "text/plain; charset=utf-8" { |
| + t.Errorf("matched refusal type changed for %s", path) |
| + } |
| + } |
| + for _, path := range []string{"/", "/events/new"} { |
| + r := get(t, h, path) |
| + if r.Code != http.StatusSeeOther || !strings.HasPrefix(r.Header().Get("Location"), "/signin") { |
| + t.Errorf("protected redirect changed for %s: status %d", path, r.Code) |
| + } |
| + } |
| +} |