mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-19 17:59:05 -05:00
768 lines
18 KiB
Go
768 lines
18 KiB
Go
// Copyright (c) The OpenTofu Authors
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
// Copyright (c) 2023 HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package funcs
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
|
|
homedir "github.com/mitchellh/go-homedir"
|
|
"github.com/zclconf/go-cty/cty"
|
|
"github.com/zclconf/go-cty/cty/function"
|
|
"github.com/zclconf/go-cty/cty/function/stdlib"
|
|
|
|
"github.com/opentofu/opentofu/internal/lang/marks"
|
|
)
|
|
|
|
func TestFile(t *testing.T) {
|
|
tests := []struct {
|
|
Path cty.Value
|
|
Want cty.Value
|
|
Err string
|
|
}{
|
|
{
|
|
cty.StringVal("testdata/hello.txt"),
|
|
cty.StringVal("Hello World"),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/icon.png"),
|
|
cty.NilVal,
|
|
`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`,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/icon.png").Mark(marks.Sensitive),
|
|
cty.NilVal,
|
|
`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`,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/missing"),
|
|
cty.NilVal,
|
|
`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`,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/missing").Mark(marks.Sensitive),
|
|
cty.NilVal,
|
|
`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`,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(fmt.Sprintf("File(\".\", %#v)", test.Path), func(t *testing.T) {
|
|
got, err := File(".", test.Path)
|
|
|
|
if test.Err != "" {
|
|
if err == nil {
|
|
t.Fatal("succeeded; want error")
|
|
}
|
|
if got, want := err.Error(), test.Err; got != want {
|
|
t.Errorf("wrong error\ngot: %s\nwant: %s", got, want)
|
|
}
|
|
return
|
|
} else if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
|
|
if !got.RawEquals(test.Want) {
|
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTemplateFile(t *testing.T) {
|
|
tests := []struct {
|
|
Path cty.Value
|
|
Vars cty.Value
|
|
Want cty.Value
|
|
Err string
|
|
}{
|
|
{
|
|
cty.StringVal("testdata/hello.txt"),
|
|
cty.EmptyObjectVal,
|
|
cty.StringVal("Hello World"),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/icon.png"),
|
|
cty.EmptyObjectVal,
|
|
cty.NilVal,
|
|
`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`,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/missing"),
|
|
cty.EmptyObjectVal,
|
|
cty.NilVal,
|
|
`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`,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/secrets.txt").Mark(marks.Sensitive),
|
|
cty.EmptyObjectVal,
|
|
cty.NilVal,
|
|
`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`,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/hello.tmpl"),
|
|
cty.MapVal(map[string]cty.Value{
|
|
"name": cty.StringVal("Jodie"),
|
|
}),
|
|
cty.StringVal("Hello, Jodie!"),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/hello.tmpl"),
|
|
cty.MapVal(map[string]cty.Value{
|
|
"name!": cty.StringVal("Jodie"),
|
|
}),
|
|
cty.NilVal,
|
|
`invalid template variable name "name!": must start with a letter, followed by zero or more letters, digits, and underscores`,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/hello.tmpl"),
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"name": cty.StringVal("Jimbo"),
|
|
}),
|
|
cty.StringVal("Hello, Jimbo!"),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/hello.tmpl"),
|
|
cty.EmptyObjectVal,
|
|
cty.NilVal,
|
|
`vars map does not contain key "name", referenced at testdata/hello.tmpl:1,10-14`,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/func.tmpl"),
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"list": cty.ListVal([]cty.Value{
|
|
cty.StringVal("a"),
|
|
cty.StringVal("b"),
|
|
cty.StringVal("c"),
|
|
}),
|
|
}),
|
|
cty.StringVal("The items are a, b, c"),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/recursive.tmpl"),
|
|
cty.MapValEmpty(cty.String),
|
|
cty.NilVal,
|
|
`maximum recursion depth 1024 reached in testdata/recursive.tmpl:1,3-16, testdata/recursive.tmpl:1,3-16 ... `,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/list.tmpl"),
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"list": cty.ListVal([]cty.Value{
|
|
cty.StringVal("a"),
|
|
cty.StringVal("b"),
|
|
cty.StringVal("c"),
|
|
}),
|
|
}),
|
|
cty.StringVal("- a\n- b\n- c\n"),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/list.tmpl"),
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"list": cty.True,
|
|
}),
|
|
cty.NilVal,
|
|
`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.`,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/bare.tmpl"),
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"val": cty.True,
|
|
}),
|
|
cty.True, // since this template contains only an interpolation, its true value shines through
|
|
``,
|
|
},
|
|
{
|
|
// write to a sensitive file path that exists
|
|
cty.StringVal("testdata/hello.txt").Mark(marks.Sensitive),
|
|
cty.EmptyObjectVal,
|
|
cty.StringVal("Hello World").Mark(marks.Sensitive),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/bare.tmpl"),
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
"val": cty.True.Mark(marks.Sensitive),
|
|
}),
|
|
cty.True.Mark(marks.Sensitive),
|
|
``,
|
|
},
|
|
}
|
|
|
|
templateFileFn := MakeTemplateFileFunc(".", func() map[string]function.Function {
|
|
return map[string]function.Function{
|
|
"join": stdlib.JoinFunc,
|
|
"templatefile": MakeFileFunc(".", false), // just a placeholder, since templatefile itself overrides this
|
|
}
|
|
})
|
|
|
|
for _, test := range tests {
|
|
t.Run(fmt.Sprintf("TemplateFile(%#v, %#v)", test.Path, test.Vars), func(t *testing.T) {
|
|
got, err := templateFileFn.Call([]cty.Value{test.Path, test.Vars})
|
|
|
|
var argErr function.ArgError
|
|
if errors.As(err, &argErr) {
|
|
if argErr.Index < 0 || argErr.Index > 1 {
|
|
t.Errorf("ArgError index %d is out of range for templatefile (must be 0 or 1)", argErr.Index)
|
|
}
|
|
}
|
|
|
|
if test.Err != "" {
|
|
if err == nil {
|
|
t.Fatal("succeeded; want error")
|
|
}
|
|
if got, want := err.Error(), test.Err; got != want {
|
|
t.Errorf("wrong error\ngot: %s\nwant: %s", got, want)
|
|
}
|
|
return
|
|
} else if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
|
|
if !got.RawEquals(test.Want) {
|
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_templateMaxRecursionDepth(t *testing.T) {
|
|
tests := []struct {
|
|
Input string
|
|
Want int
|
|
Err string
|
|
}{
|
|
{
|
|
"",
|
|
1024,
|
|
``,
|
|
}, {
|
|
"4096",
|
|
4096,
|
|
``,
|
|
}, {
|
|
"apple",
|
|
-1,
|
|
`invalid value for TF_TEMPLATE_RECURSION_DEPTH: strconv.Atoi: parsing "apple": invalid syntax`,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(fmt.Sprintf("templateMaxRecursion(%s)", test.Input), func(t *testing.T) {
|
|
os.Setenv("TF_TEMPLATE_RECURSION_DEPTH", test.Input)
|
|
got, err := templateMaxRecursionDepth()
|
|
if test.Err != "" {
|
|
if err == nil {
|
|
t.Fatal("succeeded; want error")
|
|
}
|
|
if got, want := err.Error(), test.Err; got != want {
|
|
t.Errorf("wrong error\ngot: %s\nwant: %s", got, want)
|
|
}
|
|
return
|
|
} else if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
|
|
if got != test.Want {
|
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFileExists(t *testing.T) {
|
|
tests := []struct {
|
|
Path cty.Value
|
|
Want cty.Value
|
|
Err string
|
|
}{
|
|
{
|
|
cty.StringVal("testdata/hello.txt"),
|
|
cty.BoolVal(true),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal(""),
|
|
cty.BoolVal(false),
|
|
`"." is a directory, not a file`,
|
|
},
|
|
{
|
|
cty.StringVal("testdata").Mark(marks.Sensitive),
|
|
cty.BoolVal(false),
|
|
`(sensitive value) is a directory, not a file`,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/missing"),
|
|
cty.BoolVal(false),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/unreadable/foobar"),
|
|
cty.BoolVal(false),
|
|
fmt.Sprintf("failed to stat %q", filepath.FromSlash("testdata/unreadable/foobar")),
|
|
},
|
|
{
|
|
cty.StringVal("testdata/unreadable/foobar").Mark(marks.Sensitive),
|
|
cty.BoolVal(false),
|
|
`failed to stat (sensitive value)`,
|
|
},
|
|
}
|
|
|
|
// Ensure "unreadable" directory cannot be listed during the test run
|
|
fi, err := os.Lstat("testdata/unreadable")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// This won't work on Windows. See https://pkg.go.dev/os#Chmod
|
|
if err := os.Chmod("testdata/unreadable", 0000); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
defer func(mode os.FileMode) {
|
|
if err := os.Chmod("testdata/unreadable", mode); err != nil {
|
|
panic(err)
|
|
}
|
|
}(fi.Mode())
|
|
|
|
for _, test := range tests {
|
|
t.Run(fmt.Sprintf("FileExists(\".\", %#v)", test.Path), func(t *testing.T) {
|
|
got, err := FileExists(".", test.Path)
|
|
|
|
if test.Err != "" {
|
|
if runtime.GOOS == "windows" && strings.Contains(test.Err, "stat") {
|
|
t.Skip("Since chmod 0000 doesn't work on Windows, FileExists won't raise errors there.")
|
|
}
|
|
if err == nil {
|
|
t.Fatal("succeeded; want error")
|
|
}
|
|
if got, want := err.Error(), test.Err; got != want {
|
|
t.Errorf("wrong error\ngot: %s\nwant: %s", got, want)
|
|
}
|
|
return
|
|
} else if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
|
|
if !got.RawEquals(test.Want) {
|
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFileSet(t *testing.T) {
|
|
tests := []struct {
|
|
Path cty.Value
|
|
Pattern cty.Value
|
|
Want cty.Value
|
|
Err string
|
|
}{
|
|
{
|
|
cty.StringVal("."),
|
|
cty.StringVal("testdata*"),
|
|
cty.SetValEmpty(cty.String),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("."),
|
|
cty.StringVal("testdata"),
|
|
cty.SetValEmpty(cty.String),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("."),
|
|
cty.StringVal("{testdata,missing}"),
|
|
cty.SetValEmpty(cty.String),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("."),
|
|
cty.StringVal("testdata/missing"),
|
|
cty.SetValEmpty(cty.String),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("."),
|
|
cty.StringVal("testdata/missing*"),
|
|
cty.SetValEmpty(cty.String),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("."),
|
|
cty.StringVal("*/missing"),
|
|
cty.SetValEmpty(cty.String),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("."),
|
|
cty.StringVal("**/missing"),
|
|
cty.SetValEmpty(cty.String),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("."),
|
|
cty.StringVal("testdata/*.txt"),
|
|
cty.SetVal([]cty.Value{
|
|
cty.StringVal("testdata/hello.txt"),
|
|
}),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("."),
|
|
cty.StringVal("testdata/hello.txt"),
|
|
cty.SetVal([]cty.Value{
|
|
cty.StringVal("testdata/hello.txt"),
|
|
}),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("."),
|
|
cty.StringVal("testdata/hello.???"),
|
|
cty.SetVal([]cty.Value{
|
|
cty.StringVal("testdata/hello.txt"),
|
|
}),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("."),
|
|
cty.StringVal("testdata/hello*"),
|
|
cty.SetVal([]cty.Value{
|
|
cty.StringVal("testdata/hello.tmpl"),
|
|
cty.StringVal("testdata/hello.txt"),
|
|
}),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("."),
|
|
cty.StringVal("testdata/hello.{tmpl,txt}"),
|
|
cty.SetVal([]cty.Value{
|
|
cty.StringVal("testdata/hello.tmpl"),
|
|
cty.StringVal("testdata/hello.txt"),
|
|
}),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("."),
|
|
cty.StringVal("*/hello.txt"),
|
|
cty.SetVal([]cty.Value{
|
|
cty.StringVal("testdata/hello.txt"),
|
|
}),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("."),
|
|
cty.StringVal("*/*.txt"),
|
|
cty.SetVal([]cty.Value{
|
|
cty.StringVal("testdata/hello.txt"),
|
|
}),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("."),
|
|
cty.StringVal("*/hello*"),
|
|
cty.SetVal([]cty.Value{
|
|
cty.StringVal("testdata/hello.tmpl"),
|
|
cty.StringVal("testdata/hello.txt"),
|
|
}),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("."),
|
|
cty.StringVal("**/hello*"),
|
|
cty.SetVal([]cty.Value{
|
|
cty.StringVal("testdata/hello.tmpl"),
|
|
cty.StringVal("testdata/hello.txt"),
|
|
}),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("."),
|
|
cty.StringVal("**/hello.{tmpl,txt}"),
|
|
cty.SetVal([]cty.Value{
|
|
cty.StringVal("testdata/hello.tmpl"),
|
|
cty.StringVal("testdata/hello.txt"),
|
|
}),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("."),
|
|
cty.StringVal("["),
|
|
cty.SetValEmpty(cty.String),
|
|
`failed to glob pattern "[": syntax error in pattern`,
|
|
},
|
|
{
|
|
cty.StringVal("."),
|
|
cty.StringVal("[").Mark(marks.Sensitive),
|
|
cty.SetValEmpty(cty.String),
|
|
`failed to glob pattern (sensitive value): syntax error in pattern`,
|
|
},
|
|
{
|
|
cty.StringVal("testdata"),
|
|
cty.StringVal("missing"),
|
|
cty.SetValEmpty(cty.String),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("testdata"),
|
|
cty.StringVal("missing*"),
|
|
cty.SetValEmpty(cty.String),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("testdata"),
|
|
cty.StringVal("*.txt"),
|
|
cty.SetVal([]cty.Value{
|
|
cty.StringVal("hello.txt"),
|
|
}),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("testdata"),
|
|
cty.StringVal("hello.txt"),
|
|
cty.SetVal([]cty.Value{
|
|
cty.StringVal("hello.txt"),
|
|
}),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("testdata"),
|
|
cty.StringVal("hello.???"),
|
|
cty.SetVal([]cty.Value{
|
|
cty.StringVal("hello.txt"),
|
|
}),
|
|
``,
|
|
},
|
|
{
|
|
cty.StringVal("testdata"),
|
|
cty.StringVal("hello*"),
|
|
cty.SetVal([]cty.Value{
|
|
cty.StringVal("hello.tmpl"),
|
|
cty.StringVal("hello.txt"),
|
|
}),
|
|
``,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(fmt.Sprintf("FileSet(\".\", %#v, %#v)", test.Path, test.Pattern), func(t *testing.T) {
|
|
got, err := FileSet(".", test.Path, test.Pattern)
|
|
|
|
if test.Err != "" {
|
|
if err == nil {
|
|
t.Fatal("succeeded; want error")
|
|
}
|
|
if got, want := err.Error(), test.Err; got != want {
|
|
t.Errorf("wrong error\ngot: %s\nwant: %s", got, want)
|
|
}
|
|
return
|
|
} else if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
|
|
if !got.RawEquals(test.Want) {
|
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFileBase64(t *testing.T) {
|
|
tests := []struct {
|
|
Path cty.Value
|
|
Want cty.Value
|
|
Err bool
|
|
}{
|
|
{
|
|
cty.StringVal("testdata/hello.txt"),
|
|
cty.StringVal("SGVsbG8gV29ybGQ="),
|
|
false,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/icon.png"),
|
|
cty.StringVal("iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAINSURBVHgBjZNdSBRRFMf/M7tttl9zsdBld7EbZdi6xRZR0UPUU0UvPaeLC71JHxCFWg+2RAkhNRVaSLELGRRIQYX41gci9Bz2UMSOkWgY7szO6vqxznVmxGFdd1f/cGDmnnN+5z/DuUAFERohetBKNbZyjQ5ndduxkPpay2vtC7ZabNseGJuTJ2VsJG8wfDV0sD7dc4ewia8ww3jef6g+5Qk0xorrOWtqMHzS48onoqenacvZ//C6tHXwJwM1+DAsSNI/R1wdH01an+D1h2/7dywkBrt/kRORLLY6WEl3R0MzOLxvlgyOkPNcVQ03r0595ldSjEbPLeKKuAtvvxCUk5G72deI5gstYOB2Gmf8arLpzDz6buWg5Kpx6vJejHx3Wz/s2w8XLokHoDjvYujjJ7RejFpQe+GEOp+GjtisDuPRlfSR98M5jE9twZ5wE14k2iB4PWadoqilAUqWh+DWTNDT9ixeDVXhz+INdFxrRTnxhS+9A3rDJG+CLFdBPyppDcCwL7iZCSqEbALASV1Jpz7dZgJWQFrJBiWjovf5S32B2NiahLFlkSMNqQdxmmY/fcyI/seU9b95xwzJSobd6+5hdaHjKbe+dKt9XPEEA7Q7sNR5vTlHzXTtQ/P8vvhM/i39jc9MjIqF9Vwpm8TXQPO8Pabb7CREkNNy5pHdkRVlSdr4MhWDCKWkUs0yuFTBNunfXEAAAAAASUVORK5CYII="),
|
|
false,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/missing"),
|
|
cty.NilVal,
|
|
true, // no file exists
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(fmt.Sprintf("FileBase64(\".\", %#v)", test.Path), func(t *testing.T) {
|
|
got, err := FileBase64(".", test.Path)
|
|
|
|
if test.Err {
|
|
if err == nil {
|
|
t.Fatal("succeeded; want error")
|
|
}
|
|
return
|
|
} else if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
|
|
if !got.RawEquals(test.Want) {
|
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBasename(t *testing.T) {
|
|
tests := []struct {
|
|
Path cty.Value
|
|
Want cty.Value
|
|
Err bool
|
|
}{
|
|
{
|
|
cty.StringVal("testdata/hello.txt"),
|
|
cty.StringVal("hello.txt"),
|
|
false,
|
|
},
|
|
{
|
|
cty.StringVal("hello.txt"),
|
|
cty.StringVal("hello.txt"),
|
|
false,
|
|
},
|
|
{
|
|
cty.StringVal(""),
|
|
cty.StringVal("."),
|
|
false,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(fmt.Sprintf("Basename(%#v)", test.Path), func(t *testing.T) {
|
|
got, err := Basename(test.Path)
|
|
|
|
if test.Err {
|
|
if err == nil {
|
|
t.Fatal("succeeded; want error")
|
|
}
|
|
return
|
|
} else if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
|
|
if !got.RawEquals(test.Want) {
|
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDirname(t *testing.T) {
|
|
tests := []struct {
|
|
Path cty.Value
|
|
Want cty.Value
|
|
Err bool
|
|
}{
|
|
{
|
|
cty.StringVal("testdata/hello.txt"),
|
|
cty.StringVal("testdata"),
|
|
false,
|
|
},
|
|
{
|
|
cty.StringVal("testdata/foo/hello.txt"),
|
|
cty.StringVal(filepath.FromSlash("testdata/foo")),
|
|
false,
|
|
},
|
|
{
|
|
cty.StringVal("hello.txt"),
|
|
cty.StringVal("."),
|
|
false,
|
|
},
|
|
{
|
|
cty.StringVal(""),
|
|
cty.StringVal("."),
|
|
false,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(fmt.Sprintf("Dirname(%#v)", test.Path), func(t *testing.T) {
|
|
got, err := Dirname(test.Path)
|
|
|
|
if test.Err {
|
|
if err == nil {
|
|
t.Fatal("succeeded; want error")
|
|
}
|
|
return
|
|
} else if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
|
|
if !got.RawEquals(test.Want) {
|
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPathExpand(t *testing.T) {
|
|
homePath, err := homedir.Dir()
|
|
if err != nil {
|
|
t.Fatalf("Error getting home directory: %v", err)
|
|
}
|
|
|
|
tests := []struct {
|
|
Path cty.Value
|
|
Want cty.Value
|
|
Err bool
|
|
}{
|
|
{
|
|
cty.StringVal("~/test-file"),
|
|
cty.StringVal(filepath.Join(homePath, "test-file")),
|
|
false,
|
|
},
|
|
{
|
|
cty.StringVal("~/another/test/file"),
|
|
cty.StringVal(filepath.Join(homePath, "another/test/file")),
|
|
false,
|
|
},
|
|
{
|
|
cty.StringVal("/root/file"),
|
|
cty.StringVal("/root/file"),
|
|
false,
|
|
},
|
|
{
|
|
cty.StringVal("/"),
|
|
cty.StringVal("/"),
|
|
false,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(fmt.Sprintf("Dirname(%#v)", test.Path), func(t *testing.T) {
|
|
got, err := Pathexpand(test.Path)
|
|
|
|
if test.Err {
|
|
if err == nil {
|
|
t.Fatal("succeeded; want error")
|
|
}
|
|
return
|
|
} else if err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
|
|
if !got.RawEquals(test.Want) {
|
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
|
}
|
|
})
|
|
}
|
|
}
|