package hcl2shim import ( "fmt" "math/big" "testing" "github.com/zclconf/go-cty/cty" ) func TestValuesSDKEquivalent(t *testing.T) { piBig, _, err := big.ParseFloat("3.14159265358979323846264338327950288419716939937510582097494459", 10, 512, big.ToZero) if err != nil { t.Fatal(err) } pi64, _ := piBig.Float64() tests := []struct { A, B cty.Value Want bool }{ // Strings { cty.StringVal("hello"), cty.StringVal("hello"), true, }, { cty.StringVal("hello"), cty.StringVal("world"), false, }, { cty.StringVal("hello"), cty.StringVal(""), false, }, { cty.NullVal(cty.String), cty.StringVal(""), true, }, // Numbers { cty.NumberIntVal(1), cty.NumberIntVal(1), true, }, { cty.NumberIntVal(1), cty.NumberIntVal(2), false, }, { cty.NumberIntVal(1), cty.Zero, false, }, { cty.NullVal(cty.Number), cty.Zero, true, }, { cty.NumberVal(piBig), cty.Zero, false, }, { cty.NumberFloatVal(pi64), cty.Zero, false, }, { cty.NumberFloatVal(pi64), cty.NumberVal(piBig), true, }, // Bools { cty.True, cty.True, true, }, { cty.True, cty.False, false, }, { cty.NullVal(cty.Bool), cty.False, true, }, // Mixed primitives { cty.StringVal("hello"), cty.False, false, }, { cty.StringVal(""), cty.False, true, }, { cty.NumberIntVal(0), cty.False, true, }, { cty.StringVal(""), cty.NumberIntVal(0), true, }, { cty.NullVal(cty.Bool), cty.NullVal(cty.Number), true, }, { cty.StringVal(""), cty.NullVal(cty.Number), true, }, // Lists { cty.ListValEmpty(cty.String), cty.ListValEmpty(cty.String), true, }, { cty.ListValEmpty(cty.String), cty.NullVal(cty.List(cty.String)), true, }, { cty.ListVal([]cty.Value{cty.StringVal("hello")}), cty.ListVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("hello")}), false, }, { cty.ListVal([]cty.Value{cty.StringVal("hello")}), cty.ListValEmpty(cty.String), false, }, { cty.ListVal([]cty.Value{cty.StringVal("hello")}), cty.ListVal([]cty.Value{cty.StringVal("hello")}), true, }, { cty.ListVal([]cty.Value{cty.StringVal("hello")}), cty.ListVal([]cty.Value{cty.StringVal("world")}), false, }, { cty.ListVal([]cty.Value{cty.NullVal(cty.String)}), cty.ListVal([]cty.Value{cty.StringVal("")}), true, }, // Tuples { cty.EmptyTupleVal, cty.EmptyTupleVal, true, }, { cty.EmptyTupleVal, cty.NullVal(cty.EmptyTuple), true, }, { cty.TupleVal([]cty.Value{cty.StringVal("hello")}), cty.TupleVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("hello")}), false, }, { cty.TupleVal([]cty.Value{cty.StringVal("hello")}), cty.EmptyTupleVal, false, }, { cty.TupleVal([]cty.Value{cty.StringVal("hello")}), cty.TupleVal([]cty.Value{cty.StringVal("hello")}), true, }, { cty.TupleVal([]cty.Value{cty.StringVal("hello")}), cty.TupleVal([]cty.Value{cty.StringVal("world")}), false, }, { cty.TupleVal([]cty.Value{cty.NullVal(cty.String)}), cty.TupleVal([]cty.Value{cty.StringVal("")}), true, }, // Sets { cty.SetValEmpty(cty.String), cty.SetValEmpty(cty.String), true, }, { cty.SetValEmpty(cty.String), cty.NullVal(cty.Set(cty.String)), true, }, { cty.SetVal([]cty.Value{cty.StringVal("hello")}), cty.SetValEmpty(cty.String), false, }, { cty.SetVal([]cty.Value{cty.StringVal("hello")}), cty.SetVal([]cty.Value{cty.StringVal("hello")}), true, }, { cty.SetVal([]cty.Value{cty.StringVal("hello")}), cty.SetVal([]cty.Value{cty.StringVal("world")}), false, }, { cty.SetVal([]cty.Value{cty.NullVal(cty.String)}), cty.SetVal([]cty.Value{cty.StringVal("")}), true, }, { cty.SetVal([]cty.Value{ cty.NullVal(cty.String), cty.StringVal(""), }), cty.SetVal([]cty.Value{ cty.NullVal(cty.String), }), false, // because the element count is different }, { cty.SetVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "a": cty.StringVal(""), "b": cty.StringVal(""), }), cty.ObjectVal(map[string]cty.Value{ "a": cty.NullVal(cty.String), "b": cty.StringVal(""), }), }), cty.SetVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "a": cty.StringVal(""), "b": cty.StringVal(""), }), cty.ObjectVal(map[string]cty.Value{ "a": cty.StringVal(""), "b": cty.NullVal(cty.String), }), }), true, }, { cty.SetVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "a": cty.StringVal("boop"), "b": cty.StringVal(""), }), cty.ObjectVal(map[string]cty.Value{ "a": cty.NullVal(cty.String), "b": cty.StringVal(""), }), }), cty.SetVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "a": cty.StringVal("beep"), "b": cty.StringVal(""), }), cty.ObjectVal(map[string]cty.Value{ "a": cty.StringVal(""), "b": cty.NullVal(cty.String), }), }), false, }, { cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ "list": cty.ListValEmpty(cty.String), "list_block": cty.ListVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "unused": cty.StringVal(""), }), }), })}), cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ "list": cty.ListValEmpty(cty.String), "list_block": cty.ListVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "unused": cty.NullVal(cty.String), }), }), })}), true, }, // Maps { cty.MapValEmpty(cty.String), cty.MapValEmpty(cty.String), true, }, { cty.MapValEmpty(cty.String), cty.NullVal(cty.Map(cty.String)), true, }, { cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("hello"), "hey": cty.StringVal("hello")}), false, }, { cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), cty.MapValEmpty(cty.String), false, }, { cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), true, }, { cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("world")}), false, }, { cty.MapVal(map[string]cty.Value{"hi": cty.NullVal(cty.String)}), cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("")}), true, }, // Objects { cty.EmptyObjectVal, cty.EmptyObjectVal, true, }, { cty.EmptyObjectVal, cty.NullVal(cty.EmptyObject), true, }, { cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("hello"), "hey": cty.StringVal("hello")}), false, }, { cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), cty.EmptyObjectVal, false, }, { cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), true, }, { cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("world")}), false, }, { cty.ObjectVal(map[string]cty.Value{"hi": cty.NullVal(cty.String)}), cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("")}), true, }, // Unknown values { cty.UnknownVal(cty.String), cty.UnknownVal(cty.String), true, }, { cty.StringVal("hello"), cty.UnknownVal(cty.String), false, }, { cty.StringVal(""), cty.UnknownVal(cty.String), false, }, { cty.NullVal(cty.String), cty.UnknownVal(cty.String), false, }, } run := func(t *testing.T, a, b cty.Value, want bool) { got := ValuesSDKEquivalent(a, b) if got != want { t.Errorf("wrong result\nfor: %#v ≈ %#v\ngot %#v, but want %#v", a, b, got, want) } } for _, test := range tests { t.Run(fmt.Sprintf("%#v ≈ %#v", test.A, test.B), func(t *testing.T) { run(t, test.A, test.B, test.Want) }) // This function is symmetrical, so we'll also test in reverse so // we don't need to manually copy all of the test cases. (But this does // mean that one failure normally becomes two, of course!) if !test.A.RawEquals(test.B) { t.Run(fmt.Sprintf("%#v ≈ %#v", test.B, test.A), func(t *testing.T) { run(t, test.B, test.A, test.Want) }) } } }