github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/lang/funcs/conversion_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package funcs 5 6 import ( 7 "fmt" 8 "testing" 9 10 "github.com/terramate-io/tf/lang/marks" 11 "github.com/zclconf/go-cty/cty" 12 ) 13 14 func TestTo(t *testing.T) { 15 tests := []struct { 16 Value cty.Value 17 TargetTy cty.Type 18 Want cty.Value 19 Err string 20 }{ 21 { 22 cty.StringVal("a"), 23 cty.String, 24 cty.StringVal("a"), 25 ``, 26 }, 27 { 28 cty.UnknownVal(cty.String), 29 cty.String, 30 cty.UnknownVal(cty.String), 31 ``, 32 }, 33 { 34 cty.NullVal(cty.String), 35 cty.String, 36 cty.NullVal(cty.String), 37 ``, 38 }, 39 { 40 // This test case represents evaluating the expression tostring(null) 41 // from HCL, since null in HCL is cty.NullVal(cty.DynamicPseudoType). 42 // The result in that case should still be null, but a null specifically 43 // of type string. 44 cty.NullVal(cty.DynamicPseudoType), 45 cty.String, 46 cty.NullVal(cty.String), 47 ``, 48 }, 49 { 50 cty.StringVal("a").Mark("boop"), 51 cty.String, 52 cty.StringVal("a").Mark("boop"), 53 ``, 54 }, 55 { 56 cty.NullVal(cty.String).Mark("boop"), 57 cty.String, 58 cty.NullVal(cty.String).Mark("boop"), 59 ``, 60 }, 61 { 62 cty.True, 63 cty.String, 64 cty.StringVal("true"), 65 ``, 66 }, 67 { 68 cty.StringVal("a"), 69 cty.Bool, 70 cty.DynamicVal, 71 `cannot convert "a" to bool; only the strings "true" or "false" are allowed`, 72 }, 73 { 74 cty.StringVal("a").Mark("boop"), 75 cty.Bool, 76 cty.DynamicVal, 77 `cannot convert "a" to bool; only the strings "true" or "false" are allowed`, 78 }, 79 { 80 cty.StringVal("a").Mark(marks.Sensitive), 81 cty.Bool, 82 cty.DynamicVal, 83 `cannot convert this sensitive string to bool`, 84 }, 85 { 86 cty.StringVal("a"), 87 cty.Number, 88 cty.DynamicVal, 89 `cannot convert "a" to number; given string must be a decimal representation of a number`, 90 }, 91 { 92 cty.StringVal("a").Mark("boop"), 93 cty.Number, 94 cty.DynamicVal, 95 `cannot convert "a" to number; given string must be a decimal representation of a number`, 96 }, 97 { 98 cty.StringVal("a").Mark(marks.Sensitive), 99 cty.Number, 100 cty.DynamicVal, 101 `cannot convert this sensitive string to number`, 102 }, 103 { 104 cty.NullVal(cty.String), 105 cty.Number, 106 cty.NullVal(cty.Number), 107 ``, 108 }, 109 { 110 cty.UnknownVal(cty.Bool), 111 cty.String, 112 cty.UnknownVal(cty.String), 113 ``, 114 }, 115 { 116 cty.UnknownVal(cty.String), 117 cty.Bool, 118 cty.UnknownVal(cty.Bool), // conversion is optimistic 119 ``, 120 }, 121 { 122 cty.TupleVal([]cty.Value{cty.StringVal("hello"), cty.True}), 123 cty.List(cty.String), 124 cty.ListVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("true")}), 125 ``, 126 }, 127 { 128 cty.TupleVal([]cty.Value{cty.StringVal("hello"), cty.True}), 129 cty.Set(cty.String), 130 cty.SetVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("true")}), 131 ``, 132 }, 133 { 134 cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.True}), 135 cty.Map(cty.String), 136 cty.MapVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.StringVal("true")}), 137 ``, 138 }, 139 { 140 cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.StringVal("world").Mark("boop")}), 141 cty.Map(cty.String), 142 cty.MapVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.StringVal("world").Mark("boop")}), 143 ``, 144 }, 145 { 146 cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.StringVal("world")}).Mark("boop"), 147 cty.Map(cty.String), 148 cty.MapVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.StringVal("world")}).Mark("boop"), 149 ``, 150 }, 151 { 152 cty.TupleVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("world").Mark("boop")}), 153 cty.List(cty.String), 154 cty.ListVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("world").Mark("boop")}), 155 ``, 156 }, 157 { 158 cty.TupleVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("world")}).Mark("boop"), 159 cty.List(cty.String), 160 cty.ListVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("world")}).Mark("boop"), 161 ``, 162 }, 163 { 164 cty.EmptyTupleVal, 165 cty.String, 166 cty.DynamicVal, 167 `cannot convert tuple to string`, 168 }, 169 { 170 cty.UnknownVal(cty.EmptyTuple), 171 cty.String, 172 cty.DynamicVal, 173 `cannot convert tuple to string`, 174 }, 175 { 176 cty.EmptyObjectVal, 177 cty.Object(map[string]cty.Type{"foo": cty.String}), 178 cty.DynamicVal, 179 `incompatible object type for conversion: attribute "foo" is required`, 180 }, 181 } 182 183 for _, test := range tests { 184 t.Run(fmt.Sprintf("to %s(%#v)", test.TargetTy.FriendlyNameForConstraint(), test.Value), func(t *testing.T) { 185 f := MakeToFunc(test.TargetTy) 186 got, err := f.Call([]cty.Value{test.Value}) 187 188 if test.Err != "" { 189 if err == nil { 190 t.Fatal("succeeded; want error") 191 } 192 if got, want := err.Error(), test.Err; got != want { 193 t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) 194 } 195 return 196 } else if err != nil { 197 t.Fatalf("unexpected error: %s", err) 198 } 199 200 if !got.RawEquals(test.Want) { 201 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 202 } 203 }) 204 } 205 }