github.com/ibm-cloud/terraform@v0.6.4-0.20170726051544-8872b87621df/config/interpolate_funcs_test.go (about) 1 package config 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "reflect" 8 "testing" 9 "time" 10 11 "path/filepath" 12 13 "github.com/hashicorp/hil" 14 "github.com/hashicorp/hil/ast" 15 "github.com/mitchellh/go-homedir" 16 ) 17 18 func TestInterpolateFuncZipMap(t *testing.T) { 19 testFunction(t, testFunctionConfig{ 20 Cases: []testFunctionCase{ 21 { 22 `${zipmap(var.list, var.list2)}`, 23 map[string]interface{}{ 24 "Hello": "bar", 25 "World": "baz", 26 }, 27 false, 28 }, 29 { 30 `${zipmap(var.list, var.nonstrings)}`, 31 map[string]interface{}{ 32 "Hello": []interface{}{"bar", "baz"}, 33 "World": []interface{}{"boo", "foo"}, 34 }, 35 false, 36 }, 37 { 38 `${zipmap(var.nonstrings, var.list2)}`, 39 nil, 40 true, 41 }, 42 { 43 `${zipmap(var.list, var.differentlengthlist)}`, 44 nil, 45 true, 46 }, 47 }, 48 Vars: map[string]ast.Variable{ 49 "var.list": { 50 Type: ast.TypeList, 51 Value: []ast.Variable{ 52 { 53 Type: ast.TypeString, 54 Value: "Hello", 55 }, 56 { 57 Type: ast.TypeString, 58 Value: "World", 59 }, 60 }, 61 }, 62 "var.list2": { 63 Type: ast.TypeList, 64 Value: []ast.Variable{ 65 { 66 Type: ast.TypeString, 67 Value: "bar", 68 }, 69 { 70 Type: ast.TypeString, 71 Value: "baz", 72 }, 73 }, 74 }, 75 "var.differentlengthlist": { 76 Type: ast.TypeList, 77 Value: []ast.Variable{ 78 { 79 Type: ast.TypeString, 80 Value: "bar", 81 }, 82 { 83 Type: ast.TypeString, 84 Value: "baz", 85 }, 86 { 87 Type: ast.TypeString, 88 Value: "boo", 89 }, 90 }, 91 }, 92 "var.nonstrings": { 93 Type: ast.TypeList, 94 Value: []ast.Variable{ 95 { 96 Type: ast.TypeList, 97 Value: []ast.Variable{ 98 { 99 Type: ast.TypeString, 100 Value: "bar", 101 }, 102 { 103 Type: ast.TypeString, 104 Value: "baz", 105 }, 106 }, 107 }, 108 { 109 Type: ast.TypeList, 110 Value: []ast.Variable{ 111 { 112 Type: ast.TypeString, 113 Value: "boo", 114 }, 115 { 116 Type: ast.TypeString, 117 Value: "foo", 118 }, 119 }, 120 }, 121 }, 122 }, 123 }, 124 }) 125 } 126 127 func TestInterpolateFuncList(t *testing.T) { 128 testFunction(t, testFunctionConfig{ 129 Cases: []testFunctionCase{ 130 // empty input returns empty list 131 { 132 `${list()}`, 133 []interface{}{}, 134 false, 135 }, 136 137 // single input returns list of length 1 138 { 139 `${list("hello")}`, 140 []interface{}{"hello"}, 141 false, 142 }, 143 144 // two inputs returns list of length 2 145 { 146 `${list("hello", "world")}`, 147 []interface{}{"hello", "world"}, 148 false, 149 }, 150 151 // not a string input gives error 152 { 153 `${list("hello", 42)}`, 154 nil, 155 true, 156 }, 157 158 // list of lists 159 { 160 `${list("${var.list}", "${var.list2}")}`, 161 []interface{}{[]interface{}{"Hello", "World"}, []interface{}{"bar", "baz"}}, 162 false, 163 }, 164 165 // list of maps 166 { 167 `${list("${var.map}", "${var.map2}")}`, 168 []interface{}{map[string]interface{}{"key": "bar"}, map[string]interface{}{"key2": "baz"}}, 169 false, 170 }, 171 172 // error on a heterogeneous list 173 { 174 `${list("first", "${var.list}")}`, 175 nil, 176 true, 177 }, 178 }, 179 Vars: map[string]ast.Variable{ 180 "var.list": { 181 Type: ast.TypeList, 182 Value: []ast.Variable{ 183 { 184 Type: ast.TypeString, 185 Value: "Hello", 186 }, 187 { 188 Type: ast.TypeString, 189 Value: "World", 190 }, 191 }, 192 }, 193 "var.list2": { 194 Type: ast.TypeList, 195 Value: []ast.Variable{ 196 { 197 Type: ast.TypeString, 198 Value: "bar", 199 }, 200 { 201 Type: ast.TypeString, 202 Value: "baz", 203 }, 204 }, 205 }, 206 207 "var.map": { 208 Type: ast.TypeMap, 209 Value: map[string]ast.Variable{ 210 "key": { 211 Type: ast.TypeString, 212 Value: "bar", 213 }, 214 }, 215 }, 216 "var.map2": { 217 Type: ast.TypeMap, 218 Value: map[string]ast.Variable{ 219 "key2": { 220 Type: ast.TypeString, 221 Value: "baz", 222 }, 223 }, 224 }, 225 }, 226 }) 227 } 228 229 func TestInterpolateFuncMax(t *testing.T) { 230 testFunction(t, testFunctionConfig{ 231 Cases: []testFunctionCase{ 232 { 233 `${max()}`, 234 nil, 235 true, 236 }, 237 238 { 239 `${max("")}`, 240 nil, 241 true, 242 }, 243 244 { 245 `${max(-1, 0, 1)}`, 246 "1", 247 false, 248 }, 249 250 { 251 `${max(1, 0, -1)}`, 252 "1", 253 false, 254 }, 255 256 { 257 `${max(-1, -2)}`, 258 "-1", 259 false, 260 }, 261 262 { 263 `${max(-1)}`, 264 "-1", 265 false, 266 }, 267 }, 268 }) 269 } 270 271 func TestInterpolateFuncMin(t *testing.T) { 272 testFunction(t, testFunctionConfig{ 273 Cases: []testFunctionCase{ 274 { 275 `${min()}`, 276 nil, 277 true, 278 }, 279 280 { 281 `${min("")}`, 282 nil, 283 true, 284 }, 285 286 { 287 `${min(-1, 0, 1)}`, 288 "-1", 289 false, 290 }, 291 292 { 293 `${min(1, 0, -1)}`, 294 "-1", 295 false, 296 }, 297 298 { 299 `${min(-1, -2)}`, 300 "-2", 301 false, 302 }, 303 304 { 305 `${min(-1)}`, 306 "-1", 307 false, 308 }, 309 }, 310 }) 311 } 312 313 func TestInterpolateFuncFloor(t *testing.T) { 314 testFunction(t, testFunctionConfig{ 315 Cases: []testFunctionCase{ 316 { 317 `${floor()}`, 318 nil, 319 true, 320 }, 321 322 { 323 `${floor("")}`, 324 nil, 325 true, 326 }, 327 328 { 329 `${floor("-1.3")}`, // there appears to be a AST bug where the parsed argument ends up being -1 without the "s 330 "-2", 331 false, 332 }, 333 334 { 335 `${floor(1.7)}`, 336 "1", 337 false, 338 }, 339 }, 340 }) 341 } 342 343 func TestInterpolateFuncCeil(t *testing.T) { 344 testFunction(t, testFunctionConfig{ 345 Cases: []testFunctionCase{ 346 { 347 `${ceil()}`, 348 nil, 349 true, 350 }, 351 352 { 353 `${ceil("")}`, 354 nil, 355 true, 356 }, 357 358 { 359 `${ceil(-1.8)}`, 360 "-1", 361 false, 362 }, 363 364 { 365 `${ceil(1.2)}`, 366 "2", 367 false, 368 }, 369 }, 370 }) 371 } 372 373 func TestInterpolateFuncLog(t *testing.T) { 374 testFunction(t, testFunctionConfig{ 375 Cases: []testFunctionCase{ 376 { 377 `${log(1, 10)}`, 378 "0", 379 false, 380 }, 381 { 382 `${log(10, 10)}`, 383 "1", 384 false, 385 }, 386 387 { 388 `${log(0, 10)}`, 389 "-Inf", 390 false, 391 }, 392 { 393 `${log(10, 0)}`, 394 "-0", 395 false, 396 }, 397 }, 398 }) 399 } 400 401 func TestInterpolateFuncChomp(t *testing.T) { 402 testFunction(t, testFunctionConfig{ 403 Cases: []testFunctionCase{ 404 { 405 `${chomp()}`, 406 nil, 407 true, 408 }, 409 410 { 411 `${chomp("hello world")}`, 412 "hello world", 413 false, 414 }, 415 416 { 417 fmt.Sprintf(`${chomp("%s")}`, "goodbye\ncruel\nworld"), 418 "goodbye\ncruel\nworld", 419 false, 420 }, 421 422 { 423 fmt.Sprintf(`${chomp("%s")}`, "goodbye\r\nwindows\r\nworld"), 424 "goodbye\r\nwindows\r\nworld", 425 false, 426 }, 427 428 { 429 fmt.Sprintf(`${chomp("%s")}`, "goodbye\ncruel\nworld\n"), 430 "goodbye\ncruel\nworld", 431 false, 432 }, 433 434 { 435 fmt.Sprintf(`${chomp("%s")}`, "goodbye\ncruel\nworld\n\n\n\n"), 436 "goodbye\ncruel\nworld", 437 false, 438 }, 439 440 { 441 fmt.Sprintf(`${chomp("%s")}`, "goodbye\r\nwindows\r\nworld\r\n"), 442 "goodbye\r\nwindows\r\nworld", 443 false, 444 }, 445 446 { 447 fmt.Sprintf(`${chomp("%s")}`, "goodbye\r\nwindows\r\nworld\r\n\r\n\r\n\r\n"), 448 "goodbye\r\nwindows\r\nworld", 449 false, 450 }, 451 }, 452 }) 453 } 454 455 func TestInterpolateFuncMap(t *testing.T) { 456 testFunction(t, testFunctionConfig{ 457 Cases: []testFunctionCase{ 458 // empty input returns empty map 459 { 460 `${map()}`, 461 map[string]interface{}{}, 462 false, 463 }, 464 465 // odd args is error 466 { 467 `${map("odd")}`, 468 nil, 469 true, 470 }, 471 472 // two args returns map w/ one k/v 473 { 474 `${map("hello", "world")}`, 475 map[string]interface{}{"hello": "world"}, 476 false, 477 }, 478 479 // four args get two k/v 480 { 481 `${map("hello", "world", "what's", "up?")}`, 482 map[string]interface{}{"hello": "world", "what's": "up?"}, 483 false, 484 }, 485 486 // map of lists is okay 487 { 488 `${map("hello", list("world"), "what's", list("up?"))}`, 489 map[string]interface{}{ 490 "hello": []interface{}{"world"}, 491 "what's": []interface{}{"up?"}, 492 }, 493 false, 494 }, 495 496 // map of maps is okay 497 { 498 `${map("hello", map("there", "world"), "what's", map("really", "up?"))}`, 499 map[string]interface{}{ 500 "hello": map[string]interface{}{"there": "world"}, 501 "what's": map[string]interface{}{"really": "up?"}, 502 }, 503 false, 504 }, 505 506 // keys have to be strings 507 { 508 `${map(list("listkey"), "val")}`, 509 nil, 510 true, 511 }, 512 513 // types have to match 514 { 515 `${map("some", "strings", "also", list("lists"))}`, 516 nil, 517 true, 518 }, 519 520 // duplicate keys are an error 521 { 522 `${map("key", "val", "key", "again")}`, 523 nil, 524 true, 525 }, 526 }, 527 }) 528 } 529 530 func TestInterpolateFuncCompact(t *testing.T) { 531 testFunction(t, testFunctionConfig{ 532 Cases: []testFunctionCase{ 533 // empty string within array 534 { 535 `${compact(split(",", "a,,b"))}`, 536 []interface{}{"a", "b"}, 537 false, 538 }, 539 540 // empty string at the end of array 541 { 542 `${compact(split(",", "a,b,"))}`, 543 []interface{}{"a", "b"}, 544 false, 545 }, 546 547 // single empty string 548 { 549 `${compact(split(",", ""))}`, 550 []interface{}{}, 551 false, 552 }, 553 554 // errrors on list of lists 555 { 556 `${compact(list(list("a"), list("b")))}`, 557 nil, 558 true, 559 }, 560 }, 561 }) 562 } 563 564 func TestInterpolateFuncCidrHost(t *testing.T) { 565 testFunction(t, testFunctionConfig{ 566 Cases: []testFunctionCase{ 567 { 568 `${cidrhost("192.168.1.0/24", 5)}`, 569 "192.168.1.5", 570 false, 571 }, 572 { 573 `${cidrhost("192.168.1.0/24", -5)}`, 574 "192.168.1.251", 575 false, 576 }, 577 { 578 `${cidrhost("192.168.1.0/24", -256)}`, 579 "192.168.1.0", 580 false, 581 }, 582 { 583 `${cidrhost("192.168.1.0/30", 255)}`, 584 nil, 585 true, // 255 doesn't fit in two bits 586 }, 587 { 588 `${cidrhost("192.168.1.0/30", -255)}`, 589 nil, 590 true, // 255 doesn't fit in two bits 591 }, 592 { 593 `${cidrhost("not-a-cidr", 6)}`, 594 nil, 595 true, // not a valid CIDR mask 596 }, 597 { 598 `${cidrhost("10.256.0.0/8", 6)}`, 599 nil, 600 true, // can't have an octet >255 601 }, 602 }, 603 }) 604 } 605 606 func TestInterpolateFuncCidrNetmask(t *testing.T) { 607 testFunction(t, testFunctionConfig{ 608 Cases: []testFunctionCase{ 609 { 610 `${cidrnetmask("192.168.1.0/24")}`, 611 "255.255.255.0", 612 false, 613 }, 614 { 615 `${cidrnetmask("192.168.1.0/32")}`, 616 "255.255.255.255", 617 false, 618 }, 619 { 620 `${cidrnetmask("0.0.0.0/0")}`, 621 "0.0.0.0", 622 false, 623 }, 624 { 625 // This doesn't really make sense for IPv6 networks 626 // but it ought to do something sensible anyway. 627 `${cidrnetmask("1::/64")}`, 628 "ffff:ffff:ffff:ffff::", 629 false, 630 }, 631 { 632 `${cidrnetmask("not-a-cidr")}`, 633 nil, 634 true, // not a valid CIDR mask 635 }, 636 { 637 `${cidrnetmask("10.256.0.0/8")}`, 638 nil, 639 true, // can't have an octet >255 640 }, 641 }, 642 }) 643 } 644 645 func TestInterpolateFuncCidrSubnet(t *testing.T) { 646 testFunction(t, testFunctionConfig{ 647 Cases: []testFunctionCase{ 648 { 649 `${cidrsubnet("192.168.2.0/20", 4, 6)}`, 650 "192.168.6.0/24", 651 false, 652 }, 653 { 654 `${cidrsubnet("fe80::/48", 16, 6)}`, 655 "fe80:0:0:6::/64", 656 false, 657 }, 658 { 659 // IPv4 address encoded in IPv6 syntax gets normalized 660 `${cidrsubnet("::ffff:192.168.0.0/112", 8, 6)}`, 661 "192.168.6.0/24", 662 false, 663 }, 664 { 665 `${cidrsubnet("192.168.0.0/30", 4, 6)}`, 666 nil, 667 true, // not enough bits left 668 }, 669 { 670 `${cidrsubnet("192.168.0.0/16", 2, 16)}`, 671 nil, 672 true, // can't encode 16 in 2 bits 673 }, 674 { 675 `${cidrsubnet("not-a-cidr", 4, 6)}`, 676 nil, 677 true, // not a valid CIDR mask 678 }, 679 { 680 `${cidrsubnet("10.256.0.0/8", 4, 6)}`, 681 nil, 682 true, // can't have an octet >255 683 }, 684 }, 685 }) 686 } 687 688 func TestInterpolateFuncCoalesce(t *testing.T) { 689 testFunction(t, testFunctionConfig{ 690 Cases: []testFunctionCase{ 691 { 692 `${coalesce("first", "second", "third")}`, 693 "first", 694 false, 695 }, 696 { 697 `${coalesce("", "second", "third")}`, 698 "second", 699 false, 700 }, 701 { 702 `${coalesce("", "", "")}`, 703 "", 704 false, 705 }, 706 { 707 `${coalesce("foo")}`, 708 nil, 709 true, 710 }, 711 }, 712 }) 713 } 714 715 func TestInterpolateFuncCoalesceList(t *testing.T) { 716 testFunction(t, testFunctionConfig{ 717 Cases: []testFunctionCase{ 718 { 719 `${coalescelist(list("first"), list("second"), list("third"))}`, 720 []interface{}{"first"}, 721 false, 722 }, 723 { 724 `${coalescelist(list(), list("second"), list("third"))}`, 725 []interface{}{"second"}, 726 false, 727 }, 728 { 729 `${coalescelist(list(), list(), list())}`, 730 []interface{}{}, 731 false, 732 }, 733 { 734 `${coalescelist(list("foo"))}`, 735 nil, 736 true, 737 }, 738 }, 739 }) 740 } 741 742 func TestInterpolateFuncConcat(t *testing.T) { 743 testFunction(t, testFunctionConfig{ 744 Cases: []testFunctionCase{ 745 // String + list 746 // no longer supported, now returns an error 747 { 748 `${concat("a", split(",", "b,c"))}`, 749 nil, 750 true, 751 }, 752 753 // List + string 754 // no longer supported, now returns an error 755 { 756 `${concat(split(",", "a,b"), "c")}`, 757 nil, 758 true, 759 }, 760 761 // Single list 762 { 763 `${concat(split(",", ",foo,"))}`, 764 []interface{}{"", "foo", ""}, 765 false, 766 }, 767 { 768 `${concat(split(",", "a,b,c"))}`, 769 []interface{}{"a", "b", "c"}, 770 false, 771 }, 772 773 // Two lists 774 { 775 `${concat(split(",", "a,b,c"), split(",", "d,e"))}`, 776 []interface{}{"a", "b", "c", "d", "e"}, 777 false, 778 }, 779 // Two lists with different separators 780 { 781 `${concat(split(",", "a,b,c"), split(" ", "d e"))}`, 782 []interface{}{"a", "b", "c", "d", "e"}, 783 false, 784 }, 785 786 // More lists 787 { 788 `${concat(split(",", "a,b"), split(",", "c,d"), split(",", "e,f"), split(",", "0,1"))}`, 789 []interface{}{"a", "b", "c", "d", "e", "f", "0", "1"}, 790 false, 791 }, 792 793 // list vars 794 { 795 `${concat("${var.list}", "${var.list}")}`, 796 []interface{}{"a", "b", "a", "b"}, 797 false, 798 }, 799 // lists of lists 800 { 801 `${concat("${var.lists}", "${var.lists}")}`, 802 []interface{}{[]interface{}{"c", "d"}, []interface{}{"c", "d"}}, 803 false, 804 }, 805 806 // lists of maps 807 { 808 `${concat("${var.maps}", "${var.maps}")}`, 809 []interface{}{map[string]interface{}{"key1": "a", "key2": "b"}, map[string]interface{}{"key1": "a", "key2": "b"}}, 810 false, 811 }, 812 813 // multiple strings 814 // no longer supported, now returns an error 815 { 816 `${concat("string1", "string2")}`, 817 nil, 818 true, 819 }, 820 821 // mismatched types 822 { 823 `${concat("${var.lists}", "${var.maps}")}`, 824 nil, 825 true, 826 }, 827 }, 828 Vars: map[string]ast.Variable{ 829 "var.list": { 830 Type: ast.TypeList, 831 Value: []ast.Variable{ 832 { 833 Type: ast.TypeString, 834 Value: "a", 835 }, 836 { 837 Type: ast.TypeString, 838 Value: "b", 839 }, 840 }, 841 }, 842 "var.lists": { 843 Type: ast.TypeList, 844 Value: []ast.Variable{ 845 { 846 Type: ast.TypeList, 847 Value: []ast.Variable{ 848 { 849 Type: ast.TypeString, 850 Value: "c", 851 }, 852 { 853 Type: ast.TypeString, 854 Value: "d", 855 }, 856 }, 857 }, 858 }, 859 }, 860 "var.maps": { 861 Type: ast.TypeList, 862 Value: []ast.Variable{ 863 { 864 Type: ast.TypeMap, 865 Value: map[string]ast.Variable{ 866 "key1": { 867 Type: ast.TypeString, 868 Value: "a", 869 }, 870 "key2": { 871 Type: ast.TypeString, 872 Value: "b", 873 }, 874 }, 875 }, 876 }, 877 }, 878 }, 879 }) 880 } 881 882 func TestInterpolateFuncMerge(t *testing.T) { 883 testFunction(t, testFunctionConfig{ 884 Cases: []testFunctionCase{ 885 // basic merge 886 { 887 `${merge(map("a", "b"), map("c", "d"))}`, 888 map[string]interface{}{"a": "b", "c": "d"}, 889 false, 890 }, 891 892 // merge with conflicts is ok, last in wins. 893 { 894 `${merge(map("a", "b", "c", "X"), map("c", "d"))}`, 895 map[string]interface{}{"a": "b", "c": "d"}, 896 false, 897 }, 898 899 // merge variadic 900 { 901 `${merge(map("a", "b"), map("c", "d"), map("e", "f"))}`, 902 map[string]interface{}{"a": "b", "c": "d", "e": "f"}, 903 false, 904 }, 905 906 // merge with variables 907 { 908 `${merge(var.maps[0], map("c", "d"))}`, 909 map[string]interface{}{"key1": "a", "key2": "b", "c": "d"}, 910 false, 911 }, 912 913 // only accept maps 914 { 915 `${merge(map("a", "b"), list("c", "d"))}`, 916 nil, 917 true, 918 }, 919 920 // merge maps of maps 921 { 922 `${merge(map("a", var.maps[0]), map("b", var.maps[1]))}`, 923 map[string]interface{}{ 924 "b": map[string]interface{}{"key3": "d", "key4": "c"}, 925 "a": map[string]interface{}{"key1": "a", "key2": "b"}, 926 }, 927 false, 928 }, 929 // merge maps of lists 930 { 931 `${merge(map("a", list("b")), map("c", list("d", "e")))}`, 932 map[string]interface{}{"a": []interface{}{"b"}, "c": []interface{}{"d", "e"}}, 933 false, 934 }, 935 // merge map of various kinds 936 { 937 `${merge(map("a", var.maps[0]), map("b", list("c", "d")))}`, 938 map[string]interface{}{"a": map[string]interface{}{"key1": "a", "key2": "b"}, "b": []interface{}{"c", "d"}}, 939 false, 940 }, 941 }, 942 Vars: map[string]ast.Variable{ 943 "var.maps": { 944 Type: ast.TypeList, 945 Value: []ast.Variable{ 946 { 947 Type: ast.TypeMap, 948 Value: map[string]ast.Variable{ 949 "key1": { 950 Type: ast.TypeString, 951 Value: "a", 952 }, 953 "key2": { 954 Type: ast.TypeString, 955 Value: "b", 956 }, 957 }, 958 }, 959 { 960 Type: ast.TypeMap, 961 Value: map[string]ast.Variable{ 962 "key3": { 963 Type: ast.TypeString, 964 Value: "d", 965 }, 966 "key4": { 967 Type: ast.TypeString, 968 Value: "c", 969 }, 970 }, 971 }, 972 }, 973 }, 974 }, 975 }) 976 977 } 978 979 func TestInterpolateFuncDirname(t *testing.T) { 980 testFunction(t, testFunctionConfig{ 981 Cases: []testFunctionCase{ 982 { 983 `${dirname("/foo/bar/baz")}`, 984 "/foo/bar", 985 false, 986 }, 987 }, 988 }) 989 } 990 991 func TestInterpolateFuncDistinct(t *testing.T) { 992 testFunction(t, testFunctionConfig{ 993 Cases: []testFunctionCase{ 994 // 3 duplicates 995 { 996 `${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user2,user3")))}`, 997 []interface{}{"user1", "user2", "user3"}, 998 false, 999 }, 1000 // 1 duplicate 1001 { 1002 `${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")))}`, 1003 []interface{}{"user1", "user2", "user3", "user4"}, 1004 false, 1005 }, 1006 // too many args 1007 { 1008 `${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")), "foo")}`, 1009 nil, 1010 true, 1011 }, 1012 // non-flat list is an error 1013 { 1014 `${distinct(list(list("a"), list("a")))}`, 1015 nil, 1016 true, 1017 }, 1018 }, 1019 }) 1020 } 1021 1022 func TestInterpolateFuncMatchKeys(t *testing.T) { 1023 testFunction(t, testFunctionConfig{ 1024 Cases: []testFunctionCase{ 1025 // normal usage 1026 { 1027 `${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref2"))}`, 1028 []interface{}{"b"}, 1029 false, 1030 }, 1031 // normal usage 2, check the order 1032 { 1033 `${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref2", "ref1"))}`, 1034 []interface{}{"a", "b"}, 1035 false, 1036 }, 1037 // duplicate item in searchset 1038 { 1039 `${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref2", "ref2"))}`, 1040 []interface{}{"b"}, 1041 false, 1042 }, 1043 // no matches 1044 { 1045 `${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref4"))}`, 1046 []interface{}{}, 1047 false, 1048 }, 1049 // no matches 2 1050 { 1051 `${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list())}`, 1052 []interface{}{}, 1053 false, 1054 }, 1055 // zero case 1056 { 1057 `${matchkeys(list(), list(), list("nope"))}`, 1058 []interface{}{}, 1059 false, 1060 }, 1061 // complex values 1062 { 1063 `${matchkeys(list(list("a", "a")), list("a"), list("a"))}`, 1064 []interface{}{[]interface{}{"a", "a"}}, 1065 false, 1066 }, 1067 // errors 1068 // different types 1069 { 1070 `${matchkeys(list("a"), list(1), list("a"))}`, 1071 nil, 1072 true, 1073 }, 1074 // different types 1075 { 1076 `${matchkeys(list("a"), list(list("a"), list("a")), list("a"))}`, 1077 nil, 1078 true, 1079 }, 1080 // lists of different length is an error 1081 { 1082 `${matchkeys(list("a"), list("a", "b"), list("a"))}`, 1083 nil, 1084 true, 1085 }, 1086 }, 1087 }) 1088 } 1089 1090 func TestInterpolateFuncFile(t *testing.T) { 1091 tf, err := ioutil.TempFile("", "tf") 1092 if err != nil { 1093 t.Fatalf("err: %s", err) 1094 } 1095 path := tf.Name() 1096 tf.Write([]byte("foo")) 1097 tf.Close() 1098 defer os.Remove(path) 1099 1100 testFunction(t, testFunctionConfig{ 1101 Cases: []testFunctionCase{ 1102 { 1103 fmt.Sprintf(`${file("%s")}`, path), 1104 "foo", 1105 false, 1106 }, 1107 1108 // Invalid path 1109 { 1110 `${file("/i/dont/exist")}`, 1111 nil, 1112 true, 1113 }, 1114 1115 // Too many args 1116 { 1117 `${file("foo", "bar")}`, 1118 nil, 1119 true, 1120 }, 1121 }, 1122 }) 1123 } 1124 1125 func TestInterpolateFuncFormat(t *testing.T) { 1126 testFunction(t, testFunctionConfig{ 1127 Cases: []testFunctionCase{ 1128 { 1129 `${format("hello")}`, 1130 "hello", 1131 false, 1132 }, 1133 1134 { 1135 `${format("hello %s", "world")}`, 1136 "hello world", 1137 false, 1138 }, 1139 1140 { 1141 `${format("hello %d", 42)}`, 1142 "hello 42", 1143 false, 1144 }, 1145 1146 { 1147 `${format("hello %05d", 42)}`, 1148 "hello 00042", 1149 false, 1150 }, 1151 1152 { 1153 `${format("hello %05d", 12345)}`, 1154 "hello 12345", 1155 false, 1156 }, 1157 }, 1158 }) 1159 } 1160 1161 func TestInterpolateFuncFormatList(t *testing.T) { 1162 testFunction(t, testFunctionConfig{ 1163 Cases: []testFunctionCase{ 1164 // formatlist requires at least one list 1165 { 1166 `${formatlist("hello")}`, 1167 nil, 1168 true, 1169 }, 1170 { 1171 `${formatlist("hello %s", "world")}`, 1172 nil, 1173 true, 1174 }, 1175 // formatlist applies to each list element in turn 1176 { 1177 `${formatlist("<%s>", split(",", "A,B"))}`, 1178 []interface{}{"<A>", "<B>"}, 1179 false, 1180 }, 1181 // formatlist repeats scalar elements 1182 { 1183 `${join(", ", formatlist("%s=%s", "x", split(",", "A,B,C")))}`, 1184 "x=A, x=B, x=C", 1185 false, 1186 }, 1187 // Multiple lists are walked in parallel 1188 { 1189 `${join(", ", formatlist("%s=%s", split(",", "A,B,C"), split(",", "1,2,3")))}`, 1190 "A=1, B=2, C=3", 1191 false, 1192 }, 1193 // Mismatched list lengths generate an error 1194 { 1195 `${formatlist("%s=%2s", split(",", "A,B,C,D"), split(",", "1,2,3"))}`, 1196 nil, 1197 true, 1198 }, 1199 // Works with lists of length 1 [GH-2240] 1200 { 1201 `${formatlist("%s.id", split(",", "demo-rest-elb"))}`, 1202 []interface{}{"demo-rest-elb.id"}, 1203 false, 1204 }, 1205 // Works with empty lists [GH-7607] 1206 { 1207 `${formatlist("%s", var.emptylist)}`, 1208 []interface{}{}, 1209 false, 1210 }, 1211 }, 1212 Vars: map[string]ast.Variable{ 1213 "var.emptylist": { 1214 Type: ast.TypeList, 1215 Value: []ast.Variable{}, 1216 }, 1217 }, 1218 }) 1219 } 1220 1221 func TestInterpolateFuncIndex(t *testing.T) { 1222 testFunction(t, testFunctionConfig{ 1223 Vars: map[string]ast.Variable{ 1224 "var.list1": interfaceToVariableSwallowError([]string{"notfoo", "stillnotfoo", "bar"}), 1225 "var.list2": interfaceToVariableSwallowError([]string{"foo"}), 1226 "var.list3": interfaceToVariableSwallowError([]string{"foo", "spam", "bar", "eggs"}), 1227 }, 1228 Cases: []testFunctionCase{ 1229 { 1230 `${index("test", "")}`, 1231 nil, 1232 true, 1233 }, 1234 1235 { 1236 `${index(var.list1, "foo")}`, 1237 nil, 1238 true, 1239 }, 1240 1241 { 1242 `${index(var.list2, "foo")}`, 1243 "0", 1244 false, 1245 }, 1246 1247 { 1248 `${index(var.list3, "bar")}`, 1249 "2", 1250 false, 1251 }, 1252 }, 1253 }) 1254 } 1255 1256 func TestInterpolateFuncJoin(t *testing.T) { 1257 testFunction(t, testFunctionConfig{ 1258 Vars: map[string]ast.Variable{ 1259 "var.a_list": interfaceToVariableSwallowError([]string{"foo"}), 1260 "var.a_longer_list": interfaceToVariableSwallowError([]string{"foo", "bar", "baz"}), 1261 "var.list_of_lists": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"bar"}, []string{"baz"}}), 1262 }, 1263 Cases: []testFunctionCase{ 1264 { 1265 `${join(",")}`, 1266 nil, 1267 true, 1268 }, 1269 1270 { 1271 `${join(",", var.a_list)}`, 1272 "foo", 1273 false, 1274 }, 1275 1276 { 1277 `${join(".", var.a_longer_list)}`, 1278 "foo.bar.baz", 1279 false, 1280 }, 1281 1282 { 1283 `${join(".", var.list_of_lists)}`, 1284 nil, 1285 true, 1286 }, 1287 { 1288 `${join(".", list(list("nested")))}`, 1289 nil, 1290 true, 1291 }, 1292 }, 1293 }) 1294 } 1295 1296 func TestInterpolateFuncJSONEncode(t *testing.T) { 1297 testFunction(t, testFunctionConfig{ 1298 Vars: map[string]ast.Variable{ 1299 "easy": ast.Variable{ 1300 Value: "test", 1301 Type: ast.TypeString, 1302 }, 1303 "hard": ast.Variable{ 1304 Value: " foo \\ \n \t \" bar ", 1305 Type: ast.TypeString, 1306 }, 1307 "list": interfaceToVariableSwallowError([]string{"foo", "bar\tbaz"}), 1308 // XXX can't use InterfaceToVariable as it converts empty slice into empty 1309 // map. 1310 "emptylist": ast.Variable{ 1311 Value: []ast.Variable{}, 1312 Type: ast.TypeList, 1313 }, 1314 "map": interfaceToVariableSwallowError(map[string]string{ 1315 "foo": "bar", 1316 "ba \n z": "q\\x", 1317 }), 1318 "emptymap": interfaceToVariableSwallowError(map[string]string{}), 1319 1320 // Not yet supported (but it would be nice) 1321 "nestedlist": interfaceToVariableSwallowError([][]string{{"foo"}}), 1322 "nestedmap": interfaceToVariableSwallowError(map[string][]string{"foo": {"bar"}}), 1323 }, 1324 Cases: []testFunctionCase{ 1325 { 1326 `${jsonencode("test")}`, 1327 `"test"`, 1328 false, 1329 }, 1330 { 1331 `${jsonencode(easy)}`, 1332 `"test"`, 1333 false, 1334 }, 1335 { 1336 `${jsonencode(hard)}`, 1337 `" foo \\ \n \t \" bar "`, 1338 false, 1339 }, 1340 { 1341 `${jsonencode("")}`, 1342 `""`, 1343 false, 1344 }, 1345 { 1346 `${jsonencode()}`, 1347 nil, 1348 true, 1349 }, 1350 { 1351 `${jsonencode(list)}`, 1352 `["foo","bar\tbaz"]`, 1353 false, 1354 }, 1355 { 1356 `${jsonencode(emptylist)}`, 1357 `[]`, 1358 false, 1359 }, 1360 { 1361 `${jsonencode(map)}`, 1362 `{"ba \n z":"q\\x","foo":"bar"}`, 1363 false, 1364 }, 1365 { 1366 `${jsonencode(emptymap)}`, 1367 `{}`, 1368 false, 1369 }, 1370 { 1371 `${jsonencode(nestedlist)}`, 1372 nil, 1373 true, 1374 }, 1375 { 1376 `${jsonencode(nestedmap)}`, 1377 nil, 1378 true, 1379 }, 1380 }, 1381 }) 1382 } 1383 1384 func TestInterpolateFuncReplace(t *testing.T) { 1385 testFunction(t, testFunctionConfig{ 1386 Cases: []testFunctionCase{ 1387 // Regular search and replace 1388 { 1389 `${replace("hello", "hel", "bel")}`, 1390 "bello", 1391 false, 1392 }, 1393 1394 // Search string doesn't match 1395 { 1396 `${replace("hello", "nope", "bel")}`, 1397 "hello", 1398 false, 1399 }, 1400 1401 // Regular expression 1402 { 1403 `${replace("hello", "/l/", "L")}`, 1404 "heLLo", 1405 false, 1406 }, 1407 1408 { 1409 `${replace("helo", "/(l)/", "$1$1")}`, 1410 "hello", 1411 false, 1412 }, 1413 1414 // Bad regexp 1415 { 1416 `${replace("helo", "/(l/", "$1$1")}`, 1417 nil, 1418 true, 1419 }, 1420 }, 1421 }) 1422 } 1423 1424 func TestInterpolateFuncLength(t *testing.T) { 1425 testFunction(t, testFunctionConfig{ 1426 Cases: []testFunctionCase{ 1427 // Raw strings 1428 { 1429 `${length("")}`, 1430 "0", 1431 false, 1432 }, 1433 { 1434 `${length("a")}`, 1435 "1", 1436 false, 1437 }, 1438 { 1439 `${length(" ")}`, 1440 "1", 1441 false, 1442 }, 1443 { 1444 `${length(" a ,")}`, 1445 "4", 1446 false, 1447 }, 1448 { 1449 `${length("aaa")}`, 1450 "3", 1451 false, 1452 }, 1453 1454 // Lists 1455 { 1456 `${length(split(",", "a"))}`, 1457 "1", 1458 false, 1459 }, 1460 { 1461 `${length(split(",", "foo,"))}`, 1462 "2", 1463 false, 1464 }, 1465 { 1466 `${length(split(",", ",foo,"))}`, 1467 "3", 1468 false, 1469 }, 1470 { 1471 `${length(split(",", "foo,bar"))}`, 1472 "2", 1473 false, 1474 }, 1475 { 1476 `${length(split(".", "one.two.three.four.five"))}`, 1477 "5", 1478 false, 1479 }, 1480 // Want length 0 if we split an empty string then compact 1481 { 1482 `${length(compact(split(",", "")))}`, 1483 "0", 1484 false, 1485 }, 1486 // Works for maps 1487 { 1488 `${length(map("k", "v"))}`, 1489 "1", 1490 false, 1491 }, 1492 { 1493 `${length(map("k1", "v1", "k2", "v2"))}`, 1494 "2", 1495 false, 1496 }, 1497 }, 1498 }) 1499 } 1500 1501 func TestInterpolateFuncSignum(t *testing.T) { 1502 testFunction(t, testFunctionConfig{ 1503 Cases: []testFunctionCase{ 1504 { 1505 `${signum()}`, 1506 nil, 1507 true, 1508 }, 1509 1510 { 1511 `${signum("")}`, 1512 nil, 1513 true, 1514 }, 1515 1516 { 1517 `${signum(0)}`, 1518 "0", 1519 false, 1520 }, 1521 1522 { 1523 `${signum(15)}`, 1524 "1", 1525 false, 1526 }, 1527 1528 { 1529 `${signum(-29)}`, 1530 "-1", 1531 false, 1532 }, 1533 }, 1534 }) 1535 } 1536 1537 func TestInterpolateFuncSlice(t *testing.T) { 1538 testFunction(t, testFunctionConfig{ 1539 Cases: []testFunctionCase{ 1540 // Negative from index 1541 { 1542 `${slice(list("a"), -1, 0)}`, 1543 nil, 1544 true, 1545 }, 1546 // From index > to index 1547 { 1548 `${slice(list("a", "b", "c"), 2, 1)}`, 1549 nil, 1550 true, 1551 }, 1552 // To index too large 1553 { 1554 `${slice(var.list_of_strings, 1, 4)}`, 1555 nil, 1556 true, 1557 }, 1558 // Empty slice 1559 { 1560 `${slice(var.list_of_strings, 1, 1)}`, 1561 []interface{}{}, 1562 false, 1563 }, 1564 { 1565 `${slice(var.list_of_strings, 1, 2)}`, 1566 []interface{}{"b"}, 1567 false, 1568 }, 1569 { 1570 `${slice(var.list_of_strings, 0, length(var.list_of_strings) - 1)}`, 1571 []interface{}{"a", "b"}, 1572 false, 1573 }, 1574 }, 1575 Vars: map[string]ast.Variable{ 1576 "var.list_of_strings": { 1577 Type: ast.TypeList, 1578 Value: []ast.Variable{ 1579 { 1580 Type: ast.TypeString, 1581 Value: "a", 1582 }, 1583 { 1584 Type: ast.TypeString, 1585 Value: "b", 1586 }, 1587 { 1588 Type: ast.TypeString, 1589 Value: "c", 1590 }, 1591 }, 1592 }, 1593 }, 1594 }) 1595 } 1596 1597 func TestInterpolateFuncSort(t *testing.T) { 1598 testFunction(t, testFunctionConfig{ 1599 Vars: map[string]ast.Variable{ 1600 "var.strings": ast.Variable{ 1601 Type: ast.TypeList, 1602 Value: []ast.Variable{ 1603 {Type: ast.TypeString, Value: "c"}, 1604 {Type: ast.TypeString, Value: "a"}, 1605 {Type: ast.TypeString, Value: "b"}, 1606 }, 1607 }, 1608 "var.notstrings": ast.Variable{ 1609 Type: ast.TypeList, 1610 Value: []ast.Variable{ 1611 {Type: ast.TypeList, Value: []ast.Variable{}}, 1612 {Type: ast.TypeString, Value: "b"}, 1613 }, 1614 }, 1615 }, 1616 Cases: []testFunctionCase{ 1617 { 1618 `${sort(var.strings)}`, 1619 []interface{}{"a", "b", "c"}, 1620 false, 1621 }, 1622 { 1623 `${sort(var.notstrings)}`, 1624 nil, 1625 true, 1626 }, 1627 }, 1628 }) 1629 } 1630 1631 func TestInterpolateFuncSplit(t *testing.T) { 1632 testFunction(t, testFunctionConfig{ 1633 Cases: []testFunctionCase{ 1634 { 1635 `${split(",")}`, 1636 nil, 1637 true, 1638 }, 1639 1640 { 1641 `${split(",", "")}`, 1642 []interface{}{""}, 1643 false, 1644 }, 1645 1646 { 1647 `${split(",", "foo")}`, 1648 []interface{}{"foo"}, 1649 false, 1650 }, 1651 1652 { 1653 `${split(",", ",,,")}`, 1654 []interface{}{"", "", "", ""}, 1655 false, 1656 }, 1657 1658 { 1659 `${split(",", "foo,")}`, 1660 []interface{}{"foo", ""}, 1661 false, 1662 }, 1663 1664 { 1665 `${split(",", ",foo,")}`, 1666 []interface{}{"", "foo", ""}, 1667 false, 1668 }, 1669 1670 { 1671 `${split(".", "foo.bar.baz")}`, 1672 []interface{}{"foo", "bar", "baz"}, 1673 false, 1674 }, 1675 }, 1676 }) 1677 } 1678 1679 func TestInterpolateFuncLookup(t *testing.T) { 1680 testFunction(t, testFunctionConfig{ 1681 Vars: map[string]ast.Variable{ 1682 "var.foo": { 1683 Type: ast.TypeMap, 1684 Value: map[string]ast.Variable{ 1685 "bar": { 1686 Type: ast.TypeString, 1687 Value: "baz", 1688 }, 1689 }, 1690 }, 1691 "var.map_of_lists": ast.Variable{ 1692 Type: ast.TypeMap, 1693 Value: map[string]ast.Variable{ 1694 "bar": { 1695 Type: ast.TypeList, 1696 Value: []ast.Variable{ 1697 { 1698 Type: ast.TypeString, 1699 Value: "baz", 1700 }, 1701 }, 1702 }, 1703 }, 1704 }, 1705 }, 1706 Cases: []testFunctionCase{ 1707 { 1708 `${lookup(var.foo, "bar")}`, 1709 "baz", 1710 false, 1711 }, 1712 1713 // Invalid key 1714 { 1715 `${lookup(var.foo, "baz")}`, 1716 nil, 1717 true, 1718 }, 1719 1720 // Supplied default with valid key 1721 { 1722 `${lookup(var.foo, "bar", "")}`, 1723 "baz", 1724 false, 1725 }, 1726 1727 // Supplied default with invalid key 1728 { 1729 `${lookup(var.foo, "zip", "")}`, 1730 "", 1731 false, 1732 }, 1733 1734 // Too many args 1735 { 1736 `${lookup(var.foo, "bar", "", "abc")}`, 1737 nil, 1738 true, 1739 }, 1740 1741 // Cannot lookup into map of lists 1742 { 1743 `${lookup(var.map_of_lists, "bar")}`, 1744 nil, 1745 true, 1746 }, 1747 1748 // Non-empty default 1749 { 1750 `${lookup(var.foo, "zap", "xyz")}`, 1751 "xyz", 1752 false, 1753 }, 1754 }, 1755 }) 1756 } 1757 1758 func TestInterpolateFuncKeys(t *testing.T) { 1759 testFunction(t, testFunctionConfig{ 1760 Vars: map[string]ast.Variable{ 1761 "var.foo": ast.Variable{ 1762 Type: ast.TypeMap, 1763 Value: map[string]ast.Variable{ 1764 "bar": ast.Variable{ 1765 Value: "baz", 1766 Type: ast.TypeString, 1767 }, 1768 "qux": ast.Variable{ 1769 Value: "quack", 1770 Type: ast.TypeString, 1771 }, 1772 }, 1773 }, 1774 "var.str": ast.Variable{ 1775 Value: "astring", 1776 Type: ast.TypeString, 1777 }, 1778 }, 1779 Cases: []testFunctionCase{ 1780 { 1781 `${keys(var.foo)}`, 1782 []interface{}{"bar", "qux"}, 1783 false, 1784 }, 1785 1786 // Invalid key 1787 { 1788 `${keys(var.not)}`, 1789 nil, 1790 true, 1791 }, 1792 1793 // Too many args 1794 { 1795 `${keys(var.foo, "bar")}`, 1796 nil, 1797 true, 1798 }, 1799 1800 // Not a map 1801 { 1802 `${keys(var.str)}`, 1803 nil, 1804 true, 1805 }, 1806 }, 1807 }) 1808 } 1809 1810 // Confirm that keys return in sorted order, and values return in the order of 1811 // their sorted keys. 1812 func TestInterpolateFuncKeyValOrder(t *testing.T) { 1813 testFunction(t, testFunctionConfig{ 1814 Vars: map[string]ast.Variable{ 1815 "var.foo": ast.Variable{ 1816 Type: ast.TypeMap, 1817 Value: map[string]ast.Variable{ 1818 "D": ast.Variable{ 1819 Value: "2", 1820 Type: ast.TypeString, 1821 }, 1822 "C": ast.Variable{ 1823 Value: "Y", 1824 Type: ast.TypeString, 1825 }, 1826 "A": ast.Variable{ 1827 Value: "X", 1828 Type: ast.TypeString, 1829 }, 1830 "10": ast.Variable{ 1831 Value: "Z", 1832 Type: ast.TypeString, 1833 }, 1834 "1": ast.Variable{ 1835 Value: "4", 1836 Type: ast.TypeString, 1837 }, 1838 "3": ast.Variable{ 1839 Value: "W", 1840 Type: ast.TypeString, 1841 }, 1842 }, 1843 }, 1844 }, 1845 Cases: []testFunctionCase{ 1846 { 1847 `${keys(var.foo)}`, 1848 []interface{}{"1", "10", "3", "A", "C", "D"}, 1849 false, 1850 }, 1851 1852 { 1853 `${values(var.foo)}`, 1854 []interface{}{"4", "Z", "W", "X", "Y", "2"}, 1855 false, 1856 }, 1857 }, 1858 }) 1859 } 1860 1861 func TestInterpolateFuncValues(t *testing.T) { 1862 testFunction(t, testFunctionConfig{ 1863 Vars: map[string]ast.Variable{ 1864 "var.foo": ast.Variable{ 1865 Type: ast.TypeMap, 1866 Value: map[string]ast.Variable{ 1867 "bar": ast.Variable{ 1868 Value: "quack", 1869 Type: ast.TypeString, 1870 }, 1871 "qux": ast.Variable{ 1872 Value: "baz", 1873 Type: ast.TypeString, 1874 }, 1875 }, 1876 }, 1877 "var.str": ast.Variable{ 1878 Value: "astring", 1879 Type: ast.TypeString, 1880 }, 1881 }, 1882 Cases: []testFunctionCase{ 1883 { 1884 `${values(var.foo)}`, 1885 []interface{}{"quack", "baz"}, 1886 false, 1887 }, 1888 1889 // Invalid key 1890 { 1891 `${values(var.not)}`, 1892 nil, 1893 true, 1894 }, 1895 1896 // Too many args 1897 { 1898 `${values(var.foo, "bar")}`, 1899 nil, 1900 true, 1901 }, 1902 1903 // Not a map 1904 { 1905 `${values(var.str)}`, 1906 nil, 1907 true, 1908 }, 1909 1910 // Map of lists 1911 { 1912 `${values(map("one", list()))}`, 1913 nil, 1914 true, 1915 }, 1916 }, 1917 }) 1918 } 1919 1920 func interfaceToVariableSwallowError(input interface{}) ast.Variable { 1921 variable, _ := hil.InterfaceToVariable(input) 1922 return variable 1923 } 1924 1925 func TestInterpolateFuncElement(t *testing.T) { 1926 testFunction(t, testFunctionConfig{ 1927 Vars: map[string]ast.Variable{ 1928 "var.a_list": interfaceToVariableSwallowError([]string{"foo", "baz"}), 1929 "var.a_short_list": interfaceToVariableSwallowError([]string{"foo"}), 1930 "var.empty_list": interfaceToVariableSwallowError([]interface{}{}), 1931 "var.a_nested_list": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"baz"}}), 1932 }, 1933 Cases: []testFunctionCase{ 1934 { 1935 `${element(var.a_list, "1")}`, 1936 "baz", 1937 false, 1938 }, 1939 1940 { 1941 `${element(var.a_short_list, "0")}`, 1942 "foo", 1943 false, 1944 }, 1945 1946 // Invalid index should wrap vs. out-of-bounds 1947 { 1948 `${element(var.a_list, "2")}`, 1949 "foo", 1950 false, 1951 }, 1952 1953 // Negative number should fail 1954 { 1955 `${element(var.a_short_list, "-1")}`, 1956 nil, 1957 true, 1958 }, 1959 1960 // Empty list should fail 1961 { 1962 `${element(var.empty_list, 0)}`, 1963 nil, 1964 true, 1965 }, 1966 1967 // Too many args 1968 { 1969 `${element(var.a_list, "0", "2")}`, 1970 nil, 1971 true, 1972 }, 1973 1974 // Only works on single-level lists 1975 { 1976 `${element(var.a_nested_list, "0")}`, 1977 nil, 1978 true, 1979 }, 1980 }, 1981 }) 1982 } 1983 1984 func TestInterpolateFuncBasename(t *testing.T) { 1985 testFunction(t, testFunctionConfig{ 1986 Cases: []testFunctionCase{ 1987 { 1988 `${basename("/foo/bar/baz")}`, 1989 "baz", 1990 false, 1991 }, 1992 }, 1993 }) 1994 } 1995 1996 func TestInterpolateFuncBase64Encode(t *testing.T) { 1997 testFunction(t, testFunctionConfig{ 1998 Cases: []testFunctionCase{ 1999 // Regular base64 encoding 2000 { 2001 `${base64encode("abc123!?$*&()'-=@~")}`, 2002 "YWJjMTIzIT8kKiYoKSctPUB+", 2003 false, 2004 }, 2005 }, 2006 }) 2007 } 2008 2009 func TestInterpolateFuncBase64Decode(t *testing.T) { 2010 testFunction(t, testFunctionConfig{ 2011 Cases: []testFunctionCase{ 2012 // Regular base64 decoding 2013 { 2014 `${base64decode("YWJjMTIzIT8kKiYoKSctPUB+")}`, 2015 "abc123!?$*&()'-=@~", 2016 false, 2017 }, 2018 2019 // Invalid base64 data decoding 2020 { 2021 `${base64decode("this-is-an-invalid-base64-data")}`, 2022 nil, 2023 true, 2024 }, 2025 }, 2026 }) 2027 } 2028 2029 func TestInterpolateFuncLower(t *testing.T) { 2030 testFunction(t, testFunctionConfig{ 2031 Cases: []testFunctionCase{ 2032 { 2033 `${lower("HELLO")}`, 2034 "hello", 2035 false, 2036 }, 2037 2038 { 2039 `${lower("")}`, 2040 "", 2041 false, 2042 }, 2043 2044 { 2045 `${lower()}`, 2046 nil, 2047 true, 2048 }, 2049 }, 2050 }) 2051 } 2052 2053 func TestInterpolateFuncUpper(t *testing.T) { 2054 testFunction(t, testFunctionConfig{ 2055 Cases: []testFunctionCase{ 2056 { 2057 `${upper("hello")}`, 2058 "HELLO", 2059 false, 2060 }, 2061 2062 { 2063 `${upper("")}`, 2064 "", 2065 false, 2066 }, 2067 2068 { 2069 `${upper()}`, 2070 nil, 2071 true, 2072 }, 2073 }, 2074 }) 2075 } 2076 2077 func TestInterpolateFuncSha1(t *testing.T) { 2078 testFunction(t, testFunctionConfig{ 2079 Cases: []testFunctionCase{ 2080 { 2081 `${sha1("test")}`, 2082 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", 2083 false, 2084 }, 2085 }, 2086 }) 2087 } 2088 2089 func TestInterpolateFuncSha256(t *testing.T) { 2090 testFunction(t, testFunctionConfig{ 2091 Cases: []testFunctionCase{ 2092 { // hexadecimal representation of sha256 sum 2093 `${sha256("test")}`, 2094 "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", 2095 false, 2096 }, 2097 }, 2098 }) 2099 } 2100 2101 func TestInterpolateFuncSha512(t *testing.T) { 2102 testFunction(t, testFunctionConfig{ 2103 Cases: []testFunctionCase{ 2104 { 2105 `${sha512("test")}`, 2106 "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff", 2107 false, 2108 }, 2109 }, 2110 }) 2111 } 2112 2113 func TestInterpolateFuncTitle(t *testing.T) { 2114 testFunction(t, testFunctionConfig{ 2115 Cases: []testFunctionCase{ 2116 { 2117 `${title("hello")}`, 2118 "Hello", 2119 false, 2120 }, 2121 2122 { 2123 `${title("hello world")}`, 2124 "Hello World", 2125 false, 2126 }, 2127 2128 { 2129 `${title("")}`, 2130 "", 2131 false, 2132 }, 2133 2134 { 2135 `${title()}`, 2136 nil, 2137 true, 2138 }, 2139 }, 2140 }) 2141 } 2142 2143 func TestInterpolateFuncTrimSpace(t *testing.T) { 2144 testFunction(t, testFunctionConfig{ 2145 Cases: []testFunctionCase{ 2146 { 2147 `${trimspace(" test ")}`, 2148 "test", 2149 false, 2150 }, 2151 }, 2152 }) 2153 } 2154 2155 func TestInterpolateFuncBase64Sha256(t *testing.T) { 2156 testFunction(t, testFunctionConfig{ 2157 Cases: []testFunctionCase{ 2158 { 2159 `${base64sha256("test")}`, 2160 "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=", 2161 false, 2162 }, 2163 { // This will differ because we're base64-encoding hex represantiation, not raw bytes 2164 `${base64encode(sha256("test"))}`, 2165 "OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZjMTViMGYwMGEwOA==", 2166 false, 2167 }, 2168 }, 2169 }) 2170 } 2171 2172 func TestInterpolateFuncBase64Sha512(t *testing.T) { 2173 testFunction(t, testFunctionConfig{ 2174 Cases: []testFunctionCase{ 2175 { 2176 `${base64sha512("test")}`, 2177 "7iaw3Ur350mqGo7jwQrpkj9hiYB3Lkc/iBml1JQODbJ6wYX4oOHV+E+IvIh/1nsUNzLDBMxfqa2Ob1f1ACio/w==", 2178 false, 2179 }, 2180 { // This will differ because we're base64-encoding hex represantiation, not raw bytes 2181 `${base64encode(sha512("test"))}`, 2182 "ZWUyNmIwZGQ0YWY3ZTc0OWFhMWE4ZWUzYzEwYWU5OTIzZjYxODk4MDc3MmU0NzNmODgxOWE1ZDQ5NDBlMGRiMjdhYzE4NWY4YTBlMWQ1Zjg0Zjg4YmM4ODdmZDY3YjE0MzczMmMzMDRjYzVmYTlhZDhlNmY1N2Y1MDAyOGE4ZmY=", 2183 false, 2184 }, 2185 }, 2186 }) 2187 } 2188 2189 func TestInterpolateFuncMd5(t *testing.T) { 2190 testFunction(t, testFunctionConfig{ 2191 Cases: []testFunctionCase{ 2192 { 2193 `${md5("tada")}`, 2194 "ce47d07243bb6eaf5e1322c81baf9bbf", 2195 false, 2196 }, 2197 { // Confirm that we're not trimming any whitespaces 2198 `${md5(" tada ")}`, 2199 "aadf191a583e53062de2d02c008141c4", 2200 false, 2201 }, 2202 { // We accept empty string too 2203 `${md5("")}`, 2204 "d41d8cd98f00b204e9800998ecf8427e", 2205 false, 2206 }, 2207 }, 2208 }) 2209 } 2210 2211 func TestInterpolateFuncUUID(t *testing.T) { 2212 results := make(map[string]bool) 2213 2214 for i := 0; i < 100; i++ { 2215 ast, err := hil.Parse("${uuid()}") 2216 if err != nil { 2217 t.Fatalf("err: %s", err) 2218 } 2219 2220 result, err := hil.Eval(ast, langEvalConfig(nil)) 2221 if err != nil { 2222 t.Fatalf("err: %s", err) 2223 } 2224 2225 if results[result.Value.(string)] { 2226 t.Fatalf("Got unexpected duplicate uuid: %s", result.Value) 2227 } 2228 2229 results[result.Value.(string)] = true 2230 } 2231 } 2232 2233 func TestInterpolateFuncTimestamp(t *testing.T) { 2234 currentTime := time.Now().UTC() 2235 ast, err := hil.Parse("${timestamp()}") 2236 if err != nil { 2237 t.Fatalf("err: %s", err) 2238 } 2239 2240 result, err := hil.Eval(ast, langEvalConfig(nil)) 2241 if err != nil { 2242 t.Fatalf("err: %s", err) 2243 } 2244 resultTime, err := time.Parse(time.RFC3339, result.Value.(string)) 2245 if err != nil { 2246 t.Fatalf("Error parsing timestamp: %s", err) 2247 } 2248 2249 if resultTime.Sub(currentTime).Seconds() > 10.0 { 2250 t.Fatalf("Timestamp Diff too large. Expected: %s\nReceived: %s", currentTime.Format(time.RFC3339), result.Value.(string)) 2251 } 2252 } 2253 2254 type testFunctionConfig struct { 2255 Cases []testFunctionCase 2256 Vars map[string]ast.Variable 2257 } 2258 2259 type testFunctionCase struct { 2260 Input string 2261 Result interface{} 2262 Error bool 2263 } 2264 2265 func testFunction(t *testing.T, config testFunctionConfig) { 2266 for i, tc := range config.Cases { 2267 ast, err := hil.Parse(tc.Input) 2268 if err != nil { 2269 t.Fatalf("Case #%d: input: %#v\nerr: %v", i, tc.Input, err) 2270 } 2271 2272 result, err := hil.Eval(ast, langEvalConfig(config.Vars)) 2273 if err != nil != tc.Error { 2274 t.Fatalf("Case #%d:\ninput: %#v\nerr: %v", i, tc.Input, err) 2275 } 2276 2277 if !reflect.DeepEqual(result.Value, tc.Result) { 2278 t.Fatalf("%d: bad output for input: %s\n\nOutput: %#v\nExpected: %#v", 2279 i, tc.Input, result.Value, tc.Result) 2280 } 2281 } 2282 } 2283 2284 func TestInterpolateFuncPathExpand(t *testing.T) { 2285 homePath, err := homedir.Dir() 2286 if err != nil { 2287 t.Fatalf("Error getting home directory: %v", err) 2288 } 2289 testFunction(t, testFunctionConfig{ 2290 Cases: []testFunctionCase{ 2291 { 2292 `${pathexpand("~/test-file")}`, 2293 filepath.Join(homePath, "test-file"), 2294 false, 2295 }, 2296 { 2297 `${pathexpand("~/another/test/file")}`, 2298 filepath.Join(homePath, "another/test/file"), 2299 false, 2300 }, 2301 { 2302 `${pathexpand("/root/file")}`, 2303 "/root/file", 2304 false, 2305 }, 2306 { 2307 `${pathexpand("/")}`, 2308 "/", 2309 false, 2310 }, 2311 { 2312 `${pathexpand()}`, 2313 nil, 2314 true, 2315 }, 2316 }, 2317 }) 2318 } 2319 2320 func TestInterpolateFuncSubstr(t *testing.T) { 2321 testFunction(t, testFunctionConfig{ 2322 Cases: []testFunctionCase{ 2323 { 2324 `${substr("foobar", 0, 0)}`, 2325 "", 2326 false, 2327 }, 2328 { 2329 `${substr("foobar", 0, -1)}`, 2330 "foobar", 2331 false, 2332 }, 2333 { 2334 `${substr("foobar", 0, 3)}`, 2335 "foo", 2336 false, 2337 }, 2338 { 2339 `${substr("foobar", 3, 3)}`, 2340 "bar", 2341 false, 2342 }, 2343 { 2344 `${substr("foobar", -3, 3)}`, 2345 "bar", 2346 false, 2347 }, 2348 2349 // empty string 2350 { 2351 `${substr("", 0, 0)}`, 2352 "", 2353 false, 2354 }, 2355 2356 // invalid offset 2357 { 2358 `${substr("", 1, 0)}`, 2359 nil, 2360 true, 2361 }, 2362 2363 // invalid length 2364 { 2365 `${substr("", 0, 1)}`, 2366 nil, 2367 true, 2368 }, 2369 { 2370 `${substr("", 0, -2)}`, 2371 nil, 2372 true, 2373 }, 2374 }, 2375 }) 2376 }