github.com/2matz/terraform@v0.6.1-0.20150714181608-a03cbdb5d5bd/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/terraform/config/lang" 11 "github.com/hashicorp/terraform/config/lang/ast" 12 ) 13 14 func TestInterpolateFuncDeprecatedConcat(t *testing.T) { 15 testFunction(t, testFunctionConfig{ 16 Cases: []testFunctionCase{ 17 { 18 `${concat("foo", "bar")}`, 19 "foobar", 20 false, 21 }, 22 23 { 24 `${concat("foo")}`, 25 "foo", 26 false, 27 }, 28 29 { 30 `${concat()}`, 31 nil, 32 true, 33 }, 34 }, 35 }) 36 } 37 38 func TestInterpolateFuncConcat(t *testing.T) { 39 testFunction(t, testFunctionConfig{ 40 Cases: []testFunctionCase{ 41 // String + list 42 { 43 `${concat("a", split(",", "b,c"))}`, 44 NewStringList([]string{"a", "b", "c"}).String(), 45 false, 46 }, 47 48 // List + string 49 { 50 `${concat(split(",", "a,b"), "c")}`, 51 NewStringList([]string{"a", "b", "c"}).String(), 52 false, 53 }, 54 55 // Single list 56 { 57 `${concat(split(",", ",foo,"))}`, 58 NewStringList([]string{"", "foo", ""}).String(), 59 false, 60 }, 61 { 62 `${concat(split(",", "a,b,c"))}`, 63 NewStringList([]string{"a", "b", "c"}).String(), 64 false, 65 }, 66 67 // Two lists 68 { 69 `${concat(split(",", "a,b,c"), split(",", "d,e"))}`, 70 NewStringList([]string{"a", "b", "c", "d", "e"}).String(), 71 false, 72 }, 73 // Two lists with different separators 74 { 75 `${concat(split(",", "a,b,c"), split(" ", "d e"))}`, 76 NewStringList([]string{"a", "b", "c", "d", "e"}).String(), 77 false, 78 }, 79 80 // More lists 81 { 82 `${concat(split(",", "a,b"), split(",", "c,d"), split(",", "e,f"), split(",", "0,1"))}`, 83 NewStringList([]string{"a", "b", "c", "d", "e", "f", "0", "1"}).String(), 84 false, 85 }, 86 }, 87 }) 88 } 89 90 func TestInterpolateFuncFile(t *testing.T) { 91 tf, err := ioutil.TempFile("", "tf") 92 if err != nil { 93 t.Fatalf("err: %s", err) 94 } 95 path := tf.Name() 96 tf.Write([]byte("foo")) 97 tf.Close() 98 defer os.Remove(path) 99 100 testFunction(t, testFunctionConfig{ 101 Cases: []testFunctionCase{ 102 { 103 fmt.Sprintf(`${file("%s")}`, path), 104 "foo", 105 false, 106 }, 107 108 // Invalid path 109 { 110 `${file("/i/dont/exist")}`, 111 nil, 112 true, 113 }, 114 115 // Too many args 116 { 117 `${file("foo", "bar")}`, 118 nil, 119 true, 120 }, 121 }, 122 }) 123 } 124 125 func TestInterpolateFuncFormat(t *testing.T) { 126 testFunction(t, testFunctionConfig{ 127 Cases: []testFunctionCase{ 128 { 129 `${format("hello")}`, 130 "hello", 131 false, 132 }, 133 134 { 135 `${format("hello %s", "world")}`, 136 "hello world", 137 false, 138 }, 139 140 { 141 `${format("hello %d", 42)}`, 142 "hello 42", 143 false, 144 }, 145 146 { 147 `${format("hello %05d", 42)}`, 148 "hello 00042", 149 false, 150 }, 151 152 { 153 `${format("hello %05d", 12345)}`, 154 "hello 12345", 155 false, 156 }, 157 }, 158 }) 159 } 160 161 func TestInterpolateFuncFormatList(t *testing.T) { 162 testFunction(t, testFunctionConfig{ 163 Cases: []testFunctionCase{ 164 // formatlist requires at least one list 165 { 166 `${formatlist("hello")}`, 167 nil, 168 true, 169 }, 170 { 171 `${formatlist("hello %s", "world")}`, 172 nil, 173 true, 174 }, 175 // formatlist applies to each list element in turn 176 { 177 `${formatlist("<%s>", split(",", "A,B"))}`, 178 NewStringList([]string{"<A>", "<B>"}).String(), 179 false, 180 }, 181 // formatlist repeats scalar elements 182 { 183 `${join(", ", formatlist("%s=%s", "x", split(",", "A,B,C")))}`, 184 "x=A, x=B, x=C", 185 false, 186 }, 187 // Multiple lists are walked in parallel 188 { 189 `${join(", ", formatlist("%s=%s", split(",", "A,B,C"), split(",", "1,2,3")))}`, 190 "A=1, B=2, C=3", 191 false, 192 }, 193 // Mismatched list lengths generate an error 194 { 195 `${formatlist("%s=%2s", split(",", "A,B,C,D"), split(",", "1,2,3"))}`, 196 nil, 197 true, 198 }, 199 // Works with lists of length 1 [GH-2240] 200 { 201 `${formatlist("%s.id", split(",", "demo-rest-elb"))}`, 202 NewStringList([]string{"demo-rest-elb.id"}).String(), 203 false, 204 }, 205 }, 206 }) 207 } 208 209 func TestInterpolateFuncJoin(t *testing.T) { 210 testFunction(t, testFunctionConfig{ 211 Cases: []testFunctionCase{ 212 { 213 `${join(",")}`, 214 nil, 215 true, 216 }, 217 218 { 219 fmt.Sprintf(`${join(",", "%s")}`, 220 NewStringList([]string{"foo"}).String()), 221 "foo", 222 false, 223 }, 224 225 /* 226 TODO 227 { 228 `${join(",", "foo", "bar")}`, 229 "foo,bar", 230 false, 231 }, 232 */ 233 234 { 235 fmt.Sprintf(`${join(".", "%s")}`, 236 NewStringList([]string{"foo", "bar", "baz"}).String()), 237 "foo.bar.baz", 238 false, 239 }, 240 }, 241 }) 242 } 243 244 func TestInterpolateFuncReplace(t *testing.T) { 245 testFunction(t, testFunctionConfig{ 246 Cases: []testFunctionCase{ 247 // Regular search and replace 248 { 249 `${replace("hello", "hel", "bel")}`, 250 "bello", 251 false, 252 }, 253 254 // Search string doesn't match 255 { 256 `${replace("hello", "nope", "bel")}`, 257 "hello", 258 false, 259 }, 260 261 // Regular expression 262 { 263 `${replace("hello", "/l/", "L")}`, 264 "heLLo", 265 false, 266 }, 267 268 { 269 `${replace("helo", "/(l)/", "$1$1")}`, 270 "hello", 271 false, 272 }, 273 274 // Bad regexp 275 { 276 `${replace("helo", "/(l/", "$1$1")}`, 277 nil, 278 true, 279 }, 280 }, 281 }) 282 } 283 284 func TestInterpolateFuncLength(t *testing.T) { 285 testFunction(t, testFunctionConfig{ 286 Cases: []testFunctionCase{ 287 // Raw strings 288 { 289 `${length("")}`, 290 "0", 291 false, 292 }, 293 { 294 `${length("a")}`, 295 "1", 296 false, 297 }, 298 { 299 `${length(" ")}`, 300 "1", 301 false, 302 }, 303 { 304 `${length(" a ,")}`, 305 "4", 306 false, 307 }, 308 { 309 `${length("aaa")}`, 310 "3", 311 false, 312 }, 313 314 // Lists 315 { 316 `${length(split(",", "a"))}`, 317 "1", 318 false, 319 }, 320 { 321 `${length(split(",", "foo,"))}`, 322 "2", 323 false, 324 }, 325 { 326 `${length(split(",", ",foo,"))}`, 327 "3", 328 false, 329 }, 330 { 331 `${length(split(",", "foo,bar"))}`, 332 "2", 333 false, 334 }, 335 { 336 `${length(split(".", "one.two.three.four.five"))}`, 337 "5", 338 false, 339 }, 340 }, 341 }) 342 } 343 344 func TestInterpolateFuncSplit(t *testing.T) { 345 testFunction(t, testFunctionConfig{ 346 Cases: []testFunctionCase{ 347 { 348 `${split(",")}`, 349 nil, 350 true, 351 }, 352 353 { 354 `${split(",", "")}`, 355 NewStringList([]string{""}).String(), 356 false, 357 }, 358 359 { 360 `${split(",", "foo")}`, 361 NewStringList([]string{"foo"}).String(), 362 false, 363 }, 364 365 { 366 `${split(",", ",,,")}`, 367 NewStringList([]string{"", "", "", ""}).String(), 368 false, 369 }, 370 371 { 372 `${split(",", "foo,")}`, 373 NewStringList([]string{"foo", ""}).String(), 374 false, 375 }, 376 377 { 378 `${split(",", ",foo,")}`, 379 NewStringList([]string{"", "foo", ""}).String(), 380 false, 381 }, 382 383 { 384 `${split(".", "foo.bar.baz")}`, 385 NewStringList([]string{"foo", "bar", "baz"}).String(), 386 false, 387 }, 388 }, 389 }) 390 } 391 392 func TestInterpolateFuncLookup(t *testing.T) { 393 testFunction(t, testFunctionConfig{ 394 Vars: map[string]ast.Variable{ 395 "var.foo.bar": ast.Variable{ 396 Value: "baz", 397 Type: ast.TypeString, 398 }, 399 }, 400 Cases: []testFunctionCase{ 401 { 402 `${lookup("foo", "bar")}`, 403 "baz", 404 false, 405 }, 406 407 // Invalid key 408 { 409 `${lookup("foo", "baz")}`, 410 nil, 411 true, 412 }, 413 414 // Too many args 415 { 416 `${lookup("foo", "bar", "baz")}`, 417 nil, 418 true, 419 }, 420 }, 421 }) 422 } 423 424 func TestInterpolateFuncKeys(t *testing.T) { 425 testFunction(t, testFunctionConfig{ 426 Vars: map[string]ast.Variable{ 427 "var.foo.bar": ast.Variable{ 428 Value: "baz", 429 Type: ast.TypeString, 430 }, 431 "var.foo.qux": ast.Variable{ 432 Value: "quack", 433 Type: ast.TypeString, 434 }, 435 "var.str": ast.Variable{ 436 Value: "astring", 437 Type: ast.TypeString, 438 }, 439 }, 440 Cases: []testFunctionCase{ 441 { 442 `${keys("foo")}`, 443 NewStringList([]string{"bar", "qux"}).String(), 444 false, 445 }, 446 447 // Invalid key 448 { 449 `${keys("not")}`, 450 nil, 451 true, 452 }, 453 454 // Too many args 455 { 456 `${keys("foo", "bar")}`, 457 nil, 458 true, 459 }, 460 461 // Not a map 462 { 463 `${keys("str")}`, 464 nil, 465 true, 466 }, 467 }, 468 }) 469 } 470 471 func TestInterpolateFuncValues(t *testing.T) { 472 testFunction(t, testFunctionConfig{ 473 Vars: map[string]ast.Variable{ 474 "var.foo.bar": ast.Variable{ 475 Value: "quack", 476 Type: ast.TypeString, 477 }, 478 "var.foo.qux": ast.Variable{ 479 Value: "baz", 480 Type: ast.TypeString, 481 }, 482 "var.str": ast.Variable{ 483 Value: "astring", 484 Type: ast.TypeString, 485 }, 486 }, 487 Cases: []testFunctionCase{ 488 { 489 `${values("foo")}`, 490 NewStringList([]string{"quack", "baz"}).String(), 491 false, 492 }, 493 494 // Invalid key 495 { 496 `${values("not")}`, 497 nil, 498 true, 499 }, 500 501 // Too many args 502 { 503 `${values("foo", "bar")}`, 504 nil, 505 true, 506 }, 507 508 // Not a map 509 { 510 `${values("str")}`, 511 nil, 512 true, 513 }, 514 }, 515 }) 516 } 517 518 func TestInterpolateFuncElement(t *testing.T) { 519 testFunction(t, testFunctionConfig{ 520 Cases: []testFunctionCase{ 521 { 522 fmt.Sprintf(`${element("%s", "1")}`, 523 NewStringList([]string{"foo", "baz"}).String()), 524 "baz", 525 false, 526 }, 527 528 { 529 fmt.Sprintf(`${element("%s", "0")}`, 530 NewStringList([]string{"foo"}).String()), 531 "foo", 532 false, 533 }, 534 535 // Invalid index should wrap vs. out-of-bounds 536 { 537 fmt.Sprintf(`${element("%s", "2")}`, 538 NewStringList([]string{"foo", "baz"}).String()), 539 "foo", 540 false, 541 }, 542 543 // Too many args 544 { 545 fmt.Sprintf(`${element("%s", "0", "2")}`, 546 NewStringList([]string{"foo", "baz"}).String()), 547 nil, 548 true, 549 }, 550 }, 551 }) 552 } 553 554 type testFunctionConfig struct { 555 Cases []testFunctionCase 556 Vars map[string]ast.Variable 557 } 558 559 type testFunctionCase struct { 560 Input string 561 Result interface{} 562 Error bool 563 } 564 565 func testFunction(t *testing.T, config testFunctionConfig) { 566 for i, tc := range config.Cases { 567 ast, err := lang.Parse(tc.Input) 568 if err != nil { 569 t.Fatalf("Case #%d: input: %#v\nerr: %s", i, tc.Input, err) 570 } 571 572 out, _, err := lang.Eval(ast, langEvalConfig(config.Vars)) 573 if (err != nil) != tc.Error { 574 t.Fatalf("Case #%d:\ninput: %#v\nerr: %s", i, tc.Input, err) 575 } 576 577 if !reflect.DeepEqual(out, tc.Result) { 578 t.Fatalf( 579 "%d: bad output for input: %s\n\nOutput: %#v\nExpected: %#v", 580 i, tc.Input, out, tc.Result) 581 } 582 } 583 }