fix(restheadspec): honour string URL ids on POST updates

POST with a non-numeric URL id (e.g. a string primary key) was treated as
having no id, so the body primary key was used to look up the existing row.
That broke primary key changes. Treat any non-empty, non-zero URL id as the
update target, and add an integration test through Handle for POST and PUT.
This commit is contained in:
Hein
2026-10-01 10:23:23 +02:00
parent 3bd3e46409
commit 0ff4f04a9b
2 changed files with 32 additions and 2 deletions
+4 -2
View File
@@ -241,9 +241,11 @@ func (h *Handler) Handle(w common.ResponseWriter, r common.Request, params map[s
h.sendError(w, http.StatusBadRequest, "invalid_request", "Invalid request body", err)
return
}
validId, _ := strconv.ParseInt(id, 10, 64)
// A URL id is valid when it is a positive integer or any non-numeric
// string (string primary keys); "", "0" and negatives mean no id.
validId, parseErr := strconv.ParseInt(id, 10, 64)
updateID := id
isUpdate := validId > 0
isUpdate := id != "" && (parseErr != nil || validId > 0)
if !isUpdate {
// No valid /:id in the URL - check whether the body itself carries
// a valid primary key value and treat this as an update if so.
@@ -8,6 +8,7 @@ import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
@@ -123,3 +124,30 @@ func TestUpdateChangesPrimaryKeyWhenURLIDGiven(t *testing.T) {
require.NoError(t, db.QueryRow(`SELECT category FROM public.t_pkasset WHERE description = 'new-pk'`).Scan(&cat))
require.Equal(t, "cat5", cat)
}
// Exercises the real dispatch: a POST with a string id in the URL and a different
// PK in the body must update the row identified by the URL id.
func TestHandlePostWithStringURLIDChangesPrimaryKey(t *testing.T) {
db := setupPKTestDB(t)
reg := modelregistry.NewModelRegistry()
require.NoError(t, reg.RegisterModel("public.t_pkasset", pkAsset{}))
h := NewHandler(database.NewBunAdapter(bun.NewDB(db, pgdialect.New())), reg)
for _, method := range []string{http.MethodPost, http.MethodPut} {
t.Run(method, func(t *testing.T) {
from, to := "old-pk", "new-pk-"+method
if method == http.MethodPut {
from = "new-pk-" + http.MethodPost
}
req := httptest.NewRequest(method, "/public/t_pkasset/"+from,
strings.NewReader(`{"description":"`+to+`","category":"c"}`))
rec := httptest.NewRecorder()
w, r := common.WrapHTTPRequest(rec, req)
h.Handle(w, r, map[string]string{"schema": "public", "entity": "t_pkasset", "id": from})
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
require.Equal(t, 0, countPK(t, db, from))
require.Equal(t, 1, countPK(t, db, to))
})
}
}