github.com/hugorut/terraform@v1.1.3/src/lang/funcs/conversion_test.go (about) 1 package funcs 2 3 import ( 4 "fmt" 5 "testing" 6 7 "github.com/google/go-cmp/cmp" 8 "github.com/hugorut/terraform/src/lang/marks" 9 "github.com/zclconf/go-cty/cty" 10 ) 11 12 func TestTo(t *testing.T) { 13 tests := []struct { 14 Value cty.Value 15 TargetTy cty.Type 16 Want cty.Value 17 Err string 18 }{ 19 { 20 cty.StringVal("a"), 21 cty.String, 22 cty.StringVal("a"), 23 ``, 24 }, 25 { 26 cty.UnknownVal(cty.String), 27 cty.String, 28 cty.UnknownVal(cty.String), 29 ``, 30 }, 31 { 32 cty.NullVal(cty.String), 33 cty.String, 34 cty.NullVal(cty.String), 35 ``, 36 }, 37 { 38 cty.StringVal("a").Mark("boop"), 39 cty.String, 40 cty.StringVal("a").Mark("boop"), 41 ``, 42 }, 43 { 44 cty.NullVal(cty.String).Mark("boop"), 45 cty.String, 46 cty.NullVal(cty.String).Mark("boop"), 47 ``, 48 }, 49 { 50 cty.True, 51 cty.String, 52 cty.StringVal("true"), 53 ``, 54 }, 55 { 56 cty.StringVal("a"), 57 cty.Bool, 58 cty.DynamicVal, 59 `cannot convert "a" to bool; only the strings "true" or "false" are allowed`, 60 }, 61 { 62 cty.StringVal("a").Mark("boop"), 63 cty.Bool, 64 cty.DynamicVal, 65 `cannot convert "a" to bool; only the strings "true" or "false" are allowed`, 66 }, 67 { 68 cty.StringVal("a").Mark(marks.Sensitive), 69 cty.Bool, 70 cty.DynamicVal, 71 `cannot convert this sensitive string to bool`, 72 }, 73 { 74 cty.StringVal("a"), 75 cty.Number, 76 cty.DynamicVal, 77 `cannot convert "a" to number; given string must be a decimal representation of a number`, 78 }, 79 { 80 cty.StringVal("a").Mark("boop"), 81 cty.Number, 82 cty.DynamicVal, 83 `cannot convert "a" to number; given string must be a decimal representation of a number`, 84 }, 85 { 86 cty.StringVal("a").Mark(marks.Sensitive), 87 cty.Number, 88 cty.DynamicVal, 89 `cannot convert this sensitive string to number`, 90 }, 91 { 92 cty.NullVal(cty.String), 93 cty.Number, 94 cty.NullVal(cty.Number), 95 ``, 96 }, 97 { 98 cty.UnknownVal(cty.Bool), 99 cty.String, 100 cty.UnknownVal(cty.String), 101 ``, 102 }, 103 { 104 cty.UnknownVal(cty.String), 105 cty.Bool, 106 cty.UnknownVal(cty.Bool), // conversion is optimistic 107 ``, 108 }, 109 { 110 cty.TupleVal([]cty.Value{cty.StringVal("hello"), cty.True}), 111 cty.List(cty.String), 112 cty.ListVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("true")}), 113 ``, 114 }, 115 { 116 cty.TupleVal([]cty.Value{cty.StringVal("hello"), cty.True}), 117 cty.Set(cty.String), 118 cty.SetVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("true")}), 119 ``, 120 }, 121 { 122 cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.True}), 123 cty.Map(cty.String), 124 cty.MapVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.StringVal("true")}), 125 ``, 126 }, 127 { 128 cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.StringVal("world").Mark("boop")}), 129 cty.Map(cty.String), 130 cty.MapVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.StringVal("world").Mark("boop")}), 131 ``, 132 }, 133 { 134 cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.StringVal("world")}).Mark("boop"), 135 cty.Map(cty.String), 136 cty.MapVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.StringVal("world")}).Mark("boop"), 137 ``, 138 }, 139 { 140 cty.TupleVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("world").Mark("boop")}), 141 cty.List(cty.String), 142 cty.ListVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("world").Mark("boop")}), 143 ``, 144 }, 145 { 146 cty.TupleVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("world")}).Mark("boop"), 147 cty.List(cty.String), 148 cty.ListVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("world")}).Mark("boop"), 149 ``, 150 }, 151 { 152 cty.EmptyTupleVal, 153 cty.String, 154 cty.DynamicVal, 155 `cannot convert tuple to string`, 156 }, 157 { 158 cty.UnknownVal(cty.EmptyTuple), 159 cty.String, 160 cty.DynamicVal, 161 `cannot convert tuple to string`, 162 }, 163 { 164 cty.EmptyObjectVal, 165 cty.Object(map[string]cty.Type{"foo": cty.String}), 166 cty.DynamicVal, 167 `incompatible object type for conversion: attribute "foo" is required`, 168 }, 169 } 170 171 for _, test := range tests { 172 t.Run(fmt.Sprintf("to %s(%#v)", test.TargetTy.FriendlyNameForConstraint(), test.Value), func(t *testing.T) { 173 f := MakeToFunc(test.TargetTy) 174 got, err := f.Call([]cty.Value{test.Value}) 175 176 if test.Err != "" { 177 if err == nil { 178 t.Fatal("succeeded; want error") 179 } 180 if got, want := err.Error(), test.Err; got != want { 181 t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) 182 } 183 return 184 } else if err != nil { 185 t.Fatalf("unexpected error: %s", err) 186 } 187 188 if !got.RawEquals(test.Want) { 189 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 190 } 191 }) 192 } 193 } 194 195 func TestType(t *testing.T) { 196 tests := []struct { 197 Input cty.Value 198 Want string 199 }{ 200 // Primititves 201 { 202 cty.StringVal("a"), 203 "string", 204 }, 205 { 206 cty.NumberIntVal(42), 207 "number", 208 }, 209 { 210 cty.BoolVal(true), 211 "bool", 212 }, 213 // Collections 214 { 215 cty.EmptyObjectVal, 216 `object({})`, 217 }, 218 { 219 cty.EmptyTupleVal, 220 `tuple([])`, 221 }, 222 { 223 cty.ListValEmpty(cty.String), 224 `list(string)`, 225 }, 226 { 227 cty.MapValEmpty(cty.String), 228 `map(string)`, 229 }, 230 { 231 cty.SetValEmpty(cty.String), 232 `set(string)`, 233 }, 234 { 235 cty.ListVal([]cty.Value{cty.StringVal("a")}), 236 `list(string)`, 237 }, 238 { 239 cty.ListVal([]cty.Value{cty.ListVal([]cty.Value{cty.NumberIntVal(42)})}), 240 `list(list(number))`, 241 }, 242 { 243 cty.ListVal([]cty.Value{cty.MapValEmpty(cty.String)}), 244 `list(map(string))`, 245 }, 246 { 247 cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ 248 "foo": cty.StringVal("bar"), 249 })}), 250 "list(\n object({\n foo: string,\n }),\n)", 251 }, 252 // Unknowns and Nulls 253 { 254 cty.UnknownVal(cty.String), 255 "string", 256 }, 257 { 258 cty.NullVal(cty.Object(map[string]cty.Type{ 259 "foo": cty.String, 260 })), 261 "object({\n foo: string,\n})", 262 }, 263 { // irrelevant marks do nothing 264 cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ 265 "foo": cty.StringVal("bar").Mark("ignore me"), 266 })}), 267 "list(\n object({\n foo: string,\n }),\n)", 268 }, 269 } 270 for _, test := range tests { 271 got, err := Type([]cty.Value{test.Input}) 272 if err != nil { 273 t.Fatalf("unexpected error: %s", err) 274 } 275 // The value is marked to help with formatting 276 got, _ = got.Unmark() 277 278 if got.AsString() != test.Want { 279 t.Errorf("wrong result:\n%s", cmp.Diff(got.AsString(), test.Want)) 280 } 281 } 282 }