From c417c892d98dfa42c38cc849b35fc6c3e5ccbcc6 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Wed, 10 Jun 2026 22:11:38 -0500 Subject: [PATCH] Enhance JSON payload decoding to accept both typed and pointer payloads, and add corresponding tests --- internal/normalizers/common/json.go | 12 ++++++ internal/normalizers/common/json_test.go | 39 +++++++++++++++++++ .../spc/convective_outlook_test.go | 15 +++++++ 3 files changed, 66 insertions(+) create mode 100644 internal/normalizers/common/json_test.go diff --git a/internal/normalizers/common/json.go b/internal/normalizers/common/json.go index 3455d44..a536f98 100644 --- a/internal/normalizers/common/json.go +++ b/internal/normalizers/common/json.go @@ -18,6 +18,18 @@ import ( // Errors include a small amount of operation context ("extract payload", "decode raw payload"). // Callers typically wrap these with a provider/kind label. func DecodeJSONPayload[T any](in event.Event) (T, error) { + var zero T + + if typed, ok := in.Payload.(T); ok { + return typed, nil + } + if ptr, ok := in.Payload.(*T); ok { + if ptr == nil { + return zero, fmt.Errorf("extract payload: payload pointer is nil") + } + return *ptr, nil + } + return fknormalize.DecodeJSONPayload[T](in) } diff --git a/internal/normalizers/common/json_test.go b/internal/normalizers/common/json_test.go new file mode 100644 index 0000000..b21f6f3 --- /dev/null +++ b/internal/normalizers/common/json_test.go @@ -0,0 +1,39 @@ +package common + +import ( + "testing" + + "gitea.maximumdirect.net/ejr/feedkit/event" +) + +func TestDecodeJSONPayloadAcceptsTypedPayload(t *testing.T) { + type rawPayload struct { + Value string `json:"value"` + } + + got, err := DecodeJSONPayload[rawPayload](event.Event{ + Payload: rawPayload{Value: "ok"}, + }) + if err != nil { + t.Fatalf("DecodeJSONPayload() error = %v", err) + } + if got.Value != "ok" { + t.Fatalf("Value = %q, want ok", got.Value) + } +} + +func TestDecodeJSONPayloadAcceptsTypedPointerPayload(t *testing.T) { + type rawPayload struct { + Value string `json:"value"` + } + + got, err := DecodeJSONPayload[rawPayload](event.Event{ + Payload: &rawPayload{Value: "ok"}, + }) + if err != nil { + t.Fatalf("DecodeJSONPayload() error = %v", err) + } + if got.Value != "ok" { + t.Fatalf("Value = %q, want ok", got.Value) + } +} diff --git a/internal/normalizers/spc/convective_outlook_test.go b/internal/normalizers/spc/convective_outlook_test.go index 4a2958b..e76b1c9 100644 --- a/internal/normalizers/spc/convective_outlook_test.go +++ b/internal/normalizers/spc/convective_outlook_test.go @@ -107,6 +107,21 @@ func TestConvectiveOutlookNormalizerProducesCanonicalSchemaAndMapsSample(t *test } } +func TestConvectiveOutlookNormalizerAcceptsTypedSourcePayload(t *testing.T) { + bundle := spcBundle(t, 38.5, -90.5) + in := spcRawEvent(t, bundle) + in.Payload = bundle + + out, err := (ConvectiveOutlookNormalizer{}).Normalize(nil, in) + if err != nil { + t.Fatalf("Normalize() error = %v", err) + } + run := out.Payload.(model.WeatherOutlookRun) + if len(run.Outlooks) != 9 { + t.Fatalf("Outlooks length = %d, want 9", len(run.Outlooks)) + } +} + func TestConvectiveOutlookNormalizerOrdersProductsByDayAndType(t *testing.T) { bundle := spcBundle(t, 0, 0) for i, j := 0, len(bundle.Products)-1; i < j; i, j = i+1, j-1 {