github.com/danrjohnson/terraform@v0.7.0-rc2.0.20160627135212-d0fc1fa086ff/config/interpolate_funcs_test.go (about) 1 package config 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "reflect" 8 "strings" 9 "testing" 10 11 "github.com/hashicorp/hil" 12 "github.com/hashicorp/hil/ast" 13 ) 14 15 func TestInterpolateFuncCompact(t *testing.T) { 16 testFunction(t, testFunctionConfig{ 17 Cases: []testFunctionCase{ 18 // empty string within array 19 { 20 `${compact(split(",", "a,,b"))}`, 21 []interface{}{"a", "b"}, 22 false, 23 }, 24 25 // empty string at the end of array 26 { 27 `${compact(split(",", "a,b,"))}`, 28 []interface{}{"a", "b"}, 29 false, 30 }, 31 32 // single empty string 33 { 34 `${compact(split(",", ""))}`, 35 []interface{}{}, 36 false, 37 }, 38 }, 39 }) 40 } 41 42 func TestInterpolateFuncCidrHost(t *testing.T) { 43 testFunction(t, testFunctionConfig{ 44 Cases: []testFunctionCase{ 45 { 46 `${cidrhost("192.168.1.0/24", 5)}`, 47 "192.168.1.5", 48 false, 49 }, 50 { 51 `${cidrhost("192.168.1.0/30", 255)}`, 52 nil, 53 true, // 255 doesn't fit in two bits 54 }, 55 { 56 `${cidrhost("not-a-cidr", 6)}`, 57 nil, 58 true, // not a valid CIDR mask 59 }, 60 { 61 `${cidrhost("10.256.0.0/8", 6)}`, 62 nil, 63 true, // can't have an octet >255 64 }, 65 }, 66 }) 67 } 68 69 func TestInterpolateFuncCidrNetmask(t *testing.T) { 70 testFunction(t, testFunctionConfig{ 71 Cases: []testFunctionCase{ 72 { 73 `${cidrnetmask("192.168.1.0/24")}`, 74 "255.255.255.0", 75 false, 76 }, 77 { 78 `${cidrnetmask("192.168.1.0/32")}`, 79 "255.255.255.255", 80 false, 81 }, 82 { 83 `${cidrnetmask("0.0.0.0/0")}`, 84 "0.0.0.0", 85 false, 86 }, 87 { 88 // This doesn't really make sense for IPv6 networks 89 // but it ought to do something sensible anyway. 90 `${cidrnetmask("1::/64")}`, 91 "ffff:ffff:ffff:ffff::", 92 false, 93 }, 94 { 95 `${cidrnetmask("not-a-cidr")}`, 96 nil, 97 true, // not a valid CIDR mask 98 }, 99 { 100 `${cidrnetmask("10.256.0.0/8")}`, 101 nil, 102 true, // can't have an octet >255 103 }, 104 }, 105 }) 106 } 107 108 func TestInterpolateFuncCidrSubnet(t *testing.T) { 109 testFunction(t, testFunctionConfig{ 110 Cases: []testFunctionCase{ 111 { 112 `${cidrsubnet("192.168.2.0/20", 4, 6)}`, 113 "192.168.6.0/24", 114 false, 115 }, 116 { 117 `${cidrsubnet("fe80::/48", 16, 6)}`, 118 "fe80:0:0:6::/64", 119 false, 120 }, 121 { 122 // IPv4 address encoded in IPv6 syntax gets normalized 123 `${cidrsubnet("::ffff:192.168.0.0/112", 8, 6)}`, 124 "192.168.6.0/24", 125 false, 126 }, 127 { 128 `${cidrsubnet("192.168.0.0/30", 4, 6)}`, 129 nil, 130 true, // not enough bits left 131 }, 132 { 133 `${cidrsubnet("192.168.0.0/16", 2, 16)}`, 134 nil, 135 true, // can't encode 16 in 2 bits 136 }, 137 { 138 `${cidrsubnet("not-a-cidr", 4, 6)}`, 139 nil, 140 true, // not a valid CIDR mask 141 }, 142 { 143 `${cidrsubnet("10.256.0.0/8", 4, 6)}`, 144 nil, 145 true, // can't have an octet >255 146 }, 147 }, 148 }) 149 } 150 151 func TestInterpolateFuncCoalesce(t *testing.T) { 152 testFunction(t, testFunctionConfig{ 153 Cases: []testFunctionCase{ 154 { 155 `${coalesce("first", "second", "third")}`, 156 "first", 157 false, 158 }, 159 { 160 `${coalesce("", "second", "third")}`, 161 "second", 162 false, 163 }, 164 { 165 `${coalesce("", "", "")}`, 166 "", 167 false, 168 }, 169 { 170 `${coalesce("foo")}`, 171 nil, 172 true, 173 }, 174 }, 175 }) 176 } 177 178 func TestInterpolateFuncConcat(t *testing.T) { 179 testFunction(t, testFunctionConfig{ 180 Cases: []testFunctionCase{ 181 // String + list 182 { 183 `${concat("a", split(",", "b,c"))}`, 184 []interface{}{"a", "b", "c"}, 185 false, 186 }, 187 188 // List + string 189 { 190 `${concat(split(",", "a,b"), "c")}`, 191 []interface{}{"a", "b", "c"}, 192 false, 193 }, 194 195 // Single list 196 { 197 `${concat(split(",", ",foo,"))}`, 198 []interface{}{"", "foo", ""}, 199 false, 200 }, 201 { 202 `${concat(split(",", "a,b,c"))}`, 203 []interface{}{"a", "b", "c"}, 204 false, 205 }, 206 207 // Two lists 208 { 209 `${concat(split(",", "a,b,c"), split(",", "d,e"))}`, 210 []interface{}{"a", "b", "c", "d", "e"}, 211 false, 212 }, 213 // Two lists with different separators 214 { 215 `${concat(split(",", "a,b,c"), split(" ", "d e"))}`, 216 []interface{}{"a", "b", "c", "d", "e"}, 217 false, 218 }, 219 220 // More lists 221 { 222 `${concat(split(",", "a,b"), split(",", "c,d"), split(",", "e,f"), split(",", "0,1"))}`, 223 []interface{}{"a", "b", "c", "d", "e", "f", "0", "1"}, 224 false, 225 }, 226 }, 227 }) 228 } 229 230 // TODO: This test is split out and calls a private function 231 // because there's no good way to get a list of maps into the unit 232 // tests due to GH-7142 - once lists of maps can be expressed properly as 233 // literals this unit test can be wrapped back into the suite above. 234 // 235 // Reproduces crash reported in GH-7030. 236 func TestInterpolationFuncConcatListOfMaps(t *testing.T) { 237 listOfMapsOne := ast.Variable{ 238 Type: ast.TypeList, 239 Value: []ast.Variable{ 240 { 241 Type: ast.TypeMap, 242 Value: map[string]interface{}{"one": "foo"}, 243 }, 244 }, 245 } 246 listOfMapsTwo := ast.Variable{ 247 Type: ast.TypeList, 248 Value: []ast.Variable{ 249 { 250 Type: ast.TypeMap, 251 Value: map[string]interface{}{"two": "bar"}, 252 }, 253 }, 254 } 255 args := []interface{}{listOfMapsOne.Value, listOfMapsTwo.Value} 256 257 _, err := interpolationFuncConcat().Callback(args) 258 259 if err == nil || !strings.Contains(err.Error(), "concat() does not support lists of type map") { 260 t.Fatalf("Expected err, got: %v", err) 261 } 262 } 263 264 func TestInterpolateFuncDistinct(t *testing.T) { 265 testFunction(t, testFunctionConfig{ 266 Cases: []testFunctionCase{ 267 // 3 duplicates 268 { 269 `${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user2,user3")))}`, 270 []interface{}{"user1", "user2", "user3"}, 271 false, 272 }, 273 // 1 duplicate 274 { 275 `${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")))}`, 276 []interface{}{"user1", "user2", "user3", "user4"}, 277 false, 278 }, 279 // too many args 280 { 281 `${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")), "foo")}`, 282 nil, 283 true, 284 }, 285 }, 286 }) 287 } 288 289 func TestInterpolateFuncFile(t *testing.T) { 290 tf, err := ioutil.TempFile("", "tf") 291 if err != nil { 292 t.Fatalf("err: %s", err) 293 } 294 path := tf.Name() 295 tf.Write([]byte("foo")) 296 tf.Close() 297 defer os.Remove(path) 298 299 testFunction(t, testFunctionConfig{ 300 Cases: []testFunctionCase{ 301 { 302 fmt.Sprintf(`${file("%s")}`, path), 303 "foo", 304 false, 305 }, 306 307 // Invalid path 308 { 309 `${file("/i/dont/exist")}`, 310 nil, 311 true, 312 }, 313 314 // Too many args 315 { 316 `${file("foo", "bar")}`, 317 nil, 318 true, 319 }, 320 }, 321 }) 322 } 323 324 func TestInterpolateFuncFormat(t *testing.T) { 325 testFunction(t, testFunctionConfig{ 326 Cases: []testFunctionCase{ 327 { 328 `${format("hello")}`, 329 "hello", 330 false, 331 }, 332 333 { 334 `${format("hello %s", "world")}`, 335 "hello world", 336 false, 337 }, 338 339 { 340 `${format("hello %d", 42)}`, 341 "hello 42", 342 false, 343 }, 344 345 { 346 `${format("hello %05d", 42)}`, 347 "hello 00042", 348 false, 349 }, 350 351 { 352 `${format("hello %05d", 12345)}`, 353 "hello 12345", 354 false, 355 }, 356 }, 357 }) 358 } 359 360 func TestInterpolateFuncFormatList(t *testing.T) { 361 testFunction(t, testFunctionConfig{ 362 Cases: []testFunctionCase{ 363 // formatlist requires at least one list 364 { 365 `${formatlist("hello")}`, 366 nil, 367 true, 368 }, 369 { 370 `${formatlist("hello %s", "world")}`, 371 nil, 372 true, 373 }, 374 // formatlist applies to each list element in turn 375 { 376 `${formatlist("<%s>", split(",", "A,B"))}`, 377 []interface{}{"<A>", "<B>"}, 378 false, 379 }, 380 // formatlist repeats scalar elements 381 { 382 `${join(", ", formatlist("%s=%s", "x", split(",", "A,B,C")))}`, 383 "x=A, x=B, x=C", 384 false, 385 }, 386 // Multiple lists are walked in parallel 387 { 388 `${join(", ", formatlist("%s=%s", split(",", "A,B,C"), split(",", "1,2,3")))}`, 389 "A=1, B=2, C=3", 390 false, 391 }, 392 // Mismatched list lengths generate an error 393 { 394 `${formatlist("%s=%2s", split(",", "A,B,C,D"), split(",", "1,2,3"))}`, 395 nil, 396 true, 397 }, 398 // Works with lists of length 1 [GH-2240] 399 { 400 `${formatlist("%s.id", split(",", "demo-rest-elb"))}`, 401 []interface{}{"demo-rest-elb.id"}, 402 false, 403 }, 404 }, 405 }) 406 } 407 408 func TestInterpolateFuncIndex(t *testing.T) { 409 testFunction(t, testFunctionConfig{ 410 Vars: map[string]ast.Variable{ 411 "var.list1": interfaceToVariableSwallowError([]string{"notfoo", "stillnotfoo", "bar"}), 412 "var.list2": interfaceToVariableSwallowError([]string{"foo"}), 413 "var.list3": interfaceToVariableSwallowError([]string{"foo", "spam", "bar", "eggs"}), 414 }, 415 Cases: []testFunctionCase{ 416 { 417 `${index("test", "")}`, 418 nil, 419 true, 420 }, 421 422 { 423 `${index(var.list1, "foo")}`, 424 nil, 425 true, 426 }, 427 428 { 429 `${index(var.list2, "foo")}`, 430 "0", 431 false, 432 }, 433 434 { 435 `${index(var.list3, "bar")}`, 436 "2", 437 false, 438 }, 439 }, 440 }) 441 } 442 443 func TestInterpolateFuncJoin(t *testing.T) { 444 testFunction(t, testFunctionConfig{ 445 Vars: map[string]ast.Variable{ 446 "var.a_list": interfaceToVariableSwallowError([]string{"foo"}), 447 "var.a_longer_list": interfaceToVariableSwallowError([]string{"foo", "bar", "baz"}), 448 }, 449 Cases: []testFunctionCase{ 450 { 451 `${join(",")}`, 452 nil, 453 true, 454 }, 455 456 { 457 `${join(",", var.a_list)}`, 458 "foo", 459 false, 460 }, 461 462 { 463 `${join(".", var.a_longer_list)}`, 464 "foo.bar.baz", 465 false, 466 }, 467 }, 468 }) 469 } 470 471 func TestInterpolateFuncJSONEncode(t *testing.T) { 472 testFunction(t, testFunctionConfig{ 473 Vars: map[string]ast.Variable{ 474 "easy": ast.Variable{ 475 Value: "test", 476 Type: ast.TypeString, 477 }, 478 "hard": ast.Variable{ 479 Value: " foo \\ \n \t \" bar ", 480 Type: ast.TypeString, 481 }, 482 "list": interfaceToVariableSwallowError([]string{"foo", "bar\tbaz"}), 483 // XXX can't use InterfaceToVariable as it converts empty slice into empty 484 // map. 485 "emptylist": ast.Variable{ 486 Value: []ast.Variable{}, 487 Type: ast.TypeList, 488 }, 489 "map": interfaceToVariableSwallowError(map[string]string{ 490 "foo": "bar", 491 "ba \n z": "q\\x", 492 }), 493 "emptymap": interfaceToVariableSwallowError(map[string]string{}), 494 495 // Not yet supported (but it would be nice) 496 "nestedlist": interfaceToVariableSwallowError([][]string{{"foo"}}), 497 "nestedmap": interfaceToVariableSwallowError(map[string][]string{"foo": {"bar"}}), 498 }, 499 Cases: []testFunctionCase{ 500 { 501 `${jsonencode("test")}`, 502 `"test"`, 503 false, 504 }, 505 { 506 `${jsonencode(easy)}`, 507 `"test"`, 508 false, 509 }, 510 { 511 `${jsonencode(hard)}`, 512 `" foo \\ \n \t \" bar "`, 513 false, 514 }, 515 { 516 `${jsonencode("")}`, 517 `""`, 518 false, 519 }, 520 { 521 `${jsonencode()}`, 522 nil, 523 true, 524 }, 525 { 526 `${jsonencode(list)}`, 527 `["foo","bar\tbaz"]`, 528 false, 529 }, 530 { 531 `${jsonencode(emptylist)}`, 532 `[]`, 533 false, 534 }, 535 { 536 `${jsonencode(map)}`, 537 `{"ba \n z":"q\\x","foo":"bar"}`, 538 false, 539 }, 540 { 541 `${jsonencode(emptymap)}`, 542 `{}`, 543 false, 544 }, 545 { 546 `${jsonencode(nestedlist)}`, 547 nil, 548 true, 549 }, 550 { 551 `${jsonencode(nestedmap)}`, 552 nil, 553 true, 554 }, 555 }, 556 }) 557 } 558 559 func TestInterpolateFuncReplace(t *testing.T) { 560 testFunction(t, testFunctionConfig{ 561 Cases: []testFunctionCase{ 562 // Regular search and replace 563 { 564 `${replace("hello", "hel", "bel")}`, 565 "bello", 566 false, 567 }, 568 569 // Search string doesn't match 570 { 571 `${replace("hello", "nope", "bel")}`, 572 "hello", 573 false, 574 }, 575 576 // Regular expression 577 { 578 `${replace("hello", "/l/", "L")}`, 579 "heLLo", 580 false, 581 }, 582 583 { 584 `${replace("helo", "/(l)/", "$1$1")}`, 585 "hello", 586 false, 587 }, 588 589 // Bad regexp 590 { 591 `${replace("helo", "/(l/", "$1$1")}`, 592 nil, 593 true, 594 }, 595 }, 596 }) 597 } 598 599 func TestInterpolateFuncLength(t *testing.T) { 600 testFunction(t, testFunctionConfig{ 601 Cases: []testFunctionCase{ 602 // Raw strings 603 { 604 `${length("")}`, 605 "0", 606 false, 607 }, 608 { 609 `${length("a")}`, 610 "1", 611 false, 612 }, 613 { 614 `${length(" ")}`, 615 "1", 616 false, 617 }, 618 { 619 `${length(" a ,")}`, 620 "4", 621 false, 622 }, 623 { 624 `${length("aaa")}`, 625 "3", 626 false, 627 }, 628 629 // Lists 630 { 631 `${length(split(",", "a"))}`, 632 "1", 633 false, 634 }, 635 { 636 `${length(split(",", "foo,"))}`, 637 "2", 638 false, 639 }, 640 { 641 `${length(split(",", ",foo,"))}`, 642 "3", 643 false, 644 }, 645 { 646 `${length(split(",", "foo,bar"))}`, 647 "2", 648 false, 649 }, 650 { 651 `${length(split(".", "one.two.three.four.five"))}`, 652 "5", 653 false, 654 }, 655 // Want length 0 if we split an empty string then compact 656 { 657 `${length(compact(split(",", "")))}`, 658 "0", 659 false, 660 }, 661 }, 662 }) 663 } 664 665 func TestInterpolateFuncSignum(t *testing.T) { 666 testFunction(t, testFunctionConfig{ 667 Cases: []testFunctionCase{ 668 { 669 `${signum()}`, 670 nil, 671 true, 672 }, 673 674 { 675 `${signum("")}`, 676 nil, 677 true, 678 }, 679 680 { 681 `${signum(0)}`, 682 "0", 683 false, 684 }, 685 686 { 687 `${signum(15)}`, 688 "1", 689 false, 690 }, 691 692 { 693 `${signum(-29)}`, 694 "-1", 695 false, 696 }, 697 }, 698 }) 699 } 700 701 func TestInterpolateFuncSort(t *testing.T) { 702 testFunction(t, testFunctionConfig{ 703 Vars: map[string]ast.Variable{ 704 "var.strings": ast.Variable{ 705 Type: ast.TypeList, 706 Value: []ast.Variable{ 707 {Type: ast.TypeString, Value: "c"}, 708 {Type: ast.TypeString, Value: "a"}, 709 {Type: ast.TypeString, Value: "b"}, 710 }, 711 }, 712 "var.notstrings": ast.Variable{ 713 Type: ast.TypeList, 714 Value: []ast.Variable{ 715 {Type: ast.TypeList, Value: []ast.Variable{}}, 716 {Type: ast.TypeString, Value: "b"}, 717 }, 718 }, 719 }, 720 Cases: []testFunctionCase{ 721 { 722 `${sort(var.strings)}`, 723 []interface{}{"a", "b", "c"}, 724 false, 725 }, 726 { 727 `${sort(var.notstrings)}`, 728 nil, 729 true, 730 }, 731 }, 732 }) 733 } 734 735 func TestInterpolateFuncSplit(t *testing.T) { 736 testFunction(t, testFunctionConfig{ 737 Cases: []testFunctionCase{ 738 { 739 `${split(",")}`, 740 nil, 741 true, 742 }, 743 744 { 745 `${split(",", "")}`, 746 []interface{}{""}, 747 false, 748 }, 749 750 { 751 `${split(",", "foo")}`, 752 []interface{}{"foo"}, 753 false, 754 }, 755 756 { 757 `${split(",", ",,,")}`, 758 []interface{}{"", "", "", ""}, 759 false, 760 }, 761 762 { 763 `${split(",", "foo,")}`, 764 []interface{}{"foo", ""}, 765 false, 766 }, 767 768 { 769 `${split(",", ",foo,")}`, 770 []interface{}{"", "foo", ""}, 771 false, 772 }, 773 774 { 775 `${split(".", "foo.bar.baz")}`, 776 []interface{}{"foo", "bar", "baz"}, 777 false, 778 }, 779 }, 780 }) 781 } 782 783 func TestInterpolateFuncLookup(t *testing.T) { 784 testFunction(t, testFunctionConfig{ 785 Vars: map[string]ast.Variable{ 786 "var.foo": ast.Variable{ 787 Type: ast.TypeMap, 788 Value: map[string]ast.Variable{ 789 "bar": ast.Variable{ 790 Type: ast.TypeString, 791 Value: "baz", 792 }, 793 }, 794 }, 795 }, 796 Cases: []testFunctionCase{ 797 { 798 `${lookup(var.foo, "bar")}`, 799 "baz", 800 false, 801 }, 802 803 // Invalid key 804 { 805 `${lookup(var.foo, "baz")}`, 806 nil, 807 true, 808 }, 809 810 // Supplied default with valid key 811 { 812 `${lookup(var.foo, "bar", "")}`, 813 "baz", 814 false, 815 }, 816 817 // Supplied default with invalid key 818 { 819 `${lookup(var.foo, "zip", "")}`, 820 "", 821 false, 822 }, 823 824 // Too many args 825 { 826 `${lookup(var.foo, "bar", "", "abc")}`, 827 nil, 828 true, 829 }, 830 831 // Non-empty default 832 { 833 `${lookup(var.foo, "zap", "xyz")}`, 834 "xyz", 835 false, 836 }, 837 }, 838 }) 839 } 840 841 func TestInterpolateFuncKeys(t *testing.T) { 842 testFunction(t, testFunctionConfig{ 843 Vars: map[string]ast.Variable{ 844 "var.foo": ast.Variable{ 845 Type: ast.TypeMap, 846 Value: map[string]ast.Variable{ 847 "bar": ast.Variable{ 848 Value: "baz", 849 Type: ast.TypeString, 850 }, 851 "qux": ast.Variable{ 852 Value: "quack", 853 Type: ast.TypeString, 854 }, 855 }, 856 }, 857 "var.str": ast.Variable{ 858 Value: "astring", 859 Type: ast.TypeString, 860 }, 861 }, 862 Cases: []testFunctionCase{ 863 { 864 `${keys(var.foo)}`, 865 []interface{}{"bar", "qux"}, 866 false, 867 }, 868 869 // Invalid key 870 { 871 `${keys(var.not)}`, 872 nil, 873 true, 874 }, 875 876 // Too many args 877 { 878 `${keys(var.foo, "bar")}`, 879 nil, 880 true, 881 }, 882 883 // Not a map 884 { 885 `${keys(var.str)}`, 886 nil, 887 true, 888 }, 889 }, 890 }) 891 } 892 893 func TestInterpolateFuncValues(t *testing.T) { 894 testFunction(t, testFunctionConfig{ 895 Vars: map[string]ast.Variable{ 896 "var.foo": ast.Variable{ 897 Type: ast.TypeMap, 898 Value: map[string]ast.Variable{ 899 "bar": ast.Variable{ 900 Value: "quack", 901 Type: ast.TypeString, 902 }, 903 "qux": ast.Variable{ 904 Value: "baz", 905 Type: ast.TypeString, 906 }, 907 }, 908 }, 909 "var.str": ast.Variable{ 910 Value: "astring", 911 Type: ast.TypeString, 912 }, 913 }, 914 Cases: []testFunctionCase{ 915 { 916 `${values(var.foo)}`, 917 []interface{}{"quack", "baz"}, 918 false, 919 }, 920 921 // Invalid key 922 { 923 `${values(var.not)}`, 924 nil, 925 true, 926 }, 927 928 // Too many args 929 { 930 `${values(var.foo, "bar")}`, 931 nil, 932 true, 933 }, 934 935 // Not a map 936 { 937 `${values(var.str)}`, 938 nil, 939 true, 940 }, 941 }, 942 }) 943 } 944 945 func interfaceToVariableSwallowError(input interface{}) ast.Variable { 946 variable, _ := hil.InterfaceToVariable(input) 947 return variable 948 } 949 950 func TestInterpolateFuncElement(t *testing.T) { 951 testFunction(t, testFunctionConfig{ 952 Vars: map[string]ast.Variable{ 953 "var.a_list": interfaceToVariableSwallowError([]string{"foo", "baz"}), 954 "var.a_short_list": interfaceToVariableSwallowError([]string{"foo"}), 955 "var.empty_list": interfaceToVariableSwallowError([]interface{}{}), 956 }, 957 Cases: []testFunctionCase{ 958 { 959 `${element(var.a_list, "1")}`, 960 "baz", 961 false, 962 }, 963 964 { 965 `${element(var.a_short_list, "0")}`, 966 "foo", 967 false, 968 }, 969 970 // Invalid index should wrap vs. out-of-bounds 971 { 972 `${element(var.a_list, "2")}`, 973 "foo", 974 false, 975 }, 976 977 // Negative number should fail 978 { 979 `${element(var.a_short_list, "-1")}`, 980 nil, 981 true, 982 }, 983 984 // Empty list should fail 985 { 986 `${element(var.empty_list, 0)}`, 987 nil, 988 true, 989 }, 990 991 // Too many args 992 { 993 `${element(var.a_list, "0", "2")}`, 994 nil, 995 true, 996 }, 997 }, 998 }) 999 } 1000 1001 func TestInterpolateFuncBase64Encode(t *testing.T) { 1002 testFunction(t, testFunctionConfig{ 1003 Cases: []testFunctionCase{ 1004 // Regular base64 encoding 1005 { 1006 `${base64encode("abc123!?$*&()'-=@~")}`, 1007 "YWJjMTIzIT8kKiYoKSctPUB+", 1008 false, 1009 }, 1010 }, 1011 }) 1012 } 1013 1014 func TestInterpolateFuncBase64Decode(t *testing.T) { 1015 testFunction(t, testFunctionConfig{ 1016 Cases: []testFunctionCase{ 1017 // Regular base64 decoding 1018 { 1019 `${base64decode("YWJjMTIzIT8kKiYoKSctPUB+")}`, 1020 "abc123!?$*&()'-=@~", 1021 false, 1022 }, 1023 1024 // Invalid base64 data decoding 1025 { 1026 `${base64decode("this-is-an-invalid-base64-data")}`, 1027 nil, 1028 true, 1029 }, 1030 }, 1031 }) 1032 } 1033 1034 func TestInterpolateFuncLower(t *testing.T) { 1035 testFunction(t, testFunctionConfig{ 1036 Cases: []testFunctionCase{ 1037 { 1038 `${lower("HELLO")}`, 1039 "hello", 1040 false, 1041 }, 1042 1043 { 1044 `${lower("")}`, 1045 "", 1046 false, 1047 }, 1048 1049 { 1050 `${lower()}`, 1051 nil, 1052 true, 1053 }, 1054 }, 1055 }) 1056 } 1057 1058 func TestInterpolateFuncUpper(t *testing.T) { 1059 testFunction(t, testFunctionConfig{ 1060 Cases: []testFunctionCase{ 1061 { 1062 `${upper("hello")}`, 1063 "HELLO", 1064 false, 1065 }, 1066 1067 { 1068 `${upper("")}`, 1069 "", 1070 false, 1071 }, 1072 1073 { 1074 `${upper()}`, 1075 nil, 1076 true, 1077 }, 1078 }, 1079 }) 1080 } 1081 1082 func TestInterpolateFuncSha1(t *testing.T) { 1083 testFunction(t, testFunctionConfig{ 1084 Cases: []testFunctionCase{ 1085 { 1086 `${sha1("test")}`, 1087 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", 1088 false, 1089 }, 1090 }, 1091 }) 1092 } 1093 1094 func TestInterpolateFuncSha256(t *testing.T) { 1095 testFunction(t, testFunctionConfig{ 1096 Cases: []testFunctionCase{ 1097 { // hexadecimal representation of sha256 sum 1098 `${sha256("test")}`, 1099 "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", 1100 false, 1101 }, 1102 }, 1103 }) 1104 } 1105 1106 func TestInterpolateFuncTrimSpace(t *testing.T) { 1107 testFunction(t, testFunctionConfig{ 1108 Cases: []testFunctionCase{ 1109 { 1110 `${trimspace(" test ")}`, 1111 "test", 1112 false, 1113 }, 1114 }, 1115 }) 1116 } 1117 1118 func TestInterpolateFuncBase64Sha256(t *testing.T) { 1119 testFunction(t, testFunctionConfig{ 1120 Cases: []testFunctionCase{ 1121 { 1122 `${base64sha256("test")}`, 1123 "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=", 1124 false, 1125 }, 1126 { // This will differ because we're base64-encoding hex represantiation, not raw bytes 1127 `${base64encode(sha256("test"))}`, 1128 "OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZjMTViMGYwMGEwOA==", 1129 false, 1130 }, 1131 }, 1132 }) 1133 } 1134 1135 func TestInterpolateFuncMd5(t *testing.T) { 1136 testFunction(t, testFunctionConfig{ 1137 Cases: []testFunctionCase{ 1138 { 1139 `${md5("tada")}`, 1140 "ce47d07243bb6eaf5e1322c81baf9bbf", 1141 false, 1142 }, 1143 { // Confirm that we're not trimming any whitespaces 1144 `${md5(" tada ")}`, 1145 "aadf191a583e53062de2d02c008141c4", 1146 false, 1147 }, 1148 { // We accept empty string too 1149 `${md5("")}`, 1150 "d41d8cd98f00b204e9800998ecf8427e", 1151 false, 1152 }, 1153 }, 1154 }) 1155 } 1156 1157 func TestInterpolateFuncUUID(t *testing.T) { 1158 results := make(map[string]bool) 1159 1160 for i := 0; i < 100; i++ { 1161 ast, err := hil.Parse("${uuid()}") 1162 if err != nil { 1163 t.Fatalf("err: %s", err) 1164 } 1165 1166 result, err := hil.Eval(ast, langEvalConfig(nil)) 1167 if err != nil { 1168 t.Fatalf("err: %s", err) 1169 } 1170 1171 if results[result.Value.(string)] { 1172 t.Fatalf("Got unexpected duplicate uuid: %s", result.Value) 1173 } 1174 1175 results[result.Value.(string)] = true 1176 } 1177 } 1178 1179 type testFunctionConfig struct { 1180 Cases []testFunctionCase 1181 Vars map[string]ast.Variable 1182 } 1183 1184 type testFunctionCase struct { 1185 Input string 1186 Result interface{} 1187 Error bool 1188 } 1189 1190 func testFunction(t *testing.T, config testFunctionConfig) { 1191 for i, tc := range config.Cases { 1192 ast, err := hil.Parse(tc.Input) 1193 if err != nil { 1194 t.Fatalf("Case #%d: input: %#v\nerr: %s", i, tc.Input, err) 1195 } 1196 1197 result, err := hil.Eval(ast, langEvalConfig(config.Vars)) 1198 if err != nil != tc.Error { 1199 t.Fatalf("Case #%d:\ninput: %#v\nerr: %s", i, tc.Input, err) 1200 } 1201 1202 if !reflect.DeepEqual(result.Value, tc.Result) { 1203 t.Fatalf("%d: bad output for input: %s\n\nOutput: %#v\nExpected: %#v", 1204 i, tc.Input, result.Value, tc.Result) 1205 } 1206 } 1207 }