github.com/hugorut/terraform@v1.1.3/src/lang/funcs/filesystem_test.go (about) 1 package funcs 2 3 import ( 4 "fmt" 5 "path/filepath" 6 "testing" 7 8 homedir "github.com/mitchellh/go-homedir" 9 "github.com/zclconf/go-cty/cty" 10 "github.com/zclconf/go-cty/cty/function" 11 "github.com/zclconf/go-cty/cty/function/stdlib" 12 ) 13 14 func TestFile(t *testing.T) { 15 tests := []struct { 16 Path cty.Value 17 Want cty.Value 18 Err bool 19 }{ 20 { 21 cty.StringVal("testdata/hello.txt"), 22 cty.StringVal("Hello World"), 23 false, 24 }, 25 { 26 cty.StringVal("testdata/icon.png"), 27 cty.NilVal, 28 true, // Not valid UTF-8 29 }, 30 { 31 cty.StringVal("testdata/missing"), 32 cty.NilVal, 33 true, // no file exists 34 }, 35 } 36 37 for _, test := range tests { 38 t.Run(fmt.Sprintf("File(\".\", %#v)", test.Path), func(t *testing.T) { 39 got, err := File(".", test.Path) 40 41 if test.Err { 42 if err == nil { 43 t.Fatal("succeeded; want error") 44 } 45 return 46 } else if err != nil { 47 t.Fatalf("unexpected error: %s", err) 48 } 49 50 if !got.RawEquals(test.Want) { 51 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 52 } 53 }) 54 } 55 } 56 57 func TestTemplateFile(t *testing.T) { 58 tests := []struct { 59 Path cty.Value 60 Vars cty.Value 61 Want cty.Value 62 Err string 63 }{ 64 { 65 cty.StringVal("testdata/hello.txt"), 66 cty.EmptyObjectVal, 67 cty.StringVal("Hello World"), 68 ``, 69 }, 70 { 71 cty.StringVal("testdata/icon.png"), 72 cty.EmptyObjectVal, 73 cty.NilVal, 74 `contents of testdata/icon.png are not valid UTF-8; use the filebase64 function to obtain the Base64 encoded contents or the other file functions (e.g. filemd5, filesha256) to obtain file hashing results instead`, 75 }, 76 { 77 cty.StringVal("testdata/missing"), 78 cty.EmptyObjectVal, 79 cty.NilVal, 80 `no file exists at testdata/missing; this function works only with files that are distributed as part of the configuration source code, so if this file will be created by a resource in this configuration you must instead obtain this result from an attribute of that resource`, 81 }, 82 { 83 cty.StringVal("testdata/hello.tmpl"), 84 cty.MapVal(map[string]cty.Value{ 85 "name": cty.StringVal("Jodie"), 86 }), 87 cty.StringVal("Hello, Jodie!"), 88 ``, 89 }, 90 { 91 cty.StringVal("testdata/hello.tmpl"), 92 cty.MapVal(map[string]cty.Value{ 93 "name!": cty.StringVal("Jodie"), 94 }), 95 cty.NilVal, 96 `invalid template variable name "name!": must start with a letter, followed by zero or more letters, digits, and underscores`, 97 }, 98 { 99 cty.StringVal("testdata/hello.tmpl"), 100 cty.ObjectVal(map[string]cty.Value{ 101 "name": cty.StringVal("Jimbo"), 102 }), 103 cty.StringVal("Hello, Jimbo!"), 104 ``, 105 }, 106 { 107 cty.StringVal("testdata/hello.tmpl"), 108 cty.EmptyObjectVal, 109 cty.NilVal, 110 `vars map does not contain key "name", referenced at testdata/hello.tmpl:1,10-14`, 111 }, 112 { 113 cty.StringVal("testdata/func.tmpl"), 114 cty.ObjectVal(map[string]cty.Value{ 115 "list": cty.ListVal([]cty.Value{ 116 cty.StringVal("a"), 117 cty.StringVal("b"), 118 cty.StringVal("c"), 119 }), 120 }), 121 cty.StringVal("The items are a, b, c"), 122 ``, 123 }, 124 { 125 cty.StringVal("testdata/recursive.tmpl"), 126 cty.MapValEmpty(cty.String), 127 cty.NilVal, 128 `testdata/recursive.tmpl:1,3-16: Error in function call; Call to function "templatefile" failed: cannot recursively call templatefile from inside templatefile call.`, 129 }, 130 { 131 cty.StringVal("testdata/list.tmpl"), 132 cty.ObjectVal(map[string]cty.Value{ 133 "list": cty.ListVal([]cty.Value{ 134 cty.StringVal("a"), 135 cty.StringVal("b"), 136 cty.StringVal("c"), 137 }), 138 }), 139 cty.StringVal("- a\n- b\n- c\n"), 140 ``, 141 }, 142 { 143 cty.StringVal("testdata/list.tmpl"), 144 cty.ObjectVal(map[string]cty.Value{ 145 "list": cty.True, 146 }), 147 cty.NilVal, 148 `testdata/list.tmpl:1,13-17: Iteration over non-iterable value; A value of type bool cannot be used as the collection in a 'for' expression.`, 149 }, 150 { 151 cty.StringVal("testdata/bare.tmpl"), 152 cty.ObjectVal(map[string]cty.Value{ 153 "val": cty.True, 154 }), 155 cty.True, // since this template contains only an interpolation, its true value shines through 156 ``, 157 }, 158 } 159 160 templateFileFn := MakeTemplateFileFunc(".", func() map[string]function.Function { 161 return map[string]function.Function{ 162 "join": stdlib.JoinFunc, 163 "templatefile": MakeFileFunc(".", false), // just a placeholder, since templatefile itself overrides this 164 } 165 }) 166 167 for _, test := range tests { 168 t.Run(fmt.Sprintf("TemplateFile(%#v, %#v)", test.Path, test.Vars), func(t *testing.T) { 169 got, err := templateFileFn.Call([]cty.Value{test.Path, test.Vars}) 170 171 if argErr, ok := err.(function.ArgError); ok { 172 if argErr.Index < 0 || argErr.Index > 1 { 173 t.Errorf("ArgError index %d is out of range for templatefile (must be 0 or 1)", argErr.Index) 174 } 175 } 176 177 if test.Err != "" { 178 if err == nil { 179 t.Fatal("succeeded; want error") 180 } 181 if got, want := err.Error(), test.Err; got != want { 182 t.Errorf("wrong error\ngot: %s\nwant: %s", got, want) 183 } 184 return 185 } else if err != nil { 186 t.Fatalf("unexpected error: %s", err) 187 } 188 189 if !got.RawEquals(test.Want) { 190 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 191 } 192 }) 193 } 194 } 195 196 func TestFileExists(t *testing.T) { 197 tests := []struct { 198 Path cty.Value 199 Want cty.Value 200 Err bool 201 }{ 202 { 203 cty.StringVal("testdata/hello.txt"), 204 cty.BoolVal(true), 205 false, 206 }, 207 { 208 cty.StringVal(""), // empty path 209 cty.BoolVal(false), 210 true, 211 }, 212 { 213 cty.StringVal("testdata/missing"), 214 cty.BoolVal(false), 215 false, // no file exists 216 }, 217 } 218 219 for _, test := range tests { 220 t.Run(fmt.Sprintf("FileExists(\".\", %#v)", test.Path), func(t *testing.T) { 221 got, err := FileExists(".", test.Path) 222 223 if test.Err { 224 if err == nil { 225 t.Fatal("succeeded; want error") 226 } 227 return 228 } else if err != nil { 229 t.Fatalf("unexpected error: %s", err) 230 } 231 232 if !got.RawEquals(test.Want) { 233 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 234 } 235 }) 236 } 237 } 238 239 func TestFileSet(t *testing.T) { 240 tests := []struct { 241 Path cty.Value 242 Pattern cty.Value 243 Want cty.Value 244 Err bool 245 }{ 246 { 247 cty.StringVal("."), 248 cty.StringVal("testdata*"), 249 cty.SetValEmpty(cty.String), 250 false, 251 }, 252 { 253 cty.StringVal("."), 254 cty.StringVal("testdata"), 255 cty.SetValEmpty(cty.String), 256 false, 257 }, 258 { 259 cty.StringVal("."), 260 cty.StringVal("{testdata,missing}"), 261 cty.SetValEmpty(cty.String), 262 false, 263 }, 264 { 265 cty.StringVal("."), 266 cty.StringVal("testdata/missing"), 267 cty.SetValEmpty(cty.String), 268 false, 269 }, 270 { 271 cty.StringVal("."), 272 cty.StringVal("testdata/missing*"), 273 cty.SetValEmpty(cty.String), 274 false, 275 }, 276 { 277 cty.StringVal("."), 278 cty.StringVal("*/missing"), 279 cty.SetValEmpty(cty.String), 280 false, 281 }, 282 { 283 cty.StringVal("."), 284 cty.StringVal("**/missing"), 285 cty.SetValEmpty(cty.String), 286 false, 287 }, 288 { 289 cty.StringVal("."), 290 cty.StringVal("testdata/*.txt"), 291 cty.SetVal([]cty.Value{ 292 cty.StringVal("testdata/hello.txt"), 293 }), 294 false, 295 }, 296 { 297 cty.StringVal("."), 298 cty.StringVal("testdata/hello.txt"), 299 cty.SetVal([]cty.Value{ 300 cty.StringVal("testdata/hello.txt"), 301 }), 302 false, 303 }, 304 { 305 cty.StringVal("."), 306 cty.StringVal("testdata/hello.???"), 307 cty.SetVal([]cty.Value{ 308 cty.StringVal("testdata/hello.txt"), 309 }), 310 false, 311 }, 312 { 313 cty.StringVal("."), 314 cty.StringVal("testdata/hello*"), 315 cty.SetVal([]cty.Value{ 316 cty.StringVal("testdata/hello.tmpl"), 317 cty.StringVal("testdata/hello.txt"), 318 }), 319 false, 320 }, 321 { 322 cty.StringVal("."), 323 cty.StringVal("testdata/hello.{tmpl,txt}"), 324 cty.SetVal([]cty.Value{ 325 cty.StringVal("testdata/hello.tmpl"), 326 cty.StringVal("testdata/hello.txt"), 327 }), 328 false, 329 }, 330 { 331 cty.StringVal("."), 332 cty.StringVal("*/hello.txt"), 333 cty.SetVal([]cty.Value{ 334 cty.StringVal("testdata/hello.txt"), 335 }), 336 false, 337 }, 338 { 339 cty.StringVal("."), 340 cty.StringVal("*/*.txt"), 341 cty.SetVal([]cty.Value{ 342 cty.StringVal("testdata/hello.txt"), 343 }), 344 false, 345 }, 346 { 347 cty.StringVal("."), 348 cty.StringVal("*/hello*"), 349 cty.SetVal([]cty.Value{ 350 cty.StringVal("testdata/hello.tmpl"), 351 cty.StringVal("testdata/hello.txt"), 352 }), 353 false, 354 }, 355 { 356 cty.StringVal("."), 357 cty.StringVal("**/hello*"), 358 cty.SetVal([]cty.Value{ 359 cty.StringVal("testdata/hello.tmpl"), 360 cty.StringVal("testdata/hello.txt"), 361 }), 362 false, 363 }, 364 { 365 cty.StringVal("."), 366 cty.StringVal("**/hello.{tmpl,txt}"), 367 cty.SetVal([]cty.Value{ 368 cty.StringVal("testdata/hello.tmpl"), 369 cty.StringVal("testdata/hello.txt"), 370 }), 371 false, 372 }, 373 { 374 cty.StringVal("."), 375 cty.StringVal("["), 376 cty.SetValEmpty(cty.String), 377 true, 378 }, 379 { 380 cty.StringVal("."), 381 cty.StringVal("\\"), 382 cty.SetValEmpty(cty.String), 383 true, 384 }, 385 { 386 cty.StringVal("testdata"), 387 cty.StringVal("missing"), 388 cty.SetValEmpty(cty.String), 389 false, 390 }, 391 { 392 cty.StringVal("testdata"), 393 cty.StringVal("missing*"), 394 cty.SetValEmpty(cty.String), 395 false, 396 }, 397 { 398 cty.StringVal("testdata"), 399 cty.StringVal("*.txt"), 400 cty.SetVal([]cty.Value{ 401 cty.StringVal("hello.txt"), 402 }), 403 false, 404 }, 405 { 406 cty.StringVal("testdata"), 407 cty.StringVal("hello.txt"), 408 cty.SetVal([]cty.Value{ 409 cty.StringVal("hello.txt"), 410 }), 411 false, 412 }, 413 { 414 cty.StringVal("testdata"), 415 cty.StringVal("hello.???"), 416 cty.SetVal([]cty.Value{ 417 cty.StringVal("hello.txt"), 418 }), 419 false, 420 }, 421 { 422 cty.StringVal("testdata"), 423 cty.StringVal("hello*"), 424 cty.SetVal([]cty.Value{ 425 cty.StringVal("hello.tmpl"), 426 cty.StringVal("hello.txt"), 427 }), 428 false, 429 }, 430 } 431 432 for _, test := range tests { 433 t.Run(fmt.Sprintf("FileSet(\".\", %#v, %#v)", test.Path, test.Pattern), func(t *testing.T) { 434 got, err := FileSet(".", test.Path, test.Pattern) 435 436 if test.Err { 437 if err == nil { 438 t.Fatal("succeeded; want error") 439 } 440 return 441 } else if err != nil { 442 t.Fatalf("unexpected error: %s", err) 443 } 444 445 if !got.RawEquals(test.Want) { 446 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 447 } 448 }) 449 } 450 } 451 452 func TestFileBase64(t *testing.T) { 453 tests := []struct { 454 Path cty.Value 455 Want cty.Value 456 Err bool 457 }{ 458 { 459 cty.StringVal("testdata/hello.txt"), 460 cty.StringVal("SGVsbG8gV29ybGQ="), 461 false, 462 }, 463 { 464 cty.StringVal("testdata/icon.png"), 465 cty.StringVal("iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAq1BMVEX///9cTuVeUeRcTuZcTuZcT+VbSe1cTuVdT+MAAP9JSbZcT+VcTuZAQLFAQLJcTuVcTuZcUuBBQbA/P7JAQLJaTuRcT+RcTuVGQ7xAQLJVVf9cTuVcTuVGRMFeUeRbTeJcTuU/P7JeTeZbTOVcTeZAQLJBQbNAQLNaUORcTeZbT+VcTuRAQLNAQLRdTuRHR8xgUOdgUN9cTuVdTeRdT+VZTulcTuVAQLL///8+GmETAAAANnRSTlMApibw+osO6DcBB3fIX87+oRk3yehB0/Nj/gNs7nsTRv3dHmu//JYUMLVr3bssjxkgEK5CaxeK03nIAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAADoQAAA6EBvJf9gwAAAAd0SU1FB+EEBRIQDxZNTKsAAACCSURBVBjTfc7JFsFQEATQQpCYxyBEzJ55rvf/f0ZHcyQLvelTd1GngEwWycs5+UISyKLraSi9geWKK9Gr1j7AeqOJVtt2XtD1Bchef2BjQDAcCTC0CsA4mihMtXw2XwgsV2sFw812F+4P3y2GdI6nn3FGSs//4HJNAXDzU4Dg/oj/E+bsEbhf5cMsAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTA0LTA1VDE4OjE2OjE1KzAyOjAws5bLVQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0wNC0wNVQxODoxNjoxNSswMjowMMLLc+kAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAC3RFWHRUaXRsZQBHcm91cJYfIowAAABXelRYdFJhdyBwcm9maWxlIHR5cGUgaXB0YwAAeJzj8gwIcVYoKMpPy8xJ5VIAAyMLLmMLEyMTS5MUAxMgRIA0w2QDI7NUIMvY1MjEzMQcxAfLgEigSi4A6hcRdPJCNZUAAAAASUVORK5CYII="), 466 false, 467 }, 468 { 469 cty.StringVal("testdata/missing"), 470 cty.NilVal, 471 true, // no file exists 472 }, 473 } 474 475 for _, test := range tests { 476 t.Run(fmt.Sprintf("FileBase64(\".\", %#v)", test.Path), func(t *testing.T) { 477 got, err := FileBase64(".", test.Path) 478 479 if test.Err { 480 if err == nil { 481 t.Fatal("succeeded; want error") 482 } 483 return 484 } else if err != nil { 485 t.Fatalf("unexpected error: %s", err) 486 } 487 488 if !got.RawEquals(test.Want) { 489 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 490 } 491 }) 492 } 493 } 494 495 func TestBasename(t *testing.T) { 496 tests := []struct { 497 Path cty.Value 498 Want cty.Value 499 Err bool 500 }{ 501 { 502 cty.StringVal("testdata/hello.txt"), 503 cty.StringVal("hello.txt"), 504 false, 505 }, 506 { 507 cty.StringVal("hello.txt"), 508 cty.StringVal("hello.txt"), 509 false, 510 }, 511 { 512 cty.StringVal(""), 513 cty.StringVal("."), 514 false, 515 }, 516 } 517 518 for _, test := range tests { 519 t.Run(fmt.Sprintf("Basename(%#v)", test.Path), func(t *testing.T) { 520 got, err := Basename(test.Path) 521 522 if test.Err { 523 if err == nil { 524 t.Fatal("succeeded; want error") 525 } 526 return 527 } else if err != nil { 528 t.Fatalf("unexpected error: %s", err) 529 } 530 531 if !got.RawEquals(test.Want) { 532 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 533 } 534 }) 535 } 536 } 537 538 func TestDirname(t *testing.T) { 539 tests := []struct { 540 Path cty.Value 541 Want cty.Value 542 Err bool 543 }{ 544 { 545 cty.StringVal("testdata/hello.txt"), 546 cty.StringVal("testdata"), 547 false, 548 }, 549 { 550 cty.StringVal("testdata/foo/hello.txt"), 551 cty.StringVal("testdata/foo"), 552 false, 553 }, 554 { 555 cty.StringVal("hello.txt"), 556 cty.StringVal("."), 557 false, 558 }, 559 { 560 cty.StringVal(""), 561 cty.StringVal("."), 562 false, 563 }, 564 } 565 566 for _, test := range tests { 567 t.Run(fmt.Sprintf("Dirname(%#v)", test.Path), func(t *testing.T) { 568 got, err := Dirname(test.Path) 569 570 if test.Err { 571 if err == nil { 572 t.Fatal("succeeded; want error") 573 } 574 return 575 } else if err != nil { 576 t.Fatalf("unexpected error: %s", err) 577 } 578 579 if !got.RawEquals(test.Want) { 580 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 581 } 582 }) 583 } 584 } 585 586 func TestPathExpand(t *testing.T) { 587 homePath, err := homedir.Dir() 588 if err != nil { 589 t.Fatalf("Error getting home directory: %v", err) 590 } 591 592 tests := []struct { 593 Path cty.Value 594 Want cty.Value 595 Err bool 596 }{ 597 { 598 cty.StringVal("~/test-file"), 599 cty.StringVal(filepath.Join(homePath, "test-file")), 600 false, 601 }, 602 { 603 cty.StringVal("~/another/test/file"), 604 cty.StringVal(filepath.Join(homePath, "another/test/file")), 605 false, 606 }, 607 { 608 cty.StringVal("/root/file"), 609 cty.StringVal("/root/file"), 610 false, 611 }, 612 { 613 cty.StringVal("/"), 614 cty.StringVal("/"), 615 false, 616 }, 617 } 618 619 for _, test := range tests { 620 t.Run(fmt.Sprintf("Dirname(%#v)", test.Path), func(t *testing.T) { 621 got, err := Pathexpand(test.Path) 622 623 if test.Err { 624 if err == nil { 625 t.Fatal("succeeded; want error") 626 } 627 return 628 } else if err != nil { 629 t.Fatalf("unexpected error: %s", err) 630 } 631 632 if !got.RawEquals(test.Want) { 633 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 634 } 635 }) 636 } 637 }