diff --git a/.changes/unreleased/BUG FIXES-20240228-171104.yaml b/.changes/unreleased/BUG FIXES-20240228-171104.yaml new file mode 100644 index 000000000..698f25f12 --- /dev/null +++ b/.changes/unreleased/BUG FIXES-20240228-171104.yaml @@ -0,0 +1,6 @@ +kind: BUG FIXES +body: 'tftypes: Fixed an edge-case where `(Value).Equal` would panic when comparing + two values with underlying `DynamicPseudoType` types and different concrete values.' +time: 2024-02-28T17:11:04.381759-05:00 +custom: + Issue: "383" diff --git a/tftypes/value.go b/tftypes/value.go index b84507839..63570211f 100644 --- a/tftypes/value.go +++ b/tftypes/value.go @@ -223,7 +223,7 @@ func (val Value) Equal(o Value) bool { } deepEqual, err := val.deepEqual(o) if err != nil { - panic(err) + return false } return deepEqual } diff --git a/tftypes/value_test.go b/tftypes/value_test.go index 61d9b5b7f..f13d0c5bd 100644 --- a/tftypes/value_test.go +++ b/tftypes/value_test.go @@ -743,6 +743,11 @@ func TestValueEqual(t *testing.T) { val2: NewValue(String, "world"), equal: false, }, + "stringDiff-wrong-type": { + val1: NewValue(String, "true"), + val2: NewValue(Bool, true), + equal: false, + }, "boolEqual": { val1: NewValue(Bool, true), val2: NewValue(Bool, true), @@ -753,6 +758,11 @@ func TestValueEqual(t *testing.T) { val2: NewValue(Bool, true), equal: false, }, + "boolDiff-wrong-type": { + val1: NewValue(Bool, true), + val2: NewValue(String, "true"), + equal: false, + }, "numberEqual": { val1: NewValue(Number, big.NewFloat(123)), val2: NewValue(Number, big.NewFloat(0).SetInt64(123)), @@ -763,6 +773,11 @@ func TestValueEqual(t *testing.T) { val2: NewValue(Number, big.NewFloat(2)), equal: false, }, + "numberDiff-wrong-type": { + val1: NewValue(Number, big.NewFloat(1)), + val2: NewValue(String, "1"), + equal: false, + }, "unknownEqual": { val1: NewValue(String, UnknownValue), val2: NewValue(String, UnknownValue), @@ -811,6 +826,19 @@ func TestValueEqual(t *testing.T) { }), equal: false, }, + "listDiff-wrong-type": { + val1: NewValue(List{ElementType: String}, []Value{ + NewValue(String, "hello"), + NewValue(String, "world"), + NewValue(String, "abc"), + }), + val2: NewValue(Set{ElementType: String}, []Value{ + NewValue(String, "hello"), + NewValue(String, "world"), + NewValue(String, "abc"), + }), + equal: false, + }, "setEqual": { val1: NewValue(Set{ElementType: String}, []Value{ NewValue(String, "hello"), @@ -849,6 +877,19 @@ func TestValueEqual(t *testing.T) { }), equal: false, }, + "setDiff-wrong-type": { + val1: NewValue(Set{ElementType: String}, []Value{ + NewValue(String, "hello"), + NewValue(String, "world"), + NewValue(String, "abc"), + }), + val2: NewValue(List{ElementType: String}, []Value{ + NewValue(String, "hello"), + NewValue(String, "world"), + NewValue(String, "abc"), + }), + equal: false, + }, "tupleEqual": { val1: NewValue(Tuple{ElementTypes: []Type{ String, Bool, Number, List{ElementType: String}, @@ -981,6 +1022,17 @@ func TestValueEqual(t *testing.T) { }), equal: false, }, + "mapDiff-wrong-types": { + val1: NewValue(Map{ElementType: String}, map[string]Value{ + "one": NewValue(String, "true"), + "two": NewValue(String, "false"), + }), + val2: NewValue(Map{ElementType: Bool}, map[string]Value{ + "one": NewValue(Bool, true), + "two": NewValue(Bool, false), + }), + equal: false, + }, "objectEqual": { val1: NewValue(Object{AttributeTypes: map[string]Type{ "one": Number, @@ -1068,6 +1120,68 @@ func TestValueEqual(t *testing.T) { }), equal: false, }, + "DynamicPseudoType-tupleEqual": { + val1: NewValue(Tuple{ElementTypes: []Type{ + DynamicPseudoType, DynamicPseudoType, + }}, []Value{ + NewValue(Bool, true), + NewValue(List{ElementType: String}, []Value{ + NewValue(String, "a"), + NewValue(String, "b"), + NewValue(String, "c"), + }), + }), + val2: NewValue(Tuple{ElementTypes: []Type{ + DynamicPseudoType, DynamicPseudoType, + }}, []Value{ + NewValue(Bool, true), + NewValue(List{ElementType: String}, []Value{ + NewValue(String, "a"), + NewValue(String, "b"), + NewValue(String, "c"), + }), + }), + equal: true, + }, + // Previously, different value types with a DynamicPseudoType would cause a panic + // https://github.com/hashicorp/terraform-plugin-go/pull/383 + "DynamicPseudoType-tupleDiff-different-value-types": { + val1: NewValue(Tuple{ElementTypes: []Type{DynamicPseudoType}}, []Value{ + NewValue(String, "false"), + }), + val2: NewValue(Tuple{ElementTypes: []Type{DynamicPseudoType}}, []Value{ + NewValue(Bool, false), // This value type is different than val1 + }), + equal: false, + }, + "DynamicPseudoType-objectEqual": { + val1: NewValue(Object{AttributeTypes: map[string]Type{ + "dyn_val": DynamicPseudoType, + }}, map[string]Value{ + "dyn_val": NewValue(String, "hello"), + }), + val2: NewValue(Object{AttributeTypes: map[string]Type{ + "dyn_val": DynamicPseudoType, + }}, map[string]Value{ + "dyn_val": NewValue(String, "hello"), + }), + equal: true, + }, + // Previously, different value types with a DynamicPseudoType would cause a panic + // https://github.com/hashicorp/terraform-plugin-go/pull/383 + "DynamicPseudoType-objectDiff-different-value-types": { + val1: NewValue(Object{AttributeTypes: map[string]Type{ + "dyn_val": DynamicPseudoType, + }}, map[string]Value{ + "dyn_val": NewValue(String, "1"), + }), + val2: NewValue(Object{AttributeTypes: map[string]Type{ + "dyn_val": DynamicPseudoType, + }}, map[string]Value{ + "dyn_val": NewValue(Number, big.NewFloat(1)), // This value type is different than val1 + }), + equal: false, + }, "nullEqual": { val1: NewValue(String, nil), val2: NewValue(String, nil),