github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/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 TestInterpolateFuncDistinct(t *testing.T) { 856 testFunction(t, testFunctionConfig{ 857 Cases: []testFunctionCase{ 858 // 3 duplicates 859 { 860 `${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user2,user3")))}`, 861 []interface{}{"user1", "user2", "user3"}, 862 false, 863 }, 864 // 1 duplicate 865 { 866 `${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")))}`, 867 []interface{}{"user1", "user2", "user3", "user4"}, 868 false, 869 }, 870 // too many args 871 { 872 `${distinct(concat(split(",", "user1,user2,user3"), split(",", "user1,user4")), "foo")}`, 873 nil, 874 true, 875 }, 876 // non-flat list is an error 877 { 878 `${distinct(list(list("a"), list("a")))}`, 879 nil, 880 true, 881 }, 882 }, 883 }) 884 } 885 886 func TestInterpolateFuncFile(t *testing.T) { 887 tf, err := ioutil.TempFile("", "tf") 888 if err != nil { 889 t.Fatalf("err: %s", err) 890 } 891 path := tf.Name() 892 tf.Write([]byte("foo")) 893 tf.Close() 894 defer os.Remove(path) 895 896 testFunction(t, testFunctionConfig{ 897 Cases: []testFunctionCase{ 898 { 899 fmt.Sprintf(`${file("%s")}`, path), 900 "foo", 901 false, 902 }, 903 904 // Invalid path 905 { 906 `${file("/i/dont/exist")}`, 907 nil, 908 true, 909 }, 910 911 // Too many args 912 { 913 `${file("foo", "bar")}`, 914 nil, 915 true, 916 }, 917 }, 918 }) 919 } 920 921 func TestInterpolateFuncFormat(t *testing.T) { 922 testFunction(t, testFunctionConfig{ 923 Cases: []testFunctionCase{ 924 { 925 `${format("hello")}`, 926 "hello", 927 false, 928 }, 929 930 { 931 `${format("hello %s", "world")}`, 932 "hello world", 933 false, 934 }, 935 936 { 937 `${format("hello %d", 42)}`, 938 "hello 42", 939 false, 940 }, 941 942 { 943 `${format("hello %05d", 42)}`, 944 "hello 00042", 945 false, 946 }, 947 948 { 949 `${format("hello %05d", 12345)}`, 950 "hello 12345", 951 false, 952 }, 953 }, 954 }) 955 } 956 957 func TestInterpolateFuncFormatList(t *testing.T) { 958 testFunction(t, testFunctionConfig{ 959 Cases: []testFunctionCase{ 960 // formatlist requires at least one list 961 { 962 `${formatlist("hello")}`, 963 nil, 964 true, 965 }, 966 { 967 `${formatlist("hello %s", "world")}`, 968 nil, 969 true, 970 }, 971 // formatlist applies to each list element in turn 972 { 973 `${formatlist("<%s>", split(",", "A,B"))}`, 974 []interface{}{"<A>", "<B>"}, 975 false, 976 }, 977 // formatlist repeats scalar elements 978 { 979 `${join(", ", formatlist("%s=%s", "x", split(",", "A,B,C")))}`, 980 "x=A, x=B, x=C", 981 false, 982 }, 983 // Multiple lists are walked in parallel 984 { 985 `${join(", ", formatlist("%s=%s", split(",", "A,B,C"), split(",", "1,2,3")))}`, 986 "A=1, B=2, C=3", 987 false, 988 }, 989 // Mismatched list lengths generate an error 990 { 991 `${formatlist("%s=%2s", split(",", "A,B,C,D"), split(",", "1,2,3"))}`, 992 nil, 993 true, 994 }, 995 // Works with lists of length 1 [GH-2240] 996 { 997 `${formatlist("%s.id", split(",", "demo-rest-elb"))}`, 998 []interface{}{"demo-rest-elb.id"}, 999 false, 1000 }, 1001 // Works with empty lists [GH-7607] 1002 { 1003 `${formatlist("%s", var.emptylist)}`, 1004 []interface{}{}, 1005 false, 1006 }, 1007 }, 1008 Vars: map[string]ast.Variable{ 1009 "var.emptylist": { 1010 Type: ast.TypeList, 1011 Value: []ast.Variable{}, 1012 }, 1013 }, 1014 }) 1015 } 1016 1017 func TestInterpolateFuncIndex(t *testing.T) { 1018 testFunction(t, testFunctionConfig{ 1019 Vars: map[string]ast.Variable{ 1020 "var.list1": interfaceToVariableSwallowError([]string{"notfoo", "stillnotfoo", "bar"}), 1021 "var.list2": interfaceToVariableSwallowError([]string{"foo"}), 1022 "var.list3": interfaceToVariableSwallowError([]string{"foo", "spam", "bar", "eggs"}), 1023 }, 1024 Cases: []testFunctionCase{ 1025 { 1026 `${index("test", "")}`, 1027 nil, 1028 true, 1029 }, 1030 1031 { 1032 `${index(var.list1, "foo")}`, 1033 nil, 1034 true, 1035 }, 1036 1037 { 1038 `${index(var.list2, "foo")}`, 1039 "0", 1040 false, 1041 }, 1042 1043 { 1044 `${index(var.list3, "bar")}`, 1045 "2", 1046 false, 1047 }, 1048 }, 1049 }) 1050 } 1051 1052 func TestInterpolateFuncJoin(t *testing.T) { 1053 testFunction(t, testFunctionConfig{ 1054 Vars: map[string]ast.Variable{ 1055 "var.a_list": interfaceToVariableSwallowError([]string{"foo"}), 1056 "var.a_longer_list": interfaceToVariableSwallowError([]string{"foo", "bar", "baz"}), 1057 "var.list_of_lists": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"bar"}, []string{"baz"}}), 1058 }, 1059 Cases: []testFunctionCase{ 1060 { 1061 `${join(",")}`, 1062 nil, 1063 true, 1064 }, 1065 1066 { 1067 `${join(",", var.a_list)}`, 1068 "foo", 1069 false, 1070 }, 1071 1072 { 1073 `${join(".", var.a_longer_list)}`, 1074 "foo.bar.baz", 1075 false, 1076 }, 1077 1078 { 1079 `${join(".", var.list_of_lists)}`, 1080 nil, 1081 true, 1082 }, 1083 { 1084 `${join(".", list(list("nested")))}`, 1085 nil, 1086 true, 1087 }, 1088 }, 1089 }) 1090 } 1091 1092 func TestInterpolateFuncJSONEncode(t *testing.T) { 1093 testFunction(t, testFunctionConfig{ 1094 Vars: map[string]ast.Variable{ 1095 "easy": ast.Variable{ 1096 Value: "test", 1097 Type: ast.TypeString, 1098 }, 1099 "hard": ast.Variable{ 1100 Value: " foo \\ \n \t \" bar ", 1101 Type: ast.TypeString, 1102 }, 1103 "list": interfaceToVariableSwallowError([]string{"foo", "bar\tbaz"}), 1104 // XXX can't use InterfaceToVariable as it converts empty slice into empty 1105 // map. 1106 "emptylist": ast.Variable{ 1107 Value: []ast.Variable{}, 1108 Type: ast.TypeList, 1109 }, 1110 "map": interfaceToVariableSwallowError(map[string]string{ 1111 "foo": "bar", 1112 "ba \n z": "q\\x", 1113 }), 1114 "emptymap": interfaceToVariableSwallowError(map[string]string{}), 1115 1116 // Not yet supported (but it would be nice) 1117 "nestedlist": interfaceToVariableSwallowError([][]string{{"foo"}}), 1118 "nestedmap": interfaceToVariableSwallowError(map[string][]string{"foo": {"bar"}}), 1119 }, 1120 Cases: []testFunctionCase{ 1121 { 1122 `${jsonencode("test")}`, 1123 `"test"`, 1124 false, 1125 }, 1126 { 1127 `${jsonencode(easy)}`, 1128 `"test"`, 1129 false, 1130 }, 1131 { 1132 `${jsonencode(hard)}`, 1133 `" foo \\ \n \t \" bar "`, 1134 false, 1135 }, 1136 { 1137 `${jsonencode("")}`, 1138 `""`, 1139 false, 1140 }, 1141 { 1142 `${jsonencode()}`, 1143 nil, 1144 true, 1145 }, 1146 { 1147 `${jsonencode(list)}`, 1148 `["foo","bar\tbaz"]`, 1149 false, 1150 }, 1151 { 1152 `${jsonencode(emptylist)}`, 1153 `[]`, 1154 false, 1155 }, 1156 { 1157 `${jsonencode(map)}`, 1158 `{"ba \n z":"q\\x","foo":"bar"}`, 1159 false, 1160 }, 1161 { 1162 `${jsonencode(emptymap)}`, 1163 `{}`, 1164 false, 1165 }, 1166 { 1167 `${jsonencode(nestedlist)}`, 1168 nil, 1169 true, 1170 }, 1171 { 1172 `${jsonencode(nestedmap)}`, 1173 nil, 1174 true, 1175 }, 1176 }, 1177 }) 1178 } 1179 1180 func TestInterpolateFuncReplace(t *testing.T) { 1181 testFunction(t, testFunctionConfig{ 1182 Cases: []testFunctionCase{ 1183 // Regular search and replace 1184 { 1185 `${replace("hello", "hel", "bel")}`, 1186 "bello", 1187 false, 1188 }, 1189 1190 // Search string doesn't match 1191 { 1192 `${replace("hello", "nope", "bel")}`, 1193 "hello", 1194 false, 1195 }, 1196 1197 // Regular expression 1198 { 1199 `${replace("hello", "/l/", "L")}`, 1200 "heLLo", 1201 false, 1202 }, 1203 1204 { 1205 `${replace("helo", "/(l)/", "$1$1")}`, 1206 "hello", 1207 false, 1208 }, 1209 1210 // Bad regexp 1211 { 1212 `${replace("helo", "/(l/", "$1$1")}`, 1213 nil, 1214 true, 1215 }, 1216 }, 1217 }) 1218 } 1219 1220 func TestInterpolateFuncLength(t *testing.T) { 1221 testFunction(t, testFunctionConfig{ 1222 Cases: []testFunctionCase{ 1223 // Raw strings 1224 { 1225 `${length("")}`, 1226 "0", 1227 false, 1228 }, 1229 { 1230 `${length("a")}`, 1231 "1", 1232 false, 1233 }, 1234 { 1235 `${length(" ")}`, 1236 "1", 1237 false, 1238 }, 1239 { 1240 `${length(" a ,")}`, 1241 "4", 1242 false, 1243 }, 1244 { 1245 `${length("aaa")}`, 1246 "3", 1247 false, 1248 }, 1249 1250 // Lists 1251 { 1252 `${length(split(",", "a"))}`, 1253 "1", 1254 false, 1255 }, 1256 { 1257 `${length(split(",", "foo,"))}`, 1258 "2", 1259 false, 1260 }, 1261 { 1262 `${length(split(",", ",foo,"))}`, 1263 "3", 1264 false, 1265 }, 1266 { 1267 `${length(split(",", "foo,bar"))}`, 1268 "2", 1269 false, 1270 }, 1271 { 1272 `${length(split(".", "one.two.three.four.five"))}`, 1273 "5", 1274 false, 1275 }, 1276 // Want length 0 if we split an empty string then compact 1277 { 1278 `${length(compact(split(",", "")))}`, 1279 "0", 1280 false, 1281 }, 1282 // Works for maps 1283 { 1284 `${length(map("k", "v"))}`, 1285 "1", 1286 false, 1287 }, 1288 { 1289 `${length(map("k1", "v1", "k2", "v2"))}`, 1290 "2", 1291 false, 1292 }, 1293 }, 1294 }) 1295 } 1296 1297 func TestInterpolateFuncSignum(t *testing.T) { 1298 testFunction(t, testFunctionConfig{ 1299 Cases: []testFunctionCase{ 1300 { 1301 `${signum()}`, 1302 nil, 1303 true, 1304 }, 1305 1306 { 1307 `${signum("")}`, 1308 nil, 1309 true, 1310 }, 1311 1312 { 1313 `${signum(0)}`, 1314 "0", 1315 false, 1316 }, 1317 1318 { 1319 `${signum(15)}`, 1320 "1", 1321 false, 1322 }, 1323 1324 { 1325 `${signum(-29)}`, 1326 "-1", 1327 false, 1328 }, 1329 }, 1330 }) 1331 } 1332 1333 func TestInterpolateFuncSlice(t *testing.T) { 1334 testFunction(t, testFunctionConfig{ 1335 Cases: []testFunctionCase{ 1336 // Negative from index 1337 { 1338 `${slice(list("a"), -1, 0)}`, 1339 nil, 1340 true, 1341 }, 1342 // From index > to index 1343 { 1344 `${slice(list("a", "b", "c"), 2, 1)}`, 1345 nil, 1346 true, 1347 }, 1348 // To index too large 1349 { 1350 `${slice(var.list_of_strings, 1, 4)}`, 1351 nil, 1352 true, 1353 }, 1354 // Empty slice 1355 { 1356 `${slice(var.list_of_strings, 1, 1)}`, 1357 []interface{}{}, 1358 false, 1359 }, 1360 { 1361 `${slice(var.list_of_strings, 1, 2)}`, 1362 []interface{}{"b"}, 1363 false, 1364 }, 1365 { 1366 `${slice(var.list_of_strings, 0, length(var.list_of_strings) - 1)}`, 1367 []interface{}{"a", "b"}, 1368 false, 1369 }, 1370 }, 1371 Vars: map[string]ast.Variable{ 1372 "var.list_of_strings": { 1373 Type: ast.TypeList, 1374 Value: []ast.Variable{ 1375 { 1376 Type: ast.TypeString, 1377 Value: "a", 1378 }, 1379 { 1380 Type: ast.TypeString, 1381 Value: "b", 1382 }, 1383 { 1384 Type: ast.TypeString, 1385 Value: "c", 1386 }, 1387 }, 1388 }, 1389 }, 1390 }) 1391 } 1392 1393 func TestInterpolateFuncSort(t *testing.T) { 1394 testFunction(t, testFunctionConfig{ 1395 Vars: map[string]ast.Variable{ 1396 "var.strings": ast.Variable{ 1397 Type: ast.TypeList, 1398 Value: []ast.Variable{ 1399 {Type: ast.TypeString, Value: "c"}, 1400 {Type: ast.TypeString, Value: "a"}, 1401 {Type: ast.TypeString, Value: "b"}, 1402 }, 1403 }, 1404 "var.notstrings": ast.Variable{ 1405 Type: ast.TypeList, 1406 Value: []ast.Variable{ 1407 {Type: ast.TypeList, Value: []ast.Variable{}}, 1408 {Type: ast.TypeString, Value: "b"}, 1409 }, 1410 }, 1411 }, 1412 Cases: []testFunctionCase{ 1413 { 1414 `${sort(var.strings)}`, 1415 []interface{}{"a", "b", "c"}, 1416 false, 1417 }, 1418 { 1419 `${sort(var.notstrings)}`, 1420 nil, 1421 true, 1422 }, 1423 }, 1424 }) 1425 } 1426 1427 func TestInterpolateFuncSplit(t *testing.T) { 1428 testFunction(t, testFunctionConfig{ 1429 Cases: []testFunctionCase{ 1430 { 1431 `${split(",")}`, 1432 nil, 1433 true, 1434 }, 1435 1436 { 1437 `${split(",", "")}`, 1438 []interface{}{""}, 1439 false, 1440 }, 1441 1442 { 1443 `${split(",", "foo")}`, 1444 []interface{}{"foo"}, 1445 false, 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(",", ",foo,")}`, 1462 []interface{}{"", "foo", ""}, 1463 false, 1464 }, 1465 1466 { 1467 `${split(".", "foo.bar.baz")}`, 1468 []interface{}{"foo", "bar", "baz"}, 1469 false, 1470 }, 1471 }, 1472 }) 1473 } 1474 1475 func TestInterpolateFuncLookup(t *testing.T) { 1476 testFunction(t, testFunctionConfig{ 1477 Vars: map[string]ast.Variable{ 1478 "var.foo": { 1479 Type: ast.TypeMap, 1480 Value: map[string]ast.Variable{ 1481 "bar": { 1482 Type: ast.TypeString, 1483 Value: "baz", 1484 }, 1485 }, 1486 }, 1487 "var.map_of_lists": ast.Variable{ 1488 Type: ast.TypeMap, 1489 Value: map[string]ast.Variable{ 1490 "bar": { 1491 Type: ast.TypeList, 1492 Value: []ast.Variable{ 1493 { 1494 Type: ast.TypeString, 1495 Value: "baz", 1496 }, 1497 }, 1498 }, 1499 }, 1500 }, 1501 }, 1502 Cases: []testFunctionCase{ 1503 { 1504 `${lookup(var.foo, "bar")}`, 1505 "baz", 1506 false, 1507 }, 1508 1509 // Invalid key 1510 { 1511 `${lookup(var.foo, "baz")}`, 1512 nil, 1513 true, 1514 }, 1515 1516 // Supplied default with valid key 1517 { 1518 `${lookup(var.foo, "bar", "")}`, 1519 "baz", 1520 false, 1521 }, 1522 1523 // Supplied default with invalid key 1524 { 1525 `${lookup(var.foo, "zip", "")}`, 1526 "", 1527 false, 1528 }, 1529 1530 // Too many args 1531 { 1532 `${lookup(var.foo, "bar", "", "abc")}`, 1533 nil, 1534 true, 1535 }, 1536 1537 // Cannot lookup into map of lists 1538 { 1539 `${lookup(var.map_of_lists, "bar")}`, 1540 nil, 1541 true, 1542 }, 1543 1544 // Non-empty default 1545 { 1546 `${lookup(var.foo, "zap", "xyz")}`, 1547 "xyz", 1548 false, 1549 }, 1550 }, 1551 }) 1552 } 1553 1554 func TestInterpolateFuncKeys(t *testing.T) { 1555 testFunction(t, testFunctionConfig{ 1556 Vars: map[string]ast.Variable{ 1557 "var.foo": ast.Variable{ 1558 Type: ast.TypeMap, 1559 Value: map[string]ast.Variable{ 1560 "bar": ast.Variable{ 1561 Value: "baz", 1562 Type: ast.TypeString, 1563 }, 1564 "qux": ast.Variable{ 1565 Value: "quack", 1566 Type: ast.TypeString, 1567 }, 1568 }, 1569 }, 1570 "var.str": ast.Variable{ 1571 Value: "astring", 1572 Type: ast.TypeString, 1573 }, 1574 }, 1575 Cases: []testFunctionCase{ 1576 { 1577 `${keys(var.foo)}`, 1578 []interface{}{"bar", "qux"}, 1579 false, 1580 }, 1581 1582 // Invalid key 1583 { 1584 `${keys(var.not)}`, 1585 nil, 1586 true, 1587 }, 1588 1589 // Too many args 1590 { 1591 `${keys(var.foo, "bar")}`, 1592 nil, 1593 true, 1594 }, 1595 1596 // Not a map 1597 { 1598 `${keys(var.str)}`, 1599 nil, 1600 true, 1601 }, 1602 }, 1603 }) 1604 } 1605 1606 // Confirm that keys return in sorted order, and values return in the order of 1607 // their sorted keys. 1608 func TestInterpolateFuncKeyValOrder(t *testing.T) { 1609 testFunction(t, testFunctionConfig{ 1610 Vars: map[string]ast.Variable{ 1611 "var.foo": ast.Variable{ 1612 Type: ast.TypeMap, 1613 Value: map[string]ast.Variable{ 1614 "D": ast.Variable{ 1615 Value: "2", 1616 Type: ast.TypeString, 1617 }, 1618 "C": ast.Variable{ 1619 Value: "Y", 1620 Type: ast.TypeString, 1621 }, 1622 "A": ast.Variable{ 1623 Value: "X", 1624 Type: ast.TypeString, 1625 }, 1626 "10": ast.Variable{ 1627 Value: "Z", 1628 Type: ast.TypeString, 1629 }, 1630 "1": ast.Variable{ 1631 Value: "4", 1632 Type: ast.TypeString, 1633 }, 1634 "3": ast.Variable{ 1635 Value: "W", 1636 Type: ast.TypeString, 1637 }, 1638 }, 1639 }, 1640 }, 1641 Cases: []testFunctionCase{ 1642 { 1643 `${keys(var.foo)}`, 1644 []interface{}{"1", "10", "3", "A", "C", "D"}, 1645 false, 1646 }, 1647 1648 { 1649 `${values(var.foo)}`, 1650 []interface{}{"4", "Z", "W", "X", "Y", "2"}, 1651 false, 1652 }, 1653 }, 1654 }) 1655 } 1656 1657 func TestInterpolateFuncValues(t *testing.T) { 1658 testFunction(t, testFunctionConfig{ 1659 Vars: map[string]ast.Variable{ 1660 "var.foo": ast.Variable{ 1661 Type: ast.TypeMap, 1662 Value: map[string]ast.Variable{ 1663 "bar": ast.Variable{ 1664 Value: "quack", 1665 Type: ast.TypeString, 1666 }, 1667 "qux": ast.Variable{ 1668 Value: "baz", 1669 Type: ast.TypeString, 1670 }, 1671 }, 1672 }, 1673 "var.str": ast.Variable{ 1674 Value: "astring", 1675 Type: ast.TypeString, 1676 }, 1677 }, 1678 Cases: []testFunctionCase{ 1679 { 1680 `${values(var.foo)}`, 1681 []interface{}{"quack", "baz"}, 1682 false, 1683 }, 1684 1685 // Invalid key 1686 { 1687 `${values(var.not)}`, 1688 nil, 1689 true, 1690 }, 1691 1692 // Too many args 1693 { 1694 `${values(var.foo, "bar")}`, 1695 nil, 1696 true, 1697 }, 1698 1699 // Not a map 1700 { 1701 `${values(var.str)}`, 1702 nil, 1703 true, 1704 }, 1705 1706 // Map of lists 1707 { 1708 `${values(map("one", list()))}`, 1709 nil, 1710 true, 1711 }, 1712 }, 1713 }) 1714 } 1715 1716 func interfaceToVariableSwallowError(input interface{}) ast.Variable { 1717 variable, _ := hil.InterfaceToVariable(input) 1718 return variable 1719 } 1720 1721 func TestInterpolateFuncElement(t *testing.T) { 1722 testFunction(t, testFunctionConfig{ 1723 Vars: map[string]ast.Variable{ 1724 "var.a_list": interfaceToVariableSwallowError([]string{"foo", "baz"}), 1725 "var.a_short_list": interfaceToVariableSwallowError([]string{"foo"}), 1726 "var.empty_list": interfaceToVariableSwallowError([]interface{}{}), 1727 "var.a_nested_list": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"baz"}}), 1728 }, 1729 Cases: []testFunctionCase{ 1730 { 1731 `${element(var.a_list, "1")}`, 1732 "baz", 1733 false, 1734 }, 1735 1736 { 1737 `${element(var.a_short_list, "0")}`, 1738 "foo", 1739 false, 1740 }, 1741 1742 // Invalid index should wrap vs. out-of-bounds 1743 { 1744 `${element(var.a_list, "2")}`, 1745 "foo", 1746 false, 1747 }, 1748 1749 // Negative number should fail 1750 { 1751 `${element(var.a_short_list, "-1")}`, 1752 nil, 1753 true, 1754 }, 1755 1756 // Empty list should fail 1757 { 1758 `${element(var.empty_list, 0)}`, 1759 nil, 1760 true, 1761 }, 1762 1763 // Too many args 1764 { 1765 `${element(var.a_list, "0", "2")}`, 1766 nil, 1767 true, 1768 }, 1769 1770 // Only works on single-level lists 1771 { 1772 `${element(var.a_nested_list, "0")}`, 1773 nil, 1774 true, 1775 }, 1776 }, 1777 }) 1778 } 1779 1780 func TestInterpolateFuncBase64Encode(t *testing.T) { 1781 testFunction(t, testFunctionConfig{ 1782 Cases: []testFunctionCase{ 1783 // Regular base64 encoding 1784 { 1785 `${base64encode("abc123!?$*&()'-=@~")}`, 1786 "YWJjMTIzIT8kKiYoKSctPUB+", 1787 false, 1788 }, 1789 }, 1790 }) 1791 } 1792 1793 func TestInterpolateFuncBase64Decode(t *testing.T) { 1794 testFunction(t, testFunctionConfig{ 1795 Cases: []testFunctionCase{ 1796 // Regular base64 decoding 1797 { 1798 `${base64decode("YWJjMTIzIT8kKiYoKSctPUB+")}`, 1799 "abc123!?$*&()'-=@~", 1800 false, 1801 }, 1802 1803 // Invalid base64 data decoding 1804 { 1805 `${base64decode("this-is-an-invalid-base64-data")}`, 1806 nil, 1807 true, 1808 }, 1809 }, 1810 }) 1811 } 1812 1813 func TestInterpolateFuncLower(t *testing.T) { 1814 testFunction(t, testFunctionConfig{ 1815 Cases: []testFunctionCase{ 1816 { 1817 `${lower("HELLO")}`, 1818 "hello", 1819 false, 1820 }, 1821 1822 { 1823 `${lower("")}`, 1824 "", 1825 false, 1826 }, 1827 1828 { 1829 `${lower()}`, 1830 nil, 1831 true, 1832 }, 1833 }, 1834 }) 1835 } 1836 1837 func TestInterpolateFuncUpper(t *testing.T) { 1838 testFunction(t, testFunctionConfig{ 1839 Cases: []testFunctionCase{ 1840 { 1841 `${upper("hello")}`, 1842 "HELLO", 1843 false, 1844 }, 1845 1846 { 1847 `${upper("")}`, 1848 "", 1849 false, 1850 }, 1851 1852 { 1853 `${upper()}`, 1854 nil, 1855 true, 1856 }, 1857 }, 1858 }) 1859 } 1860 1861 func TestInterpolateFuncSha1(t *testing.T) { 1862 testFunction(t, testFunctionConfig{ 1863 Cases: []testFunctionCase{ 1864 { 1865 `${sha1("test")}`, 1866 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", 1867 false, 1868 }, 1869 }, 1870 }) 1871 } 1872 1873 func TestInterpolateFuncSha256(t *testing.T) { 1874 testFunction(t, testFunctionConfig{ 1875 Cases: []testFunctionCase{ 1876 { // hexadecimal representation of sha256 sum 1877 `${sha256("test")}`, 1878 "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", 1879 false, 1880 }, 1881 }, 1882 }) 1883 } 1884 1885 func TestInterpolateFuncTitle(t *testing.T) { 1886 testFunction(t, testFunctionConfig{ 1887 Cases: []testFunctionCase{ 1888 { 1889 `${title("hello")}`, 1890 "Hello", 1891 false, 1892 }, 1893 1894 { 1895 `${title("hello world")}`, 1896 "Hello World", 1897 false, 1898 }, 1899 1900 { 1901 `${title("")}`, 1902 "", 1903 false, 1904 }, 1905 1906 { 1907 `${title()}`, 1908 nil, 1909 true, 1910 }, 1911 }, 1912 }) 1913 } 1914 1915 func TestInterpolateFuncTrimSpace(t *testing.T) { 1916 testFunction(t, testFunctionConfig{ 1917 Cases: []testFunctionCase{ 1918 { 1919 `${trimspace(" test ")}`, 1920 "test", 1921 false, 1922 }, 1923 }, 1924 }) 1925 } 1926 1927 func TestInterpolateFuncBase64Sha256(t *testing.T) { 1928 testFunction(t, testFunctionConfig{ 1929 Cases: []testFunctionCase{ 1930 { 1931 `${base64sha256("test")}`, 1932 "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=", 1933 false, 1934 }, 1935 { // This will differ because we're base64-encoding hex represantiation, not raw bytes 1936 `${base64encode(sha256("test"))}`, 1937 "OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZjMTViMGYwMGEwOA==", 1938 false, 1939 }, 1940 }, 1941 }) 1942 } 1943 1944 func TestInterpolateFuncMd5(t *testing.T) { 1945 testFunction(t, testFunctionConfig{ 1946 Cases: []testFunctionCase{ 1947 { 1948 `${md5("tada")}`, 1949 "ce47d07243bb6eaf5e1322c81baf9bbf", 1950 false, 1951 }, 1952 { // Confirm that we're not trimming any whitespaces 1953 `${md5(" tada ")}`, 1954 "aadf191a583e53062de2d02c008141c4", 1955 false, 1956 }, 1957 { // We accept empty string too 1958 `${md5("")}`, 1959 "d41d8cd98f00b204e9800998ecf8427e", 1960 false, 1961 }, 1962 }, 1963 }) 1964 } 1965 1966 func TestInterpolateFuncUUID(t *testing.T) { 1967 results := make(map[string]bool) 1968 1969 for i := 0; i < 100; i++ { 1970 ast, err := hil.Parse("${uuid()}") 1971 if err != nil { 1972 t.Fatalf("err: %s", err) 1973 } 1974 1975 result, err := hil.Eval(ast, langEvalConfig(nil)) 1976 if err != nil { 1977 t.Fatalf("err: %s", err) 1978 } 1979 1980 if results[result.Value.(string)] { 1981 t.Fatalf("Got unexpected duplicate uuid: %s", result.Value) 1982 } 1983 1984 results[result.Value.(string)] = true 1985 } 1986 } 1987 1988 func TestInterpolateFuncTimestamp(t *testing.T) { 1989 currentTime := time.Now().UTC() 1990 ast, err := hil.Parse("${timestamp()}") 1991 if err != nil { 1992 t.Fatalf("err: %s", err) 1993 } 1994 1995 result, err := hil.Eval(ast, langEvalConfig(nil)) 1996 if err != nil { 1997 t.Fatalf("err: %s", err) 1998 } 1999 resultTime, err := time.Parse(time.RFC3339, result.Value.(string)) 2000 if err != nil { 2001 t.Fatalf("Error parsing timestamp: %s", err) 2002 } 2003 2004 if resultTime.Sub(currentTime).Seconds() > 10.0 { 2005 t.Fatalf("Timestamp Diff too large. Expected: %s\nRecieved: %s", currentTime.Format(time.RFC3339), result.Value.(string)) 2006 } 2007 } 2008 2009 type testFunctionConfig struct { 2010 Cases []testFunctionCase 2011 Vars map[string]ast.Variable 2012 } 2013 2014 type testFunctionCase struct { 2015 Input string 2016 Result interface{} 2017 Error bool 2018 } 2019 2020 func testFunction(t *testing.T, config testFunctionConfig) { 2021 for i, tc := range config.Cases { 2022 ast, err := hil.Parse(tc.Input) 2023 if err != nil { 2024 t.Fatalf("Case #%d: input: %#v\nerr: %v", i, tc.Input, err) 2025 } 2026 2027 result, err := hil.Eval(ast, langEvalConfig(config.Vars)) 2028 if err != nil != tc.Error { 2029 t.Fatalf("Case #%d:\ninput: %#v\nerr: %v", i, tc.Input, err) 2030 } 2031 2032 if !reflect.DeepEqual(result.Value, tc.Result) { 2033 t.Fatalf("%d: bad output for input: %s\n\nOutput: %#v\nExpected: %#v", 2034 i, tc.Input, result.Value, tc.Result) 2035 } 2036 } 2037 } 2038 2039 func TestInterpolateFuncPathExpand(t *testing.T) { 2040 homePath, err := homedir.Dir() 2041 if err != nil { 2042 t.Fatalf("Error getting home directory: %v", err) 2043 } 2044 testFunction(t, testFunctionConfig{ 2045 Cases: []testFunctionCase{ 2046 { 2047 `${pathexpand("~/test-file")}`, 2048 filepath.Join(homePath, "test-file"), 2049 false, 2050 }, 2051 { 2052 `${pathexpand("~/another/test/file")}`, 2053 filepath.Join(homePath, "another/test/file"), 2054 false, 2055 }, 2056 { 2057 `${pathexpand("/root/file")}`, 2058 "/root/file", 2059 false, 2060 }, 2061 { 2062 `${pathexpand("/")}`, 2063 "/", 2064 false, 2065 }, 2066 { 2067 `${pathexpand()}`, 2068 nil, 2069 true, 2070 }, 2071 }, 2072 }) 2073 }