github.com/armen/terraform@v0.5.2-0.20150529052519-caa8117a08f1/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 TestInterpolateFuncConcat(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 TestInterpolateFuncFile(t *testing.T) { 39 tf, err := ioutil.TempFile("", "tf") 40 if err != nil { 41 t.Fatalf("err: %s", err) 42 } 43 path := tf.Name() 44 tf.Write([]byte("foo")) 45 tf.Close() 46 defer os.Remove(path) 47 48 testFunction(t, testFunctionConfig{ 49 Cases: []testFunctionCase{ 50 { 51 fmt.Sprintf(`${file("%s")}`, path), 52 "foo", 53 false, 54 }, 55 56 // Invalid path 57 { 58 `${file("/i/dont/exist")}`, 59 nil, 60 true, 61 }, 62 63 // Too many args 64 { 65 `${file("foo", "bar")}`, 66 nil, 67 true, 68 }, 69 }, 70 }) 71 } 72 73 func TestInterpolateFuncFormat(t *testing.T) { 74 testFunction(t, testFunctionConfig{ 75 Cases: []testFunctionCase{ 76 { 77 `${format("hello")}`, 78 "hello", 79 false, 80 }, 81 82 { 83 `${format("hello %s", "world")}`, 84 "hello world", 85 false, 86 }, 87 88 { 89 `${format("hello %d", 42)}`, 90 "hello 42", 91 false, 92 }, 93 94 { 95 `${format("hello %05d", 42)}`, 96 "hello 00042", 97 false, 98 }, 99 100 { 101 `${format("hello %05d", 12345)}`, 102 "hello 12345", 103 false, 104 }, 105 }, 106 }) 107 } 108 109 func TestInterpolateFuncFormatList(t *testing.T) { 110 testFunction(t, testFunctionConfig{ 111 Cases: []testFunctionCase{ 112 // formatlist requires at least one list 113 { 114 `${formatlist("hello")}`, 115 nil, 116 true, 117 }, 118 { 119 `${formatlist("hello %s", "world")}`, 120 nil, 121 true, 122 }, 123 // formatlist applies to each list element in turn 124 { 125 `${formatlist("<%s>", split(",", "A,B"))}`, 126 "<A>" + InterpSplitDelim + "<B>", 127 false, 128 }, 129 // formatlist repeats scalar elements 130 { 131 `${join(", ", formatlist("%s=%s", "x", split(",", "A,B,C")))}`, 132 "x=A, x=B, x=C", 133 false, 134 }, 135 // Multiple lists are walked in parallel 136 { 137 `${join(", ", formatlist("%s=%s", split(",", "A,B,C"), split(",", "1,2,3")))}`, 138 "A=1, B=2, C=3", 139 false, 140 }, 141 // formatlist of lists of length zero/one are repeated, just as scalars are 142 { 143 `${join(", ", formatlist("%s=%s", split(",", ""), split(",", "1,2,3")))}`, 144 "=1, =2, =3", 145 false, 146 }, 147 { 148 `${join(", ", formatlist("%s=%s", split(",", "A"), split(",", "1,2,3")))}`, 149 "A=1, A=2, A=3", 150 false, 151 }, 152 // Mismatched list lengths generate an error 153 { 154 `${formatlist("%s=%2s", split(",", "A,B,C,D"), split(",", "1,2,3"))}`, 155 nil, 156 true, 157 }, 158 }, 159 }) 160 } 161 162 func TestInterpolateFuncJoin(t *testing.T) { 163 testFunction(t, testFunctionConfig{ 164 Cases: []testFunctionCase{ 165 { 166 `${join(",")}`, 167 nil, 168 true, 169 }, 170 171 { 172 `${join(",", "foo")}`, 173 "foo", 174 false, 175 }, 176 177 /* 178 TODO 179 { 180 `${join(",", "foo", "bar")}`, 181 "foo,bar", 182 false, 183 }, 184 */ 185 186 { 187 fmt.Sprintf(`${join(".", "%s")}`, 188 fmt.Sprintf( 189 "foo%sbar%sbaz", 190 InterpSplitDelim, 191 InterpSplitDelim)), 192 "foo.bar.baz", 193 false, 194 }, 195 }, 196 }) 197 } 198 199 func TestInterpolateFuncReplace(t *testing.T) { 200 testFunction(t, testFunctionConfig{ 201 Cases: []testFunctionCase{ 202 // Regular search and replace 203 { 204 `${replace("hello", "hel", "bel")}`, 205 "bello", 206 false, 207 }, 208 209 // Search string doesn't match 210 { 211 `${replace("hello", "nope", "bel")}`, 212 "hello", 213 false, 214 }, 215 216 // Regular expression 217 { 218 `${replace("hello", "/l/", "L")}`, 219 "heLLo", 220 false, 221 }, 222 223 { 224 `${replace("helo", "/(l)/", "$1$1")}`, 225 "hello", 226 false, 227 }, 228 229 // Bad regexp 230 { 231 `${replace("helo", "/(l/", "$1$1")}`, 232 nil, 233 true, 234 }, 235 }, 236 }) 237 } 238 239 func TestInterpolateFuncLength(t *testing.T) { 240 testFunction(t, testFunctionConfig{ 241 Cases: []testFunctionCase{ 242 // Raw strings 243 { 244 `${length("")}`, 245 "0", 246 false, 247 }, 248 { 249 `${length("a")}`, 250 "1", 251 false, 252 }, 253 { 254 `${length(" ")}`, 255 "1", 256 false, 257 }, 258 { 259 `${length(" a ,")}`, 260 "4", 261 false, 262 }, 263 { 264 `${length("aaa")}`, 265 "3", 266 false, 267 }, 268 269 // Lists 270 { 271 `${length(split(",", "a"))}`, 272 "1", 273 false, 274 }, 275 { 276 `${length(split(",", "foo,"))}`, 277 "2", 278 false, 279 }, 280 { 281 `${length(split(",", ",foo,"))}`, 282 "3", 283 false, 284 }, 285 { 286 `${length(split(",", "foo,bar"))}`, 287 "2", 288 false, 289 }, 290 { 291 `${length(split(".", "one.two.three.four.five"))}`, 292 "5", 293 false, 294 }, 295 }, 296 }) 297 } 298 299 func TestInterpolateFuncSplit(t *testing.T) { 300 testFunction(t, testFunctionConfig{ 301 Cases: []testFunctionCase{ 302 { 303 `${split(",")}`, 304 nil, 305 true, 306 }, 307 308 { 309 `${split(",", "foo")}`, 310 "foo", 311 false, 312 }, 313 314 { 315 `${split(",", ",,,")}`, 316 fmt.Sprintf( 317 "%s%s%s", 318 InterpSplitDelim, 319 InterpSplitDelim, 320 InterpSplitDelim), 321 false, 322 }, 323 324 { 325 `${split(",", "foo,")}`, 326 fmt.Sprintf( 327 "%s%s", 328 "foo", 329 InterpSplitDelim), 330 false, 331 }, 332 333 { 334 `${split(",", ",foo,")}`, 335 fmt.Sprintf( 336 "%s%s%s", 337 InterpSplitDelim, 338 "foo", 339 InterpSplitDelim), 340 false, 341 }, 342 343 { 344 `${split(".", "foo.bar.baz")}`, 345 fmt.Sprintf( 346 "foo%sbar%sbaz", 347 InterpSplitDelim, 348 InterpSplitDelim), 349 false, 350 }, 351 }, 352 }) 353 } 354 355 func TestInterpolateFuncLookup(t *testing.T) { 356 testFunction(t, testFunctionConfig{ 357 Vars: map[string]ast.Variable{ 358 "var.foo.bar": ast.Variable{ 359 Value: "baz", 360 Type: ast.TypeString, 361 }, 362 }, 363 Cases: []testFunctionCase{ 364 { 365 `${lookup("foo", "bar")}`, 366 "baz", 367 false, 368 }, 369 370 // Invalid key 371 { 372 `${lookup("foo", "baz")}`, 373 nil, 374 true, 375 }, 376 377 // Too many args 378 { 379 `${lookup("foo", "bar", "baz")}`, 380 nil, 381 true, 382 }, 383 }, 384 }) 385 } 386 387 func TestInterpolateFuncElement(t *testing.T) { 388 testFunction(t, testFunctionConfig{ 389 Cases: []testFunctionCase{ 390 { 391 fmt.Sprintf(`${element("%s", "1")}`, 392 "foo"+InterpSplitDelim+"baz"), 393 "baz", 394 false, 395 }, 396 397 { 398 `${element("foo", "0")}`, 399 "foo", 400 false, 401 }, 402 403 // Invalid index should wrap vs. out-of-bounds 404 { 405 fmt.Sprintf(`${element("%s", "2")}`, 406 "foo"+InterpSplitDelim+"baz"), 407 "foo", 408 false, 409 }, 410 411 // Too many args 412 { 413 fmt.Sprintf(`${element("%s", "0", "2")}`, 414 "foo"+InterpSplitDelim+"baz"), 415 nil, 416 true, 417 }, 418 }, 419 }) 420 } 421 422 type testFunctionConfig struct { 423 Cases []testFunctionCase 424 Vars map[string]ast.Variable 425 } 426 427 type testFunctionCase struct { 428 Input string 429 Result interface{} 430 Error bool 431 } 432 433 func testFunction(t *testing.T, config testFunctionConfig) { 434 for i, tc := range config.Cases { 435 ast, err := lang.Parse(tc.Input) 436 if err != nil { 437 t.Fatalf("%d: err: %s", i, err) 438 } 439 440 out, _, err := lang.Eval(ast, langEvalConfig(config.Vars)) 441 if (err != nil) != tc.Error { 442 t.Fatalf("%d: err: %s", i, err) 443 } 444 445 if !reflect.DeepEqual(out, tc.Result) { 446 t.Fatalf( 447 "%d: bad output for input: %s\n\nOutput: %#v\nExpected: %#v", 448 i, tc.Input, out, tc.Result) 449 } 450 } 451 }