github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/configs/hcl2shim/values_equiv_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package hcl2shim 5 6 import ( 7 "fmt" 8 "math/big" 9 "testing" 10 11 "github.com/zclconf/go-cty/cty" 12 ) 13 14 func TestValuesSDKEquivalent(t *testing.T) { 15 piBig, _, err := big.ParseFloat("3.14159265358979323846264338327950288419716939937510582097494459", 10, 512, big.ToZero) 16 if err != nil { 17 t.Fatal(err) 18 } 19 pi64, _ := piBig.Float64() 20 21 tests := []struct { 22 A, B cty.Value 23 Want bool 24 }{ 25 // Strings 26 { 27 cty.StringVal("hello"), 28 cty.StringVal("hello"), 29 true, 30 }, 31 { 32 cty.StringVal("hello"), 33 cty.StringVal("world"), 34 false, 35 }, 36 { 37 cty.StringVal("hello"), 38 cty.StringVal(""), 39 false, 40 }, 41 { 42 cty.NullVal(cty.String), 43 cty.StringVal(""), 44 true, 45 }, 46 47 // Numbers 48 { 49 cty.NumberIntVal(1), 50 cty.NumberIntVal(1), 51 true, 52 }, 53 { 54 cty.NumberIntVal(1), 55 cty.NumberIntVal(2), 56 false, 57 }, 58 { 59 cty.NumberIntVal(1), 60 cty.Zero, 61 false, 62 }, 63 { 64 cty.NullVal(cty.Number), 65 cty.Zero, 66 true, 67 }, 68 { 69 cty.NumberVal(piBig), 70 cty.Zero, 71 false, 72 }, 73 { 74 cty.NumberFloatVal(pi64), 75 cty.Zero, 76 false, 77 }, 78 { 79 cty.NumberFloatVal(pi64), 80 cty.NumberVal(piBig), 81 true, 82 }, 83 84 // Bools 85 { 86 cty.True, 87 cty.True, 88 true, 89 }, 90 { 91 cty.True, 92 cty.False, 93 false, 94 }, 95 { 96 cty.NullVal(cty.Bool), 97 cty.False, 98 true, 99 }, 100 101 // Mixed primitives 102 { 103 cty.StringVal("hello"), 104 cty.False, 105 false, 106 }, 107 { 108 cty.StringVal(""), 109 cty.False, 110 true, 111 }, 112 { 113 cty.NumberIntVal(0), 114 cty.False, 115 true, 116 }, 117 { 118 cty.StringVal(""), 119 cty.NumberIntVal(0), 120 true, 121 }, 122 { 123 cty.NullVal(cty.Bool), 124 cty.NullVal(cty.Number), 125 true, 126 }, 127 { 128 cty.StringVal(""), 129 cty.NullVal(cty.Number), 130 true, 131 }, 132 133 // Lists 134 { 135 cty.ListValEmpty(cty.String), 136 cty.ListValEmpty(cty.String), 137 true, 138 }, 139 { 140 cty.ListValEmpty(cty.String), 141 cty.NullVal(cty.List(cty.String)), 142 true, 143 }, 144 { 145 cty.ListVal([]cty.Value{cty.StringVal("hello")}), 146 cty.ListVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("hello")}), 147 false, 148 }, 149 { 150 cty.ListVal([]cty.Value{cty.StringVal("hello")}), 151 cty.ListValEmpty(cty.String), 152 false, 153 }, 154 { 155 cty.ListVal([]cty.Value{cty.StringVal("hello")}), 156 cty.ListVal([]cty.Value{cty.StringVal("hello")}), 157 true, 158 }, 159 { 160 cty.ListVal([]cty.Value{cty.StringVal("hello")}), 161 cty.ListVal([]cty.Value{cty.StringVal("world")}), 162 false, 163 }, 164 { 165 cty.ListVal([]cty.Value{cty.NullVal(cty.String)}), 166 cty.ListVal([]cty.Value{cty.StringVal("")}), 167 true, 168 }, 169 170 // Tuples 171 { 172 cty.EmptyTupleVal, 173 cty.EmptyTupleVal, 174 true, 175 }, 176 { 177 cty.EmptyTupleVal, 178 cty.NullVal(cty.EmptyTuple), 179 true, 180 }, 181 { 182 cty.TupleVal([]cty.Value{cty.StringVal("hello")}), 183 cty.TupleVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("hello")}), 184 false, 185 }, 186 { 187 cty.TupleVal([]cty.Value{cty.StringVal("hello")}), 188 cty.EmptyTupleVal, 189 false, 190 }, 191 { 192 cty.TupleVal([]cty.Value{cty.StringVal("hello")}), 193 cty.TupleVal([]cty.Value{cty.StringVal("hello")}), 194 true, 195 }, 196 { 197 cty.TupleVal([]cty.Value{cty.StringVal("hello")}), 198 cty.TupleVal([]cty.Value{cty.StringVal("world")}), 199 false, 200 }, 201 { 202 cty.TupleVal([]cty.Value{cty.NullVal(cty.String)}), 203 cty.TupleVal([]cty.Value{cty.StringVal("")}), 204 true, 205 }, 206 207 // Sets 208 { 209 cty.SetValEmpty(cty.String), 210 cty.SetValEmpty(cty.String), 211 true, 212 }, 213 { 214 cty.SetValEmpty(cty.String), 215 cty.NullVal(cty.Set(cty.String)), 216 true, 217 }, 218 { 219 cty.SetVal([]cty.Value{cty.StringVal("hello")}), 220 cty.SetValEmpty(cty.String), 221 false, 222 }, 223 { 224 cty.SetVal([]cty.Value{cty.StringVal("hello")}), 225 cty.SetVal([]cty.Value{cty.StringVal("hello")}), 226 true, 227 }, 228 { 229 cty.SetVal([]cty.Value{cty.StringVal("hello")}), 230 cty.SetVal([]cty.Value{cty.StringVal("world")}), 231 false, 232 }, 233 { 234 cty.SetVal([]cty.Value{cty.NullVal(cty.String)}), 235 cty.SetVal([]cty.Value{cty.StringVal("")}), 236 true, 237 }, 238 { 239 cty.SetVal([]cty.Value{ 240 cty.NullVal(cty.String), 241 cty.StringVal(""), 242 }), 243 cty.SetVal([]cty.Value{ 244 cty.NullVal(cty.String), 245 }), 246 false, // because the element count is different 247 }, 248 { 249 cty.SetVal([]cty.Value{ 250 cty.ObjectVal(map[string]cty.Value{ 251 "a": cty.StringVal(""), 252 "b": cty.StringVal(""), 253 }), 254 cty.ObjectVal(map[string]cty.Value{ 255 "a": cty.NullVal(cty.String), 256 "b": cty.StringVal(""), 257 }), 258 }), 259 cty.SetVal([]cty.Value{ 260 cty.ObjectVal(map[string]cty.Value{ 261 "a": cty.StringVal(""), 262 "b": cty.StringVal(""), 263 }), 264 cty.ObjectVal(map[string]cty.Value{ 265 "a": cty.StringVal(""), 266 "b": cty.NullVal(cty.String), 267 }), 268 }), 269 true, 270 }, 271 { 272 cty.SetVal([]cty.Value{ 273 cty.ObjectVal(map[string]cty.Value{ 274 "a": cty.StringVal("boop"), 275 "b": cty.StringVal(""), 276 }), 277 cty.ObjectVal(map[string]cty.Value{ 278 "a": cty.NullVal(cty.String), 279 "b": cty.StringVal(""), 280 }), 281 }), 282 cty.SetVal([]cty.Value{ 283 cty.ObjectVal(map[string]cty.Value{ 284 "a": cty.StringVal("beep"), 285 "b": cty.StringVal(""), 286 }), 287 cty.ObjectVal(map[string]cty.Value{ 288 "a": cty.StringVal(""), 289 "b": cty.NullVal(cty.String), 290 }), 291 }), 292 false, 293 }, 294 { 295 cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ 296 "list": cty.ListValEmpty(cty.String), 297 "list_block": cty.ListVal([]cty.Value{ 298 cty.ObjectVal(map[string]cty.Value{ 299 "unused": cty.StringVal(""), 300 }), 301 }), 302 })}), 303 cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ 304 "list": cty.ListValEmpty(cty.String), 305 "list_block": cty.ListVal([]cty.Value{ 306 cty.ObjectVal(map[string]cty.Value{ 307 "unused": cty.NullVal(cty.String), 308 }), 309 }), 310 })}), 311 true, 312 }, 313 314 // Maps 315 { 316 cty.MapValEmpty(cty.String), 317 cty.MapValEmpty(cty.String), 318 true, 319 }, 320 { 321 cty.MapValEmpty(cty.String), 322 cty.NullVal(cty.Map(cty.String)), 323 true, 324 }, 325 { 326 cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), 327 cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("hello"), "hey": cty.StringVal("hello")}), 328 false, 329 }, 330 { 331 cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), 332 cty.MapValEmpty(cty.String), 333 false, 334 }, 335 { 336 cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), 337 cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), 338 true, 339 }, 340 { 341 cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), 342 cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("world")}), 343 false, 344 }, 345 { 346 cty.MapVal(map[string]cty.Value{"hi": cty.NullVal(cty.String)}), 347 cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("")}), 348 true, 349 }, 350 351 // Objects 352 { 353 cty.EmptyObjectVal, 354 cty.EmptyObjectVal, 355 true, 356 }, 357 { 358 cty.EmptyObjectVal, 359 cty.NullVal(cty.EmptyObject), 360 true, 361 }, 362 { 363 cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), 364 cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("hello"), "hey": cty.StringVal("hello")}), 365 false, 366 }, 367 { 368 cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), 369 cty.EmptyObjectVal, 370 false, 371 }, 372 { 373 cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), 374 cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), 375 true, 376 }, 377 { 378 cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), 379 cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("world")}), 380 false, 381 }, 382 { 383 cty.ObjectVal(map[string]cty.Value{"hi": cty.NullVal(cty.String)}), 384 cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("")}), 385 true, 386 }, 387 388 // Unknown values 389 { 390 cty.UnknownVal(cty.String), 391 cty.UnknownVal(cty.String), 392 true, 393 }, 394 { 395 cty.StringVal("hello"), 396 cty.UnknownVal(cty.String), 397 false, 398 }, 399 { 400 cty.StringVal(""), 401 cty.UnknownVal(cty.String), 402 false, 403 }, 404 { 405 cty.NullVal(cty.String), 406 cty.UnknownVal(cty.String), 407 false, 408 }, 409 } 410 411 run := func(t *testing.T, a, b cty.Value, want bool) { 412 got := ValuesSDKEquivalent(a, b) 413 414 if got != want { 415 t.Errorf("wrong result\nfor: %#v ≈ %#v\ngot %#v, but want %#v", a, b, got, want) 416 } 417 } 418 419 for _, test := range tests { 420 t.Run(fmt.Sprintf("%#v ≈ %#v", test.A, test.B), func(t *testing.T) { 421 run(t, test.A, test.B, test.Want) 422 }) 423 // This function is symmetrical, so we'll also test in reverse so 424 // we don't need to manually copy all of the test cases. (But this does 425 // mean that one failure normally becomes two, of course!) 426 if !test.A.RawEquals(test.B) { 427 t.Run(fmt.Sprintf("%#v ≈ %#v", test.B, test.A), func(t *testing.T) { 428 run(t, test.B, test.A, test.Want) 429 }) 430 } 431 } 432 }