fix(10.1): WR-04 judge action fill values by their JSON encoding

isJSONScalar now encodes each value and keeps it only when the encoding
is a string, number, boolean or null, so a named scalar whose MarshalJSON
writes an array or object, NaN and the infinities are dropped. writeJSON
encodes into a buffer before the status line, so an encode failure is a
logged 500 with the generic envelope instead of a 200 with a truncated
body.
This commit is contained in:
Jakub Zych
2026-09-29 09:56:44 +02:00
parent 7333f450ad
commit 0bdb6ebcac
5 changed files with 98 additions and 22 deletions

View File

@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"errors"
"math"
"net/http"
"net/http/httptest"
"strings"
@@ -552,3 +553,55 @@ func TestCRUDFillTypeIsValidation(t *testing.T) {
t.Fatalf("year after rejected update = %v, want 1977", after.Year)
}
}
// TestWriteJSONEncodeFailure covers WR-04: a body that cannot be encoded is
// a 500 with the generic error envelope, not a 200 with a truncated body.
func TestWriteJSONEncodeFailure(t *testing.T) {
rec := httptest.NewRecorder()
WriteData(rec, http.StatusOK, map[string]any{"price": math.Inf(1)}, nil)
if rec.Code != http.StatusInternalServerError {
t.Fatalf("status=%d body=%q, want 500", rec.Code, rec.Body.String())
}
var body struct {
Error struct {
Code string `json:"code"`
Message string `json:"message"`
Details map[string]any `json:"details"`
} `json:"error"`
}
if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil {
t.Fatalf("body %q: %v", rec.Body.String(), err)
}
if body.Error.Code != "error" || body.Error.Message != msgServerError || body.Error.Details == nil {
t.Fatalf("envelope=%s", rec.Body.String())
}
if ct := rec.Header().Get("Content-Type"); ct != "application/json" {
t.Fatalf("content type %q", ct)
}
ok := httptest.NewRecorder()
WriteData(ok, http.StatusCreated, map[string]any{"n": 1}, nil)
if ok.Code != http.StatusCreated || ok.Body.String() != "{\"data\":{\"n\":1},\"meta\":{}}\n" {
t.Fatalf("status=%d body=%q", ok.Code, ok.Body.String())
}
}
// TestIsJSONScalar covers WR-04: scalar-ness is decided by the encoding.
func TestIsJSONScalar(t *testing.T) {
name := "x"
var nilPtr *string
for _, v := range []any{nil, "s", true, 1, uint8(2), 1.5, json.Number("3"), &name, nilPtr} {
if !isJSONScalar(v) {
t.Fatalf("%#v refused", v)
}
}
for _, v := range []any{math.NaN(), math.Inf(-1), []string{"a"}, map[string]any{}, struct{}{}, scalarAsArray("a"), func() {}} {
if isJSONScalar(v) {
t.Fatalf("%#v accepted", v)
}
}
}
type scalarAsArray string
func (s scalarAsArray) MarshalJSON() ([]byte, error) { return json.Marshal([]string{string(s)}) }