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

@@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"io/fs"
"math"
"net/http"
"net/http/httptest"
"os"
@@ -52,6 +53,11 @@ type actGroup struct {
func (actGroup) TableName() string { return "cabana_ext_groups" }
// actTags is a string kind that encodes as a JSON array.
type actTags string
func (t actTags) MarshalJSON() ([]byte, error) { return json.Marshal(strings.Split(string(t), ",")) }
// actSpy records the inputs the registered actions receive.
type actSpy struct {
mu sync.Mutex
@@ -136,6 +142,9 @@ func (c actController) AdminActions() []pact.AdminAction {
return pact.AdminActionResult{}, errors.New("upstream said hunter2")
case "nested":
return pact.AdminActionResult{Fill: map[string]any{"name": []string{"a"}, "active": false}}, nil
case "encoded":
// Scalar kinds that do not encode as JSON scalars (WR-04).
return pact.AdminActionResult{Fill: map[string]any{"name": actTags("a,b"), "active": math.NaN()}}, nil
}
return pact.AdminActionResult{
Message: "acme.demo::lang.gadgets.looked_up",
@@ -361,6 +370,14 @@ func TestPhase101Actions(t *testing.T) {
}
})
t.Run("fill is judged by its JSON encoding, not its kind", func(t *testing.T) {
rec := env.expect(t, http.StatusOK, http.MethodPost, widget, `{"values":{"name":"encoded"}}`, "bearer")
if result := actResult(t, rec); !reflect.DeepEqual(result.Fill, map[string]any{}) {
t.Fatalf("fill = %#v", result.Fill)
}
env.spy.take()
})
t.Run("widget on the create form gets no record", func(t *testing.T) {
env.expect(t, http.StatusOK, http.MethodPost, widget, `{}`, "bearer")
calls := env.spy.take()