github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/lang/funcs/filesystem_test.go (about)

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