|
1 | 1 | package yaml |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "encoding/json" |
4 | 5 | "fmt" |
5 | 6 | "math" |
6 | 7 | "reflect" |
| 8 | + "sort" |
7 | 9 | "strconv" |
8 | 10 | "testing" |
| 11 | + |
| 12 | + "github.com/davecgh/go-spew/spew" |
| 13 | + yaml "gopkg.in/yaml.v2" |
9 | 14 | ) |
10 | 15 |
|
11 | 16 | type MarshalTest struct { |
@@ -421,3 +426,120 @@ foo: baz |
421 | 426 | t.Error("expected YAMLtoJSONStrict to fail on duplicate field names") |
422 | 427 | } |
423 | 428 | } |
| 429 | + |
| 430 | +func TestJSONObjectToYAMLObject(t *testing.T) { |
| 431 | + intOrInt64 := func(i64 int64) interface{} { |
| 432 | + if i := int(i64); i64 == int64(i) { |
| 433 | + return i |
| 434 | + } |
| 435 | + return i64 |
| 436 | + } |
| 437 | + |
| 438 | + tests := []struct { |
| 439 | + name string |
| 440 | + input map[string]interface{} |
| 441 | + expected yaml.MapSlice |
| 442 | + }{ |
| 443 | + {name: "nil", expected: yaml.MapSlice(nil)}, |
| 444 | + {name: "empty", input: map[string]interface{}{}, expected: yaml.MapSlice(nil)}, |
| 445 | + { |
| 446 | + name: "values", |
| 447 | + input: map[string]interface{}{ |
| 448 | + "nil slice": []interface{}(nil), |
| 449 | + "nil map": map[string]interface{}(nil), |
| 450 | + "empty slice": []interface{}{}, |
| 451 | + "empty map": map[string]interface{}{}, |
| 452 | + "bool": true, |
| 453 | + "float64": float64(42.1), |
| 454 | + "fractionless": float64(42), |
| 455 | + "int": int(42), |
| 456 | + "int64": int64(42), |
| 457 | + "int64 big": float64(math.Pow(2, 62)), |
| 458 | + "negative int64 big": -float64(math.Pow(2, 62)), |
| 459 | + "map": map[string]interface{}{"foo": "bar"}, |
| 460 | + "slice": []interface{}{"foo", "bar"}, |
| 461 | + "string": string("foo"), |
| 462 | + "uint64 big": float64(math.Pow(2, 63)), |
| 463 | + }, |
| 464 | + expected: yaml.MapSlice{ |
| 465 | + {Key: "nil slice"}, |
| 466 | + {Key: "nil map"}, |
| 467 | + {Key: "empty slice", Value: []interface{}{}}, |
| 468 | + {Key: "empty map", Value: yaml.MapSlice(nil)}, |
| 469 | + {Key: "bool", Value: true}, |
| 470 | + {Key: "float64", Value: float64(42.1)}, |
| 471 | + {Key: "fractionless", Value: int(42)}, |
| 472 | + {Key: "int", Value: int(42)}, |
| 473 | + {Key: "int64", Value: int(42)}, |
| 474 | + {Key: "int64 big", Value: intOrInt64(int64(1) << 62)}, |
| 475 | + {Key: "negative int64 big", Value: intOrInt64(-(1 << 62))}, |
| 476 | + {Key: "map", Value: yaml.MapSlice{{Key: "foo", Value: "bar"}}}, |
| 477 | + {Key: "slice", Value: []interface{}{"foo", "bar"}}, |
| 478 | + {Key: "string", Value: string("foo")}, |
| 479 | + {Key: "uint64 big", Value: uint64(1) << 63}, |
| 480 | + }, |
| 481 | + }, |
| 482 | + } |
| 483 | + for _, tt := range tests { |
| 484 | + t.Run(tt.name, func(t *testing.T) { |
| 485 | + got := JSONObjectToYAMLObject(tt.input) |
| 486 | + sortMapSlicesInPlace(tt.expected) |
| 487 | + sortMapSlicesInPlace(got) |
| 488 | + if !reflect.DeepEqual(got, tt.expected) { |
| 489 | + t.Errorf("jsonToYAML() = %v, want %v", spew.Sdump(got), spew.Sdump(tt.expected)) |
| 490 | + } |
| 491 | + |
| 492 | + jsonBytes, err := json.Marshal(tt.input) |
| 493 | + if err != nil { |
| 494 | + t.Fatalf("unexpected json.Marshal error: %v", err) |
| 495 | + } |
| 496 | + var gotByRoundtrip yaml.MapSlice |
| 497 | + if err := yaml.Unmarshal(jsonBytes, &gotByRoundtrip); err != nil { |
| 498 | + t.Fatalf("unexpected yaml.Unmarshal error: %v", err) |
| 499 | + } |
| 500 | + |
| 501 | + // yaml.Unmarshal loses precision, it's rounding to the 4th last digit. |
| 502 | + // Replicate this here in the test, but don't change the type. |
| 503 | + for i := range got { |
| 504 | + switch got[i].Key { |
| 505 | + case "int64 big", "uint64 big", "negative int64 big": |
| 506 | + switch v := got[i].Value.(type) { |
| 507 | + case int64: |
| 508 | + d := int64(500) |
| 509 | + if v < 0 { |
| 510 | + d = -500 |
| 511 | + } |
| 512 | + got[i].Value = int64((v+d)/1000) * 1000 |
| 513 | + case uint64: |
| 514 | + got[i].Value = uint64((v+500)/1000) * 1000 |
| 515 | + case int: |
| 516 | + d := int(500) |
| 517 | + if v < 0 { |
| 518 | + d = -500 |
| 519 | + } |
| 520 | + got[i].Value = int((v+d)/1000) * 1000 |
| 521 | + default: |
| 522 | + t.Fatalf("unexpected type for key %s: %v:%T", got[i].Key, v, v) |
| 523 | + } |
| 524 | + } |
| 525 | + } |
| 526 | + |
| 527 | + if !reflect.DeepEqual(got, gotByRoundtrip) { |
| 528 | + t.Errorf("yaml.Unmarshal(json.Marshal(tt.input)) = %v, want %v\njson: %s", spew.Sdump(gotByRoundtrip), spew.Sdump(got), string(jsonBytes)) |
| 529 | + } |
| 530 | + }) |
| 531 | + } |
| 532 | +} |
| 533 | + |
| 534 | +func sortMapSlicesInPlace(x interface{}) { |
| 535 | + switch x := x.(type) { |
| 536 | + case []interface{}: |
| 537 | + for i := range x { |
| 538 | + sortMapSlicesInPlace(x[i]) |
| 539 | + } |
| 540 | + case yaml.MapSlice: |
| 541 | + sort.Slice(x, func(a, b int) bool { |
| 542 | + return x[a].Key.(string) < x[b].Key.(string) |
| 543 | + }) |
| 544 | + } |
| 545 | +} |
0 commit comments