github.com/medzin/terraform@v0.11.11/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 TestInterpolateFuncIndent(t *testing.T) { 1358 testFunction(t, testFunctionConfig{ 1359 Cases: []testFunctionCase{ 1360 { 1361 `${indent(4, "Fleas: 1362 Adam 1363 Had'em 1364 1365 E.E. Cummings")}`, 1366 "Fleas:\n Adam\n Had'em\n \n E.E. Cummings", 1367 false, 1368 }, 1369 { 1370 `${indent(4, "oneliner")}`, 1371 "oneliner", 1372 false, 1373 }, 1374 { 1375 `${indent(4, "#!/usr/bin/env bash 1376 date 1377 pwd")}`, 1378 "#!/usr/bin/env bash\n date\n pwd", 1379 false, 1380 }, 1381 }, 1382 }) 1383 } 1384 1385 func TestInterpolateFuncJoin(t *testing.T) { 1386 testFunction(t, testFunctionConfig{ 1387 Vars: map[string]ast.Variable{ 1388 "var.a_list": interfaceToVariableSwallowError([]string{"foo"}), 1389 "var.a_longer_list": interfaceToVariableSwallowError([]string{"foo", "bar", "baz"}), 1390 "var.list_of_lists": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"bar"}, []string{"baz"}}), 1391 }, 1392 Cases: []testFunctionCase{ 1393 { 1394 `${join(",")}`, 1395 nil, 1396 true, 1397 }, 1398 1399 { 1400 `${join(",", var.a_list)}`, 1401 "foo", 1402 false, 1403 }, 1404 1405 { 1406 `${join(".", var.a_longer_list)}`, 1407 "foo.bar.baz", 1408 false, 1409 }, 1410 1411 { 1412 `${join(".", var.list_of_lists)}`, 1413 nil, 1414 true, 1415 }, 1416 { 1417 `${join(".", list(list("nested")))}`, 1418 nil, 1419 true, 1420 }, 1421 }, 1422 }) 1423 } 1424 1425 func TestInterpolateFuncJSONEncode(t *testing.T) { 1426 testFunction(t, testFunctionConfig{ 1427 Vars: map[string]ast.Variable{ 1428 "easy": ast.Variable{ 1429 Value: "test", 1430 Type: ast.TypeString, 1431 }, 1432 "hard": ast.Variable{ 1433 Value: " foo \\ \n \t \" bar ", 1434 Type: ast.TypeString, 1435 }, 1436 "list": interfaceToVariableSwallowError([]string{"foo", "bar\tbaz"}), 1437 "emptylist": ast.Variable{ 1438 Value: []ast.Variable{}, 1439 Type: ast.TypeList, 1440 }, 1441 "map": interfaceToVariableSwallowError(map[string]string{ 1442 "foo": "bar", 1443 "ba \n z": "q\\x", 1444 }), 1445 "emptymap": interfaceToVariableSwallowError(map[string]string{}), 1446 "nestedlist": interfaceToVariableSwallowError([][]string{{"foo"}}), 1447 "nestedmap": interfaceToVariableSwallowError(map[string][]string{"foo": {"bar"}}), 1448 }, 1449 Cases: []testFunctionCase{ 1450 { 1451 `${jsonencode("test")}`, 1452 `"test"`, 1453 false, 1454 }, 1455 { 1456 `${jsonencode(easy)}`, 1457 `"test"`, 1458 false, 1459 }, 1460 { 1461 `${jsonencode(hard)}`, 1462 `" foo \\ \n \t \" bar "`, 1463 false, 1464 }, 1465 { 1466 `${jsonencode("")}`, 1467 `""`, 1468 false, 1469 }, 1470 { 1471 `${jsonencode()}`, 1472 nil, 1473 true, 1474 }, 1475 { 1476 `${jsonencode(list)}`, 1477 `["foo","bar\tbaz"]`, 1478 false, 1479 }, 1480 { 1481 `${jsonencode(emptylist)}`, 1482 `[]`, 1483 false, 1484 }, 1485 { 1486 `${jsonencode(map)}`, 1487 `{"ba \n z":"q\\x","foo":"bar"}`, 1488 false, 1489 }, 1490 { 1491 `${jsonencode(emptymap)}`, 1492 `{}`, 1493 false, 1494 }, 1495 { 1496 `${jsonencode(nestedlist)}`, 1497 `[["foo"]]`, 1498 false, 1499 }, 1500 { 1501 `${jsonencode(nestedmap)}`, 1502 `{"foo":["bar"]}`, 1503 false, 1504 }, 1505 }, 1506 }) 1507 } 1508 1509 func TestInterpolateFuncReplace(t *testing.T) { 1510 testFunction(t, testFunctionConfig{ 1511 Cases: []testFunctionCase{ 1512 // Regular search and replace 1513 { 1514 `${replace("hello", "hel", "bel")}`, 1515 "bello", 1516 false, 1517 }, 1518 1519 // Search string doesn't match 1520 { 1521 `${replace("hello", "nope", "bel")}`, 1522 "hello", 1523 false, 1524 }, 1525 1526 // Regular expression 1527 { 1528 `${replace("hello", "/l/", "L")}`, 1529 "heLLo", 1530 false, 1531 }, 1532 1533 { 1534 `${replace("helo", "/(l)/", "$1$1")}`, 1535 "hello", 1536 false, 1537 }, 1538 1539 // Bad regexp 1540 { 1541 `${replace("helo", "/(l/", "$1$1")}`, 1542 nil, 1543 true, 1544 }, 1545 }, 1546 }) 1547 } 1548 1549 func TestInterpolateFuncLength(t *testing.T) { 1550 testFunction(t, testFunctionConfig{ 1551 Cases: []testFunctionCase{ 1552 // Raw strings 1553 { 1554 `${length("")}`, 1555 "0", 1556 false, 1557 }, 1558 { 1559 `${length("a")}`, 1560 "1", 1561 false, 1562 }, 1563 { 1564 `${length(" ")}`, 1565 "1", 1566 false, 1567 }, 1568 { 1569 `${length(" a ,")}`, 1570 "4", 1571 false, 1572 }, 1573 { 1574 `${length("aaa")}`, 1575 "3", 1576 false, 1577 }, 1578 1579 // Lists 1580 { 1581 `${length(split(",", "a"))}`, 1582 "1", 1583 false, 1584 }, 1585 { 1586 `${length(split(",", "foo,"))}`, 1587 "2", 1588 false, 1589 }, 1590 { 1591 `${length(split(",", ",foo,"))}`, 1592 "3", 1593 false, 1594 }, 1595 { 1596 `${length(split(",", "foo,bar"))}`, 1597 "2", 1598 false, 1599 }, 1600 { 1601 `${length(split(".", "one.two.three.four.five"))}`, 1602 "5", 1603 false, 1604 }, 1605 // Want length 0 if we split an empty string then compact 1606 { 1607 `${length(compact(split(",", "")))}`, 1608 "0", 1609 false, 1610 }, 1611 // Works for maps 1612 { 1613 `${length(map("k", "v"))}`, 1614 "1", 1615 false, 1616 }, 1617 { 1618 `${length(map("k1", "v1", "k2", "v2"))}`, 1619 "2", 1620 false, 1621 }, 1622 }, 1623 }) 1624 } 1625 1626 func TestInterpolateFuncSignum(t *testing.T) { 1627 testFunction(t, testFunctionConfig{ 1628 Cases: []testFunctionCase{ 1629 { 1630 `${signum()}`, 1631 nil, 1632 true, 1633 }, 1634 1635 { 1636 `${signum("")}`, 1637 nil, 1638 true, 1639 }, 1640 1641 { 1642 `${signum(0)}`, 1643 "0", 1644 false, 1645 }, 1646 1647 { 1648 `${signum(15)}`, 1649 "1", 1650 false, 1651 }, 1652 1653 { 1654 `${signum(-29)}`, 1655 "-1", 1656 false, 1657 }, 1658 }, 1659 }) 1660 } 1661 1662 func TestInterpolateFuncSlice(t *testing.T) { 1663 testFunction(t, testFunctionConfig{ 1664 Cases: []testFunctionCase{ 1665 // Negative from index 1666 { 1667 `${slice(list("a"), -1, 0)}`, 1668 nil, 1669 true, 1670 }, 1671 // From index > to index 1672 { 1673 `${slice(list("a", "b", "c"), 2, 1)}`, 1674 nil, 1675 true, 1676 }, 1677 // To index too large 1678 { 1679 `${slice(var.list_of_strings, 1, 4)}`, 1680 nil, 1681 true, 1682 }, 1683 // Empty slice 1684 { 1685 `${slice(var.list_of_strings, 1, 1)}`, 1686 []interface{}{}, 1687 false, 1688 }, 1689 { 1690 `${slice(var.list_of_strings, 1, 2)}`, 1691 []interface{}{"b"}, 1692 false, 1693 }, 1694 { 1695 `${slice(var.list_of_strings, 0, length(var.list_of_strings) - 1)}`, 1696 []interface{}{"a", "b"}, 1697 false, 1698 }, 1699 }, 1700 Vars: map[string]ast.Variable{ 1701 "var.list_of_strings": { 1702 Type: ast.TypeList, 1703 Value: []ast.Variable{ 1704 { 1705 Type: ast.TypeString, 1706 Value: "a", 1707 }, 1708 { 1709 Type: ast.TypeString, 1710 Value: "b", 1711 }, 1712 { 1713 Type: ast.TypeString, 1714 Value: "c", 1715 }, 1716 }, 1717 }, 1718 }, 1719 }) 1720 } 1721 1722 func TestInterpolateFuncSort(t *testing.T) { 1723 testFunction(t, testFunctionConfig{ 1724 Vars: map[string]ast.Variable{ 1725 "var.strings": ast.Variable{ 1726 Type: ast.TypeList, 1727 Value: []ast.Variable{ 1728 {Type: ast.TypeString, Value: "c"}, 1729 {Type: ast.TypeString, Value: "a"}, 1730 {Type: ast.TypeString, Value: "b"}, 1731 }, 1732 }, 1733 "var.notstrings": ast.Variable{ 1734 Type: ast.TypeList, 1735 Value: []ast.Variable{ 1736 {Type: ast.TypeList, Value: []ast.Variable{}}, 1737 {Type: ast.TypeString, Value: "b"}, 1738 }, 1739 }, 1740 }, 1741 Cases: []testFunctionCase{ 1742 { 1743 `${sort(var.strings)}`, 1744 []interface{}{"a", "b", "c"}, 1745 false, 1746 }, 1747 { 1748 `${sort(var.notstrings)}`, 1749 nil, 1750 true, 1751 }, 1752 }, 1753 }) 1754 } 1755 1756 func TestInterpolateFuncSplit(t *testing.T) { 1757 testFunction(t, testFunctionConfig{ 1758 Cases: []testFunctionCase{ 1759 { 1760 `${split(",")}`, 1761 nil, 1762 true, 1763 }, 1764 1765 { 1766 `${split(",", "")}`, 1767 []interface{}{""}, 1768 false, 1769 }, 1770 1771 { 1772 `${split(",", "foo")}`, 1773 []interface{}{"foo"}, 1774 false, 1775 }, 1776 1777 { 1778 `${split(",", ",,,")}`, 1779 []interface{}{"", "", "", ""}, 1780 false, 1781 }, 1782 1783 { 1784 `${split(",", "foo,")}`, 1785 []interface{}{"foo", ""}, 1786 false, 1787 }, 1788 1789 { 1790 `${split(",", ",foo,")}`, 1791 []interface{}{"", "foo", ""}, 1792 false, 1793 }, 1794 1795 { 1796 `${split(".", "foo.bar.baz")}`, 1797 []interface{}{"foo", "bar", "baz"}, 1798 false, 1799 }, 1800 }, 1801 }) 1802 } 1803 1804 func TestInterpolateFuncLookup(t *testing.T) { 1805 testFunction(t, testFunctionConfig{ 1806 Vars: map[string]ast.Variable{ 1807 "var.foo": { 1808 Type: ast.TypeMap, 1809 Value: map[string]ast.Variable{ 1810 "bar": { 1811 Type: ast.TypeString, 1812 Value: "baz", 1813 }, 1814 }, 1815 }, 1816 "var.map_of_lists": ast.Variable{ 1817 Type: ast.TypeMap, 1818 Value: map[string]ast.Variable{ 1819 "bar": { 1820 Type: ast.TypeList, 1821 Value: []ast.Variable{ 1822 { 1823 Type: ast.TypeString, 1824 Value: "baz", 1825 }, 1826 }, 1827 }, 1828 }, 1829 }, 1830 }, 1831 Cases: []testFunctionCase{ 1832 { 1833 `${lookup(var.foo, "bar")}`, 1834 "baz", 1835 false, 1836 }, 1837 1838 // Invalid key 1839 { 1840 `${lookup(var.foo, "baz")}`, 1841 nil, 1842 true, 1843 }, 1844 1845 // Supplied default with valid key 1846 { 1847 `${lookup(var.foo, "bar", "")}`, 1848 "baz", 1849 false, 1850 }, 1851 1852 // Supplied default with invalid key 1853 { 1854 `${lookup(var.foo, "zip", "")}`, 1855 "", 1856 false, 1857 }, 1858 1859 // Too many args 1860 { 1861 `${lookup(var.foo, "bar", "", "abc")}`, 1862 nil, 1863 true, 1864 }, 1865 1866 // Cannot lookup into map of lists 1867 { 1868 `${lookup(var.map_of_lists, "bar")}`, 1869 nil, 1870 true, 1871 }, 1872 1873 // Non-empty default 1874 { 1875 `${lookup(var.foo, "zap", "xyz")}`, 1876 "xyz", 1877 false, 1878 }, 1879 }, 1880 }) 1881 } 1882 1883 func TestInterpolateFuncKeys(t *testing.T) { 1884 testFunction(t, testFunctionConfig{ 1885 Vars: map[string]ast.Variable{ 1886 "var.foo": ast.Variable{ 1887 Type: ast.TypeMap, 1888 Value: map[string]ast.Variable{ 1889 "bar": ast.Variable{ 1890 Value: "baz", 1891 Type: ast.TypeString, 1892 }, 1893 "qux": ast.Variable{ 1894 Value: "quack", 1895 Type: ast.TypeString, 1896 }, 1897 }, 1898 }, 1899 "var.str": ast.Variable{ 1900 Value: "astring", 1901 Type: ast.TypeString, 1902 }, 1903 }, 1904 Cases: []testFunctionCase{ 1905 { 1906 `${keys(var.foo)}`, 1907 []interface{}{"bar", "qux"}, 1908 false, 1909 }, 1910 1911 // Invalid key 1912 { 1913 `${keys(var.not)}`, 1914 nil, 1915 true, 1916 }, 1917 1918 // Too many args 1919 { 1920 `${keys(var.foo, "bar")}`, 1921 nil, 1922 true, 1923 }, 1924 1925 // Not a map 1926 { 1927 `${keys(var.str)}`, 1928 nil, 1929 true, 1930 }, 1931 }, 1932 }) 1933 } 1934 1935 // Confirm that keys return in sorted order, and values return in the order of 1936 // their sorted keys. 1937 func TestInterpolateFuncKeyValOrder(t *testing.T) { 1938 testFunction(t, testFunctionConfig{ 1939 Vars: map[string]ast.Variable{ 1940 "var.foo": ast.Variable{ 1941 Type: ast.TypeMap, 1942 Value: map[string]ast.Variable{ 1943 "D": ast.Variable{ 1944 Value: "2", 1945 Type: ast.TypeString, 1946 }, 1947 "C": ast.Variable{ 1948 Value: "Y", 1949 Type: ast.TypeString, 1950 }, 1951 "A": ast.Variable{ 1952 Value: "X", 1953 Type: ast.TypeString, 1954 }, 1955 "10": ast.Variable{ 1956 Value: "Z", 1957 Type: ast.TypeString, 1958 }, 1959 "1": ast.Variable{ 1960 Value: "4", 1961 Type: ast.TypeString, 1962 }, 1963 "3": ast.Variable{ 1964 Value: "W", 1965 Type: ast.TypeString, 1966 }, 1967 }, 1968 }, 1969 }, 1970 Cases: []testFunctionCase{ 1971 { 1972 `${keys(var.foo)}`, 1973 []interface{}{"1", "10", "3", "A", "C", "D"}, 1974 false, 1975 }, 1976 1977 { 1978 `${values(var.foo)}`, 1979 []interface{}{"4", "Z", "W", "X", "Y", "2"}, 1980 false, 1981 }, 1982 }, 1983 }) 1984 } 1985 1986 func TestInterpolateFuncValues(t *testing.T) { 1987 testFunction(t, testFunctionConfig{ 1988 Vars: map[string]ast.Variable{ 1989 "var.foo": ast.Variable{ 1990 Type: ast.TypeMap, 1991 Value: map[string]ast.Variable{ 1992 "bar": ast.Variable{ 1993 Value: "quack", 1994 Type: ast.TypeString, 1995 }, 1996 "qux": ast.Variable{ 1997 Value: "baz", 1998 Type: ast.TypeString, 1999 }, 2000 }, 2001 }, 2002 "var.str": ast.Variable{ 2003 Value: "astring", 2004 Type: ast.TypeString, 2005 }, 2006 }, 2007 Cases: []testFunctionCase{ 2008 { 2009 `${values(var.foo)}`, 2010 []interface{}{"quack", "baz"}, 2011 false, 2012 }, 2013 2014 // Invalid key 2015 { 2016 `${values(var.not)}`, 2017 nil, 2018 true, 2019 }, 2020 2021 // Too many args 2022 { 2023 `${values(var.foo, "bar")}`, 2024 nil, 2025 true, 2026 }, 2027 2028 // Not a map 2029 { 2030 `${values(var.str)}`, 2031 nil, 2032 true, 2033 }, 2034 2035 // Map of lists 2036 { 2037 `${values(map("one", list()))}`, 2038 nil, 2039 true, 2040 }, 2041 }, 2042 }) 2043 } 2044 2045 func interfaceToVariableSwallowError(input interface{}) ast.Variable { 2046 variable, _ := hil.InterfaceToVariable(input) 2047 return variable 2048 } 2049 2050 func TestInterpolateFuncElement(t *testing.T) { 2051 testFunction(t, testFunctionConfig{ 2052 Vars: map[string]ast.Variable{ 2053 "var.a_list": interfaceToVariableSwallowError([]string{"foo", "baz"}), 2054 "var.a_short_list": interfaceToVariableSwallowError([]string{"foo"}), 2055 "var.empty_list": interfaceToVariableSwallowError([]interface{}{}), 2056 "var.a_nested_list": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"baz"}}), 2057 }, 2058 Cases: []testFunctionCase{ 2059 { 2060 `${element(var.a_list, "1")}`, 2061 "baz", 2062 false, 2063 }, 2064 2065 { 2066 `${element(var.a_short_list, "0")}`, 2067 "foo", 2068 false, 2069 }, 2070 2071 // Invalid index should wrap vs. out-of-bounds 2072 { 2073 `${element(var.a_list, "2")}`, 2074 "foo", 2075 false, 2076 }, 2077 2078 // Negative number should fail 2079 { 2080 `${element(var.a_short_list, "-1")}`, 2081 nil, 2082 true, 2083 }, 2084 2085 // Empty list should fail 2086 { 2087 `${element(var.empty_list, 0)}`, 2088 nil, 2089 true, 2090 }, 2091 2092 // Too many args 2093 { 2094 `${element(var.a_list, "0", "2")}`, 2095 nil, 2096 true, 2097 }, 2098 2099 // Only works on single-level lists 2100 { 2101 `${element(var.a_nested_list, "0")}`, 2102 nil, 2103 true, 2104 }, 2105 }, 2106 }) 2107 } 2108 2109 func TestInterpolateFuncChunklist(t *testing.T) { 2110 testFunction(t, testFunctionConfig{ 2111 Cases: []testFunctionCase{ 2112 // normal usage 2113 { 2114 `${chunklist(list("a", "b", "c"), 1)}`, 2115 []interface{}{ 2116 []interface{}{"a"}, 2117 []interface{}{"b"}, 2118 []interface{}{"c"}, 2119 }, 2120 false, 2121 }, 2122 // `size` is pair and the list has an impair number of items 2123 { 2124 `${chunklist(list("a", "b", "c"), 2)}`, 2125 []interface{}{ 2126 []interface{}{"a", "b"}, 2127 []interface{}{"c"}, 2128 }, 2129 false, 2130 }, 2131 // list made of the same list, since size is 0 2132 { 2133 `${chunklist(list("a", "b", "c"), 0)}`, 2134 []interface{}{[]interface{}{"a", "b", "c"}}, 2135 false, 2136 }, 2137 // negative size of chunks 2138 { 2139 `${chunklist(list("a", "b", "c"), -1)}`, 2140 nil, 2141 true, 2142 }, 2143 }, 2144 }) 2145 } 2146 2147 func TestInterpolateFuncBasename(t *testing.T) { 2148 testFunction(t, testFunctionConfig{ 2149 Cases: []testFunctionCase{ 2150 { 2151 `${basename("/foo/bar/baz")}`, 2152 "baz", 2153 false, 2154 }, 2155 }, 2156 }) 2157 } 2158 2159 func TestInterpolateFuncBase64Encode(t *testing.T) { 2160 testFunction(t, testFunctionConfig{ 2161 Cases: []testFunctionCase{ 2162 // Regular base64 encoding 2163 { 2164 `${base64encode("abc123!?$*&()'-=@~")}`, 2165 "YWJjMTIzIT8kKiYoKSctPUB+", 2166 false, 2167 }, 2168 }, 2169 }) 2170 } 2171 2172 func TestInterpolateFuncBase64Decode(t *testing.T) { 2173 testFunction(t, testFunctionConfig{ 2174 Cases: []testFunctionCase{ 2175 // Regular base64 decoding 2176 { 2177 `${base64decode("YWJjMTIzIT8kKiYoKSctPUB+")}`, 2178 "abc123!?$*&()'-=@~", 2179 false, 2180 }, 2181 2182 // Invalid base64 data decoding 2183 { 2184 `${base64decode("this-is-an-invalid-base64-data")}`, 2185 nil, 2186 true, 2187 }, 2188 }, 2189 }) 2190 } 2191 2192 func TestInterpolateFuncLower(t *testing.T) { 2193 testFunction(t, testFunctionConfig{ 2194 Cases: []testFunctionCase{ 2195 { 2196 `${lower("HELLO")}`, 2197 "hello", 2198 false, 2199 }, 2200 2201 { 2202 `${lower("")}`, 2203 "", 2204 false, 2205 }, 2206 2207 { 2208 `${lower()}`, 2209 nil, 2210 true, 2211 }, 2212 }, 2213 }) 2214 } 2215 2216 func TestInterpolateFuncUpper(t *testing.T) { 2217 testFunction(t, testFunctionConfig{ 2218 Cases: []testFunctionCase{ 2219 { 2220 `${upper("hello")}`, 2221 "HELLO", 2222 false, 2223 }, 2224 2225 { 2226 `${upper("")}`, 2227 "", 2228 false, 2229 }, 2230 2231 { 2232 `${upper()}`, 2233 nil, 2234 true, 2235 }, 2236 }, 2237 }) 2238 } 2239 2240 func TestInterpolateFuncSha1(t *testing.T) { 2241 testFunction(t, testFunctionConfig{ 2242 Cases: []testFunctionCase{ 2243 { 2244 `${sha1("test")}`, 2245 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", 2246 false, 2247 }, 2248 }, 2249 }) 2250 } 2251 2252 func TestInterpolateFuncSha256(t *testing.T) { 2253 testFunction(t, testFunctionConfig{ 2254 Cases: []testFunctionCase{ 2255 { // hexadecimal representation of sha256 sum 2256 `${sha256("test")}`, 2257 "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", 2258 false, 2259 }, 2260 }, 2261 }) 2262 } 2263 2264 func TestInterpolateFuncSha512(t *testing.T) { 2265 testFunction(t, testFunctionConfig{ 2266 Cases: []testFunctionCase{ 2267 { 2268 `${sha512("test")}`, 2269 "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff", 2270 false, 2271 }, 2272 }, 2273 }) 2274 } 2275 2276 func TestInterpolateFuncTitle(t *testing.T) { 2277 testFunction(t, testFunctionConfig{ 2278 Cases: []testFunctionCase{ 2279 { 2280 `${title("hello")}`, 2281 "Hello", 2282 false, 2283 }, 2284 2285 { 2286 `${title("hello world")}`, 2287 "Hello World", 2288 false, 2289 }, 2290 2291 { 2292 `${title("")}`, 2293 "", 2294 false, 2295 }, 2296 2297 { 2298 `${title()}`, 2299 nil, 2300 true, 2301 }, 2302 }, 2303 }) 2304 } 2305 2306 func TestInterpolateFuncTrimSpace(t *testing.T) { 2307 testFunction(t, testFunctionConfig{ 2308 Cases: []testFunctionCase{ 2309 { 2310 `${trimspace(" test ")}`, 2311 "test", 2312 false, 2313 }, 2314 }, 2315 }) 2316 } 2317 2318 func TestInterpolateFuncBase64Gzip(t *testing.T) { 2319 testFunction(t, testFunctionConfig{ 2320 Cases: []testFunctionCase{ 2321 { 2322 `${base64gzip("test")}`, 2323 "H4sIAAAAAAAA/ypJLS4BAAAA//8BAAD//wx+f9gEAAAA", 2324 false, 2325 }, 2326 }, 2327 }) 2328 } 2329 2330 func TestInterpolateFuncBase64Sha256(t *testing.T) { 2331 testFunction(t, testFunctionConfig{ 2332 Cases: []testFunctionCase{ 2333 { 2334 `${base64sha256("test")}`, 2335 "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=", 2336 false, 2337 }, 2338 { // This will differ because we're base64-encoding hex represantiation, not raw bytes 2339 `${base64encode(sha256("test"))}`, 2340 "OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZjMTViMGYwMGEwOA==", 2341 false, 2342 }, 2343 }, 2344 }) 2345 } 2346 2347 func TestInterpolateFuncBase64Sha512(t *testing.T) { 2348 testFunction(t, testFunctionConfig{ 2349 Cases: []testFunctionCase{ 2350 { 2351 `${base64sha512("test")}`, 2352 "7iaw3Ur350mqGo7jwQrpkj9hiYB3Lkc/iBml1JQODbJ6wYX4oOHV+E+IvIh/1nsUNzLDBMxfqa2Ob1f1ACio/w==", 2353 false, 2354 }, 2355 { // This will differ because we're base64-encoding hex represantiation, not raw bytes 2356 `${base64encode(sha512("test"))}`, 2357 "ZWUyNmIwZGQ0YWY3ZTc0OWFhMWE4ZWUzYzEwYWU5OTIzZjYxODk4MDc3MmU0NzNmODgxOWE1ZDQ5NDBlMGRiMjdhYzE4NWY4YTBlMWQ1Zjg0Zjg4YmM4ODdmZDY3YjE0MzczMmMzMDRjYzVmYTlhZDhlNmY1N2Y1MDAyOGE4ZmY=", 2358 false, 2359 }, 2360 }, 2361 }) 2362 } 2363 2364 func TestInterpolateFuncMd5(t *testing.T) { 2365 testFunction(t, testFunctionConfig{ 2366 Cases: []testFunctionCase{ 2367 { 2368 `${md5("tada")}`, 2369 "ce47d07243bb6eaf5e1322c81baf9bbf", 2370 false, 2371 }, 2372 { // Confirm that we're not trimming any whitespaces 2373 `${md5(" tada ")}`, 2374 "aadf191a583e53062de2d02c008141c4", 2375 false, 2376 }, 2377 { // We accept empty string too 2378 `${md5("")}`, 2379 "d41d8cd98f00b204e9800998ecf8427e", 2380 false, 2381 }, 2382 }, 2383 }) 2384 } 2385 2386 func TestInterpolateFuncUUID(t *testing.T) { 2387 results := make(map[string]bool) 2388 2389 for i := 0; i < 100; i++ { 2390 ast, err := hil.Parse("${uuid()}") 2391 if err != nil { 2392 t.Fatalf("err: %s", err) 2393 } 2394 2395 result, err := hil.Eval(ast, langEvalConfig(nil)) 2396 if err != nil { 2397 t.Fatalf("err: %s", err) 2398 } 2399 2400 if results[result.Value.(string)] { 2401 t.Fatalf("Got unexpected duplicate uuid: %s", result.Value) 2402 } 2403 2404 results[result.Value.(string)] = true 2405 } 2406 } 2407 2408 func TestInterpolateFuncTimestamp(t *testing.T) { 2409 currentTime := time.Now().UTC() 2410 ast, err := hil.Parse("${timestamp()}") 2411 if err != nil { 2412 t.Fatalf("err: %s", err) 2413 } 2414 2415 result, err := hil.Eval(ast, langEvalConfig(nil)) 2416 if err != nil { 2417 t.Fatalf("err: %s", err) 2418 } 2419 resultTime, err := time.Parse(time.RFC3339, result.Value.(string)) 2420 if err != nil { 2421 t.Fatalf("Error parsing timestamp: %s", err) 2422 } 2423 2424 if resultTime.Sub(currentTime).Seconds() > 10.0 { 2425 t.Fatalf("Timestamp Diff too large. Expected: %s\nReceived: %s", currentTime.Format(time.RFC3339), result.Value.(string)) 2426 } 2427 } 2428 2429 func TestInterpolateFuncTimeAdd(t *testing.T) { 2430 testFunction(t, testFunctionConfig{ 2431 Cases: []testFunctionCase{ 2432 { 2433 `${timeadd("2017-11-22T00:00:00Z", "1s")}`, 2434 "2017-11-22T00:00:01Z", 2435 false, 2436 }, 2437 { 2438 `${timeadd("2017-11-22T00:00:00Z", "10m1s")}`, 2439 "2017-11-22T00:10:01Z", 2440 false, 2441 }, 2442 { // also support subtraction 2443 `${timeadd("2017-11-22T00:00:00Z", "-1h")}`, 2444 "2017-11-21T23:00:00Z", 2445 false, 2446 }, 2447 { // Invalid format timestamp 2448 `${timeadd("2017-11-22", "-1h")}`, 2449 nil, 2450 true, 2451 }, 2452 { // Invalid format duration (day is not supported by ParseDuration) 2453 `${timeadd("2017-11-22T00:00:00Z", "1d")}`, 2454 nil, 2455 true, 2456 }, 2457 }, 2458 }) 2459 } 2460 2461 type testFunctionConfig struct { 2462 Cases []testFunctionCase 2463 Vars map[string]ast.Variable 2464 } 2465 2466 type testFunctionCase struct { 2467 Input string 2468 Result interface{} 2469 Error bool 2470 } 2471 2472 func testFunction(t *testing.T, config testFunctionConfig) { 2473 t.Helper() 2474 for _, tc := range config.Cases { 2475 t.Run(tc.Input, func(t *testing.T) { 2476 ast, err := hil.Parse(tc.Input) 2477 if err != nil { 2478 t.Fatalf("unexpected parse error: %s", err) 2479 } 2480 2481 result, err := hil.Eval(ast, langEvalConfig(config.Vars)) 2482 if err != nil != tc.Error { 2483 t.Fatalf("unexpected eval error: %s", err) 2484 } 2485 2486 if !reflect.DeepEqual(result.Value, tc.Result) { 2487 t.Errorf("wrong result\ngiven: %s\ngot: %#v\nwant: %#v", tc.Input, result.Value, tc.Result) 2488 } 2489 }) 2490 } 2491 } 2492 2493 func TestInterpolateFuncPathExpand(t *testing.T) { 2494 homePath, err := homedir.Dir() 2495 if err != nil { 2496 t.Fatalf("Error getting home directory: %v", err) 2497 } 2498 testFunction(t, testFunctionConfig{ 2499 Cases: []testFunctionCase{ 2500 { 2501 `${pathexpand("~/test-file")}`, 2502 filepath.Join(homePath, "test-file"), 2503 false, 2504 }, 2505 { 2506 `${pathexpand("~/another/test/file")}`, 2507 filepath.Join(homePath, "another/test/file"), 2508 false, 2509 }, 2510 { 2511 `${pathexpand("/root/file")}`, 2512 "/root/file", 2513 false, 2514 }, 2515 { 2516 `${pathexpand("/")}`, 2517 "/", 2518 false, 2519 }, 2520 { 2521 `${pathexpand()}`, 2522 nil, 2523 true, 2524 }, 2525 }, 2526 }) 2527 } 2528 2529 func TestInterpolateFuncSubstr(t *testing.T) { 2530 testFunction(t, testFunctionConfig{ 2531 Cases: []testFunctionCase{ 2532 { 2533 `${substr("foobar", 0, 0)}`, 2534 "", 2535 false, 2536 }, 2537 { 2538 `${substr("foobar", 0, -1)}`, 2539 "foobar", 2540 false, 2541 }, 2542 { 2543 `${substr("foobar", 0, 3)}`, 2544 "foo", 2545 false, 2546 }, 2547 { 2548 `${substr("foobar", 3, 3)}`, 2549 "bar", 2550 false, 2551 }, 2552 { 2553 `${substr("foobar", -3, 3)}`, 2554 "bar", 2555 false, 2556 }, 2557 2558 // empty string 2559 { 2560 `${substr("", 0, 0)}`, 2561 "", 2562 false, 2563 }, 2564 2565 // invalid offset 2566 { 2567 `${substr("", 1, 0)}`, 2568 nil, 2569 true, 2570 }, 2571 { 2572 `${substr("foo", -4, -1)}`, 2573 nil, 2574 true, 2575 }, 2576 2577 // invalid length 2578 { 2579 `${substr("", 0, 1)}`, 2580 nil, 2581 true, 2582 }, 2583 { 2584 `${substr("", 0, -2)}`, 2585 nil, 2586 true, 2587 }, 2588 }, 2589 }) 2590 } 2591 2592 func TestInterpolateFuncBcrypt(t *testing.T) { 2593 node, err := hil.Parse(`${bcrypt("test")}`) 2594 if err != nil { 2595 t.Fatalf("err: %s", err) 2596 } 2597 2598 result, err := hil.Eval(node, langEvalConfig(nil)) 2599 if err != nil { 2600 t.Fatalf("err: %s", err) 2601 } 2602 err = bcrypt.CompareHashAndPassword([]byte(result.Value.(string)), []byte("test")) 2603 2604 if err != nil { 2605 t.Fatalf("Error comparing hash and password: %s", err) 2606 } 2607 2608 testFunction(t, testFunctionConfig{ 2609 Cases: []testFunctionCase{ 2610 //Negative test for more than two parameters 2611 { 2612 `${bcrypt("test", 15, 12)}`, 2613 nil, 2614 true, 2615 }, 2616 }, 2617 }) 2618 } 2619 2620 func TestInterpolateFuncFlatten(t *testing.T) { 2621 testFunction(t, testFunctionConfig{ 2622 Cases: []testFunctionCase{ 2623 // empty string within array 2624 { 2625 `${flatten(split(",", "a,,b"))}`, 2626 []interface{}{"a", "", "b"}, 2627 false, 2628 }, 2629 2630 // typical array 2631 { 2632 `${flatten(split(",", "a,b,c"))}`, 2633 []interface{}{"a", "b", "c"}, 2634 false, 2635 }, 2636 2637 // empty array 2638 { 2639 `${flatten(split(",", ""))}`, 2640 []interface{}{""}, 2641 false, 2642 }, 2643 2644 // list of lists 2645 { 2646 `${flatten(list(list("a"), list("b")))}`, 2647 []interface{}{"a", "b"}, 2648 false, 2649 }, 2650 // list of lists of lists 2651 { 2652 `${flatten(list(list("a"), list(list("b","c"))))}`, 2653 []interface{}{"a", "b", "c"}, 2654 false, 2655 }, 2656 // list of strings 2657 { 2658 `${flatten(list("a", "b", "c"))}`, 2659 []interface{}{"a", "b", "c"}, 2660 false, 2661 }, 2662 }, 2663 }) 2664 } 2665 2666 func TestInterpolateFuncURLEncode(t *testing.T) { 2667 testFunction(t, testFunctionConfig{ 2668 Cases: []testFunctionCase{ 2669 { 2670 `${urlencode("abc123-_")}`, 2671 "abc123-_", 2672 false, 2673 }, 2674 { 2675 `${urlencode("foo:bar@localhost?foo=bar&bar=baz")}`, 2676 "foo%3Abar%40localhost%3Ffoo%3Dbar%26bar%3Dbaz", 2677 false, 2678 }, 2679 { 2680 `${urlencode("mailto:email?subject=this+is+my+subject")}`, 2681 "mailto%3Aemail%3Fsubject%3Dthis%2Bis%2Bmy%2Bsubject", 2682 false, 2683 }, 2684 { 2685 `${urlencode("foo/bar")}`, 2686 "foo%2Fbar", 2687 false, 2688 }, 2689 }, 2690 }) 2691 } 2692 2693 func TestInterpolateFuncTranspose(t *testing.T) { 2694 testFunction(t, testFunctionConfig{ 2695 Vars: map[string]ast.Variable{ 2696 "var.map": ast.Variable{ 2697 Type: ast.TypeMap, 2698 Value: map[string]ast.Variable{ 2699 "key1": ast.Variable{ 2700 Type: ast.TypeList, 2701 Value: []ast.Variable{ 2702 {Type: ast.TypeString, Value: "a"}, 2703 {Type: ast.TypeString, Value: "b"}, 2704 }, 2705 }, 2706 "key2": ast.Variable{ 2707 Type: ast.TypeList, 2708 Value: []ast.Variable{ 2709 {Type: ast.TypeString, Value: "a"}, 2710 {Type: ast.TypeString, Value: "b"}, 2711 {Type: ast.TypeString, Value: "c"}, 2712 }, 2713 }, 2714 "key3": ast.Variable{ 2715 Type: ast.TypeList, 2716 Value: []ast.Variable{ 2717 {Type: ast.TypeString, Value: "c"}, 2718 }, 2719 }, 2720 "key4": ast.Variable{ 2721 Type: ast.TypeList, 2722 Value: []ast.Variable{}, 2723 }, 2724 }}, 2725 "var.badmap": ast.Variable{ 2726 Type: ast.TypeMap, 2727 Value: map[string]ast.Variable{ 2728 "key1": ast.Variable{ 2729 Type: ast.TypeList, 2730 Value: []ast.Variable{ 2731 {Type: ast.TypeList, Value: []ast.Variable{}}, 2732 {Type: ast.TypeList, Value: []ast.Variable{}}, 2733 }, 2734 }, 2735 }}, 2736 "var.worsemap": ast.Variable{ 2737 Type: ast.TypeMap, 2738 Value: map[string]ast.Variable{ 2739 "key1": ast.Variable{ 2740 Type: ast.TypeString, 2741 Value: "not-a-list", 2742 }, 2743 }}, 2744 }, 2745 Cases: []testFunctionCase{ 2746 { 2747 `${transpose(var.map)}`, 2748 map[string]interface{}{ 2749 "a": []interface{}{"key1", "key2"}, 2750 "b": []interface{}{"key1", "key2"}, 2751 "c": []interface{}{"key2", "key3"}, 2752 }, 2753 false, 2754 }, 2755 { 2756 `${transpose(var.badmap)}`, 2757 nil, 2758 true, 2759 }, 2760 { 2761 `${transpose(var.worsemap)}`, 2762 nil, 2763 true, 2764 }, 2765 }, 2766 }) 2767 } 2768 2769 func TestInterpolateFuncAbs(t *testing.T) { 2770 testFunction(t, testFunctionConfig{ 2771 Cases: []testFunctionCase{ 2772 { 2773 `${abs()}`, 2774 nil, 2775 true, 2776 }, 2777 { 2778 `${abs("")}`, 2779 nil, 2780 true, 2781 }, 2782 { 2783 `${abs(0)}`, 2784 "0", 2785 false, 2786 }, 2787 { 2788 `${abs(1)}`, 2789 "1", 2790 false, 2791 }, 2792 { 2793 `${abs(-1)}`, 2794 "1", 2795 false, 2796 }, 2797 { 2798 `${abs(1.0)}`, 2799 "1", 2800 false, 2801 }, 2802 { 2803 `${abs(-1.0)}`, 2804 "1", 2805 false, 2806 }, 2807 { 2808 `${abs(-3.14)}`, 2809 "3.14", 2810 false, 2811 }, 2812 { 2813 `${abs(-42.001)}`, 2814 "42.001", 2815 false, 2816 }, 2817 }, 2818 }) 2819 } 2820 2821 func TestInterpolateFuncRsaDecrypt(t *testing.T) { 2822 testFunction(t, testFunctionConfig{ 2823 Vars: map[string]ast.Variable{ 2824 "var.cipher_base64": ast.Variable{ 2825 Type: ast.TypeString, 2826 Value: "eczGaDhXDbOFRZGhjx2etVzWbRqWDlmq0bvNt284JHVbwCgObiuyX9uV0LSAMY707IEgMkExJqXmsB4OWKxvB7epRB9G/3+F+pcrQpODlDuL9oDUAsa65zEpYF0Wbn7Oh7nrMQncyUPpyr9WUlALl0gRWytOA23S+y5joa4M34KFpawFgoqTu/2EEH4Xl1zo+0fy73fEto+nfkUY+meuyGZ1nUx/+DljP7ZqxHBFSlLODmtuTMdswUbHbXbWneW51D7Jm7xB8nSdiA2JQNK5+Sg5x8aNfgvFTt/m2w2+qpsyFa5Wjeu6fZmXSl840CA07aXbk9vN4I81WmJyblD/ZA==", 2827 }, 2828 "var.private_key": ast.Variable{ 2829 Type: ast.TypeString, 2830 Value: ` 2831 -----BEGIN RSA PRIVATE KEY----- 2832 MIIEowIBAAKCAQEAgUElV5mwqkloIrM8ZNZ72gSCcnSJt7+/Usa5G+D15YQUAdf9 2833 c1zEekTfHgDP+04nw/uFNFaE5v1RbHaPxhZYVg5ZErNCa/hzn+x10xzcepeS3KPV 2834 Xcxae4MR0BEegvqZqJzN9loXsNL/c3H/B+2Gle3hTxjlWFb3F5qLgR+4Mf4ruhER 2835 1v6eHQa/nchi03MBpT4UeJ7MrL92hTJYLdpSyCqmr8yjxkKJDVC2uRrr+sTSxfh7 2836 r6v24u/vp/QTmBIAlNPgadVAZw17iNNb7vjV7Gwl/5gHXonCUKURaV++dBNLrHIZ 2837 pqcAM8wHRph8mD1EfL9hsz77pHewxolBATV+7QIDAQABAoIBAC1rK+kFW3vrAYm3 2838 +8/fQnQQw5nec4o6+crng6JVQXLeH32qXShNf8kLLG/Jj0vaYcTPPDZw9JCKkTMQ 2839 0mKj9XR/5DLbBMsV6eNXXuvJJ3x4iKW5eD9WkLD4FKlNarBRyO7j8sfPTqXW7uat 2840 NxWdFH7YsSRvNh/9pyQHLWA5OituidMrYbc3EUx8B1GPNyJ9W8Q8znNYLfwYOjU4 2841 Wv1SLE6qGQQH9Q0WzA2WUf8jklCYyMYTIywAjGb8kbAJlKhmj2t2Igjmqtwt1PYc 2842 pGlqbtQBDUiWXt5S4YX/1maIQ/49yeNUajjpbJiH3DbhJbHwFTzP3pZ9P9GHOzlG 2843 kYR+wSECgYEAw/Xida8kSv8n86V3qSY/I+fYQ5V+jDtXIE+JhRnS8xzbOzz3v0WS 2844 Oo5H+o4nJx5eL3Ghb3Gcm0Jn46dHrxinHbm+3RjXv/X6tlbxIYjRSQfHOTSMCTvd 2845 qcliF5vC6RCLXuc7R+IWR1Ky6eDEZGtrvt3DyeYABsp9fRUFR/6NluUCgYEAqNsw 2846 1aSl7WJa27F0DoJdlU9LWerpXcazlJcIdOz/S9QDmSK3RDQTdqfTxRmrxiYI9LEs 2847 mkOkvzlnnOBMpnZ3ZOU5qIRfprecRIi37KDAOHWGnlC0EWGgl46YLb7/jXiWf0AG 2848 Y+DfJJNd9i6TbIDWu8254/erAS6bKMhW/3q7f2kCgYAZ7Id/BiKJAWRpqTRBXlvw 2849 BhXoKvjI2HjYP21z/EyZ+PFPzur/lNaZhIUlMnUfibbwE9pFggQzzf8scM7c7Sf+ 2850 mLoVSdoQ/Rujz7CqvQzi2nKSsM7t0curUIb3lJWee5/UeEaxZcmIufoNUrzohAWH 2851 BJOIPDM4ssUTLRq7wYM9uQKBgHCBau5OP8gE6mjKuXsZXWUoahpFLKwwwmJUp2vQ 2852 pOFPJ/6WZOlqkTVT6QPAcPUbTohKrF80hsZqZyDdSfT3peFx4ZLocBrS56m6NmHR 2853 UYHMvJ8rQm76T1fryHVidz85g3zRmfBeWg8yqT5oFg4LYgfLsPm1gRjOhs8LfPvI 2854 OLlRAoGBAIZ5Uv4Z3s8O7WKXXUe/lq6j7vfiVkR1NW/Z/WLKXZpnmvJ7FgxN4e56 2855 RXT7GwNQHIY8eDjDnsHxzrxd+raOxOZeKcMHj3XyjCX3NHfTscnsBPAGYpY/Wxzh 2856 T8UYnFu6RzkixElTf2rseEav7rkdKkI3LAeIZy7B0HulKKsmqVQ7 2857 -----END RSA PRIVATE KEY----- 2858 `, 2859 }, 2860 "var.wrong_private_key": ast.Variable{ 2861 Type: ast.TypeString, 2862 Value: ` 2863 -----BEGIN RSA PRIVATE KEY----- 2864 MIIEowIBAAKCAQEAlrCgnEVgmNKCq7KPc+zUU5IrxPu1ClMNJS7RTsTPEkbwe5SB 2865 p+6V6WtCbD/X/lDRRGbOENChh1Phulb7lViqgrdpHydgsrKoS5ah3DfSIxLFLE00 2866 9Yo4TCYwgw6+s59j16ZAFVinaQ9l6Kmrb2ll136hMrz8QKh+qw+onOLd38WFgm+W 2867 ZtUqSXf2LANzfzzy4OWFNyFqKaCAolSkPdTS9Nz+svtScvp002DQp8OdP1AgPO+l 2868 o5N3M38Fftapwg0pCtJ5Zq0NRWIXEonXiTEMA6zy3gEZVOmDxoIFUWnmrqlMJLFy 2869 5S6LDrHSdqJhCxDK6WRZj43X9j8spktk3eGhMwIDAQABAoIBAAem8ID/BOi9x+Tw 2870 LFi2rhGQWqimH4tmrEQ3HGnjlKBY+d1MrUjZ1MMFr1nP5CgF8pqGnfA8p/c3Sz8r 2871 K5tp5T6+EZiDZ2WrrOApxg5ox0MAsQKO6SGO40z6o3wEQ6rbbTaGOrraxaWQIpyu 2872 AQanU4Sd6ZGqByVBaS1GnklZO+shCHqw73b7g1cpLEmFzcYnKHYHlUUIsstMe8E1 2873 BaCY0CH7JbWBjcbiTnBVwIRZuu+EjGiQuhTilYL2OWqoMVg1WU0L2IFpR8lkf/2W 2874 SBx5J6xhwbBGASOpM+qidiN580GdPzGhWYSqKGroHEzBm6xPSmV1tadNA26WFG4p 2875 pthLiAECgYEA5BsPRpNYJAQLu5B0N7mj9eEp0HABVEgL/MpwiImjaKdAwp78HM64 2876 IuPvJxs7r+xESiIz4JyjR8zrQjYOCKJsARYkmNlEuAz0SkHabCw1BdEBwUhjUGVB 2877 efoERK6GxfAoNqmSDwsOvHFOtsmDIlbHmg7G2rUxNVpeou415BSB0B8CgYEAqR4J 2878 YHKk2Ibr9rU+rBU33TcdTGw0aAkFNAVeqM9j0haWuFXmV3RArgoy09lH+2Ha6z/g 2879 fTX2xSDAWV7QUlLOlBRIhurPAo2jO2yCrGHPZcWiugstrR2hTTInigaSnCmK3i7F 2880 6sYmL3S7K01IcVNxSlWvGijtClT92Cl2WUCTfG0CgYAiEjyk4QtQTd5mxLvnOu5X 2881 oqs5PBGmwiAwQRiv/EcRMbJFn7Oupd3xMDSflbzDmTnWDOfMy/jDl8MoH6TW+1PA 2882 kcsjnYhbKWwvz0hN0giVdtOZSDO1ZXpzOrn6fEsbM7T9/TQY1SD9WrtUKCNTNL0Z 2883 sM1ZC6lu+7GZCpW4HKwLJwKBgQCRT0yxQXBg1/UxwuO5ynV4rx2Oh76z0WRWIXMH 2884 S0MyxdP1SWGkrS/SGtM3cg/GcHtA/V6vV0nUcWK0p6IJyjrTw2XZ/zGluPuTWJYi 2885 9dvVT26Vunshrz7kbH7KuwEICy3V4IyQQHeY+QzFlR70uMS0IVFWAepCoWqHbIDT 2886 CYhwNQKBgGPcLXmjpGtkZvggl0aZr9LsvCTckllSCFSI861kivL/rijdNoCHGxZv 2887 dfDkLTLcz9Gk41rD9Gxn/3sqodnTAc3Z2PxFnzg1Q/u3+x6YAgBwI/g/jE2xutGW 2888 H7CurtMwALQ/n/6LUKFmjRZjqbKX9SO2QSaC3grd6sY9Tu+bZjLe 2889 -----END RSA PRIVATE KEY----- 2890 `, 2891 }, 2892 }, 2893 Cases: []testFunctionCase{ 2894 // Base-64 encoded cipher decrypts correctly 2895 { 2896 `${rsadecrypt(var.cipher_base64, var.private_key)}`, 2897 "message", 2898 false, 2899 }, 2900 // Raw cipher 2901 { 2902 `${rsadecrypt(base64decode(var.cipher_base64), var.private_key)}`, 2903 nil, 2904 true, 2905 }, 2906 // Wrong key 2907 { 2908 `${rsadecrypt(var.cipher_base64, var.wrong_private_key)}`, 2909 nil, 2910 true, 2911 }, 2912 // Bad key 2913 { 2914 `${rsadecrypt(var.cipher_base64, "bad key")}`, 2915 nil, 2916 true, 2917 }, 2918 // Empty key 2919 { 2920 `${rsadecrypt(var.cipher_base64, "")}`, 2921 nil, 2922 true, 2923 }, 2924 // Bad cipher 2925 { 2926 `${rsadecrypt("bad cipher", var.private_key)}`, 2927 nil, 2928 true, 2929 }, 2930 // Bad base64-encoded cipher 2931 { 2932 `${rsadecrypt(base64encode("bad cipher"), var.private_key)}`, 2933 nil, 2934 true, 2935 }, 2936 // Empty cipher 2937 { 2938 `${rsadecrypt("", var.private_key)}`, 2939 nil, 2940 true, 2941 }, 2942 // Too many arguments 2943 { 2944 `${rsadecrypt("", "", "")}`, 2945 nil, 2946 true, 2947 }, 2948 // One argument 2949 { 2950 `${rsadecrypt("")}`, 2951 nil, 2952 true, 2953 }, 2954 // No arguments 2955 { 2956 `${rsadecrypt()}`, 2957 nil, 2958 true, 2959 }, 2960 }, 2961 }) 2962 }