github.com/adrian-bl/terraform@v0.7.0-rc2.0.20160705220747-de0a34fc3517/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 // Confirm that keys return in sorted order, and values return in the order of 894 // their sorted keys. 895 func TestInterpolateFuncKeyValOrder(t *testing.T) { 896 testFunction(t, testFunctionConfig{ 897 Vars: map[string]ast.Variable{ 898 "var.foo": ast.Variable{ 899 Type: ast.TypeMap, 900 Value: map[string]ast.Variable{ 901 "D": ast.Variable{ 902 Value: "2", 903 Type: ast.TypeString, 904 }, 905 "C": ast.Variable{ 906 Value: "Y", 907 Type: ast.TypeString, 908 }, 909 "A": ast.Variable{ 910 Value: "X", 911 Type: ast.TypeString, 912 }, 913 "10": ast.Variable{ 914 Value: "Z", 915 Type: ast.TypeString, 916 }, 917 "1": ast.Variable{ 918 Value: "4", 919 Type: ast.TypeString, 920 }, 921 "3": ast.Variable{ 922 Value: "W", 923 Type: ast.TypeString, 924 }, 925 }, 926 }, 927 }, 928 Cases: []testFunctionCase{ 929 { 930 `${keys(var.foo)}`, 931 []interface{}{"1", "10", "3", "A", "C", "D"}, 932 false, 933 }, 934 935 { 936 `${values(var.foo)}`, 937 []interface{}{"4", "Z", "W", "X", "Y", "2"}, 938 false, 939 }, 940 }, 941 }) 942 } 943 944 func TestInterpolateFuncValues(t *testing.T) { 945 testFunction(t, testFunctionConfig{ 946 Vars: map[string]ast.Variable{ 947 "var.foo": ast.Variable{ 948 Type: ast.TypeMap, 949 Value: map[string]ast.Variable{ 950 "bar": ast.Variable{ 951 Value: "quack", 952 Type: ast.TypeString, 953 }, 954 "qux": ast.Variable{ 955 Value: "baz", 956 Type: ast.TypeString, 957 }, 958 }, 959 }, 960 "var.str": ast.Variable{ 961 Value: "astring", 962 Type: ast.TypeString, 963 }, 964 }, 965 Cases: []testFunctionCase{ 966 { 967 `${values(var.foo)}`, 968 []interface{}{"quack", "baz"}, 969 false, 970 }, 971 972 // Invalid key 973 { 974 `${values(var.not)}`, 975 nil, 976 true, 977 }, 978 979 // Too many args 980 { 981 `${values(var.foo, "bar")}`, 982 nil, 983 true, 984 }, 985 986 // Not a map 987 { 988 `${values(var.str)}`, 989 nil, 990 true, 991 }, 992 }, 993 }) 994 } 995 996 func interfaceToVariableSwallowError(input interface{}) ast.Variable { 997 variable, _ := hil.InterfaceToVariable(input) 998 return variable 999 } 1000 1001 func TestInterpolateFuncElement(t *testing.T) { 1002 testFunction(t, testFunctionConfig{ 1003 Vars: map[string]ast.Variable{ 1004 "var.a_list": interfaceToVariableSwallowError([]string{"foo", "baz"}), 1005 "var.a_short_list": interfaceToVariableSwallowError([]string{"foo"}), 1006 "var.empty_list": interfaceToVariableSwallowError([]interface{}{}), 1007 }, 1008 Cases: []testFunctionCase{ 1009 { 1010 `${element(var.a_list, "1")}`, 1011 "baz", 1012 false, 1013 }, 1014 1015 { 1016 `${element(var.a_short_list, "0")}`, 1017 "foo", 1018 false, 1019 }, 1020 1021 // Invalid index should wrap vs. out-of-bounds 1022 { 1023 `${element(var.a_list, "2")}`, 1024 "foo", 1025 false, 1026 }, 1027 1028 // Negative number should fail 1029 { 1030 `${element(var.a_short_list, "-1")}`, 1031 nil, 1032 true, 1033 }, 1034 1035 // Empty list should fail 1036 { 1037 `${element(var.empty_list, 0)}`, 1038 nil, 1039 true, 1040 }, 1041 1042 // Too many args 1043 { 1044 `${element(var.a_list, "0", "2")}`, 1045 nil, 1046 true, 1047 }, 1048 }, 1049 }) 1050 } 1051 1052 func TestInterpolateFuncBase64Encode(t *testing.T) { 1053 testFunction(t, testFunctionConfig{ 1054 Cases: []testFunctionCase{ 1055 // Regular base64 encoding 1056 { 1057 `${base64encode("abc123!?$*&()'-=@~")}`, 1058 "YWJjMTIzIT8kKiYoKSctPUB+", 1059 false, 1060 }, 1061 }, 1062 }) 1063 } 1064 1065 func TestInterpolateFuncBase64Decode(t *testing.T) { 1066 testFunction(t, testFunctionConfig{ 1067 Cases: []testFunctionCase{ 1068 // Regular base64 decoding 1069 { 1070 `${base64decode("YWJjMTIzIT8kKiYoKSctPUB+")}`, 1071 "abc123!?$*&()'-=@~", 1072 false, 1073 }, 1074 1075 // Invalid base64 data decoding 1076 { 1077 `${base64decode("this-is-an-invalid-base64-data")}`, 1078 nil, 1079 true, 1080 }, 1081 }, 1082 }) 1083 } 1084 1085 func TestInterpolateFuncLower(t *testing.T) { 1086 testFunction(t, testFunctionConfig{ 1087 Cases: []testFunctionCase{ 1088 { 1089 `${lower("HELLO")}`, 1090 "hello", 1091 false, 1092 }, 1093 1094 { 1095 `${lower("")}`, 1096 "", 1097 false, 1098 }, 1099 1100 { 1101 `${lower()}`, 1102 nil, 1103 true, 1104 }, 1105 }, 1106 }) 1107 } 1108 1109 func TestInterpolateFuncUpper(t *testing.T) { 1110 testFunction(t, testFunctionConfig{ 1111 Cases: []testFunctionCase{ 1112 { 1113 `${upper("hello")}`, 1114 "HELLO", 1115 false, 1116 }, 1117 1118 { 1119 `${upper("")}`, 1120 "", 1121 false, 1122 }, 1123 1124 { 1125 `${upper()}`, 1126 nil, 1127 true, 1128 }, 1129 }, 1130 }) 1131 } 1132 1133 func TestInterpolateFuncSha1(t *testing.T) { 1134 testFunction(t, testFunctionConfig{ 1135 Cases: []testFunctionCase{ 1136 { 1137 `${sha1("test")}`, 1138 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", 1139 false, 1140 }, 1141 }, 1142 }) 1143 } 1144 1145 func TestInterpolateFuncSha256(t *testing.T) { 1146 testFunction(t, testFunctionConfig{ 1147 Cases: []testFunctionCase{ 1148 { // hexadecimal representation of sha256 sum 1149 `${sha256("test")}`, 1150 "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", 1151 false, 1152 }, 1153 }, 1154 }) 1155 } 1156 1157 func TestInterpolateFuncTrimSpace(t *testing.T) { 1158 testFunction(t, testFunctionConfig{ 1159 Cases: []testFunctionCase{ 1160 { 1161 `${trimspace(" test ")}`, 1162 "test", 1163 false, 1164 }, 1165 }, 1166 }) 1167 } 1168 1169 func TestInterpolateFuncBase64Sha256(t *testing.T) { 1170 testFunction(t, testFunctionConfig{ 1171 Cases: []testFunctionCase{ 1172 { 1173 `${base64sha256("test")}`, 1174 "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=", 1175 false, 1176 }, 1177 { // This will differ because we're base64-encoding hex represantiation, not raw bytes 1178 `${base64encode(sha256("test"))}`, 1179 "OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZjMTViMGYwMGEwOA==", 1180 false, 1181 }, 1182 }, 1183 }) 1184 } 1185 1186 func TestInterpolateFuncMd5(t *testing.T) { 1187 testFunction(t, testFunctionConfig{ 1188 Cases: []testFunctionCase{ 1189 { 1190 `${md5("tada")}`, 1191 "ce47d07243bb6eaf5e1322c81baf9bbf", 1192 false, 1193 }, 1194 { // Confirm that we're not trimming any whitespaces 1195 `${md5(" tada ")}`, 1196 "aadf191a583e53062de2d02c008141c4", 1197 false, 1198 }, 1199 { // We accept empty string too 1200 `${md5("")}`, 1201 "d41d8cd98f00b204e9800998ecf8427e", 1202 false, 1203 }, 1204 }, 1205 }) 1206 } 1207 1208 func TestInterpolateFuncUUID(t *testing.T) { 1209 results := make(map[string]bool) 1210 1211 for i := 0; i < 100; i++ { 1212 ast, err := hil.Parse("${uuid()}") 1213 if err != nil { 1214 t.Fatalf("err: %s", err) 1215 } 1216 1217 result, err := hil.Eval(ast, langEvalConfig(nil)) 1218 if err != nil { 1219 t.Fatalf("err: %s", err) 1220 } 1221 1222 if results[result.Value.(string)] { 1223 t.Fatalf("Got unexpected duplicate uuid: %s", result.Value) 1224 } 1225 1226 results[result.Value.(string)] = true 1227 } 1228 } 1229 1230 type testFunctionConfig struct { 1231 Cases []testFunctionCase 1232 Vars map[string]ast.Variable 1233 } 1234 1235 type testFunctionCase struct { 1236 Input string 1237 Result interface{} 1238 Error bool 1239 } 1240 1241 func testFunction(t *testing.T, config testFunctionConfig) { 1242 for i, tc := range config.Cases { 1243 ast, err := hil.Parse(tc.Input) 1244 if err != nil { 1245 t.Fatalf("Case #%d: input: %#v\nerr: %s", i, tc.Input, err) 1246 } 1247 1248 result, err := hil.Eval(ast, langEvalConfig(config.Vars)) 1249 if err != nil != tc.Error { 1250 t.Fatalf("Case #%d:\ninput: %#v\nerr: %s", i, tc.Input, err) 1251 } 1252 1253 if !reflect.DeepEqual(result.Value, tc.Result) { 1254 t.Fatalf("%d: bad output for input: %s\n\nOutput: %#v\nExpected: %#v", 1255 i, tc.Input, result.Value, tc.Result) 1256 } 1257 } 1258 }