Introduce Helmfile's own filesystem abstraction to correctly unit test some components (#307)

Use abstracted FS

Signed-off-by: Arkaitz Jimenez <arkaitzj@gmail.com>

Signed-off-by: Arkaitz Jimenez <arkaitzj@gmail.com>
This commit is contained in:
Arkaitz Jimenez
2022-08-24 12:58:43 +09:00
committed by GitHub
parent a626664f46
commit cc33e7b7d8
44 changed files with 513 additions and 425 deletions
+6 -9
View File
@@ -1,12 +1,13 @@
package tmpl
import "io/fs"
import (
"github.com/helmfile/helmfile/pkg/filesystem"
)
type Context struct {
preRender bool
basePath string
readFile func(string) ([]byte, error)
readDir func(string) ([]fs.DirEntry, error)
fs *filesystem.FileSystem
}
// SetBasePath sets the base path for the template
@@ -14,10 +15,6 @@ func (c *Context) SetBasePath(path string) {
c.basePath = path
}
func (c *Context) SetReadFile(f func(string) ([]byte, error)) {
c.readFile = f
}
func (c *Context) SetReadDir(f func(string) ([]fs.DirEntry, error)) {
c.readDir = f
func (c *Context) SetFileSystem(fs *filesystem.FileSystem) {
c.fs = fs
}
+4 -4
View File
@@ -220,11 +220,11 @@ func (c *Context) ReadFile(filename string) (string, error) {
path = filepath.Join(c.basePath, filename)
}
if c.readFile == nil {
if c.fs.ReadFile == nil {
return "", fmt.Errorf("readFile is not implemented")
}
bytes, err := c.readFile(path)
bytes, err := c.fs.ReadFile(path)
if err != nil {
return "", err
}
@@ -239,7 +239,7 @@ func (c *Context) ReadDir(path string) ([]string, error) {
contextPath = filepath.Join(c.basePath, path)
}
entries, err := c.readDir(contextPath)
entries, err := c.fs.ReadDir(contextPath)
if err != nil {
return nil, fmt.Errorf("ReadDir %q: %w", contextPath, err)
}
@@ -262,7 +262,7 @@ func (c *Context) ReadDirEntries(path string) ([]fs.DirEntry, error) {
} else {
contextPath = filepath.Join(c.basePath, path)
}
entries, err := c.readDir(contextPath)
entries, err := c.fs.ReadDir(contextPath)
if err != nil {
return nil, fmt.Errorf("ReadDirEntries %q: %w", contextPath, err)
}
+19 -16
View File
@@ -9,6 +9,8 @@ import (
"testing"
"github.com/stretchr/testify/require"
"github.com/helmfile/helmfile/pkg/filesystem"
)
func TestCreateFuncMap(t *testing.T) {
@@ -62,17 +64,23 @@ func TestCreateFuncMap_SkipInsecureTemplateFunctions(t *testing.T) {
skipInsecureTemplateFunctions = currentVal
}
func newFSExpecting(expectedFilename string, expected string) *filesystem.FileSystem {
return filesystem.FromFileSystem(filesystem.FileSystem{
ReadFile: func(filename string) ([]byte, error) {
if filename != expectedFilename {
return nil, fmt.Errorf("unexpected filename: expected=%v, actual=%s", expectedFilename, filename)
}
return []byte(expected), nil
},
})
}
func TestReadFile(t *testing.T) {
expected := `foo:
bar: BAR
`
expectedFilename := "values.yaml"
ctx := &Context{basePath: ".", readFile: func(filename string) ([]byte, error) {
if filename != expectedFilename {
return nil, fmt.Errorf("unexpected filename: expected=%v, actual=%s", expectedFilename, filename)
}
return []byte(expected), nil
}}
ctx := &Context{basePath: ".", fs: newFSExpecting(expectedFilename, expected)}
actual, err := ctx.ReadFile(expectedFilename)
require.NoError(t, err)
require.Equal(t, expected, actual)
@@ -125,12 +133,12 @@ func TestReadDir(t *testing.T) {
}
expectedDirname := "sampleDirectory"
ctx := &Context{basePath: ".", readDir: func(dirname string) ([]fs.DirEntry, error) {
ctx := &Context{basePath: ".", fs: filesystem.FromFileSystem(filesystem.FileSystem{ReadDir: func(dirname string) ([]fs.DirEntry, error) {
if dirname != expectedDirname {
return nil, fmt.Errorf("unexpected filename: expected=%v, actual=%s", expectedDirname, dirname)
}
return result, nil
}}
}})}
actual, err := ctx.ReadDir(expectedDirname)
require.NoError(t, err)
@@ -146,12 +154,12 @@ func TestReadDirEntries(t *testing.T) {
}
expectedDirname := "sampleDirectory"
ctx := &Context{basePath: ".", readDir: func(dirname string) ([]fs.DirEntry, error) {
ctx := &Context{basePath: ".", fs: filesystem.FromFileSystem(filesystem.FileSystem{ReadDir: func(dirname string) ([]fs.DirEntry, error) {
if dirname != expectedDirname {
return nil, fmt.Errorf("unexpected filename: expected=%v, actual=%s", expectedDirname, dirname)
}
return result, nil
}}
}})}
actual, err := ctx.ReadDirEntries(expectedDirname)
require.NoError(t, err)
@@ -163,12 +171,7 @@ func TestReadFile_PassAbsPath(t *testing.T) {
bar: BAR
`
expectedFilename, _ := filepath.Abs("values.yaml")
ctx := &Context{basePath: ".", readFile: func(filename string) ([]byte, error) {
if filename != expectedFilename {
return nil, fmt.Errorf("unexpected filename: expected=%v, actual=%s", expectedFilename, filename)
}
return []byte(expected), nil
}}
ctx := &Context{basePath: ".", fs: newFSExpecting(expectedFilename, expected)}
actual, err := ctx.ReadFile(expectedFilename)
require.NoError(t, err)
require.Equal(t, actual, expected)
+12 -10
View File
@@ -4,6 +4,8 @@ import (
"fmt"
"reflect"
"testing"
ffs "github.com/helmfile/helmfile/pkg/filesystem"
)
func TestRenderTemplate_Values(t *testing.T) {
@@ -14,12 +16,12 @@ func TestRenderTemplate_Values(t *testing.T) {
bar: FOO_BAR
`
expectedFilename := "values.yaml"
ctx := &Context{readFile: func(filename string) ([]byte, error) {
ctx := &Context{fs: &ffs.FileSystem{ReadFile: func(filename string) ([]byte, error) {
if filename != expectedFilename {
return nil, fmt.Errorf("unexpected filename: expected=%v, actual=%s", expectedFilename, filename)
}
return []byte(valuesYamlContent), nil
}}
}}}
buf, err := ctx.RenderTemplateToBuffer(`{{ readFile "values.yaml" | fromYaml | setValueAtPath "foo.bar" "FOO_BAR" | toYaml }}`)
if err != nil {
t.Errorf("unexpected error: %v", err)
@@ -43,12 +45,12 @@ func TestRenderTemplate_WithData(t *testing.T) {
"bar": "FOO_BAR",
},
}
ctx := &Context{readFile: func(filename string) ([]byte, error) {
ctx := &Context{fs: &ffs.FileSystem{ReadFile: func(filename string) ([]byte, error) {
if filename != expectedFilename {
return nil, fmt.Errorf("unexpected filename: expected=%v, actual=%s", expectedFilename, filename)
}
return []byte(valuesYamlContent), nil
}}
}}}
buf, err := ctx.RenderTemplateToBuffer(valuesYamlContent, data)
if err != nil {
t.Errorf("unexpected error: %v", err)
@@ -68,12 +70,12 @@ func TestRenderTemplate_AccessingMissingKeyWithGetOrNil(t *testing.T) {
`
expectedFilename := "values.yaml"
data := map[string]interface{}{}
ctx := &Context{readFile: func(filename string) ([]byte, error) {
ctx := &Context{fs: &ffs.FileSystem{ReadFile: func(filename string) ([]byte, error) {
if filename != expectedFilename {
return nil, fmt.Errorf("unexpected filename: expected=%v, actual=%s", expectedFilename, filename)
}
return []byte(valuesYamlContent), nil
}}
}}}
buf, err := ctx.RenderTemplateToBuffer(valuesYamlContent, data)
if err != nil {
t.Errorf("unexpected error: %v", err)
@@ -93,12 +95,12 @@ func TestRenderTemplate_Defaulting(t *testing.T) {
`
expectedFilename := "values.yaml"
data := map[string]interface{}{}
ctx := &Context{readFile: func(filename string) ([]byte, error) {
ctx := &Context{fs: &ffs.FileSystem{ReadFile: func(filename string) ([]byte, error) {
if filename != expectedFilename {
return nil, fmt.Errorf("unexpected filename: expected=%v, actual=%s", expectedFilename, filename)
}
return []byte(valuesYamlContent), nil
}}
}}}
buf, err := ctx.RenderTemplateToBuffer(valuesYamlContent, data)
if err != nil {
t.Errorf("unexpected error: %v", err)
@@ -110,9 +112,9 @@ func TestRenderTemplate_Defaulting(t *testing.T) {
}
func renderTemplateToString(s string, data ...interface{}) (string, error) {
ctx := &Context{readFile: func(filename string) ([]byte, error) {
ctx := &Context{fs: &ffs.FileSystem{ReadFile: func(filename string) ([]byte, error) {
return nil, fmt.Errorf("unexpected call to readFile: filename=%s", filename)
}}
}}}
tplString, err := ctx.RenderTemplateToBuffer(s, data...)
if err != nil {
return "", err
+13 -13
View File
@@ -3,43 +3,43 @@ package tmpl
import (
"bytes"
"fmt"
"os"
"strings"
"github.com/helmfile/helmfile/pkg/filesystem"
)
type FileRenderer struct {
ReadFile func(string) ([]byte, error)
Context *Context
Data interface{}
fs *filesystem.FileSystem
Context *Context
Data interface{}
}
func NewFileRenderer(readFile func(filename string) ([]byte, error), basePath string, data interface{}) *FileRenderer {
func NewFileRenderer(fs *filesystem.FileSystem, basePath string, data interface{}) *FileRenderer {
return &FileRenderer{
ReadFile: readFile,
fs: fs,
Context: &Context{
basePath: basePath,
readFile: readFile,
readDir: os.ReadDir,
fs: fs,
},
Data: data,
}
}
func NewFirstPassRenderer(basePath string, data interface{}) *FileRenderer {
fs := filesystem.DefaultFileSystem()
return &FileRenderer{
ReadFile: os.ReadFile,
fs: fs,
Context: &Context{
preRender: true,
basePath: basePath,
readFile: os.ReadFile,
readDir: os.ReadDir,
fs: fs,
},
Data: data,
}
}
func (r *FileRenderer) RenderTemplateFileToBuffer(file string) (*bytes.Buffer, error) {
content, err := r.ReadFile(file)
content, err := r.fs.ReadFile(file)
if err != nil {
return nil, err
}
@@ -60,7 +60,7 @@ func (r *FileRenderer) RenderToBytes(path string) ([]byte, error) {
yamlBytes = yamlBuf.Bytes()
} else {
var err error
yamlBytes, err = r.ReadFile(path)
yamlBytes, err = r.fs.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to load [%s]: %v", path, err)
}
+5 -4
View File
@@ -6,6 +6,7 @@ import (
"testing"
"github.com/helmfile/helmfile/pkg/environment"
"github.com/helmfile/helmfile/pkg/filesystem"
)
var emptyEnvTmplData = map[string]interface{}{
@@ -23,7 +24,7 @@ func TestRenderToBytes_Gotmpl(t *testing.T) {
`
dataFile := "data.txt"
valuesTmplFile := "values.yaml.gotmpl"
r := NewFileRenderer(func(filename string) ([]byte, error) {
r := NewFileRenderer(&filesystem.FileSystem{ReadFile: func(filename string) ([]byte, error) {
switch filename {
case valuesTmplFile:
return []byte(valuesYamlTmplContent), nil
@@ -31,7 +32,7 @@ func TestRenderToBytes_Gotmpl(t *testing.T) {
return []byte(dataFileContent), nil
}
return nil, fmt.Errorf("unexpected filename: expected=%v or %v, actual=%s", dataFile, valuesTmplFile, filename)
}, "", emptyEnvTmplData)
}}, "", emptyEnvTmplData)
buf, err := r.RenderToBytes(valuesTmplFile)
if err != nil {
t.Errorf("unexpected error: %v", err)
@@ -50,12 +51,12 @@ func TestRenderToBytes_Yaml(t *testing.T) {
bar: '{{ readFile "data.txt" }}'
`
valuesFile := "values.yaml"
r := NewFileRenderer(func(filename string) ([]byte, error) {
r := NewFileRenderer(&filesystem.FileSystem{ReadFile: func(filename string) ([]byte, error) {
if filename == valuesFile {
return []byte(valuesYamlContent), nil
}
return nil, fmt.Errorf("unexpected filename: expected=%v, actual=%s", valuesFile, filename)
}, "", emptyEnvTmplData)
}}, "", emptyEnvTmplData)
buf, err := r.RenderToBytes(valuesFile)
if err != nil {
t.Errorf("unexpected error: %v", err)
+7 -3
View File
@@ -1,5 +1,9 @@
package tmpl
import (
"github.com/helmfile/helmfile/pkg/filesystem"
)
type templateTextRenderer struct {
ReadText func(string) ([]byte, error)
Context *Context
@@ -11,12 +15,12 @@ type TextRenderer interface {
}
// nolint: golint
func NewTextRenderer(readFile func(filename string) ([]byte, error), basePath string, data interface{}) *templateTextRenderer {
func NewTextRenderer(fs *filesystem.FileSystem, basePath string, data interface{}) *templateTextRenderer {
return &templateTextRenderer{
ReadText: readFile,
ReadText: fs.ReadFile,
Context: &Context{
basePath: basePath,
readFile: readFile,
fs: fs,
},
Data: data,
}
+4 -3
View File
@@ -1,10 +1,11 @@
package tmpl
import (
"os"
"testing"
"github.com/stretchr/testify/require"
"github.com/helmfile/helmfile/pkg/filesystem"
)
// TestTextRenderer tests the text renderer.
@@ -12,7 +13,7 @@ func TestNewTextRenderer(t *testing.T) {
tData := map[string]interface{}{
"foo": "bar",
}
tr := NewTextRenderer(os.ReadFile, ".", tData)
tr := NewTextRenderer(filesystem.DefaultFileSystem(), ".", tData)
require.Equal(t, tData, tr.Data)
require.Equal(t, ".", tr.Context.basePath)
}
@@ -22,7 +23,7 @@ func TestTextRender(t *testing.T) {
tData := map[string]interface{}{
"foot": "bart",
}
tr := NewTextRenderer(os.ReadFile, ".", tData)
tr := NewTextRenderer(filesystem.DefaultFileSystem(), ".", tData)
tests := []struct {
text string