github.com/pbthorste/terraform@v0.8.6-0.20170127005045-deb56bd93da2/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 TestInterpolateFuncSort(t *testing.T) { 1334 testFunction(t, testFunctionConfig{ 1335 Vars: map[string]ast.Variable{ 1336 "var.strings": ast.Variable{ 1337 Type: ast.TypeList, 1338 Value: []ast.Variable{ 1339 {Type: ast.TypeString, Value: "c"}, 1340 {Type: ast.TypeString, Value: "a"}, 1341 {Type: ast.TypeString, Value: "b"}, 1342 }, 1343 }, 1344 "var.notstrings": ast.Variable{ 1345 Type: ast.TypeList, 1346 Value: []ast.Variable{ 1347 {Type: ast.TypeList, Value: []ast.Variable{}}, 1348 {Type: ast.TypeString, Value: "b"}, 1349 }, 1350 }, 1351 }, 1352 Cases: []testFunctionCase{ 1353 { 1354 `${sort(var.strings)}`, 1355 []interface{}{"a", "b", "c"}, 1356 false, 1357 }, 1358 { 1359 `${sort(var.notstrings)}`, 1360 nil, 1361 true, 1362 }, 1363 }, 1364 }) 1365 } 1366 1367 func TestInterpolateFuncSplit(t *testing.T) { 1368 testFunction(t, testFunctionConfig{ 1369 Cases: []testFunctionCase{ 1370 { 1371 `${split(",")}`, 1372 nil, 1373 true, 1374 }, 1375 1376 { 1377 `${split(",", "")}`, 1378 []interface{}{""}, 1379 false, 1380 }, 1381 1382 { 1383 `${split(",", "foo")}`, 1384 []interface{}{"foo"}, 1385 false, 1386 }, 1387 1388 { 1389 `${split(",", ",,,")}`, 1390 []interface{}{"", "", "", ""}, 1391 false, 1392 }, 1393 1394 { 1395 `${split(",", "foo,")}`, 1396 []interface{}{"foo", ""}, 1397 false, 1398 }, 1399 1400 { 1401 `${split(",", ",foo,")}`, 1402 []interface{}{"", "foo", ""}, 1403 false, 1404 }, 1405 1406 { 1407 `${split(".", "foo.bar.baz")}`, 1408 []interface{}{"foo", "bar", "baz"}, 1409 false, 1410 }, 1411 }, 1412 }) 1413 } 1414 1415 func TestInterpolateFuncLookup(t *testing.T) { 1416 testFunction(t, testFunctionConfig{ 1417 Vars: map[string]ast.Variable{ 1418 "var.foo": { 1419 Type: ast.TypeMap, 1420 Value: map[string]ast.Variable{ 1421 "bar": { 1422 Type: ast.TypeString, 1423 Value: "baz", 1424 }, 1425 }, 1426 }, 1427 "var.map_of_lists": ast.Variable{ 1428 Type: ast.TypeMap, 1429 Value: map[string]ast.Variable{ 1430 "bar": { 1431 Type: ast.TypeList, 1432 Value: []ast.Variable{ 1433 { 1434 Type: ast.TypeString, 1435 Value: "baz", 1436 }, 1437 }, 1438 }, 1439 }, 1440 }, 1441 }, 1442 Cases: []testFunctionCase{ 1443 { 1444 `${lookup(var.foo, "bar")}`, 1445 "baz", 1446 false, 1447 }, 1448 1449 // Invalid key 1450 { 1451 `${lookup(var.foo, "baz")}`, 1452 nil, 1453 true, 1454 }, 1455 1456 // Supplied default with valid key 1457 { 1458 `${lookup(var.foo, "bar", "")}`, 1459 "baz", 1460 false, 1461 }, 1462 1463 // Supplied default with invalid key 1464 { 1465 `${lookup(var.foo, "zip", "")}`, 1466 "", 1467 false, 1468 }, 1469 1470 // Too many args 1471 { 1472 `${lookup(var.foo, "bar", "", "abc")}`, 1473 nil, 1474 true, 1475 }, 1476 1477 // Cannot lookup into map of lists 1478 { 1479 `${lookup(var.map_of_lists, "bar")}`, 1480 nil, 1481 true, 1482 }, 1483 1484 // Non-empty default 1485 { 1486 `${lookup(var.foo, "zap", "xyz")}`, 1487 "xyz", 1488 false, 1489 }, 1490 }, 1491 }) 1492 } 1493 1494 func TestInterpolateFuncKeys(t *testing.T) { 1495 testFunction(t, testFunctionConfig{ 1496 Vars: map[string]ast.Variable{ 1497 "var.foo": ast.Variable{ 1498 Type: ast.TypeMap, 1499 Value: map[string]ast.Variable{ 1500 "bar": ast.Variable{ 1501 Value: "baz", 1502 Type: ast.TypeString, 1503 }, 1504 "qux": ast.Variable{ 1505 Value: "quack", 1506 Type: ast.TypeString, 1507 }, 1508 }, 1509 }, 1510 "var.str": ast.Variable{ 1511 Value: "astring", 1512 Type: ast.TypeString, 1513 }, 1514 }, 1515 Cases: []testFunctionCase{ 1516 { 1517 `${keys(var.foo)}`, 1518 []interface{}{"bar", "qux"}, 1519 false, 1520 }, 1521 1522 // Invalid key 1523 { 1524 `${keys(var.not)}`, 1525 nil, 1526 true, 1527 }, 1528 1529 // Too many args 1530 { 1531 `${keys(var.foo, "bar")}`, 1532 nil, 1533 true, 1534 }, 1535 1536 // Not a map 1537 { 1538 `${keys(var.str)}`, 1539 nil, 1540 true, 1541 }, 1542 }, 1543 }) 1544 } 1545 1546 // Confirm that keys return in sorted order, and values return in the order of 1547 // their sorted keys. 1548 func TestInterpolateFuncKeyValOrder(t *testing.T) { 1549 testFunction(t, testFunctionConfig{ 1550 Vars: map[string]ast.Variable{ 1551 "var.foo": ast.Variable{ 1552 Type: ast.TypeMap, 1553 Value: map[string]ast.Variable{ 1554 "D": ast.Variable{ 1555 Value: "2", 1556 Type: ast.TypeString, 1557 }, 1558 "C": ast.Variable{ 1559 Value: "Y", 1560 Type: ast.TypeString, 1561 }, 1562 "A": ast.Variable{ 1563 Value: "X", 1564 Type: ast.TypeString, 1565 }, 1566 "10": ast.Variable{ 1567 Value: "Z", 1568 Type: ast.TypeString, 1569 }, 1570 "1": ast.Variable{ 1571 Value: "4", 1572 Type: ast.TypeString, 1573 }, 1574 "3": ast.Variable{ 1575 Value: "W", 1576 Type: ast.TypeString, 1577 }, 1578 }, 1579 }, 1580 }, 1581 Cases: []testFunctionCase{ 1582 { 1583 `${keys(var.foo)}`, 1584 []interface{}{"1", "10", "3", "A", "C", "D"}, 1585 false, 1586 }, 1587 1588 { 1589 `${values(var.foo)}`, 1590 []interface{}{"4", "Z", "W", "X", "Y", "2"}, 1591 false, 1592 }, 1593 }, 1594 }) 1595 } 1596 1597 func TestInterpolateFuncValues(t *testing.T) { 1598 testFunction(t, testFunctionConfig{ 1599 Vars: map[string]ast.Variable{ 1600 "var.foo": ast.Variable{ 1601 Type: ast.TypeMap, 1602 Value: map[string]ast.Variable{ 1603 "bar": ast.Variable{ 1604 Value: "quack", 1605 Type: ast.TypeString, 1606 }, 1607 "qux": ast.Variable{ 1608 Value: "baz", 1609 Type: ast.TypeString, 1610 }, 1611 }, 1612 }, 1613 "var.str": ast.Variable{ 1614 Value: "astring", 1615 Type: ast.TypeString, 1616 }, 1617 }, 1618 Cases: []testFunctionCase{ 1619 { 1620 `${values(var.foo)}`, 1621 []interface{}{"quack", "baz"}, 1622 false, 1623 }, 1624 1625 // Invalid key 1626 { 1627 `${values(var.not)}`, 1628 nil, 1629 true, 1630 }, 1631 1632 // Too many args 1633 { 1634 `${values(var.foo, "bar")}`, 1635 nil, 1636 true, 1637 }, 1638 1639 // Not a map 1640 { 1641 `${values(var.str)}`, 1642 nil, 1643 true, 1644 }, 1645 1646 // Map of lists 1647 { 1648 `${values(map("one", list()))}`, 1649 nil, 1650 true, 1651 }, 1652 }, 1653 }) 1654 } 1655 1656 func interfaceToVariableSwallowError(input interface{}) ast.Variable { 1657 variable, _ := hil.InterfaceToVariable(input) 1658 return variable 1659 } 1660 1661 func TestInterpolateFuncElement(t *testing.T) { 1662 testFunction(t, testFunctionConfig{ 1663 Vars: map[string]ast.Variable{ 1664 "var.a_list": interfaceToVariableSwallowError([]string{"foo", "baz"}), 1665 "var.a_short_list": interfaceToVariableSwallowError([]string{"foo"}), 1666 "var.empty_list": interfaceToVariableSwallowError([]interface{}{}), 1667 "var.a_nested_list": interfaceToVariableSwallowError([]interface{}{[]string{"foo"}, []string{"baz"}}), 1668 }, 1669 Cases: []testFunctionCase{ 1670 { 1671 `${element(var.a_list, "1")}`, 1672 "baz", 1673 false, 1674 }, 1675 1676 { 1677 `${element(var.a_short_list, "0")}`, 1678 "foo", 1679 false, 1680 }, 1681 1682 // Invalid index should wrap vs. out-of-bounds 1683 { 1684 `${element(var.a_list, "2")}`, 1685 "foo", 1686 false, 1687 }, 1688 1689 // Negative number should fail 1690 { 1691 `${element(var.a_short_list, "-1")}`, 1692 nil, 1693 true, 1694 }, 1695 1696 // Empty list should fail 1697 { 1698 `${element(var.empty_list, 0)}`, 1699 nil, 1700 true, 1701 }, 1702 1703 // Too many args 1704 { 1705 `${element(var.a_list, "0", "2")}`, 1706 nil, 1707 true, 1708 }, 1709 1710 // Only works on single-level lists 1711 { 1712 `${element(var.a_nested_list, "0")}`, 1713 nil, 1714 true, 1715 }, 1716 }, 1717 }) 1718 } 1719 1720 func TestInterpolateFuncBase64Encode(t *testing.T) { 1721 testFunction(t, testFunctionConfig{ 1722 Cases: []testFunctionCase{ 1723 // Regular base64 encoding 1724 { 1725 `${base64encode("abc123!?$*&()'-=@~")}`, 1726 "YWJjMTIzIT8kKiYoKSctPUB+", 1727 false, 1728 }, 1729 }, 1730 }) 1731 } 1732 1733 func TestInterpolateFuncBase64Decode(t *testing.T) { 1734 testFunction(t, testFunctionConfig{ 1735 Cases: []testFunctionCase{ 1736 // Regular base64 decoding 1737 { 1738 `${base64decode("YWJjMTIzIT8kKiYoKSctPUB+")}`, 1739 "abc123!?$*&()'-=@~", 1740 false, 1741 }, 1742 1743 // Invalid base64 data decoding 1744 { 1745 `${base64decode("this-is-an-invalid-base64-data")}`, 1746 nil, 1747 true, 1748 }, 1749 }, 1750 }) 1751 } 1752 1753 func TestInterpolateFuncLower(t *testing.T) { 1754 testFunction(t, testFunctionConfig{ 1755 Cases: []testFunctionCase{ 1756 { 1757 `${lower("HELLO")}`, 1758 "hello", 1759 false, 1760 }, 1761 1762 { 1763 `${lower("")}`, 1764 "", 1765 false, 1766 }, 1767 1768 { 1769 `${lower()}`, 1770 nil, 1771 true, 1772 }, 1773 }, 1774 }) 1775 } 1776 1777 func TestInterpolateFuncUpper(t *testing.T) { 1778 testFunction(t, testFunctionConfig{ 1779 Cases: []testFunctionCase{ 1780 { 1781 `${upper("hello")}`, 1782 "HELLO", 1783 false, 1784 }, 1785 1786 { 1787 `${upper("")}`, 1788 "", 1789 false, 1790 }, 1791 1792 { 1793 `${upper()}`, 1794 nil, 1795 true, 1796 }, 1797 }, 1798 }) 1799 } 1800 1801 func TestInterpolateFuncSha1(t *testing.T) { 1802 testFunction(t, testFunctionConfig{ 1803 Cases: []testFunctionCase{ 1804 { 1805 `${sha1("test")}`, 1806 "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", 1807 false, 1808 }, 1809 }, 1810 }) 1811 } 1812 1813 func TestInterpolateFuncSha256(t *testing.T) { 1814 testFunction(t, testFunctionConfig{ 1815 Cases: []testFunctionCase{ 1816 { // hexadecimal representation of sha256 sum 1817 `${sha256("test")}`, 1818 "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", 1819 false, 1820 }, 1821 }, 1822 }) 1823 } 1824 1825 func TestInterpolateFuncTitle(t *testing.T) { 1826 testFunction(t, testFunctionConfig{ 1827 Cases: []testFunctionCase{ 1828 { 1829 `${title("hello")}`, 1830 "Hello", 1831 false, 1832 }, 1833 1834 { 1835 `${title("hello world")}`, 1836 "Hello World", 1837 false, 1838 }, 1839 1840 { 1841 `${title("")}`, 1842 "", 1843 false, 1844 }, 1845 1846 { 1847 `${title()}`, 1848 nil, 1849 true, 1850 }, 1851 }, 1852 }) 1853 } 1854 1855 func TestInterpolateFuncTrimSpace(t *testing.T) { 1856 testFunction(t, testFunctionConfig{ 1857 Cases: []testFunctionCase{ 1858 { 1859 `${trimspace(" test ")}`, 1860 "test", 1861 false, 1862 }, 1863 }, 1864 }) 1865 } 1866 1867 func TestInterpolateFuncBase64Sha256(t *testing.T) { 1868 testFunction(t, testFunctionConfig{ 1869 Cases: []testFunctionCase{ 1870 { 1871 `${base64sha256("test")}`, 1872 "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=", 1873 false, 1874 }, 1875 { // This will differ because we're base64-encoding hex represantiation, not raw bytes 1876 `${base64encode(sha256("test"))}`, 1877 "OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZjMTViMGYwMGEwOA==", 1878 false, 1879 }, 1880 }, 1881 }) 1882 } 1883 1884 func TestInterpolateFuncMd5(t *testing.T) { 1885 testFunction(t, testFunctionConfig{ 1886 Cases: []testFunctionCase{ 1887 { 1888 `${md5("tada")}`, 1889 "ce47d07243bb6eaf5e1322c81baf9bbf", 1890 false, 1891 }, 1892 { // Confirm that we're not trimming any whitespaces 1893 `${md5(" tada ")}`, 1894 "aadf191a583e53062de2d02c008141c4", 1895 false, 1896 }, 1897 { // We accept empty string too 1898 `${md5("")}`, 1899 "d41d8cd98f00b204e9800998ecf8427e", 1900 false, 1901 }, 1902 }, 1903 }) 1904 } 1905 1906 func TestInterpolateFuncUUID(t *testing.T) { 1907 results := make(map[string]bool) 1908 1909 for i := 0; i < 100; i++ { 1910 ast, err := hil.Parse("${uuid()}") 1911 if err != nil { 1912 t.Fatalf("err: %s", err) 1913 } 1914 1915 result, err := hil.Eval(ast, langEvalConfig(nil)) 1916 if err != nil { 1917 t.Fatalf("err: %s", err) 1918 } 1919 1920 if results[result.Value.(string)] { 1921 t.Fatalf("Got unexpected duplicate uuid: %s", result.Value) 1922 } 1923 1924 results[result.Value.(string)] = true 1925 } 1926 } 1927 1928 func TestInterpolateFuncTimestamp(t *testing.T) { 1929 currentTime := time.Now().UTC() 1930 ast, err := hil.Parse("${timestamp()}") 1931 if err != nil { 1932 t.Fatalf("err: %s", err) 1933 } 1934 1935 result, err := hil.Eval(ast, langEvalConfig(nil)) 1936 if err != nil { 1937 t.Fatalf("err: %s", err) 1938 } 1939 resultTime, err := time.Parse(time.RFC3339, result.Value.(string)) 1940 if err != nil { 1941 t.Fatalf("Error parsing timestamp: %s", err) 1942 } 1943 1944 if resultTime.Sub(currentTime).Seconds() > 10.0 { 1945 t.Fatalf("Timestamp Diff too large. Expected: %s\nRecieved: %s", currentTime.Format(time.RFC3339), result.Value.(string)) 1946 } 1947 } 1948 1949 type testFunctionConfig struct { 1950 Cases []testFunctionCase 1951 Vars map[string]ast.Variable 1952 } 1953 1954 type testFunctionCase struct { 1955 Input string 1956 Result interface{} 1957 Error bool 1958 } 1959 1960 func testFunction(t *testing.T, config testFunctionConfig) { 1961 for i, tc := range config.Cases { 1962 ast, err := hil.Parse(tc.Input) 1963 if err != nil { 1964 t.Fatalf("Case #%d: input: %#v\nerr: %v", i, tc.Input, err) 1965 } 1966 1967 result, err := hil.Eval(ast, langEvalConfig(config.Vars)) 1968 if err != nil != tc.Error { 1969 t.Fatalf("Case #%d:\ninput: %#v\nerr: %v", i, tc.Input, err) 1970 } 1971 1972 if !reflect.DeepEqual(result.Value, tc.Result) { 1973 t.Fatalf("%d: bad output for input: %s\n\nOutput: %#v\nExpected: %#v", 1974 i, tc.Input, result.Value, tc.Result) 1975 } 1976 } 1977 } 1978 1979 func TestInterpolateFuncPathExpand(t *testing.T) { 1980 homePath, err := homedir.Dir() 1981 if err != nil { 1982 t.Fatalf("Error getting home directory: %v", err) 1983 } 1984 testFunction(t, testFunctionConfig{ 1985 Cases: []testFunctionCase{ 1986 { 1987 `${pathexpand("~/test-file")}`, 1988 filepath.Join(homePath, "test-file"), 1989 false, 1990 }, 1991 { 1992 `${pathexpand("~/another/test/file")}`, 1993 filepath.Join(homePath, "another/test/file"), 1994 false, 1995 }, 1996 { 1997 `${pathexpand("/root/file")}`, 1998 "/root/file", 1999 false, 2000 }, 2001 { 2002 `${pathexpand("/")}`, 2003 "/", 2004 false, 2005 }, 2006 { 2007 `${pathexpand()}`, 2008 nil, 2009 true, 2010 }, 2011 }, 2012 }) 2013 }