// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Commercial // SPDX-FileCopyrightText: 2025-2026 Christian Galo package server import ( "net/http/httptest" "testing" ) // TestRedirectToRecordHTMX pins design D9's rule that an htmx submit answering // a navigating success never gets a bare 303 (htmx would follow it and swap // the destination's full page into the form's own target, nesting the // operator shell inside itself): it gets a 200 with HX-Redirect naming the // destination, an empty body, and Vary: HX-Request so a cache never serves // this response to a native request. func TestRedirectToRecordHTMX(t *testing.T) { req := httptest.NewRequest("POST", "/partials/operator/products", nil) req.Header.Set("HX-Request", "true") rec := httptest.NewRecorder() redirectToRecord(rec, req, "/operator/products/p1?flash=created") if rec.Code != 200 { t.Errorf("status = %d, want 200", rec.Code) } if got := rec.Header().Get("HX-Redirect"); got != "/operator/products/p1?flash=created" { t.Errorf("HX-Redirect = %q, want the destination", got) } if rec.Body.Len() != 0 { t.Errorf("body = %q, want empty", rec.Body.String()) } if got := rec.Header().Get("Vary"); got != "HX-Request" { t.Errorf("Vary = %q, want HX-Request", got) } } // TestRedirectToRecordNative pins the native-submit half of the same rule: no // HX-Request header gets a 303 straight to the destination, since redirecting // only an XHR response body would never move a native form's address bar. func TestRedirectToRecordNative(t *testing.T) { req := httptest.NewRequest("POST", "/partials/operator/products", nil) rec := httptest.NewRecorder() redirectToRecord(rec, req, "/operator/products/p1?flash=created") if rec.Code != 303 { t.Errorf("status = %d, want 303", rec.Code) } if got := rec.Header().Get("Location"); got != "/operator/products/p1?flash=created" { t.Errorf("Location = %q, want the destination", got) } if got := rec.Header().Get("HX-Redirect"); got != "" { t.Errorf("HX-Redirect = %q, want unset on a native response", got) } }