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