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