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