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