github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/lang/funcs/collection_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 "math" 9 "testing" 10 11 "github.com/terramate-io/tf/lang/marks" 12 "github.com/zclconf/go-cty/cty" 13 ) 14 15 func TestLength(t *testing.T) { 16 tests := []struct { 17 Value cty.Value 18 Want cty.Value 19 }{ 20 { 21 cty.ListValEmpty(cty.Number), 22 cty.NumberIntVal(0), 23 }, 24 { 25 cty.ListVal([]cty.Value{cty.True}), 26 cty.NumberIntVal(1), 27 }, 28 { 29 cty.ListVal([]cty.Value{cty.UnknownVal(cty.Bool)}), 30 cty.NumberIntVal(1), 31 }, 32 { 33 cty.SetValEmpty(cty.Number), 34 cty.NumberIntVal(0), 35 }, 36 { 37 cty.SetVal([]cty.Value{cty.True}), 38 cty.NumberIntVal(1), 39 }, 40 { 41 cty.MapValEmpty(cty.Bool), 42 cty.NumberIntVal(0), 43 }, 44 { 45 cty.MapVal(map[string]cty.Value{"hello": cty.True}), 46 cty.NumberIntVal(1), 47 }, 48 { 49 cty.EmptyTupleVal, 50 cty.NumberIntVal(0), 51 }, 52 { 53 cty.UnknownVal(cty.EmptyTuple), 54 cty.NumberIntVal(0), 55 }, 56 { 57 cty.TupleVal([]cty.Value{cty.True}), 58 cty.NumberIntVal(1), 59 }, 60 { 61 cty.EmptyObjectVal, 62 cty.NumberIntVal(0), 63 }, 64 { 65 cty.UnknownVal(cty.EmptyObject), 66 cty.NumberIntVal(0), 67 }, 68 { 69 cty.ObjectVal(map[string]cty.Value{"true": cty.True}), 70 cty.NumberIntVal(1), 71 }, 72 { 73 cty.UnknownVal(cty.List(cty.Bool)), 74 cty.UnknownVal(cty.Number).Refine(). 75 NotNull(). 76 NumberRangeLowerBound(cty.Zero, true). 77 NumberRangeUpperBound(cty.NumberIntVal(math.MaxInt), true). 78 NewValue(), 79 }, 80 { 81 cty.DynamicVal, 82 cty.UnknownVal(cty.Number).RefineNotNull(), 83 }, 84 { 85 cty.StringVal("hello"), 86 cty.NumberIntVal(5), 87 }, 88 { 89 cty.StringVal(""), 90 cty.NumberIntVal(0), 91 }, 92 { 93 cty.StringVal("1"), 94 cty.NumberIntVal(1), 95 }, 96 { 97 cty.StringVal("Живой Журнал"), 98 cty.NumberIntVal(12), 99 }, 100 { 101 // note that the dieresis here is intentionally a combining 102 // ligature. 103 cty.StringVal("noël"), 104 cty.NumberIntVal(4), 105 }, 106 { 107 // The Es in this string has three combining acute accents. 108 // This tests something that NFC-normalization cannot collapse 109 // into a single precombined codepoint, since otherwise we might 110 // be cheating and relying on the single-codepoint forms. 111 cty.StringVal("wé́́é́́é́́!"), 112 cty.NumberIntVal(5), 113 }, 114 { 115 // Go's normalization forms don't handle this ligature, so we 116 // will produce the wrong result but this is now a compatibility 117 // constraint and so we'll test it. 118 cty.StringVal("baffle"), 119 cty.NumberIntVal(4), 120 }, 121 { 122 cty.StringVal("😸😾"), 123 cty.NumberIntVal(2), 124 }, 125 { 126 cty.UnknownVal(cty.String), 127 cty.UnknownVal(cty.Number).Refine(). 128 NotNull(). 129 NumberRangeLowerBound(cty.Zero, true). 130 NewValue(), 131 }, 132 { // Marked collections return a marked length 133 cty.ListVal([]cty.Value{ 134 cty.StringVal("hello"), 135 cty.StringVal("world"), 136 }).Mark("secret"), 137 cty.NumberIntVal(2).Mark("secret"), 138 }, 139 { // Marks on values in unmarked collections do not propagate 140 cty.ListVal([]cty.Value{ 141 cty.StringVal("hello").Mark("a"), 142 cty.StringVal("world").Mark("b"), 143 }), 144 cty.NumberIntVal(2), 145 }, 146 { // Marked strings return a marked length 147 cty.StringVal("hello world").Mark("secret"), 148 cty.NumberIntVal(11).Mark("secret"), 149 }, 150 { // Marked tuples return a marked length 151 cty.TupleVal([]cty.Value{ 152 cty.StringVal("hello"), 153 cty.StringVal("world"), 154 }).Mark("secret"), 155 cty.NumberIntVal(2).Mark("secret"), 156 }, 157 { // Marks on values in unmarked tuples do not propagate 158 cty.TupleVal([]cty.Value{ 159 cty.StringVal("hello").Mark("a"), 160 cty.StringVal("world").Mark("b"), 161 }), 162 cty.NumberIntVal(2), 163 }, 164 { // Marked objects return a marked length 165 cty.ObjectVal(map[string]cty.Value{ 166 "a": cty.StringVal("hello"), 167 "b": cty.StringVal("world"), 168 "c": cty.StringVal("nice to meet you"), 169 }).Mark("secret"), 170 cty.NumberIntVal(3).Mark("secret"), 171 }, 172 { // Marks on object attribute values do not propagate 173 cty.ObjectVal(map[string]cty.Value{ 174 "a": cty.StringVal("hello").Mark("a"), 175 "b": cty.StringVal("world").Mark("b"), 176 "c": cty.StringVal("nice to meet you").Mark("c"), 177 }), 178 cty.NumberIntVal(3), 179 }, 180 } 181 182 for _, test := range tests { 183 t.Run(fmt.Sprintf("Length(%#v)", test.Value), func(t *testing.T) { 184 got, err := Length(test.Value) 185 186 if err != nil { 187 t.Fatalf("unexpected error: %s", err) 188 } 189 190 if !got.RawEquals(test.Want) { 191 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 192 } 193 }) 194 } 195 } 196 197 func TestAllTrue(t *testing.T) { 198 tests := []struct { 199 Collection cty.Value 200 Want cty.Value 201 Err bool 202 }{ 203 { 204 cty.ListValEmpty(cty.Bool), 205 cty.True, 206 false, 207 }, 208 { 209 cty.ListVal([]cty.Value{cty.True}), 210 cty.True, 211 false, 212 }, 213 { 214 cty.ListVal([]cty.Value{cty.False}), 215 cty.False, 216 false, 217 }, 218 { 219 cty.ListVal([]cty.Value{cty.True, cty.False}), 220 cty.False, 221 false, 222 }, 223 { 224 cty.ListVal([]cty.Value{cty.False, cty.True}), 225 cty.False, 226 false, 227 }, 228 { 229 cty.ListVal([]cty.Value{cty.True, cty.NullVal(cty.Bool)}), 230 cty.False, 231 false, 232 }, 233 { 234 cty.ListVal([]cty.Value{cty.UnknownVal(cty.Bool)}), 235 cty.UnknownVal(cty.Bool).RefineNotNull(), 236 false, 237 }, 238 { 239 cty.ListVal([]cty.Value{ 240 cty.UnknownVal(cty.Bool), 241 cty.UnknownVal(cty.Bool), 242 }), 243 cty.UnknownVal(cty.Bool).RefineNotNull(), 244 false, 245 }, 246 { 247 cty.UnknownVal(cty.List(cty.Bool)), 248 cty.UnknownVal(cty.Bool).RefineNotNull(), 249 false, 250 }, 251 { 252 cty.NullVal(cty.List(cty.Bool)), 253 cty.NilVal, 254 true, 255 }, 256 } 257 258 for _, test := range tests { 259 t.Run(fmt.Sprintf("alltrue(%#v)", test.Collection), func(t *testing.T) { 260 got, err := AllTrue(test.Collection) 261 262 if test.Err { 263 if err == nil { 264 t.Fatal("succeeded; want error") 265 } 266 return 267 } else if err != nil { 268 t.Fatalf("unexpected error: %s", err) 269 } 270 271 if !got.RawEquals(test.Want) { 272 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 273 } 274 }) 275 } 276 } 277 278 func TestAnyTrue(t *testing.T) { 279 tests := []struct { 280 Collection cty.Value 281 Want cty.Value 282 Err bool 283 }{ 284 { 285 cty.ListValEmpty(cty.Bool), 286 cty.False, 287 false, 288 }, 289 { 290 cty.ListVal([]cty.Value{cty.True}), 291 cty.True, 292 false, 293 }, 294 { 295 cty.ListVal([]cty.Value{cty.False}), 296 cty.False, 297 false, 298 }, 299 { 300 cty.ListVal([]cty.Value{cty.True, cty.False}), 301 cty.True, 302 false, 303 }, 304 { 305 cty.ListVal([]cty.Value{cty.False, cty.True}), 306 cty.True, 307 false, 308 }, 309 { 310 cty.ListVal([]cty.Value{cty.NullVal(cty.Bool), cty.True}), 311 cty.True, 312 false, 313 }, 314 { 315 cty.ListVal([]cty.Value{cty.UnknownVal(cty.Bool)}), 316 cty.UnknownVal(cty.Bool).RefineNotNull(), 317 false, 318 }, 319 { 320 cty.ListVal([]cty.Value{ 321 cty.UnknownVal(cty.Bool), 322 cty.False, 323 }), 324 cty.UnknownVal(cty.Bool).RefineNotNull(), 325 false, 326 }, 327 { 328 cty.ListVal([]cty.Value{ 329 cty.UnknownVal(cty.Bool), 330 cty.True, 331 }), 332 cty.True, 333 false, 334 }, 335 { 336 cty.UnknownVal(cty.List(cty.Bool)), 337 cty.UnknownVal(cty.Bool).RefineNotNull(), 338 false, 339 }, 340 { 341 cty.NullVal(cty.List(cty.Bool)), 342 cty.NilVal, 343 true, 344 }, 345 } 346 347 for _, test := range tests { 348 t.Run(fmt.Sprintf("anytrue(%#v)", test.Collection), func(t *testing.T) { 349 got, err := AnyTrue(test.Collection) 350 351 if test.Err { 352 if err == nil { 353 t.Fatal("succeeded; want error") 354 } 355 return 356 } else if err != nil { 357 t.Fatalf("unexpected error: %s", err) 358 } 359 360 if !got.RawEquals(test.Want) { 361 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 362 } 363 }) 364 } 365 } 366 367 func TestCoalesce(t *testing.T) { 368 tests := []struct { 369 Values []cty.Value 370 Want cty.Value 371 Err bool 372 }{ 373 { 374 []cty.Value{cty.StringVal("first"), cty.StringVal("second"), cty.StringVal("third")}, 375 cty.StringVal("first"), 376 false, 377 }, 378 { 379 []cty.Value{cty.StringVal(""), cty.StringVal("second"), cty.StringVal("third")}, 380 cty.StringVal("second"), 381 false, 382 }, 383 { 384 []cty.Value{cty.StringVal(""), cty.StringVal("")}, 385 cty.NilVal, 386 true, 387 }, 388 { 389 []cty.Value{cty.True}, 390 cty.True, 391 false, 392 }, 393 { 394 []cty.Value{cty.NullVal(cty.Bool), cty.True}, 395 cty.True, 396 false, 397 }, 398 { 399 []cty.Value{cty.NullVal(cty.Bool), cty.False}, 400 cty.False, 401 false, 402 }, 403 { 404 []cty.Value{cty.NullVal(cty.Bool), cty.False, cty.StringVal("hello")}, 405 cty.StringVal("false"), 406 false, 407 }, 408 { 409 []cty.Value{cty.True, cty.UnknownVal(cty.Bool)}, 410 cty.True, 411 false, 412 }, 413 { 414 []cty.Value{cty.UnknownVal(cty.Bool), cty.True}, 415 cty.UnknownVal(cty.Bool).RefineNotNull(), 416 false, 417 }, 418 { 419 []cty.Value{cty.UnknownVal(cty.Bool), cty.StringVal("hello")}, 420 cty.UnknownVal(cty.String).RefineNotNull(), 421 false, 422 }, 423 { 424 []cty.Value{cty.DynamicVal, cty.True}, 425 cty.UnknownVal(cty.Bool).RefineNotNull(), 426 false, 427 }, 428 { 429 []cty.Value{cty.DynamicVal}, 430 cty.DynamicVal, 431 false, 432 }, 433 } 434 435 for _, test := range tests { 436 t.Run(fmt.Sprintf("Coalesce(%#v...)", test.Values), func(t *testing.T) { 437 got, err := Coalesce(test.Values...) 438 439 if test.Err { 440 if err == nil { 441 t.Fatal("succeeded; want error") 442 } 443 return 444 } else if err != nil { 445 t.Fatalf("unexpected error: %s", err) 446 } 447 448 if !got.RawEquals(test.Want) { 449 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 450 } 451 }) 452 } 453 } 454 455 func TestIndex(t *testing.T) { 456 tests := []struct { 457 List cty.Value 458 Value cty.Value 459 Want cty.Value 460 Err bool 461 }{ 462 { 463 cty.ListVal([]cty.Value{ 464 cty.StringVal("a"), 465 cty.StringVal("b"), 466 cty.StringVal("c"), 467 }), 468 cty.StringVal("a"), 469 cty.NumberIntVal(0), 470 false, 471 }, 472 { 473 cty.ListVal([]cty.Value{ 474 cty.StringVal("a"), 475 cty.StringVal("b"), 476 cty.UnknownVal(cty.String), 477 }), 478 cty.StringVal("a"), 479 cty.NumberIntVal(0), 480 false, 481 }, 482 { 483 cty.ListVal([]cty.Value{ 484 cty.StringVal("a"), 485 cty.StringVal("b"), 486 cty.StringVal("c"), 487 }), 488 cty.StringVal("b"), 489 cty.NumberIntVal(1), 490 false, 491 }, 492 { 493 cty.ListVal([]cty.Value{ 494 cty.StringVal("a"), 495 cty.StringVal("b"), 496 cty.StringVal("c"), 497 }), 498 cty.StringVal("z"), 499 cty.NilVal, 500 true, 501 }, 502 { 503 cty.ListVal([]cty.Value{ 504 cty.StringVal("1"), 505 cty.StringVal("2"), 506 cty.StringVal("3"), 507 }), 508 cty.NumberIntVal(1), 509 cty.NumberIntVal(0), 510 true, 511 }, 512 { 513 cty.ListVal([]cty.Value{ 514 cty.NumberIntVal(1), 515 cty.NumberIntVal(2), 516 cty.NumberIntVal(3), 517 }), 518 cty.NumberIntVal(2), 519 cty.NumberIntVal(1), 520 false, 521 }, 522 { 523 cty.ListVal([]cty.Value{ 524 cty.NumberIntVal(1), 525 cty.NumberIntVal(2), 526 cty.NumberIntVal(3), 527 }), 528 cty.NumberIntVal(4), 529 cty.NilVal, 530 true, 531 }, 532 { 533 cty.ListVal([]cty.Value{ 534 cty.NumberIntVal(1), 535 cty.NumberIntVal(2), 536 cty.NumberIntVal(3), 537 }), 538 cty.StringVal("1"), 539 cty.NumberIntVal(0), 540 true, 541 }, 542 { 543 cty.TupleVal([]cty.Value{ 544 cty.NumberIntVal(1), 545 cty.NumberIntVal(2), 546 cty.NumberIntVal(3), 547 }), 548 cty.NumberIntVal(1), 549 cty.NumberIntVal(0), 550 false, 551 }, 552 } 553 554 for _, test := range tests { 555 t.Run(fmt.Sprintf("index(%#v, %#v)", test.List, test.Value), func(t *testing.T) { 556 got, err := Index(test.List, test.Value) 557 558 if test.Err { 559 if err == nil { 560 t.Fatal("succeeded; want error") 561 } 562 return 563 } else if err != nil { 564 t.Fatalf("unexpected error: %s", err) 565 } 566 567 if !got.RawEquals(test.Want) { 568 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 569 } 570 }) 571 } 572 } 573 574 func TestLookup(t *testing.T) { 575 simpleMap := cty.MapVal(map[string]cty.Value{ 576 "foo": cty.StringVal("bar"), 577 }) 578 intsMap := cty.MapVal(map[string]cty.Value{ 579 "foo": cty.NumberIntVal(42), 580 }) 581 mapOfLists := cty.MapVal(map[string]cty.Value{ 582 "foo": cty.ListVal([]cty.Value{ 583 cty.StringVal("bar"), 584 cty.StringVal("baz"), 585 }), 586 }) 587 mapOfMaps := cty.MapVal(map[string]cty.Value{ 588 "foo": cty.MapVal(map[string]cty.Value{ 589 "a": cty.StringVal("bar"), 590 }), 591 "baz": cty.MapVal(map[string]cty.Value{ 592 "b": cty.StringVal("bat"), 593 }), 594 }) 595 mapOfTuples := cty.MapVal(map[string]cty.Value{ 596 "foo": cty.TupleVal([]cty.Value{cty.StringVal("bar")}), 597 "baz": cty.TupleVal([]cty.Value{cty.StringVal("bat")}), 598 }) 599 objectOfMaps := cty.ObjectVal(map[string]cty.Value{ 600 "foo": cty.MapVal(map[string]cty.Value{ 601 "a": cty.StringVal("bar"), 602 }), 603 "baz": cty.MapVal(map[string]cty.Value{ 604 "b": cty.StringVal("bat"), 605 }), 606 }) 607 mapWithUnknowns := cty.MapVal(map[string]cty.Value{ 608 "foo": cty.StringVal("bar"), 609 "baz": cty.UnknownVal(cty.String), 610 }) 611 mapWithObjects := cty.ObjectVal(map[string]cty.Value{ 612 "foo": cty.StringVal("bar"), 613 "baz": cty.NumberIntVal(42), 614 }) 615 616 tests := []struct { 617 Values []cty.Value 618 Want cty.Value 619 Err bool 620 }{ 621 { 622 []cty.Value{ 623 simpleMap, 624 cty.StringVal("foo"), 625 }, 626 cty.StringVal("bar"), 627 false, 628 }, 629 { 630 []cty.Value{ 631 mapWithObjects, 632 cty.StringVal("foo"), 633 }, 634 cty.StringVal("bar"), 635 false, 636 }, 637 { 638 []cty.Value{ 639 intsMap, 640 cty.StringVal("foo"), 641 }, 642 cty.NumberIntVal(42), 643 false, 644 }, 645 { 646 []cty.Value{ 647 mapOfMaps, 648 cty.StringVal("foo"), 649 }, 650 cty.MapVal(map[string]cty.Value{ 651 "a": cty.StringVal("bar"), 652 }), 653 false, 654 }, 655 { 656 []cty.Value{ 657 objectOfMaps, 658 cty.StringVal("foo"), 659 }, 660 cty.MapVal(map[string]cty.Value{ 661 "a": cty.StringVal("bar"), 662 }), 663 false, 664 }, 665 { 666 []cty.Value{ 667 mapOfTuples, 668 cty.StringVal("foo"), 669 }, 670 cty.TupleVal([]cty.Value{cty.StringVal("bar")}), 671 false, 672 }, 673 { // Invalid key 674 []cty.Value{ 675 simpleMap, 676 cty.StringVal("bar"), 677 }, 678 cty.NilVal, 679 true, 680 }, 681 { // Invalid key 682 []cty.Value{ 683 mapWithObjects, 684 cty.StringVal("bar"), 685 }, 686 cty.NilVal, 687 true, 688 }, 689 { // Supplied default with valid key 690 []cty.Value{ 691 simpleMap, 692 cty.StringVal("foo"), 693 cty.StringVal(""), 694 }, 695 cty.StringVal("bar"), 696 false, 697 }, 698 { // Supplied default with valid (int) key 699 []cty.Value{ 700 simpleMap, 701 cty.StringVal("foo"), 702 cty.NumberIntVal(-1), 703 }, 704 cty.StringVal("bar"), 705 false, 706 }, 707 { // Supplied default with valid (int) key 708 []cty.Value{ 709 simpleMap, 710 cty.StringVal("foobar"), 711 cty.NumberIntVal(-1), 712 }, 713 cty.StringVal("-1"), 714 false, 715 }, 716 { // Supplied default with valid key 717 []cty.Value{ 718 mapWithObjects, 719 cty.StringVal("foobar"), 720 cty.StringVal(""), 721 }, 722 cty.StringVal(""), 723 false, 724 }, 725 { // Supplied default with invalid key 726 []cty.Value{ 727 simpleMap, 728 cty.StringVal("baz"), 729 cty.StringVal(""), 730 }, 731 cty.StringVal(""), 732 false, 733 }, 734 { // Supplied default with type mismatch: expects a map return 735 []cty.Value{ 736 mapOfMaps, 737 cty.StringVal("foo"), 738 cty.StringVal(""), 739 }, 740 cty.NilVal, 741 true, 742 }, 743 { // Supplied non-empty default with invalid key 744 []cty.Value{ 745 simpleMap, 746 cty.StringVal("bar"), 747 cty.StringVal("xyz"), 748 }, 749 cty.StringVal("xyz"), 750 false, 751 }, 752 { // too many args 753 []cty.Value{ 754 simpleMap, 755 cty.StringVal("foo"), 756 cty.StringVal("bar"), 757 cty.StringVal("baz"), 758 }, 759 cty.NilVal, 760 true, 761 }, 762 { // cannot search a map of lists 763 []cty.Value{ 764 mapOfLists, 765 cty.StringVal("baz"), 766 }, 767 cty.NilVal, 768 true, 769 }, 770 { 771 []cty.Value{ 772 mapWithUnknowns, 773 cty.StringVal("baz"), 774 }, 775 cty.UnknownVal(cty.String), 776 false, 777 }, 778 { 779 []cty.Value{ 780 mapWithUnknowns, 781 cty.StringVal("foo"), 782 }, 783 cty.StringVal("bar"), 784 false, 785 }, 786 { 787 []cty.Value{ 788 simpleMap, 789 cty.UnknownVal(cty.String), 790 }, 791 cty.UnknownVal(cty.String), 792 false, 793 }, 794 { 795 []cty.Value{ 796 cty.ObjectVal(map[string]cty.Value{ 797 "foo": cty.StringVal("a"), 798 "bar": cty.StringVal("b"), 799 }), 800 cty.UnknownVal(cty.String), 801 }, 802 cty.DynamicVal, // if the key is unknown then we don't know which object attribute and thus can't know the type 803 false, 804 }, 805 { // successful marked collection lookup returns marked value 806 []cty.Value{ 807 cty.MapVal(map[string]cty.Value{ 808 "boop": cty.StringVal("beep"), 809 }).Mark("a"), 810 cty.StringVal("boop"), 811 cty.StringVal("nope"), 812 }, 813 cty.StringVal("beep").Mark("a"), 814 false, 815 }, 816 { // apply collection marks to unknown return vaue 817 []cty.Value{ 818 cty.MapVal(map[string]cty.Value{ 819 "boop": cty.StringVal("beep"), 820 "frob": cty.UnknownVal(cty.String), 821 }).Mark("a"), 822 cty.StringVal("frob"), 823 cty.StringVal("nope"), 824 }, 825 cty.UnknownVal(cty.String).Mark("a"), 826 false, 827 }, 828 { // propagate collection marks to default when returning 829 []cty.Value{ 830 cty.MapVal(map[string]cty.Value{ 831 "boop": cty.StringVal("beep"), 832 }).Mark("a"), 833 cty.StringVal("frob"), 834 cty.StringVal("nope").Mark("b"), 835 }, 836 cty.StringVal("nope").WithMarks(cty.NewValueMarks("a", "b")), 837 false, 838 }, 839 { // on unmarked collection, return only marks from found value 840 []cty.Value{ 841 cty.MapVal(map[string]cty.Value{ 842 "boop": cty.StringVal("beep").Mark("a"), 843 "frob": cty.StringVal("honk").Mark("b"), 844 }), 845 cty.StringVal("frob"), 846 cty.StringVal("nope").Mark("c"), 847 }, 848 cty.StringVal("honk").Mark("b"), 849 false, 850 }, 851 { // on unmarked collection, return default exactly on missing 852 []cty.Value{ 853 cty.MapVal(map[string]cty.Value{ 854 "boop": cty.StringVal("beep").Mark("a"), 855 "frob": cty.StringVal("honk").Mark("b"), 856 }), 857 cty.StringVal("squish"), 858 cty.StringVal("nope").Mark("c"), 859 }, 860 cty.StringVal("nope").Mark("c"), 861 false, 862 }, 863 { // retain marks on default if converted 864 []cty.Value{ 865 cty.MapVal(map[string]cty.Value{ 866 "boop": cty.StringVal("beep").Mark("a"), 867 "frob": cty.StringVal("honk").Mark("b"), 868 }), 869 cty.StringVal("squish"), 870 cty.NumberIntVal(5).Mark("c"), 871 }, 872 cty.StringVal("5").Mark("c"), 873 false, 874 }, 875 { // propagate marks from key 876 []cty.Value{ 877 cty.MapVal(map[string]cty.Value{ 878 "boop": cty.StringVal("beep"), 879 "frob": cty.StringVal("honk"), 880 }), 881 cty.StringVal("boop").Mark("a"), 882 cty.StringVal("nope"), 883 }, 884 cty.StringVal("beep").Mark("a"), 885 false, 886 }, 887 } 888 889 for _, test := range tests { 890 t.Run(fmt.Sprintf("lookup(%#v)", test.Values), func(t *testing.T) { 891 got, err := Lookup(test.Values...) 892 893 if test.Err { 894 if err == nil { 895 t.Fatal("succeeded; want error") 896 } 897 return 898 } else if err != nil { 899 t.Fatalf("unexpected error: %s", err) 900 } 901 902 if !got.RawEquals(test.Want) { 903 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 904 } 905 }) 906 } 907 } 908 909 func TestLookup_error(t *testing.T) { 910 simpleMap := cty.MapVal(map[string]cty.Value{ 911 "foo": cty.StringVal("bar"), 912 }) 913 914 tests := map[string]struct { 915 Values []cty.Value 916 WantErr string 917 }{ 918 "failed to find non-sensitive key": { 919 []cty.Value{ 920 simpleMap, 921 cty.StringVal("boop"), 922 }, 923 `lookup failed to find key "boop"`, 924 }, 925 "failed to find sensitive key": { 926 []cty.Value{ 927 simpleMap, 928 cty.StringVal("boop").Mark(marks.Sensitive), 929 }, 930 "lookup failed to find key (sensitive value)", 931 }, 932 } 933 934 for name, test := range tests { 935 t.Run(name, func(t *testing.T) { 936 _, err := Lookup(test.Values...) 937 938 if err == nil { 939 t.Fatal("succeeded; want error") 940 } 941 942 if err.Error() != test.WantErr { 943 t.Errorf("wrong error\ngot: %#v\nwant: %#v", err, test.WantErr) 944 } 945 }) 946 } 947 } 948 949 func TestMatchkeys(t *testing.T) { 950 tests := []struct { 951 Keys cty.Value 952 Values cty.Value 953 Searchset cty.Value 954 Want cty.Value 955 Err bool 956 }{ 957 { // normal usage 958 cty.ListVal([]cty.Value{ 959 cty.StringVal("a"), 960 cty.StringVal("b"), 961 cty.StringVal("c"), 962 }), 963 cty.ListVal([]cty.Value{ 964 cty.StringVal("ref1"), 965 cty.StringVal("ref2"), 966 cty.StringVal("ref3"), 967 }), 968 cty.ListVal([]cty.Value{ 969 cty.StringVal("ref1"), 970 }), 971 cty.ListVal([]cty.Value{ 972 cty.StringVal("a"), 973 }), 974 false, 975 }, 976 { // normal usage 2, check the order 977 cty.ListVal([]cty.Value{ 978 cty.StringVal("a"), 979 cty.StringVal("b"), 980 cty.StringVal("c"), 981 }), 982 cty.ListVal([]cty.Value{ 983 cty.StringVal("ref1"), 984 cty.StringVal("ref2"), 985 cty.StringVal("ref3"), 986 }), 987 cty.ListVal([]cty.Value{ 988 cty.StringVal("ref2"), 989 cty.StringVal("ref1"), 990 }), 991 cty.ListVal([]cty.Value{ 992 cty.StringVal("a"), 993 cty.StringVal("b"), 994 }), 995 false, 996 }, 997 { // no matches 998 cty.ListVal([]cty.Value{ 999 cty.StringVal("a"), 1000 cty.StringVal("b"), 1001 cty.StringVal("c"), 1002 }), 1003 cty.ListVal([]cty.Value{ 1004 cty.StringVal("ref1"), 1005 cty.StringVal("ref2"), 1006 cty.StringVal("ref3"), 1007 }), 1008 cty.ListVal([]cty.Value{ 1009 cty.StringVal("ref4"), 1010 }), 1011 cty.ListValEmpty(cty.String), 1012 false, 1013 }, 1014 { // no matches 2 1015 cty.ListVal([]cty.Value{ 1016 cty.StringVal("a"), 1017 cty.StringVal("b"), 1018 cty.StringVal("c"), 1019 }), 1020 cty.ListVal([]cty.Value{ 1021 cty.StringVal("ref1"), 1022 cty.StringVal("ref2"), 1023 cty.StringVal("ref3"), 1024 }), 1025 cty.ListValEmpty(cty.String), 1026 cty.ListValEmpty(cty.String), 1027 false, 1028 }, 1029 { // zero case 1030 cty.ListValEmpty(cty.String), 1031 cty.ListValEmpty(cty.String), 1032 cty.ListVal([]cty.Value{cty.StringVal("nope")}), 1033 cty.ListValEmpty(cty.String), 1034 false, 1035 }, 1036 { // complex values 1037 cty.ListVal([]cty.Value{ 1038 cty.ListVal([]cty.Value{ 1039 cty.StringVal("a"), 1040 cty.StringVal("a"), 1041 }), 1042 }), 1043 cty.ListVal([]cty.Value{ 1044 cty.StringVal("a"), 1045 }), 1046 cty.ListVal([]cty.Value{ 1047 cty.StringVal("a"), 1048 }), 1049 cty.ListVal([]cty.Value{ 1050 cty.ListVal([]cty.Value{ 1051 cty.StringVal("a"), 1052 cty.StringVal("a"), 1053 }), 1054 }), 1055 false, 1056 }, 1057 { // unknowns 1058 cty.ListVal([]cty.Value{ 1059 cty.StringVal("a"), 1060 cty.StringVal("b"), 1061 cty.UnknownVal(cty.String), 1062 }), 1063 cty.ListVal([]cty.Value{ 1064 cty.StringVal("ref1"), 1065 cty.StringVal("ref2"), 1066 cty.UnknownVal(cty.String), 1067 }), 1068 cty.ListVal([]cty.Value{ 1069 cty.StringVal("ref1"), 1070 }), 1071 cty.UnknownVal(cty.List(cty.String)).RefineNotNull(), 1072 false, 1073 }, 1074 { // different types that can be unified 1075 cty.ListVal([]cty.Value{ 1076 cty.StringVal("a"), 1077 }), 1078 cty.ListVal([]cty.Value{ 1079 cty.NumberIntVal(1), 1080 }), 1081 cty.ListVal([]cty.Value{ 1082 cty.StringVal("a"), 1083 }), 1084 cty.ListValEmpty(cty.String), 1085 false, 1086 }, 1087 { // complex values: values is a different type from keys and searchset 1088 cty.ListVal([]cty.Value{ 1089 cty.MapVal(map[string]cty.Value{ 1090 "foo": cty.StringVal("bar"), 1091 }), 1092 cty.MapVal(map[string]cty.Value{ 1093 "foo": cty.StringVal("baz"), 1094 }), 1095 cty.MapVal(map[string]cty.Value{ 1096 "foo": cty.StringVal("beep"), 1097 }), 1098 }), 1099 cty.ListVal([]cty.Value{ 1100 cty.StringVal("a"), 1101 cty.StringVal("b"), 1102 cty.StringVal("c"), 1103 }), 1104 cty.ListVal([]cty.Value{ 1105 cty.StringVal("a"), 1106 cty.StringVal("c"), 1107 }), 1108 cty.ListVal([]cty.Value{ 1109 cty.MapVal(map[string]cty.Value{ 1110 "foo": cty.StringVal("bar"), 1111 }), 1112 cty.MapVal(map[string]cty.Value{ 1113 "foo": cty.StringVal("beep"), 1114 }), 1115 }), 1116 false, 1117 }, 1118 // errors 1119 { // different types 1120 cty.ListVal([]cty.Value{ 1121 cty.StringVal("a"), 1122 }), 1123 cty.ListVal([]cty.Value{ 1124 cty.ListVal([]cty.Value{ 1125 cty.StringVal("a"), 1126 }), 1127 cty.ListVal([]cty.Value{ 1128 cty.StringVal("a"), 1129 }), 1130 }), 1131 cty.ListVal([]cty.Value{ 1132 cty.StringVal("a"), 1133 }), 1134 cty.NilVal, 1135 true, 1136 }, 1137 { // lists of different length 1138 cty.ListVal([]cty.Value{ 1139 cty.StringVal("a"), 1140 }), 1141 cty.ListVal([]cty.Value{ 1142 cty.StringVal("a"), 1143 cty.StringVal("b"), 1144 }), 1145 cty.ListVal([]cty.Value{ 1146 cty.StringVal("a"), 1147 }), 1148 cty.NilVal, 1149 true, 1150 }, 1151 } 1152 1153 for _, test := range tests { 1154 t.Run(fmt.Sprintf("matchkeys(%#v, %#v, %#v)", test.Keys, test.Values, test.Searchset), func(t *testing.T) { 1155 got, err := Matchkeys(test.Keys, test.Values, test.Searchset) 1156 1157 if test.Err { 1158 if err == nil { 1159 t.Fatal("succeeded; want error") 1160 } 1161 return 1162 } else if err != nil { 1163 t.Fatalf("unexpected error: %s", err) 1164 } 1165 1166 if !got.RawEquals(test.Want) { 1167 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 1168 } 1169 }) 1170 } 1171 } 1172 1173 func TestOne(t *testing.T) { 1174 tests := []struct { 1175 List cty.Value 1176 Want cty.Value 1177 Err string 1178 }{ 1179 { 1180 cty.ListVal([]cty.Value{ 1181 cty.NumberIntVal(1), 1182 }), 1183 cty.NumberIntVal(1), 1184 "", 1185 }, 1186 { 1187 cty.ListValEmpty(cty.Number), 1188 cty.NullVal(cty.Number), 1189 "", 1190 }, 1191 { 1192 cty.ListVal([]cty.Value{ 1193 cty.NumberIntVal(1), 1194 cty.NumberIntVal(2), 1195 cty.NumberIntVal(3), 1196 }), 1197 cty.NilVal, 1198 "must be a list, set, or tuple value with either zero or one elements", 1199 }, 1200 { 1201 cty.ListVal([]cty.Value{ 1202 cty.UnknownVal(cty.Number), 1203 }), 1204 cty.UnknownVal(cty.Number), 1205 "", 1206 }, 1207 { 1208 cty.ListVal([]cty.Value{ 1209 cty.UnknownVal(cty.Number), 1210 cty.UnknownVal(cty.Number), 1211 }), 1212 cty.NilVal, 1213 "must be a list, set, or tuple value with either zero or one elements", 1214 }, 1215 { 1216 cty.UnknownVal(cty.List(cty.String)), 1217 cty.UnknownVal(cty.String), 1218 "", 1219 }, 1220 { 1221 cty.NullVal(cty.List(cty.String)), 1222 cty.NilVal, 1223 "argument must not be null", 1224 }, 1225 { 1226 cty.ListVal([]cty.Value{ 1227 cty.NumberIntVal(1), 1228 }).Mark("boop"), 1229 cty.NumberIntVal(1).Mark("boop"), 1230 "", 1231 }, 1232 { 1233 cty.ListValEmpty(cty.Bool).Mark("boop"), 1234 cty.NullVal(cty.Bool).Mark("boop"), 1235 "", 1236 }, 1237 { 1238 cty.ListVal([]cty.Value{ 1239 cty.NumberIntVal(1).Mark("boop"), 1240 }), 1241 cty.NumberIntVal(1).Mark("boop"), 1242 "", 1243 }, 1244 1245 { 1246 cty.SetVal([]cty.Value{ 1247 cty.NumberIntVal(1), 1248 }), 1249 cty.NumberIntVal(1), 1250 "", 1251 }, 1252 { 1253 cty.SetValEmpty(cty.Number), 1254 cty.NullVal(cty.Number), 1255 "", 1256 }, 1257 { 1258 cty.SetVal([]cty.Value{ 1259 cty.NumberIntVal(1), 1260 cty.NumberIntVal(2), 1261 cty.NumberIntVal(3), 1262 }), 1263 cty.NilVal, 1264 "must be a list, set, or tuple value with either zero or one elements", 1265 }, 1266 { 1267 cty.SetVal([]cty.Value{ 1268 cty.UnknownVal(cty.Number), 1269 }), 1270 cty.UnknownVal(cty.Number), 1271 "", 1272 }, 1273 { 1274 cty.SetVal([]cty.Value{ 1275 cty.UnknownVal(cty.Number), 1276 cty.UnknownVal(cty.Number), 1277 }), 1278 // The above would be valid if those two unknown values were 1279 // equal known values, so this returns unknown rather than failing. 1280 cty.UnknownVal(cty.Number), 1281 "", 1282 }, 1283 { 1284 cty.UnknownVal(cty.Set(cty.String)), 1285 cty.UnknownVal(cty.String), 1286 "", 1287 }, 1288 { 1289 cty.NullVal(cty.Set(cty.String)), 1290 cty.NilVal, 1291 "argument must not be null", 1292 }, 1293 { 1294 cty.SetVal([]cty.Value{ 1295 cty.NumberIntVal(1), 1296 }).Mark("boop"), 1297 cty.NumberIntVal(1).Mark("boop"), 1298 "", 1299 }, 1300 { 1301 cty.SetValEmpty(cty.Bool).Mark("boop"), 1302 cty.NullVal(cty.Bool).Mark("boop"), 1303 "", 1304 }, 1305 { 1306 cty.SetVal([]cty.Value{ 1307 cty.NumberIntVal(1).Mark("boop"), 1308 }), 1309 cty.NumberIntVal(1).Mark("boop"), 1310 "", 1311 }, 1312 1313 { 1314 cty.TupleVal([]cty.Value{ 1315 cty.NumberIntVal(1), 1316 }), 1317 cty.NumberIntVal(1), 1318 "", 1319 }, 1320 { 1321 cty.EmptyTupleVal, 1322 cty.NullVal(cty.DynamicPseudoType), 1323 "", 1324 }, 1325 { 1326 cty.TupleVal([]cty.Value{ 1327 cty.NumberIntVal(1), 1328 cty.NumberIntVal(2), 1329 cty.NumberIntVal(3), 1330 }), 1331 cty.NilVal, 1332 "must be a list, set, or tuple value with either zero or one elements", 1333 }, 1334 { 1335 cty.TupleVal([]cty.Value{ 1336 cty.UnknownVal(cty.Number), 1337 }), 1338 cty.UnknownVal(cty.Number), 1339 "", 1340 }, 1341 { 1342 cty.TupleVal([]cty.Value{ 1343 cty.UnknownVal(cty.Number), 1344 cty.UnknownVal(cty.Number), 1345 }), 1346 cty.NilVal, 1347 "must be a list, set, or tuple value with either zero or one elements", 1348 }, 1349 { 1350 cty.UnknownVal(cty.EmptyTuple), 1351 // Could actually return null here, but don't for consistency with unknown lists 1352 cty.UnknownVal(cty.DynamicPseudoType), 1353 "", 1354 }, 1355 { 1356 cty.UnknownVal(cty.Tuple([]cty.Type{cty.Bool})), 1357 cty.UnknownVal(cty.Bool), 1358 "", 1359 }, 1360 { 1361 cty.UnknownVal(cty.Tuple([]cty.Type{cty.Bool, cty.Number})), 1362 cty.NilVal, 1363 "must be a list, set, or tuple value with either zero or one elements", 1364 }, 1365 { 1366 cty.NullVal(cty.EmptyTuple), 1367 cty.NilVal, 1368 "argument must not be null", 1369 }, 1370 { 1371 cty.NullVal(cty.Tuple([]cty.Type{cty.Bool})), 1372 cty.NilVal, 1373 "argument must not be null", 1374 }, 1375 { 1376 cty.NullVal(cty.Tuple([]cty.Type{cty.Bool, cty.Number})), 1377 cty.NilVal, 1378 "argument must not be null", 1379 }, 1380 { 1381 cty.TupleVal([]cty.Value{ 1382 cty.NumberIntVal(1), 1383 }).Mark("boop"), 1384 cty.NumberIntVal(1).Mark("boop"), 1385 "", 1386 }, 1387 { 1388 cty.EmptyTupleVal.Mark("boop"), 1389 cty.NullVal(cty.DynamicPseudoType).Mark("boop"), 1390 "", 1391 }, 1392 { 1393 cty.TupleVal([]cty.Value{ 1394 cty.NumberIntVal(1).Mark("boop"), 1395 }), 1396 cty.NumberIntVal(1).Mark("boop"), 1397 "", 1398 }, 1399 1400 { 1401 cty.DynamicVal, 1402 cty.DynamicVal, 1403 "", 1404 }, 1405 { 1406 cty.NullVal(cty.DynamicPseudoType), 1407 cty.NilVal, 1408 "argument must not be null", 1409 }, 1410 { 1411 cty.MapValEmpty(cty.String), 1412 cty.NilVal, 1413 "must be a list, set, or tuple value with either zero or one elements", 1414 }, 1415 { 1416 cty.EmptyObjectVal, 1417 cty.NilVal, 1418 "must be a list, set, or tuple value with either zero or one elements", 1419 }, 1420 { 1421 cty.True, 1422 cty.NilVal, 1423 "must be a list, set, or tuple value with either zero or one elements", 1424 }, 1425 { 1426 cty.UnknownVal(cty.Bool), 1427 cty.NilVal, 1428 "must be a list, set, or tuple value with either zero or one elements", 1429 }, 1430 } 1431 1432 for _, test := range tests { 1433 t.Run(fmt.Sprintf("one(%#v)", test.List), func(t *testing.T) { 1434 got, err := One(test.List) 1435 1436 if test.Err != "" { 1437 if err == nil { 1438 t.Fatal("succeeded; want error") 1439 } else if got, want := err.Error(), test.Err; got != want { 1440 t.Fatalf("wrong error\n got: %s\nwant: %s", got, want) 1441 } 1442 return 1443 } else if err != nil { 1444 t.Fatalf("unexpected error: %s", err) 1445 } 1446 1447 if !test.Want.RawEquals(got) { 1448 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 1449 } 1450 }) 1451 } 1452 } 1453 1454 func TestSum(t *testing.T) { 1455 tests := []struct { 1456 List cty.Value 1457 Want cty.Value 1458 Err string 1459 }{ 1460 { 1461 cty.ListVal([]cty.Value{ 1462 cty.NumberIntVal(1), 1463 cty.NumberIntVal(2), 1464 cty.NumberIntVal(3), 1465 }), 1466 cty.NumberIntVal(6), 1467 "", 1468 }, 1469 { 1470 cty.ListVal([]cty.Value{ 1471 cty.NumberIntVal(1476), 1472 cty.NumberIntVal(2093), 1473 cty.NumberIntVal(2092495), 1474 cty.NumberIntVal(64589234), 1475 cty.NumberIntVal(234), 1476 }), 1477 cty.NumberIntVal(66685532), 1478 "", 1479 }, 1480 { 1481 cty.ListVal([]cty.Value{ 1482 cty.StringVal("a"), 1483 cty.StringVal("b"), 1484 cty.StringVal("c"), 1485 }), 1486 cty.UnknownVal(cty.String), 1487 "argument must be list, set, or tuple of number values", 1488 }, 1489 { 1490 cty.ListVal([]cty.Value{ 1491 cty.NumberIntVal(10), 1492 cty.NumberIntVal(-19), 1493 cty.NumberIntVal(5), 1494 }), 1495 cty.NumberIntVal(-4), 1496 "", 1497 }, 1498 { 1499 cty.ListVal([]cty.Value{ 1500 cty.NumberFloatVal(10.2), 1501 cty.NumberFloatVal(19.4), 1502 cty.NumberFloatVal(5.7), 1503 }), 1504 cty.NumberFloatVal(35.3), 1505 "", 1506 }, 1507 { 1508 cty.ListVal([]cty.Value{ 1509 cty.NumberFloatVal(-10.2), 1510 cty.NumberFloatVal(-19.4), 1511 cty.NumberFloatVal(-5.7), 1512 }), 1513 cty.NumberFloatVal(-35.3), 1514 "", 1515 }, 1516 { 1517 cty.ListVal([]cty.Value{cty.NullVal(cty.Number)}), 1518 cty.NilVal, 1519 "argument must be list, set, or tuple of number values", 1520 }, 1521 { 1522 cty.ListVal([]cty.Value{ 1523 cty.NumberIntVal(5), 1524 cty.NullVal(cty.Number), 1525 }), 1526 cty.NilVal, 1527 "argument must be list, set, or tuple of number values", 1528 }, 1529 { 1530 cty.SetVal([]cty.Value{ 1531 cty.StringVal("a"), 1532 cty.StringVal("b"), 1533 cty.StringVal("c"), 1534 }), 1535 cty.UnknownVal(cty.String).RefineNotNull(), 1536 "argument must be list, set, or tuple of number values", 1537 }, 1538 { 1539 cty.SetVal([]cty.Value{ 1540 cty.NumberIntVal(10), 1541 cty.NumberIntVal(-19), 1542 cty.NumberIntVal(5), 1543 }), 1544 cty.NumberIntVal(-4), 1545 "", 1546 }, 1547 { 1548 cty.SetVal([]cty.Value{ 1549 cty.NumberIntVal(10), 1550 cty.NumberIntVal(25), 1551 cty.NumberIntVal(30), 1552 }), 1553 cty.NumberIntVal(65), 1554 "", 1555 }, 1556 { 1557 cty.SetVal([]cty.Value{ 1558 cty.NumberFloatVal(2340.8), 1559 cty.NumberFloatVal(10.2), 1560 cty.NumberFloatVal(3), 1561 }), 1562 cty.NumberFloatVal(2354), 1563 "", 1564 }, 1565 { 1566 cty.SetVal([]cty.Value{ 1567 cty.NumberFloatVal(2), 1568 }), 1569 cty.NumberFloatVal(2), 1570 "", 1571 }, 1572 { 1573 cty.SetVal([]cty.Value{ 1574 cty.NumberFloatVal(-2), 1575 cty.NumberFloatVal(-50), 1576 cty.NumberFloatVal(-20), 1577 cty.NumberFloatVal(-123), 1578 cty.NumberFloatVal(-4), 1579 }), 1580 cty.NumberFloatVal(-199), 1581 "", 1582 }, 1583 { 1584 cty.TupleVal([]cty.Value{ 1585 cty.NumberIntVal(12), 1586 cty.StringVal("a"), 1587 cty.NumberIntVal(38), 1588 }), 1589 cty.UnknownVal(cty.String).RefineNotNull(), 1590 "argument must be list, set, or tuple of number values", 1591 }, 1592 { 1593 cty.NumberIntVal(12), 1594 cty.NilVal, 1595 "cannot sum noniterable", 1596 }, 1597 { 1598 cty.ListValEmpty(cty.Number), 1599 cty.NilVal, 1600 "cannot sum an empty list", 1601 }, 1602 { 1603 cty.MapVal(map[string]cty.Value{"hello": cty.True}), 1604 cty.NilVal, 1605 "argument must be list, set, or tuple. Received map of bool", 1606 }, 1607 { 1608 cty.UnknownVal(cty.Number), 1609 cty.UnknownVal(cty.Number).RefineNotNull(), 1610 "", 1611 }, 1612 { 1613 cty.UnknownVal(cty.List(cty.Number)), 1614 cty.UnknownVal(cty.Number).RefineNotNull(), 1615 "", 1616 }, 1617 { // known list containing unknown values 1618 cty.ListVal([]cty.Value{cty.UnknownVal(cty.Number)}), 1619 cty.UnknownVal(cty.Number).RefineNotNull(), 1620 "", 1621 }, 1622 { // numbers too large to represent as float64 1623 cty.ListVal([]cty.Value{ 1624 cty.MustParseNumberVal("1e+500"), 1625 cty.MustParseNumberVal("1e+500"), 1626 }), 1627 cty.MustParseNumberVal("2e+500"), 1628 "", 1629 }, 1630 { // edge case we have a special error handler for 1631 cty.ListVal([]cty.Value{ 1632 cty.NumberFloatVal(math.Inf(1)), 1633 cty.NumberFloatVal(math.Inf(-1)), 1634 }), 1635 cty.NilVal, 1636 "can't compute sum of opposing infinities", 1637 }, 1638 { 1639 cty.ListVal([]cty.Value{ 1640 cty.StringVal("1"), 1641 cty.StringVal("2"), 1642 cty.StringVal("3"), 1643 }), 1644 cty.NumberIntVal(6), 1645 "", 1646 }, 1647 } 1648 1649 for _, test := range tests { 1650 t.Run(fmt.Sprintf("sum(%#v)", test.List), func(t *testing.T) { 1651 got, err := Sum(test.List) 1652 1653 if test.Err != "" { 1654 if err == nil { 1655 t.Fatal("succeeded; want error") 1656 } else if got, want := err.Error(), test.Err; got != want { 1657 t.Fatalf("wrong error\n got: %s\nwant: %s", got, want) 1658 } 1659 return 1660 } else if err != nil { 1661 t.Fatalf("unexpected error: %s", err) 1662 } 1663 1664 if !got.RawEquals(test.Want) { 1665 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 1666 } 1667 }) 1668 } 1669 } 1670 1671 func TestTranspose(t *testing.T) { 1672 tests := []struct { 1673 Values cty.Value 1674 Want cty.Value 1675 Err bool 1676 }{ 1677 { 1678 cty.MapVal(map[string]cty.Value{ 1679 "key1": cty.ListVal([]cty.Value{ 1680 cty.StringVal("a"), 1681 cty.StringVal("b"), 1682 }), 1683 "key2": cty.ListVal([]cty.Value{ 1684 cty.StringVal("a"), 1685 cty.StringVal("b"), 1686 cty.StringVal("c"), 1687 }), 1688 "key3": cty.ListVal([]cty.Value{ 1689 cty.StringVal("c"), 1690 }), 1691 "key4": cty.ListValEmpty(cty.String), 1692 }), 1693 cty.MapVal(map[string]cty.Value{ 1694 "a": cty.ListVal([]cty.Value{ 1695 cty.StringVal("key1"), 1696 cty.StringVal("key2"), 1697 }), 1698 "b": cty.ListVal([]cty.Value{ 1699 cty.StringVal("key1"), 1700 cty.StringVal("key2"), 1701 }), 1702 "c": cty.ListVal([]cty.Value{ 1703 cty.StringVal("key2"), 1704 cty.StringVal("key3"), 1705 }), 1706 }), 1707 false, 1708 }, 1709 { // map - unknown value 1710 cty.MapVal(map[string]cty.Value{ 1711 "key1": cty.UnknownVal(cty.List(cty.String)), 1712 }), 1713 cty.UnknownVal(cty.Map(cty.List(cty.String))).RefineNotNull(), 1714 false, 1715 }, 1716 { // bad map - empty value 1717 cty.MapVal(map[string]cty.Value{ 1718 "key1": cty.ListValEmpty(cty.String), 1719 }), 1720 cty.MapValEmpty(cty.List(cty.String)), 1721 false, 1722 }, 1723 { // bad map - value not a list 1724 cty.MapVal(map[string]cty.Value{ 1725 "key1": cty.StringVal("a"), 1726 }), 1727 cty.NilVal, 1728 true, 1729 }, 1730 { // marks (deep or shallow) on any elements will propegate to the entire return value 1731 cty.MapVal(map[string]cty.Value{ 1732 "key1": cty.ListVal([]cty.Value{ 1733 cty.StringVal("a").Mark("beep"), // mark on the inner list element 1734 cty.StringVal("b"), 1735 }), 1736 "key2": cty.ListVal([]cty.Value{ 1737 cty.StringVal("a"), 1738 cty.StringVal("b"), 1739 cty.StringVal("c"), 1740 }).Mark("boop"), // mark on the map element 1741 "key3": cty.ListVal([]cty.Value{ 1742 cty.StringVal("c"), 1743 }), 1744 "key4": cty.ListValEmpty(cty.String), 1745 }), 1746 cty.MapVal(map[string]cty.Value{ 1747 "a": cty.ListVal([]cty.Value{ 1748 cty.StringVal("key1"), 1749 cty.StringVal("key2"), 1750 }), 1751 "b": cty.ListVal([]cty.Value{ 1752 cty.StringVal("key1"), 1753 cty.StringVal("key2"), 1754 }), 1755 "c": cty.ListVal([]cty.Value{ 1756 cty.StringVal("key2"), 1757 cty.StringVal("key3")}), 1758 }).WithMarks(cty.NewValueMarks("beep", "boop")), 1759 false, 1760 }, 1761 { // Marks on the input value will be applied to the return value 1762 cty.MapVal(map[string]cty.Value{ 1763 "key1": cty.ListVal([]cty.Value{ 1764 cty.StringVal("a"), 1765 cty.StringVal("b"), 1766 }), 1767 "key2": cty.ListVal([]cty.Value{ 1768 cty.StringVal("a"), 1769 cty.StringVal("b"), 1770 cty.StringVal("c"), 1771 }), 1772 "key3": cty.ListVal([]cty.Value{ 1773 cty.StringVal("c"), 1774 }), 1775 }).Mark("beep"), // mark on the entire input value 1776 cty.MapVal(map[string]cty.Value{ 1777 "a": cty.ListVal([]cty.Value{ 1778 cty.StringVal("key1"), 1779 cty.StringVal("key2"), 1780 }), 1781 "b": cty.ListVal([]cty.Value{ 1782 cty.StringVal("key1"), 1783 cty.StringVal("key2"), 1784 }), 1785 "c": cty.ListVal([]cty.Value{ 1786 cty.StringVal("key2"), 1787 cty.StringVal("key3"), 1788 }), 1789 }).Mark("beep"), 1790 false, 1791 }, 1792 { // Marks on the entire input value AND inner elements (deep or shallow) ALL apply to the return 1793 cty.MapVal(map[string]cty.Value{ 1794 "key1": cty.ListVal([]cty.Value{ 1795 cty.StringVal("a"), 1796 cty.StringVal("b"), 1797 }).Mark("beep"), // mark on the map element 1798 "key2": cty.ListVal([]cty.Value{ 1799 cty.StringVal("a"), 1800 cty.StringVal("b"), 1801 cty.StringVal("c"), 1802 }), 1803 "key3": cty.ListVal([]cty.Value{ 1804 cty.StringVal("c").Mark("boop"), // mark on the inner list element 1805 }), 1806 }).Mark("bloop"), // mark on the entire input value 1807 cty.MapVal(map[string]cty.Value{ 1808 "a": cty.ListVal([]cty.Value{ 1809 cty.StringVal("key1"), 1810 cty.StringVal("key2"), 1811 }), 1812 "b": cty.ListVal([]cty.Value{ 1813 cty.StringVal("key1"), 1814 cty.StringVal("key2"), 1815 }), 1816 "c": cty.ListVal([]cty.Value{ 1817 cty.StringVal("key2"), 1818 cty.StringVal("key3"), 1819 }), 1820 }).WithMarks(cty.NewValueMarks("beep", "boop", "bloop")), 1821 false, 1822 }, 1823 } 1824 1825 for _, test := range tests { 1826 t.Run(fmt.Sprintf("transpose(%#v)", test.Values), func(t *testing.T) { 1827 got, err := Transpose(test.Values) 1828 1829 if test.Err { 1830 if err == nil { 1831 t.Fatal("succeeded; want error") 1832 } 1833 return 1834 } else if err != nil { 1835 t.Fatalf("unexpected error: %s", err) 1836 } 1837 1838 if !got.RawEquals(test.Want) { 1839 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 1840 } 1841 }) 1842 } 1843 }