Something went wrong. Try again.
forked from tailscale/tailscale
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140// Copyright (c) Tailscale Inc & contributors// SPDX-License-Identifier: BSD-3-Clause
package jsonx
import ( "errors" "testing"
"github.com/go-json-experiment/json" "github.com/go-json-experiment/json/jsontext" "github.com/google/go-cmp/cmp")
type Interface interface { implementsInterface()}
type Foo string
func (Foo) implementsInterface() {}
type Bar int
func (Bar) implementsInterface() {}
type Baz struct{ Fizz, Buzz string }
func (*Baz) implementsInterface() {}
var interfaceCoders = MakeInterfaceCoders(map[string]Interface{ "Foo": Foo(""), "Bar": (*Bar)(nil), "Baz": (*Baz)(nil),})
type InterfaceWrapper struct{ Interface }
func (w InterfaceWrapper) MarshalJSONTo(enc *jsontext.Encoder) error { return interfaceCoders.Marshal(enc, &w.Interface)}
func (w *InterfaceWrapper) UnmarshalJSONFrom(dec *jsontext.Decoder) error { return interfaceCoders.Unmarshal(dec, &w.Interface)}
func TestInterfaceCoders(t *testing.T) { var opts json.Options = json.JoinOptions( json.WithMarshalers(json.MarshalToFunc(interfaceCoders.Marshal)), json.WithUnmarshalers(json.UnmarshalFromFunc(interfaceCoders.Unmarshal)), )
errSkipMarshal := errors.New("skip marshal") makeFiller := func() InterfaceWrapper { return InterfaceWrapper{&Baz{"fizz", "buzz"}} }
for _, tt := range []struct { label string wantVal InterfaceWrapper wantJSON string wantMarshalError error wantUnmarshalError error }{{ label: "Null", wantVal: InterfaceWrapper{}, wantJSON: `null`, }, { label: "Foo", wantVal: InterfaceWrapper{Foo("hello")}, wantJSON: `{"Foo":"hello"}`, }, { label: "BarPointer", wantVal: InterfaceWrapper{new(Bar(5))}, wantJSON: `{"Bar":5}`, }, { label: "BarValue", wantVal: InterfaceWrapper{Bar(5)}, // NOTE: We could handle BarValue just like BarPointer, // but round-trip marshal/unmarshal would not be identical. wantMarshalError: errUnknownTypeName, }, { label: "Baz", wantVal: InterfaceWrapper{&Baz{"alpha", "omega"}}, wantJSON: `{"Baz":{"Fizz":"alpha","Buzz":"omega"}}`, }, { label: "Unknown", wantVal: makeFiller(), wantJSON: `{"Unknown":[1,2,3]}`, wantMarshalError: errSkipMarshal, wantUnmarshalError: errUnknownTypeName, }, { label: "Empty", wantVal: makeFiller(), wantJSON: `{}`, wantMarshalError: errSkipMarshal, wantUnmarshalError: errNonSingularValue, }, { label: "Duplicate", wantVal: InterfaceWrapper{Foo("hello")}, // first entry wins wantJSON: `{"Foo":"hello","Bar":5}`, wantMarshalError: errSkipMarshal, wantUnmarshalError: errNonSingularValue, }} { t.Run(tt.label, func(t *testing.T) { if tt.wantMarshalError != errSkipMarshal { switch gotJSON, err := json.Marshal(&tt.wantVal); { case !errors.Is(err, tt.wantMarshalError): t.Fatalf("json.Marshal(%v) error = %v, want %v", tt.wantVal, err, tt.wantMarshalError) case string(gotJSON) != tt.wantJSON: t.Fatalf("json.Marshal(%v) = %s, want %s", tt.wantVal, gotJSON, tt.wantJSON) } switch gotJSON, err := json.Marshal(&tt.wantVal.Interface, opts); { case !errors.Is(err, tt.wantMarshalError): t.Fatalf("json.Marshal(%v) error = %v, want %v", tt.wantVal, err, tt.wantMarshalError) case string(gotJSON) != tt.wantJSON: t.Fatalf("json.Marshal(%v) = %s, want %s", tt.wantVal, gotJSON, tt.wantJSON) } }
if tt.wantJSON != "" { gotVal := makeFiller() if err := json.Unmarshal([]byte(tt.wantJSON), &gotVal); !errors.Is(err, tt.wantUnmarshalError) { t.Fatalf("json.Unmarshal(%v) error = %v, want %v", tt.wantJSON, err, tt.wantUnmarshalError) } if d := cmp.Diff(gotVal, tt.wantVal); d != "" { t.Fatalf("json.Unmarshal(%v):\n%s", tt.wantJSON, d) } gotVal = makeFiller() if err := json.Unmarshal([]byte(tt.wantJSON), &gotVal.Interface, opts); !errors.Is(err, tt.wantUnmarshalError) { t.Fatalf("json.Unmarshal(%v) error = %v, want %v", tt.wantJSON, err, tt.wantUnmarshalError) } if d := cmp.Diff(gotVal, tt.wantVal); d != "" { t.Fatalf("json.Unmarshal(%v):\n%s", tt.wantJSON, d) } } }) }}