1// Copyright 2020 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package structpb_test
6
7import (
8	"encoding/json"
9	"math"
10	"testing"
11
12	"github.com/google/go-cmp/cmp"
13	"github.com/google/go-cmp/cmp/cmpopts"
14	"google.golang.org/protobuf/reflect/protoreflect"
15	"google.golang.org/protobuf/testing/protocmp"
16
17	spb "google.golang.org/protobuf/types/known/structpb"
18)
19
20var equateJSON = cmpopts.AcyclicTransformer("UnmarshalJSON", func(in []byte) (out interface{}) {
21	if err := json.Unmarshal(in, &out); err != nil {
22		return in
23	}
24	return out
25})
26
27func TestToStruct(t *testing.T) {
28	tests := []struct {
29		in      map[string]interface{}
30		wantPB  *spb.Struct
31		wantErr error
32	}{{
33		in:     nil,
34		wantPB: new(spb.Struct),
35	}, {
36		in:     make(map[string]interface{}),
37		wantPB: new(spb.Struct),
38	}, {
39		in: map[string]interface{}{
40			"nil":     nil,
41			"bool":    bool(false),
42			"int":     int(-123),
43			"int32":   int32(math.MinInt32),
44			"int64":   int64(math.MinInt64),
45			"uint":    uint(123),
46			"uint32":  uint32(math.MaxInt32),
47			"uint64":  uint64(math.MaxInt64),
48			"float32": float32(123.456),
49			"float64": float64(123.456),
50			"string":  string("hello, world!"),
51			"bytes":   []byte("\xde\xad\xbe\xef"),
52			"map":     map[string]interface{}{"k1": "v1", "k2": "v2"},
53			"slice":   []interface{}{"one", "two", "three"},
54		},
55		wantPB: &spb.Struct{Fields: map[string]*spb.Value{
56			"nil":     spb.NewNullValue(),
57			"bool":    spb.NewBoolValue(false),
58			"int":     spb.NewNumberValue(float64(-123)),
59			"int32":   spb.NewNumberValue(float64(math.MinInt32)),
60			"int64":   spb.NewNumberValue(float64(math.MinInt64)),
61			"uint":    spb.NewNumberValue(float64(123)),
62			"uint32":  spb.NewNumberValue(float64(math.MaxInt32)),
63			"uint64":  spb.NewNumberValue(float64(math.MaxInt64)),
64			"float32": spb.NewNumberValue(float64(float32(123.456))),
65			"float64": spb.NewNumberValue(float64(float64(123.456))),
66			"string":  spb.NewStringValue("hello, world!"),
67			"bytes":   spb.NewStringValue("3q2+7w=="),
68			"map": spb.NewStructValue(&spb.Struct{Fields: map[string]*spb.Value{
69				"k1": spb.NewStringValue("v1"),
70				"k2": spb.NewStringValue("v2"),
71			}}),
72			"slice": spb.NewListValue(&spb.ListValue{Values: []*spb.Value{
73				spb.NewStringValue("one"),
74				spb.NewStringValue("two"),
75				spb.NewStringValue("three"),
76			}}),
77		}},
78	}, {
79		in:      map[string]interface{}{"\xde\xad\xbe\xef": "<invalid UTF-8>"},
80		wantErr: cmpopts.AnyError,
81	}, {
82		in:      map[string]interface{}{"<invalid UTF-8>": "\xde\xad\xbe\xef"},
83		wantErr: cmpopts.AnyError,
84	}, {
85		in:      map[string]interface{}{"key": protoreflect.Name("named string")},
86		wantErr: cmpopts.AnyError,
87	}}
88
89	for _, tt := range tests {
90		gotPB, gotErr := spb.NewStruct(tt.in)
91		if diff := cmp.Diff(tt.wantPB, gotPB, protocmp.Transform()); diff != "" {
92			t.Errorf("NewStruct(%v) output mismatch (-want +got):\n%s", tt.in, diff)
93		}
94		if diff := cmp.Diff(tt.wantErr, gotErr, cmpopts.EquateErrors()); diff != "" {
95			t.Errorf("NewStruct(%v) error mismatch (-want +got):\n%s", tt.in, diff)
96		}
97	}
98}
99
100func TestFromStruct(t *testing.T) {
101	tests := []struct {
102		in   *spb.Struct
103		want map[string]interface{}
104	}{{
105		in:   nil,
106		want: make(map[string]interface{}),
107	}, {
108		in:   new(spb.Struct),
109		want: make(map[string]interface{}),
110	}, {
111		in:   &spb.Struct{Fields: make(map[string]*spb.Value)},
112		want: make(map[string]interface{}),
113	}, {
114		in: &spb.Struct{Fields: map[string]*spb.Value{
115			"nil":     spb.NewNullValue(),
116			"bool":    spb.NewBoolValue(false),
117			"int":     spb.NewNumberValue(float64(-123)),
118			"int32":   spb.NewNumberValue(float64(math.MinInt32)),
119			"int64":   spb.NewNumberValue(float64(math.MinInt64)),
120			"uint":    spb.NewNumberValue(float64(123)),
121			"uint32":  spb.NewNumberValue(float64(math.MaxInt32)),
122			"uint64":  spb.NewNumberValue(float64(math.MaxInt64)),
123			"float32": spb.NewNumberValue(float64(float32(123.456))),
124			"float64": spb.NewNumberValue(float64(float64(123.456))),
125			"string":  spb.NewStringValue("hello, world!"),
126			"bytes":   spb.NewStringValue("3q2+7w=="),
127			"map": spb.NewStructValue(&spb.Struct{Fields: map[string]*spb.Value{
128				"k1": spb.NewStringValue("v1"),
129				"k2": spb.NewStringValue("v2"),
130			}}),
131			"slice": spb.NewListValue(&spb.ListValue{Values: []*spb.Value{
132				spb.NewStringValue("one"),
133				spb.NewStringValue("two"),
134				spb.NewStringValue("three"),
135			}}),
136		}},
137		want: map[string]interface{}{
138			"nil":     nil,
139			"bool":    bool(false),
140			"int":     float64(-123),
141			"int32":   float64(math.MinInt32),
142			"int64":   float64(math.MinInt64),
143			"uint":    float64(123),
144			"uint32":  float64(math.MaxInt32),
145			"uint64":  float64(math.MaxInt64),
146			"float32": float64(float32(123.456)),
147			"float64": float64(float64(123.456)),
148			"string":  string("hello, world!"),
149			"bytes":   string("3q2+7w=="),
150			"map":     map[string]interface{}{"k1": "v1", "k2": "v2"},
151			"slice":   []interface{}{"one", "two", "three"},
152		},
153	}}
154
155	for _, tt := range tests {
156		got := tt.in.AsMap()
157		if diff := cmp.Diff(tt.want, got); diff != "" {
158			t.Errorf("AsMap(%v) mismatch (-want +got):\n%s", tt.in, diff)
159		}
160		gotJSON, err := json.Marshal(got)
161		if err != nil {
162			t.Errorf("Marshal error: %v", err)
163		}
164		wantJSON, err := tt.in.MarshalJSON()
165		if err != nil {
166			t.Errorf("Marshal error: %v", err)
167		}
168		if diff := cmp.Diff(wantJSON, gotJSON, equateJSON); diff != "" {
169			t.Errorf("MarshalJSON(%v) mismatch (-want +got):\n%s", tt.in, diff)
170		}
171	}
172}
173
174func TestToListValue(t *testing.T) {
175	tests := []struct {
176		in      []interface{}
177		wantPB  *spb.ListValue
178		wantErr error
179	}{{
180		in:     nil,
181		wantPB: new(spb.ListValue),
182	}, {
183		in:     make([]interface{}, 0),
184		wantPB: new(spb.ListValue),
185	}, {
186		in: []interface{}{
187			nil,
188			bool(false),
189			int(-123),
190			int32(math.MinInt32),
191			int64(math.MinInt64),
192			uint(123),
193			uint32(math.MaxInt32),
194			uint64(math.MaxInt64),
195			float32(123.456),
196			float64(123.456),
197			string("hello, world!"),
198			[]byte("\xde\xad\xbe\xef"),
199			map[string]interface{}{"k1": "v1", "k2": "v2"},
200			[]interface{}{"one", "two", "three"},
201		},
202		wantPB: &spb.ListValue{Values: []*spb.Value{
203			spb.NewNullValue(),
204			spb.NewBoolValue(false),
205			spb.NewNumberValue(float64(-123)),
206			spb.NewNumberValue(float64(math.MinInt32)),
207			spb.NewNumberValue(float64(math.MinInt64)),
208			spb.NewNumberValue(float64(123)),
209			spb.NewNumberValue(float64(math.MaxInt32)),
210			spb.NewNumberValue(float64(math.MaxInt64)),
211			spb.NewNumberValue(float64(float32(123.456))),
212			spb.NewNumberValue(float64(float64(123.456))),
213			spb.NewStringValue("hello, world!"),
214			spb.NewStringValue("3q2+7w=="),
215			spb.NewStructValue(&spb.Struct{Fields: map[string]*spb.Value{
216				"k1": spb.NewStringValue("v1"),
217				"k2": spb.NewStringValue("v2"),
218			}}),
219			spb.NewListValue(&spb.ListValue{Values: []*spb.Value{
220				spb.NewStringValue("one"),
221				spb.NewStringValue("two"),
222				spb.NewStringValue("three"),
223			}}),
224		}},
225	}, {
226		in:      []interface{}{"\xde\xad\xbe\xef"},
227		wantErr: cmpopts.AnyError,
228	}, {
229		in:      []interface{}{protoreflect.Name("named string")},
230		wantErr: cmpopts.AnyError,
231	}}
232
233	for _, tt := range tests {
234		gotPB, gotErr := spb.NewList(tt.in)
235		if diff := cmp.Diff(tt.wantPB, gotPB, protocmp.Transform()); diff != "" {
236			t.Errorf("NewListValue(%v) output mismatch (-want +got):\n%s", tt.in, diff)
237		}
238		if diff := cmp.Diff(tt.wantErr, gotErr, cmpopts.EquateErrors()); diff != "" {
239			t.Errorf("NewListValue(%v) error mismatch (-want +got):\n%s", tt.in, diff)
240		}
241	}
242}
243
244func TestFromListValue(t *testing.T) {
245	tests := []struct {
246		in   *spb.ListValue
247		want []interface{}
248	}{{
249		in:   nil,
250		want: make([]interface{}, 0),
251	}, {
252		in:   new(spb.ListValue),
253		want: make([]interface{}, 0),
254	}, {
255		in:   &spb.ListValue{Values: make([]*spb.Value, 0)},
256		want: make([]interface{}, 0),
257	}, {
258		in: &spb.ListValue{Values: []*spb.Value{
259			spb.NewNullValue(),
260			spb.NewBoolValue(false),
261			spb.NewNumberValue(float64(-123)),
262			spb.NewNumberValue(float64(math.MinInt32)),
263			spb.NewNumberValue(float64(math.MinInt64)),
264			spb.NewNumberValue(float64(123)),
265			spb.NewNumberValue(float64(math.MaxInt32)),
266			spb.NewNumberValue(float64(math.MaxInt64)),
267			spb.NewNumberValue(float64(float32(123.456))),
268			spb.NewNumberValue(float64(float64(123.456))),
269			spb.NewStringValue("hello, world!"),
270			spb.NewStringValue("3q2+7w=="),
271			spb.NewStructValue(&spb.Struct{Fields: map[string]*spb.Value{
272				"k1": spb.NewStringValue("v1"),
273				"k2": spb.NewStringValue("v2"),
274			}}),
275			spb.NewListValue(&spb.ListValue{Values: []*spb.Value{
276				spb.NewStringValue("one"),
277				spb.NewStringValue("two"),
278				spb.NewStringValue("three"),
279			}}),
280		}},
281		want: []interface{}{
282			nil,
283			bool(false),
284			float64(-123),
285			float64(math.MinInt32),
286			float64(math.MinInt64),
287			float64(123),
288			float64(math.MaxInt32),
289			float64(math.MaxInt64),
290			float64(float32(123.456)),
291			float64(float64(123.456)),
292			string("hello, world!"),
293			string("3q2+7w=="),
294			map[string]interface{}{"k1": "v1", "k2": "v2"},
295			[]interface{}{"one", "two", "three"},
296		},
297	}}
298
299	for _, tt := range tests {
300		got := tt.in.AsSlice()
301		if diff := cmp.Diff(tt.want, got); diff != "" {
302			t.Errorf("AsSlice(%v) mismatch (-want +got):\n%s", tt.in, diff)
303		}
304		gotJSON, err := json.Marshal(got)
305		if err != nil {
306			t.Errorf("Marshal error: %v", err)
307		}
308		wantJSON, err := tt.in.MarshalJSON()
309		if err != nil {
310			t.Errorf("Marshal error: %v", err)
311		}
312		if diff := cmp.Diff(wantJSON, gotJSON, equateJSON); diff != "" {
313			t.Errorf("MarshalJSON(%v) mismatch (-want +got):\n%s", tt.in, diff)
314		}
315	}
316}
317
318func TestToValue(t *testing.T) {
319	tests := []struct {
320		in      interface{}
321		wantPB  *spb.Value
322		wantErr error
323	}{{
324		in:     nil,
325		wantPB: spb.NewNullValue(),
326	}, {
327		in:     bool(false),
328		wantPB: spb.NewBoolValue(false),
329	}, {
330		in:     int(-123),
331		wantPB: spb.NewNumberValue(float64(-123)),
332	}, {
333		in:     int32(math.MinInt32),
334		wantPB: spb.NewNumberValue(float64(math.MinInt32)),
335	}, {
336		in:     int64(math.MinInt64),
337		wantPB: spb.NewNumberValue(float64(math.MinInt64)),
338	}, {
339		in:     uint(123),
340		wantPB: spb.NewNumberValue(float64(123)),
341	}, {
342		in:     uint32(math.MaxInt32),
343		wantPB: spb.NewNumberValue(float64(math.MaxInt32)),
344	}, {
345		in:     uint64(math.MaxInt64),
346		wantPB: spb.NewNumberValue(float64(math.MaxInt64)),
347	}, {
348		in:     float32(123.456),
349		wantPB: spb.NewNumberValue(float64(float32(123.456))),
350	}, {
351		in:     float64(123.456),
352		wantPB: spb.NewNumberValue(float64(float64(123.456))),
353	}, {
354		in:     string("hello, world!"),
355		wantPB: spb.NewStringValue("hello, world!"),
356	}, {
357		in:     []byte("\xde\xad\xbe\xef"),
358		wantPB: spb.NewStringValue("3q2+7w=="),
359	}, {
360		in:     map[string]interface{}(nil),
361		wantPB: spb.NewStructValue(nil),
362	}, {
363		in:     make(map[string]interface{}),
364		wantPB: spb.NewStructValue(nil),
365	}, {
366		in: map[string]interface{}{"k1": "v1", "k2": "v2"},
367		wantPB: spb.NewStructValue(&spb.Struct{Fields: map[string]*spb.Value{
368			"k1": spb.NewStringValue("v1"),
369			"k2": spb.NewStringValue("v2"),
370		}}),
371	}, {
372		in:     []interface{}(nil),
373		wantPB: spb.NewListValue(nil),
374	}, {
375		in:     make([]interface{}, 0),
376		wantPB: spb.NewListValue(nil),
377	}, {
378		in: []interface{}{"one", "two", "three"},
379		wantPB: spb.NewListValue(&spb.ListValue{Values: []*spb.Value{
380			spb.NewStringValue("one"),
381			spb.NewStringValue("two"),
382			spb.NewStringValue("three"),
383		}}),
384	}, {
385		in:      "\xde\xad\xbe\xef",
386		wantErr: cmpopts.AnyError,
387	}, {
388		in:      protoreflect.Name("named string"),
389		wantErr: cmpopts.AnyError,
390	}}
391
392	for _, tt := range tests {
393		gotPB, gotErr := spb.NewValue(tt.in)
394		if diff := cmp.Diff(tt.wantPB, gotPB, protocmp.Transform()); diff != "" {
395			t.Errorf("NewValue(%v) output mismatch (-want +got):\n%s", tt.in, diff)
396		}
397		if diff := cmp.Diff(tt.wantErr, gotErr, cmpopts.EquateErrors()); diff != "" {
398			t.Errorf("NewValue(%v) error mismatch (-want +got):\n%s", tt.in, diff)
399		}
400	}
401}
402
403func TestFromValue(t *testing.T) {
404	tests := []struct {
405		in   *spb.Value
406		want interface{}
407	}{{
408		in:   nil,
409		want: nil,
410	}, {
411		in:   new(spb.Value),
412		want: nil,
413	}, {
414		in:   &spb.Value{Kind: (*spb.Value_NullValue)(nil)},
415		want: nil,
416	}, {
417		in:   spb.NewNullValue(),
418		want: nil,
419	}, {
420		in:   &spb.Value{Kind: &spb.Value_NullValue{NullValue: math.MinInt32}},
421		want: nil,
422	}, {
423		in:   &spb.Value{Kind: (*spb.Value_BoolValue)(nil)},
424		want: nil,
425	}, {
426		in:   spb.NewBoolValue(false),
427		want: bool(false),
428	}, {
429		in:   &spb.Value{Kind: (*spb.Value_NumberValue)(nil)},
430		want: nil,
431	}, {
432		in:   spb.NewNumberValue(float64(math.MinInt32)),
433		want: float64(math.MinInt32),
434	}, {
435		in:   spb.NewNumberValue(float64(math.MinInt64)),
436		want: float64(math.MinInt64),
437	}, {
438		in:   spb.NewNumberValue(float64(123)),
439		want: float64(123),
440	}, {
441		in:   spb.NewNumberValue(float64(math.MaxInt32)),
442		want: float64(math.MaxInt32),
443	}, {
444		in:   spb.NewNumberValue(float64(math.MaxInt64)),
445		want: float64(math.MaxInt64),
446	}, {
447		in:   spb.NewNumberValue(float64(float32(123.456))),
448		want: float64(float32(123.456)),
449	}, {
450		in:   spb.NewNumberValue(float64(float64(123.456))),
451		want: float64(float64(123.456)),
452	}, {
453		in:   spb.NewNumberValue(math.NaN()),
454		want: string("NaN"),
455	}, {
456		in:   spb.NewNumberValue(math.Inf(-1)),
457		want: string("-Infinity"),
458	}, {
459		in:   spb.NewNumberValue(math.Inf(+1)),
460		want: string("Infinity"),
461	}, {
462		in:   &spb.Value{Kind: (*spb.Value_StringValue)(nil)},
463		want: nil,
464	}, {
465		in:   spb.NewStringValue("hello, world!"),
466		want: string("hello, world!"),
467	}, {
468		in:   spb.NewStringValue("3q2+7w=="),
469		want: string("3q2+7w=="),
470	}, {
471		in:   &spb.Value{Kind: (*spb.Value_StructValue)(nil)},
472		want: nil,
473	}, {
474		in:   &spb.Value{Kind: &spb.Value_StructValue{}},
475		want: make(map[string]interface{}),
476	}, {
477		in: spb.NewListValue(&spb.ListValue{Values: []*spb.Value{
478			spb.NewStringValue("one"),
479			spb.NewStringValue("two"),
480			spb.NewStringValue("three"),
481		}}),
482		want: []interface{}{"one", "two", "three"},
483	}, {
484		in:   &spb.Value{Kind: (*spb.Value_ListValue)(nil)},
485		want: nil,
486	}, {
487		in:   &spb.Value{Kind: &spb.Value_ListValue{}},
488		want: make([]interface{}, 0),
489	}, {
490		in: spb.NewListValue(&spb.ListValue{Values: []*spb.Value{
491			spb.NewStringValue("one"),
492			spb.NewStringValue("two"),
493			spb.NewStringValue("three"),
494		}}),
495		want: []interface{}{"one", "two", "three"},
496	}}
497
498	for _, tt := range tests {
499		got := tt.in.AsInterface()
500		if diff := cmp.Diff(tt.want, got); diff != "" {
501			t.Errorf("AsInterface(%v) mismatch (-want +got):\n%s", tt.in, diff)
502		}
503		gotJSON, gotErr := json.Marshal(got)
504		if gotErr != nil {
505			t.Errorf("Marshal error: %v", gotErr)
506		}
507		wantJSON, wantErr := tt.in.MarshalJSON()
508		if diff := cmp.Diff(wantJSON, gotJSON, equateJSON); diff != "" && wantErr == nil {
509			t.Errorf("MarshalJSON(%v) mismatch (-want +got):\n%s", tt.in, diff)
510		}
511	}
512}
513