fix(podcasts): order selector validation — bind the htmx form wire format (issue #2)
WHY: htmx sends non-GET requests urlencoded by default; the SetFeedOrder request struct only carried a json tag, so the binder left Order empty and 'required' fired — the exact error the order dropdown AND the Serial-suggestion button produced (hx-vals JSON also arrives form-encoded). One form tag fixes both controls. Regression test drives the exact wire format: urlencoded PUT with order=oldest/native → 200, empty/invalid → 400.
This commit is contained in:
1 parent
8d9fb0046c
commit
c753780817
2 files changed
+35
-1
No files matched your search
@@ -19,6 +19,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -663,3 +664,36 @@ func adminScopeExec(t *testing.T, setup *TestServerSetup, adminID pgtype.UUID, s
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ---- task 1.9 residue: the htmx wire format is FORM-encoded (issue #2) ----
|
||||||
|
|
||||||
|
func TestPodcastOrderFormEncoding(t *testing.T) {
|
||||||
|
setup := setupTestServer(t)
|
||||||
|
lib, _ := createPodcastsLibrary(t, setup, "test OrderForm Podcasts")
|
||||||
|
feed, err := setup.DB.CreatePodcastFeed(adminSeedCtx(t, setup), database.CreatePodcastFeedParams{
|
||||||
|
LibraryID: lib.ID,
|
||||||
|
FeedUrl: "https://feed.invalid/order-form.xml",
|
||||||
|
Title: "Order Form Feed",
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
feedIDStr := uuid.UUID(feed.ID.Bytes).String()
|
||||||
|
|
||||||
|
// The UI's hx-put submits urlencoded form bodies (htmx non-GET
|
||||||
|
// default) — the binder must map them (form tag), not just JSON.
|
||||||
|
formPut := func(body string) int {
|
||||||
|
req, _ := http.NewRequest(http.MethodPut,
|
||||||
|
setup.Server.URL+"/api/podcasts/feeds/"+feedIDStr+"/order",
|
||||||
|
strings.NewReader(body))
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
req.Header.Set("Authorization", "Bearer "+setup.Token)
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
return resp.StatusCode
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, formPut("order=oldest"), "form-encoded order must bind")
|
||||||
|
assert.Equal(t, http.StatusOK, formPut("order=native"), "native binds too")
|
||||||
|
assert.Equal(t, http.StatusBadRequest, formPut(""), "missing order stays rejected (required)")
|
||||||
|
assert.Equal(t, http.StatusBadRequest, formPut("order=sideways"), "invalid order stays rejected")
|
||||||
|
}
|
||||||
@@ -459,7 +459,7 @@ func (h *PodcastHandler) SetFeedOrder(c *echo.Context) error {
|
|||||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid feed id"})
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid feed id"})
|
||||||
}
|
}
|
||||||
var req struct {
|
var req struct {
|
||||||
Order string `json:"order" validate:"required,oneof=native oldest newest"`
|
Order string `json:"order" form:"order" validate:"required,oneof=native oldest newest"`
|
||||||
}
|
}
|
||||||
if err := c.Bind(&req); err != nil {
|
if err := c.Bind(&req); err != nil {
|
||||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request"})
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request"})
|
||||||
|
|||||||
Reference in new issue
Block a user