github.com/paybyphone/terraform@v0.9.5-0.20170613192930-9706042ddd51/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 TestInterpolateFuncMerge(t *testing.T) { 947 testFunction(t, testFunctionConfig{ 948 Cases: []testFunctionCase{ 949 // basic merge 950 { 951 `${merge(map("a", "b"), map("c", "d"))}`, 952 map[string]interface{}{"a": "b", "c": "d"}, 953 false, 954 }, 955 956 // merge with conflicts is ok, last in wins. 957 { 958 `${merge(map("a", "b", "c", "X"), map("c", "d"))}`, 959 map[string]interface{}{"a": "b", "c": "d"}, 960 false, 961 }, 962 963 // merge variadic 964 { 965 `${merge(map("a", "b"), map("c", "d"), map("e", "f"))}`, 966 map[string]interface{}{"a": "b", "c": "d", "e": "f"}, 967 false, 968 }, 969 970 // merge with variables 971 { 972 `${merge(var.maps[0], map("c", "d"))}`, 973 map[string]interface{}{"key1": "a", "key2": "b", "c": "d"}, 974 false, 975 }, 976 977 // only accept maps 978 { 979 `${merge(map("a", "b"), list("c", "d"))}`, 980 nil, 981 true, 982 }, 983 984 // merge maps of maps 985 { 986 `${merge(map("a", var.maps[0]), map("b", var.maps[1]))}`, 987 map[string]interface{}{ 988 "b": map[string]interface{}{"key3": "d", "key4": "c"}, 989 "a": map[string]interface{}{"key1": "a", "key2": "b"}, 990 }, 991 false, 992 }, 993 // merge maps of lists 994 { 995 `${merge(map("a", list("b")), map("c", list("d", "e")))}`, 996 map[string]interface{}{"a": []interface{}{"b"}, "c": []interface{}{"d", "e"}}, 997 false, 998 }, 999 // merge map of various kinds 1000 { 1001 `${merge(map("a", var.maps[0]), map("b", list("c", "d")))}`, 1002 map[string]interface{}{"a": map[string]interface{}{"key1": "a", "key2": "b"}, "b": []interface{}{"c", "d"}}, 1003 false, 1004 }, 1005 }, 1006 Vars: map[string]ast.Variable{ 1007 "var.maps": { 1008 Type: ast.TypeList, 1009 Value: []ast.Variable{ 1010 { 1011 Type: ast.TypeMap, 1012 Value: map[string]ast.Variable{ 1013 "key1": { 1014 Type: ast.TypeString, 1015 Value: "a", 1016 }, 1017 "key2": { 1018 Type: ast.TypeString, 1019 Value: "b", 1020 }, 1021 }, 1022 }, 1023 { 1024 Type: ast.TypeMap, 1025 Value: map[string]ast.Variable{ 1026 "key3": { 1027 Type: ast.TypeString, 1028 Value: "d", 1029 }, 1030 "key4": { 1031 Type: ast.TypeString, 1032 Value: "c", 1033 }, 1034 }, 1035 }, 1036 }, 1037 }, 1038 }, 1039 }) 1040 1041 } 1042 1043 func TestInterpolateFuncDirname(t *testing.T) { 1044 testFunction(t, testFunctionConfig{ 1045 Cases: []testFunctionCase{ 1046 { 1047 `${dirname("/foo/bar/baz")}`, 1048 "/foo/bar", 1049 false, 1050 }, 1051 }, 1052 }) 1053 } 1054 1055 func TestInterpolateFuncDistinct(t *testing.T) { 1056 testFunction(t, testFunctionConfig{ 1057 Cases: []testFunctionCase{ 1058 // 3 duplicates 1059 { 1060 `${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user2,user3")))}`, 1061 []interface{}{"user1", "user2", "user3"}, 1062 false, 1063 }, 1064 // 1 duplicate 1065 { 1066 `${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")))}`, 1067 []interface{}{"user1", "user2", "user3", "user4"}, 1068 false, 1069 }, 1070 // too many args 1071 { 1072 `${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")), "foo")}`, 1073 nil, 1074 true, 1075 }, 1076 // non-flat list is an error 1077 { 1078 `${distinct(list(list("a"), list("a")))}`, 1079 nil, 1080 true, 1081 }, 1082 }, 1083 }) 1084 } 1085 1086 func TestInterpolateFuncMatchKeys(t *testing.T) { 1087 testFunction(t, testFunctionConfig{ 1088 Cases: []testFunctionCase{ 1089 // normal usage 1090 { 1091 `${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref2"))}`, 1092 []interface{}{"b"}, 1093 false, 1094 }, 1095 // normal usage 2, check the order 1096 { 1097 `${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref2", "ref1"))}`, 1098 []interface{}{"a", "b"}, 1099 false, 1100 }, 1101 // duplicate item in searchset 1102 { 1103 `${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref2", "ref2"))}`, 1104 []interface{}{"b"}, 1105 false, 1106 }, 1107 // no matches 1108 { 1109 `${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list("ref4"))}`, 1110 []interface{}{}, 1111 false, 1112 }, 1113 // no matches 2 1114 { 1115 `${matchkeys(list("a", "b", "c"), list("ref1", "ref2", "ref3"), list())}`, 1116 []interface{}{}, 1117 false, 1118 }, 1119 // zero case 1120 { 1121 `${matchkeys(list(), list(), list("nope"))}`, 1122 []interface{}{}, 1123 false, 1124 }, 1125 // complex values 1126 { 1127 `${matchkeys(list(list("a", "a")), list("a"), list("a"))}`, 1128 []interface{}{[]interface{}{"a", "a"}}, 1129 false, 1130 }, 1131 // errors 1132 // different types 1133 { 1134 `${matchkeys(list("a"), list(1), list("a"))}`, 1135 nil, 1136 true, 1137 }, 1138 // different types 1139 { 1140 `${matchkeys(list("a"), list(list("a"), list("a")), list("a"))}`, 1141 nil, 1142 true, 1143 }, 1144 // lists of different length is an error 1145 { 1146 `${matchkeys(list("a"), list("a", "b"), list("a"))}`, 1147 nil, 1148 true, 1149 }, 1150 }, 1151 }) 1152 } 1153 1154 func TestInterpolateFuncFile(t *testing.T) { 1155 tf, err := ioutil.TempFile("", "tf") 1156 if err != nil { 1157 t.Fatalf("err: %s", err) 1158 } 1159 path := tf.Name() 1160 tf.Write([]byte("foo")) 1161 tf.Close() 1162 defer os.Remove(path) 1163 1164 testFunction(t, testFunctionConfig{ 1165 Cases: []testFunctionCase{ 1166 { 1167 fmt.Sprintf(`${file("%s")}`, path), 1168 "foo", 1169 false, 1170 }, 1171 1172 // Invalid path 1173 { 1174 `${file("/i/dont/exist")}`, 1175 nil, 1176 true, 1177 }, 1178 1179 // Too many args 1180 { 1181 `${file("foo", "bar")}`, 1182 nil, 1183 true, 1184 }, 1185 }, 1186 }) 1187 } 1188 1189 func TestInterpolateFuncFormat(t *testing.T) { 1190 testFunction(t, testFunctionConfig{ 1191 Cases: []testFunctionCase{ 1192 { 1193 `${format("hello")}`, 1194 "hello", 1195 false, 1196 }, 1197 1198 { 1199 `${format("hello %s", "world")}`, 1200 "hello world", 1201 false, 1202 }, 1203 1204 { 1205 `${format("hello %d", 42)}`, 1206 "hello 42", 1207 false, 1208 }, 1209 1210 { 1211 `${format("hello %05d", 42)}`, 1212 "hello 00042", 1213 false, 1214 }, 1215 1216 { 1217 `${format("hello %05d", 12345)}`, 1218 "hello 12345", 1219 false, 1220 }, 1221 }, 1222 }) 1223 } 1224 1225 func TestInterpolateFuncFormatList(t *testing.T) { 1226 testFunction(t, testFunctionConfig{ 1227 Cases: []testFunctionCase{ 1228 // formatlist requires at least one list 1229 { 1230 `${formatlist("hello")}`, 1231 nil, 1232 true, 1233 }, 1234 { 1235 `${formatlist("hello %s", "world")}`, 1236 nil, 1237 true, 1238 }, 1239 // formatlist applies to each list element in turn 1240 { 1241 `${formatlist("<%s>", split(",", "A,B"))}`, 1242 []interface{}{"<A>", "<B>"}, 1243 false, 1244 }, 1245 // formatlist repeats scalar elements 1246 { 1247 `${join(", ", formatlist("%s=%s", "x", split(",", "A,B,C")))}`, 1248 "x=A, x=B, x=C", 1249 false, 1250 }, 1251 // Multiple lists are walked in parallel 1252 { 1253 `${join(", ", formatlist("%s=%s", split(",", "A,B,C"), split(",", "1,2,3")))}`, 1254 "A=1, B=2, C=3", 1255 false, 1256 }, 1257 // Mismatched list lengths generate an error 1258 { 1259 `${formatlist("%s=%2s", split(",", "A,B,C,D"), split(",", "1,2,3"))}`, 1260 nil, 1261 true, 1262 }, 1263 // Works with lists of length 1 [GH-2240] 1264 { 1265 `${formatlist("%s.id", split(",", "demo-rest-elb"))}`, 1266 []interface{}{"demo-rest-elb.id"}, 1267 false, 1268 }, 1269 // Works with empty lists [GH-7607] 1270 { 1271 `${formatlist("%s", var.emptylist)}`, 1272 []interface{}{}, 1273 false, 1274 }, 1275 }, 1276 Vars: map[string]ast.Variable{ 1277 "var.emptylist": { 1278 Type: ast.TypeList, 1279 Value: []ast.Variable{}, 1280 }, 1281 }, 1282 }) 1283 } 1284 1285 func TestInterpolateFuncIndex(t *testing.T) { 1286 testFunction(t, testFunctionConfig{ 1287 Vars: map[string]ast.Variable{ 1288 "var.list1": interfaceToVariableSwallowError([]string{"notfoo", "stillnotfoo", "bar"}), 1289 "var.list2": interfaceToVariableSwallowError([]string{"foo"}), 1290 "var.list3": interfaceToVariableSwallowError([]string{"foo", "spam", "bar", "eggs"}), 1291 }, 1292 Cases: []testFunctionCase{ 1293 { 1294 `${index("test", "")}`, 1295 nil, 1296 true, 1297 }, 1298 1299 { 1300 `${index(var.list1, "foo")}`, 1301 nil, 1302 true, 1303 }, 1304 1305 { 1306 `${index(var.list2, "foo")}`, 1307 "0", 1308 false, 1309 }, 1310 1311 { 1312 `${index(var.list3, "bar")}`, 1313 "2", 1314 false, 1315 }, 1316 }, 1317 }) 1318 } 1319 1320 func TestInterpolateFuncJoin(t *testing.T) { 1321 testFunction(t, testFunctionConfig{ 1322 Vars: map[string]ast.Variable{ 1323 "var.a_list": interfaceToVariableSwallowError([]string{"foo"}), 1324 "var.a_longer_list": interfaceToVariableSwallowError([]string{"foo", "bar", "baz"}), 1325 "var.list_of_lists": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"bar"}, []string{"baz"}}), 1326 }, 1327 Cases: []testFunctionCase{ 1328 { 1329 `${join(",")}`, 1330 nil, 1331 true, 1332 }, 1333 1334 { 1335 `${join(",", var.a_list)}`, 1336 "foo", 1337 false, 1338 }, 1339 1340 { 1341 `${join(".", var.a_longer_list)}`, 1342 "foo.bar.baz", 1343 false, 1344 }, 1345 1346 { 1347 `${join(".", var.list_of_lists)}`, 1348 nil, 1349 true, 1350 }, 1351 { 1352 `${join(".", list(list("nested")))}`, 1353 nil, 1354 true, 1355 }, 1356 }, 1357 }) 1358 } 1359 1360 func TestInterpolateFuncJSONEncode(t *testing.T) { 1361 testFunction(t, testFunctionConfig{ 1362 Vars: map[string]ast.Variable{ 1363 "easy": ast.Variable{ 1364 Value: "test", 1365 Type: ast.TypeString, 1366 }, 1367 "hard": ast.Variable{ 1368 Value: " foo \\ \n \t \" bar ", 1369 Type: ast.TypeString, 1370 }, 1371 "list": interfaceToVariableSwallowError([]string{"foo", "bar\tbaz"}), 1372 // XXX can't use InterfaceToVariable as it converts empty slice into empty 1373 // map. 1374 "emptylist": ast.Variable{ 1375 Value: []ast.Variable{}, 1376 Type: ast.TypeList, 1377 }, 1378 "map": interfaceToVariableSwallowError(map[string]string{ 1379 "foo": "bar", 1380 "ba \n z": "q\\x", 1381 }), 1382 "emptymap": interfaceToVariableSwallowError(map[string]string{}), 1383 1384 // Not yet supported (but it would be nice) 1385 "nestedlist": interfaceToVariableSwallowError([][]string{{"foo"}}), 1386 "nestedmap": interfaceToVariableSwallowError(map[string][]string{"foo": {"bar"}}), 1387 }, 1388 Cases: []testFunctionCase{ 1389 { 1390 `${jsonencode("test")}`, 1391 `"test"`, 1392 false, 1393 }, 1394 { 1395 `${jsonencode(easy)}`, 1396 `"test"`, 1397 false, 1398 }, 1399 { 1400 `${jsonencode(hard)}`, 1401 `" foo \\ \n \t \" bar "`, 1402 false, 1403 }, 1404 { 1405 `${jsonencode("")}`, 1406 `""`, 1407 false, 1408 }, 1409 { 1410 `${jsonencode()}`, 1411 nil, 1412 true, 1413 }, 1414 { 1415 `${jsonencode(list)}`, 1416 `["foo","bar\tbaz"]`, 1417 false, 1418 }, 1419 { 1420 `${jsonencode(emptylist)}`, 1421 `[]`, 1422 false, 1423 }, 1424 { 1425 `${jsonencode(map)}`, 1426 `{"ba \n z":"q\\x","foo":"bar"}`, 1427 false, 1428 }, 1429 { 1430 `${jsonencode(emptymap)}`, 1431 `{}`, 1432 false, 1433 }, 1434 { 1435 `${jsonencode(nestedlist)}`, 1436 nil, 1437 true, 1438 }, 1439 { 1440 `${jsonencode(nestedmap)}`, 1441 nil, 1442 true, 1443 }, 1444 }, 1445 }) 1446 } 1447 1448 func TestInterpolateFuncReplace(t *testing.T) { 1449 testFunction(t, testFunctionConfig{ 1450 Cases: []testFunctionCase{ 1451 // Regular search and replace 1452 { 1453 `${replace("hello", "hel", "bel")}`, 1454 "bello", 1455 false, 1456 }, 1457 1458 // Search string doesn't match 1459 { 1460 `${replace("hello", "nope", "bel")}`, 1461 "hello", 1462 false, 1463 }, 1464 1465 // Regular expression 1466 { 1467 `${replace("hello", "/l/", "L")}`, 1468 "heLLo", 1469 false, 1470 }, 1471 1472 { 1473 `${replace("helo", "/(l)/", "$1$1")}`, 1474 "hello", 1475 false, 1476 }, 1477 1478 // Bad regexp 1479 { 1480 `${replace("helo", "/(l/", "$1$1")}`, 1481 nil, 1482 true, 1483 }, 1484 }, 1485 }) 1486 } 1487 1488 func TestInterpolateFuncLength(t *testing.T) { 1489 testFunction(t, testFunctionConfig{ 1490 Cases: []testFunctionCase{ 1491 // Raw strings 1492 { 1493 `${length("")}`, 1494 "0", 1495 false, 1496 }, 1497 { 1498 `${length("a")}`, 1499 "1", 1500 false, 1501 }, 1502 { 1503 `${length(" ")}`, 1504 "1", 1505 false, 1506 }, 1507 { 1508 `${length(" a ,")}`, 1509 "4", 1510 false, 1511 }, 1512 { 1513 `${length("aaa")}`, 1514 "3", 1515 false, 1516 }, 1517 1518 // Lists 1519 { 1520 `${length(split(",", "a"))}`, 1521 "1", 1522 false, 1523 }, 1524 { 1525 `${length(split(",", "foo,"))}`, 1526 "2", 1527 false, 1528 }, 1529 { 1530 `${length(split(",", ",foo,"))}`, 1531 "3", 1532 false, 1533 }, 1534 { 1535 `${length(split(",", "foo,bar"))}`, 1536 "2", 1537 false, 1538 }, 1539 { 1540 `${length(split(".", "one.two.three.four.five"))}`, 1541 "5", 1542 false, 1543 }, 1544 // Want length 0 if we split an empty string then compact 1545 { 1546 `${length(compact(split(",", "")))}`, 1547 "0", 1548 false, 1549 }, 1550 // Works for maps 1551 { 1552 `${length(map("k", "v"))}`, 1553 "1", 1554 false, 1555 }, 1556 { 1557 `${length(map("k1", "v1", "k2", "v2"))}`, 1558 "2", 1559 false, 1560 }, 1561 }, 1562 }) 1563 } 1564 1565 func TestInterpolateFuncSignum(t *testing.T) { 1566 testFunction(t, testFunctionConfig{ 1567 Cases: []testFunctionCase{ 1568 { 1569 `${signum()}`, 1570 nil, 1571 true, 1572 }, 1573 1574 { 1575 `${signum("")}`, 1576 nil, 1577 true, 1578 }, 1579 1580 { 1581 `${signum(0)}`, 1582 "0", 1583 false, 1584 }, 1585 1586 { 1587 `${signum(15)}`, 1588 "1", 1589 false, 1590 }, 1591 1592 { 1593 `${signum(-29)}`, 1594 "-1", 1595 false, 1596 }, 1597 }, 1598 }) 1599 } 1600 1601 func TestInterpolateFuncSlice(t *testing.T) { 1602 testFunction(t, testFunctionConfig{ 1603 Cases: []testFunctionCase{ 1604 // Negative from index 1605 { 1606 `${slice(list("a"), -1, 0)}`, 1607 nil, 1608 true, 1609 }, 1610 // From index > to index 1611 { 1612 `${slice(list("a", "b", "c"), 2, 1)}`, 1613 nil, 1614 true, 1615 }, 1616 // To index too large 1617 { 1618 `${slice(var.list_of_strings, 1, 4)}`, 1619 nil, 1620 true, 1621 }, 1622 // Empty slice 1623 { 1624 `${slice(var.list_of_strings, 1, 1)}`, 1625 []interface{}{}, 1626 false, 1627 }, 1628 { 1629 `${slice(var.list_of_strings, 1, 2)}`, 1630 []interface{}{"b"}, 1631 false, 1632 }, 1633 { 1634 `${slice(var.list_of_strings, 0, length(var.list_of_strings) - 1)}`, 1635 []interface{}{"a", "b"}, 1636 false, 1637 }, 1638 }, 1639 Vars: map[string]ast.Variable{ 1640 "var.list_of_strings": { 1641 Type: ast.TypeList, 1642 Value: []ast.Variable{ 1643 { 1644 Type: ast.TypeString, 1645 Value: "a", 1646 }, 1647 { 1648 Type: ast.TypeString, 1649 Value: "b", 1650 }, 1651 { 1652 Type: ast.TypeString, 1653 Value: "c", 1654 }, 1655 }, 1656 }, 1657 }, 1658 }) 1659 } 1660 1661 func TestInterpolateFuncSort(t *testing.T) { 1662 testFunction(t, testFunctionConfig{ 1663 Vars: map[string]ast.Variable{ 1664 "var.strings": ast.Variable{ 1665 Type: ast.TypeList, 1666 Value: []ast.Variable{ 1667 {Type: ast.TypeString, Value: "c"}, 1668 {Type: ast.TypeString, Value: "a"}, 1669 {Type: ast.TypeString, Value: "b"}, 1670 }, 1671 }, 1672 "var.notstrings": ast.Variable{ 1673 Type: ast.TypeList, 1674 Value: []ast.Variable{ 1675 {Type: ast.TypeList, Value: []ast.Variable{}}, 1676 {Type: ast.TypeString, Value: "b"}, 1677 }, 1678 }, 1679 }, 1680 Cases: []testFunctionCase{ 1681 { 1682 `${sort(var.strings)}`, 1683 []interface{}{"a", "b", "c"}, 1684 false, 1685 }, 1686 { 1687 `${sort(var.notstrings)}`, 1688 nil, 1689 true, 1690 }, 1691 }, 1692 }) 1693 } 1694 1695 func TestInterpolateFuncSplit(t *testing.T) { 1696 testFunction(t, testFunctionConfig{ 1697 Cases: []testFunctionCase{ 1698 { 1699 `${split(",")}`, 1700 nil, 1701 true, 1702 }, 1703 1704 { 1705 `${split(",", "")}`, 1706 []interface{}{""}, 1707 false, 1708 }, 1709 1710 { 1711 `${split(",", "foo")}`, 1712 []interface{}{"foo"}, 1713 false, 1714 }, 1715 1716 { 1717 `${split(",", ",,,")}`, 1718 []interface{}{"", "", "", ""}, 1719 false, 1720 }, 1721 1722 { 1723 `${split(",", "foo,")}`, 1724 []interface{}{"foo", ""}, 1725 false, 1726 }, 1727 1728 { 1729 `${split(",", ",foo,")}`, 1730 []interface{}{"", "foo", ""}, 1731 false, 1732 }, 1733 1734 { 1735 `${split(".", "foo.bar.baz")}`, 1736 []interface{}{"foo", "bar", "baz"}, 1737 false, 1738 }, 1739 }, 1740 }) 1741 } 1742 1743 func TestInterpolateFuncLookup(t *testing.T) { 1744 testFunction(t, testFunctionConfig{ 1745 Vars: map[string]ast.Variable{ 1746 "var.foo": { 1747 Type: ast.TypeMap, 1748 Value: map[string]ast.Variable{ 1749 "bar": { 1750 Type: ast.TypeString, 1751 Value: "baz", 1752 }, 1753 }, 1754 }, 1755 "var.map_of_lists": ast.Variable{ 1756 Type: ast.TypeMap, 1757 Value: map[string]ast.Variable{ 1758 "bar": { 1759 Type: ast.TypeList, 1760 Value: []ast.Variable{ 1761 { 1762 Type: ast.TypeString, 1763 Value: "baz", 1764 }, 1765 }, 1766 }, 1767 }, 1768 }, 1769 }, 1770 Cases: []testFunctionCase{ 1771 { 1772 `${lookup(var.foo, "bar")}`, 1773 "baz", 1774 false, 1775 }, 1776 1777 // Invalid key 1778 { 1779 `${lookup(var.foo, "baz")}`, 1780 nil, 1781 true, 1782 }, 1783 1784 // Supplied default with valid key 1785 { 1786 `${lookup(var.foo, "bar", "")}`, 1787 "baz", 1788 false, 1789 }, 1790 1791 // Supplied default with invalid key 1792 { 1793 `${lookup(var.foo, "zip", "")}`, 1794 "", 1795 false, 1796 }, 1797 1798 // Too many args 1799 { 1800 `${lookup(var.foo, "bar", "", "abc")}`, 1801 nil, 1802 true, 1803 }, 1804 1805 // Cannot lookup into map of lists 1806 { 1807 `${lookup(var.map_of_lists, "bar")}`, 1808 nil, 1809 true, 1810 }, 1811 1812 // Non-empty default 1813 { 1814 `${lookup(var.foo, "zap", "xyz")}`, 1815 "xyz", 1816 false, 1817 }, 1818 }, 1819 }) 1820 } 1821 1822 func TestInterpolateFuncKeys(t *testing.T) { 1823 testFunction(t, testFunctionConfig{ 1824 Vars: map[string]ast.Variable{ 1825 "var.foo": ast.Variable{ 1826 Type: ast.TypeMap, 1827 Value: map[string]ast.Variable{ 1828 "bar": ast.Variable{ 1829 Value: "baz", 1830 Type: ast.TypeString, 1831 }, 1832 "qux": ast.Variable{ 1833 Value: "quack", 1834 Type: ast.TypeString, 1835 }, 1836 }, 1837 }, 1838 "var.str": ast.Variable{ 1839 Value: "astring", 1840 Type: ast.TypeString, 1841 }, 1842 }, 1843 Cases: []testFunctionCase{ 1844 { 1845 `${keys(var.foo)}`, 1846 []interface{}{"bar", "qux"}, 1847 false, 1848 }, 1849 1850 // Invalid key 1851 { 1852 `${keys(var.not)}`, 1853 nil, 1854 true, 1855 }, 1856 1857 // Too many args 1858 { 1859 `${keys(var.foo, "bar")}`, 1860 nil, 1861 true, 1862 }, 1863 1864 // Not a map 1865 { 1866 `${keys(var.str)}`, 1867 nil, 1868 true, 1869 }, 1870 }, 1871 }) 1872 } 1873 1874 // Confirm that keys return in sorted order, and values return in the order of 1875 // their sorted keys. 1876 func TestInterpolateFuncKeyValOrder(t *testing.T) { 1877 testFunction(t, testFunctionConfig{ 1878 Vars: map[string]ast.Variable{ 1879 "var.foo": ast.Variable{ 1880 Type: ast.TypeMap, 1881 Value: map[string]ast.Variable{ 1882 "D": ast.Variable{ 1883 Value: "2", 1884 Type: ast.TypeString, 1885 }, 1886 "C": ast.Variable{ 1887 Value: "Y", 1888 Type: ast.TypeString, 1889 }, 1890 "A": ast.Variable{ 1891 Value: "X", 1892 Type: ast.TypeString, 1893 }, 1894 "10": ast.Variable{ 1895 Value: "Z", 1896 Type: ast.TypeString, 1897 }, 1898 "1": ast.Variable{ 1899 Value: "4", 1900 Type: ast.TypeString, 1901 }, 1902 "3": ast.Variable{ 1903 Value: "W", 1904 Type: ast.TypeString, 1905 }, 1906 }, 1907 }, 1908 }, 1909 Cases: []testFunctionCase{ 1910 { 1911 `${keys(var.foo)}`, 1912 []interface{}{"1", "10", "3", "A", "C", "D"}, 1913 false, 1914 }, 1915 1916 { 1917 `${values(var.foo)}`, 1918 []interface{}{"4", "Z", "W", "X", "Y", "2"}, 1919 false, 1920 }, 1921 }, 1922 }) 1923 } 1924 1925 func TestInterpolateFuncValues(t *testing.T) { 1926 testFunction(t, testFunctionConfig{ 1927 Vars: map[string]ast.Variable{ 1928 "var.foo": ast.Variable{ 1929 Type: ast.TypeMap, 1930 Value: map[string]ast.Variable{ 1931 "bar": ast.Variable{ 1932 Value: "quack", 1933 Type: ast.TypeString, 1934 }, 1935 "qux": ast.Variable{ 1936 Value: "baz", 1937 Type: ast.TypeString, 1938 }, 1939 }, 1940 }, 1941 "var.str": ast.Variable{ 1942 Value: "astring", 1943 Type: ast.TypeString, 1944 }, 1945 }, 1946 Cases: []testFunctionCase{ 1947 { 1948 `${values(var.foo)}`, 1949 []interface{}{"quack", "baz"}, 1950 false, 1951 }, 1952 1953 // Invalid key 1954 { 1955 `${values(var.not)}`, 1956 nil, 1957 true, 1958 }, 1959 1960 // Too many args 1961 { 1962 `${values(var.foo, "bar")}`, 1963 nil, 1964 true, 1965 }, 1966 1967 // Not a map 1968 { 1969 `${values(var.str)}`, 1970 nil, 1971 true, 1972 }, 1973 1974 // Map of lists 1975 { 1976 `${values(map("one", list()))}`, 1977 nil, 1978 true, 1979 }, 1980 }, 1981 }) 1982 } 1983 1984 func interfaceToVariableSwallowError(input interface{}) ast.Variable { 1985 variable, _ := hil.InterfaceToVariable(input) 1986 return variable 1987 } 1988 1989 func TestInterpolateFuncElement(t *testing.T) { 1990 testFunction(t, testFunctionConfig{ 1991 Vars: map[string]ast.Variable{ 1992 "var.a_list": interfaceToVariableSwallowError([]string{"foo", "baz"}), 1993 "var.a_short_list": interfaceToVariableSwallowError([]string{"foo"}), 1994 "var.empty_list": interfaceToVariableSwallowError([]interface{}{}), 1995 "var.a_nested_list": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"baz"}}), 1996 }, 1997 Cases: []testFunctionCase{ 1998 { 1999 `${element(var.a_list, "1")}`, 2000 "baz", 2001 false, 2002 }, 2003 2004 { 2005 `${element(var.a_short_list, "0")}`, 2006 "foo", 2007 false, 2008 }, 2009 2010 // Invalid index should wrap vs. out-of-bounds 2011 { 2012 `${element(var.a_list, "2")}`, 2013 "foo", 2014 false, 2015 }, 2016 2017 // Negative number should fail 2018 { 2019 `${element(var.a_short_list, "-1")}`, 2020 nil, 2021 true, 2022 }, 2023 2024 // Empty list should fail 2025 { 2026 `${element(var.empty_list, 0)}`, 2027 nil, 2028 true, 2029 }, 2030 2031 // Too many args 2032 { 2033 `${element(var.a_list, "0", "2")}`, 2034 nil, 2035 true, 2036 }, 2037 2038 // Only works on single-level lists 2039 { 2040 `${element(var.a_nested_list, "0")}`, 2041 nil, 2042 true, 2043 }, 2044 }, 2045 }) 2046 } 2047 2048 func TestInterpolateFuncBasename(t *testing.T) { 2049 testFunction(t, testFunctionConfig{ 2050 Cases: []testFunctionCase{ 2051 { 2052 `${basename("/foo/bar/baz")}`, 2053 "baz", 2054 false, 2055 }, 2056 }, 2057 }) 2058 } 2059 2060 func TestInterpolateFuncBase64Encode(t *testing.T) { 2061 testFunction(t, testFunctionConfig{ 2062 Cases: []testFunctionCase{ 2063 // Regular base64 encoding 2064 { 2065 `${base64encode("abc123!?$*&()'-=@~")}`, 2066 "YWJjMTIzIT8kKiYoKSctPUB+", 2067 false, 2068 }, 2069 }, 2070 }) 2071 } 2072 2073 func TestInterpolateFuncBase64Decode(t *testing.T) { 2074 testFunction(t, testFunctionConfig{ 2075 Cases: []testFunctionCase{ 2076 // Regular base64 decoding 2077 { 2078 `${base64decode("YWJjMTIzIT8kKiYoKSctPUB+")}`, 2079 "abc123!?$*&()'-=@~", 2080 false, 2081 }, 2082 2083 // Invalid base64 data decoding 2084 { 2085 `${base64decode("this-is-an-invalid-base64-data")}`, 2086 nil, 2087 true, 2088 }, 2089 }, 2090 }) 2091 } 2092 2093 func TestInterpolateFuncLower(t *testing.T) { 2094 testFunction(t, testFunctionConfig{ 2095 Cases: []testFunctionCase{ 2096 { 2097 `${lower("HELLO")}`, 2098 "hello", 2099 false, 2100 }, 2101 2102 { 2103 `${lower("")}`, 2104 "", 2105 false, 2106 }, 2107 2108 { 2109 `${lower()}`, 2110 nil, 2111 true, 2112 }, 2113 }, 2114 }) 2115 } 2116 2117 func TestInterpolateFuncUpper(t *testing.T) { 2118 testFunction(t, testFunctionConfig{ 2119 Cases: []testFunctionCase{ 2120 { 2121 `${upper("hello")}`, 2122 "HELLO", 2123 false, 2124 }, 2125 2126 { 2127 `${upper("")}`, 2128 "", 2129 false, 2130 }, 2131 2132 { 2133 `${upper()}`, 2134 nil, 2135 true, 2136 }, 2137 }, 2138 }) 2139 } 2140 2141 func TestInterpolateFuncSha1(t *testing.T) { 2142 testFunction(t, testFunctionConfig{ 2143 Cases: []testFunctionCase{ 2144 { 2145 `${sha1("test")}`, 2146 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", 2147 false, 2148 }, 2149 }, 2150 }) 2151 } 2152 2153 func TestInterpolateFuncSha256(t *testing.T) { 2154 testFunction(t, testFunctionConfig{ 2155 Cases: []testFunctionCase{ 2156 { // hexadecimal representation of sha256 sum 2157 `${sha256("test")}`, 2158 "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", 2159 false, 2160 }, 2161 }, 2162 }) 2163 } 2164 2165 func TestInterpolateFuncSha512(t *testing.T) { 2166 testFunction(t, testFunctionConfig{ 2167 Cases: []testFunctionCase{ 2168 { 2169 `${sha512("test")}`, 2170 "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff", 2171 false, 2172 }, 2173 }, 2174 }) 2175 } 2176 2177 func TestInterpolateFuncTitle(t *testing.T) { 2178 testFunction(t, testFunctionConfig{ 2179 Cases: []testFunctionCase{ 2180 { 2181 `${title("hello")}`, 2182 "Hello", 2183 false, 2184 }, 2185 2186 { 2187 `${title("hello world")}`, 2188 "Hello World", 2189 false, 2190 }, 2191 2192 { 2193 `${title("")}`, 2194 "", 2195 false, 2196 }, 2197 2198 { 2199 `${title()}`, 2200 nil, 2201 true, 2202 }, 2203 }, 2204 }) 2205 } 2206 2207 func TestInterpolateFuncTrimSpace(t *testing.T) { 2208 testFunction(t, testFunctionConfig{ 2209 Cases: []testFunctionCase{ 2210 { 2211 `${trimspace(" test ")}`, 2212 "test", 2213 false, 2214 }, 2215 }, 2216 }) 2217 } 2218 2219 func TestInterpolateFuncBase64Sha256(t *testing.T) { 2220 testFunction(t, testFunctionConfig{ 2221 Cases: []testFunctionCase{ 2222 { 2223 `${base64sha256("test")}`, 2224 "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=", 2225 false, 2226 }, 2227 { // This will differ because we're base64-encoding hex represantiation, not raw bytes 2228 `${base64encode(sha256("test"))}`, 2229 "OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZjMTViMGYwMGEwOA==", 2230 false, 2231 }, 2232 }, 2233 }) 2234 } 2235 2236 func TestInterpolateFuncBase64Sha512(t *testing.T) { 2237 testFunction(t, testFunctionConfig{ 2238 Cases: []testFunctionCase{ 2239 { 2240 `${base64sha512("test")}`, 2241 "7iaw3Ur350mqGo7jwQrpkj9hiYB3Lkc/iBml1JQODbJ6wYX4oOHV+E+IvIh/1nsUNzLDBMxfqa2Ob1f1ACio/w==", 2242 false, 2243 }, 2244 { // This will differ because we're base64-encoding hex represantiation, not raw bytes 2245 `${base64encode(sha512("test"))}`, 2246 "ZWUyNmIwZGQ0YWY3ZTc0OWFhMWE4ZWUzYzEwYWU5OTIzZjYxODk4MDc3MmU0NzNmODgxOWE1ZDQ5NDBlMGRiMjdhYzE4NWY4YTBlMWQ1Zjg0Zjg4YmM4ODdmZDY3YjE0MzczMmMzMDRjYzVmYTlhZDhlNmY1N2Y1MDAyOGE4ZmY=", 2247 false, 2248 }, 2249 }, 2250 }) 2251 } 2252 2253 func TestInterpolateFuncMd5(t *testing.T) { 2254 testFunction(t, testFunctionConfig{ 2255 Cases: []testFunctionCase{ 2256 { 2257 `${md5("tada")}`, 2258 "ce47d07243bb6eaf5e1322c81baf9bbf", 2259 false, 2260 }, 2261 { // Confirm that we're not trimming any whitespaces 2262 `${md5(" tada ")}`, 2263 "aadf191a583e53062de2d02c008141c4", 2264 false, 2265 }, 2266 { // We accept empty string too 2267 `${md5("")}`, 2268 "d41d8cd98f00b204e9800998ecf8427e", 2269 false, 2270 }, 2271 }, 2272 }) 2273 } 2274 2275 func TestInterpolateFuncUUID(t *testing.T) { 2276 results := make(map[string]bool) 2277 2278 for i := 0; i < 100; i++ { 2279 ast, err := hil.Parse("${uuid()}") 2280 if err != nil { 2281 t.Fatalf("err: %s", err) 2282 } 2283 2284 result, err := hil.Eval(ast, langEvalConfig(nil)) 2285 if err != nil { 2286 t.Fatalf("err: %s", err) 2287 } 2288 2289 if results[result.Value.(string)] { 2290 t.Fatalf("Got unexpected duplicate uuid: %s", result.Value) 2291 } 2292 2293 results[result.Value.(string)] = true 2294 } 2295 } 2296 2297 func TestInterpolateFuncTimestamp(t *testing.T) { 2298 currentTime := time.Now().UTC() 2299 ast, err := hil.Parse("${timestamp()}") 2300 if err != nil { 2301 t.Fatalf("err: %s", err) 2302 } 2303 2304 result, err := hil.Eval(ast, langEvalConfig(nil)) 2305 if err != nil { 2306 t.Fatalf("err: %s", err) 2307 } 2308 resultTime, err := time.Parse(time.RFC3339, result.Value.(string)) 2309 if err != nil { 2310 t.Fatalf("Error parsing timestamp: %s", err) 2311 } 2312 2313 if resultTime.Sub(currentTime).Seconds() > 10.0 { 2314 t.Fatalf("Timestamp Diff too large. Expected: %s\nReceived: %s", currentTime.Format(time.RFC3339), result.Value.(string)) 2315 } 2316 } 2317 2318 type testFunctionConfig struct { 2319 Cases []testFunctionCase 2320 Vars map[string]ast.Variable 2321 } 2322 2323 type testFunctionCase struct { 2324 Input string 2325 Result interface{} 2326 Error bool 2327 } 2328 2329 func testFunction(t *testing.T, config testFunctionConfig) { 2330 for i, tc := range config.Cases { 2331 ast, err := hil.Parse(tc.Input) 2332 if err != nil { 2333 t.Fatalf("Case #%d: input: %#v\nerr: %v", i, tc.Input, err) 2334 } 2335 2336 result, err := hil.Eval(ast, langEvalConfig(config.Vars)) 2337 if err != nil != tc.Error { 2338 t.Fatalf("Case #%d:\ninput: %#v\nerr: %v", i, tc.Input, err) 2339 } 2340 2341 if !reflect.DeepEqual(result.Value, tc.Result) { 2342 t.Fatalf("%d: bad output for input: %s\n\nOutput: %#v\nExpected: %#v", 2343 i, tc.Input, result.Value, tc.Result) 2344 } 2345 } 2346 } 2347 2348 func TestInterpolateFuncPathExpand(t *testing.T) { 2349 homePath, err := homedir.Dir() 2350 if err != nil { 2351 t.Fatalf("Error getting home directory: %v", err) 2352 } 2353 testFunction(t, testFunctionConfig{ 2354 Cases: []testFunctionCase{ 2355 { 2356 `${pathexpand("~/test-file")}`, 2357 filepath.Join(homePath, "test-file"), 2358 false, 2359 }, 2360 { 2361 `${pathexpand("~/another/test/file")}`, 2362 filepath.Join(homePath, "another/test/file"), 2363 false, 2364 }, 2365 { 2366 `${pathexpand("/root/file")}`, 2367 "/root/file", 2368 false, 2369 }, 2370 { 2371 `${pathexpand("/")}`, 2372 "/", 2373 false, 2374 }, 2375 { 2376 `${pathexpand()}`, 2377 nil, 2378 true, 2379 }, 2380 }, 2381 }) 2382 } 2383 2384 func TestInterpolateFuncSubstr(t *testing.T) { 2385 testFunction(t, testFunctionConfig{ 2386 Cases: []testFunctionCase{ 2387 { 2388 `${substr("foobar", 0, 0)}`, 2389 "", 2390 false, 2391 }, 2392 { 2393 `${substr("foobar", 0, -1)}`, 2394 "foobar", 2395 false, 2396 }, 2397 { 2398 `${substr("foobar", 0, 3)}`, 2399 "foo", 2400 false, 2401 }, 2402 { 2403 `${substr("foobar", 3, 3)}`, 2404 "bar", 2405 false, 2406 }, 2407 { 2408 `${substr("foobar", -3, 3)}`, 2409 "bar", 2410 false, 2411 }, 2412 2413 // empty string 2414 { 2415 `${substr("", 0, 0)}`, 2416 "", 2417 false, 2418 }, 2419 2420 // invalid offset 2421 { 2422 `${substr("", 1, 0)}`, 2423 nil, 2424 true, 2425 }, 2426 2427 // invalid length 2428 { 2429 `${substr("", 0, 1)}`, 2430 nil, 2431 true, 2432 }, 2433 { 2434 `${substr("", 0, -2)}`, 2435 nil, 2436 true, 2437 }, 2438 }, 2439 }) 2440 } 2441 2442 func TestInterpolateFuncBcrypt(t *testing.T) { 2443 node, err := hil.Parse(`${bcrypt("test")}`) 2444 if err != nil { 2445 t.Fatalf("err: %s", err) 2446 } 2447 2448 result, err := hil.Eval(node, langEvalConfig(nil)) 2449 if err != nil { 2450 t.Fatalf("err: %s", err) 2451 } 2452 err = bcrypt.CompareHashAndPassword([]byte(result.Value.(string)), []byte("test")) 2453 2454 if err != nil { 2455 t.Fatalf("Error comparing hash and password: %s", err) 2456 } 2457 2458 testFunction(t, testFunctionConfig{ 2459 Cases: []testFunctionCase{ 2460 //Negative test for more than two parameters 2461 { 2462 `${bcrypt("test", 15, 12)}`, 2463 nil, 2464 true, 2465 }, 2466 }, 2467 }) 2468 }