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