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