422 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			422 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Go
		
	
	
	
| package tmpl
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"reflect"
 | |
| 	"testing"
 | |
| 
 | |
| 	ffs "github.com/helmfile/helmfile/pkg/filesystem"
 | |
| )
 | |
| 
 | |
| func TestRenderTemplate_Values(t *testing.T) {
 | |
| 	valuesYamlContent := `foo:
 | |
|   bar: BAR
 | |
| `
 | |
| 	expected := `foo:
 | |
|   bar: FOO_BAR
 | |
| `
 | |
| 	expectedFilename := "values.yaml"
 | |
| 	ctx := &Context{fs: &ffs.FileSystem{
 | |
| 		Glob: func(s string) ([]string, error) {
 | |
| 			return nil, nil
 | |
| 		},
 | |
| 		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)
 | |
| 	}
 | |
| 	actual := buf.String()
 | |
| 	if !reflect.DeepEqual(actual, expected) {
 | |
| 		t.Errorf("unexpected result: expected=%v, actual=%v", expected, actual)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestRenderTemplate_WithData(t *testing.T) {
 | |
| 	valuesYamlContent := `foo:
 | |
|   bar: {{ .foo.bar }}
 | |
| `
 | |
| 	expected := `foo:
 | |
|   bar: FOO_BAR
 | |
| `
 | |
| 	expectedFilename := "values.yaml"
 | |
| 	data := map[string]any{
 | |
| 		"foo": map[string]any{
 | |
| 			"bar": "FOO_BAR",
 | |
| 		},
 | |
| 	}
 | |
| 	ctx := &Context{fs: &ffs.FileSystem{
 | |
| 		Glob: func(s string) ([]string, error) {
 | |
| 			return nil, nil
 | |
| 		},
 | |
| 		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)
 | |
| 	}
 | |
| 	actual := buf.String()
 | |
| 	if !reflect.DeepEqual(actual, expected) {
 | |
| 		t.Errorf("unexpected result: expected=%v, actual=%v", expected, actual)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestRenderTemplate_AccessingMissingKeyWithGetOrNil(t *testing.T) {
 | |
| 	valuesYamlContent := `foo:
 | |
|   bar: {{ . | getOrNil "foo.bar" }}
 | |
| `
 | |
| 	expected := `foo:
 | |
|   bar: <no value>
 | |
| `
 | |
| 	expectedFilename := "values.yaml"
 | |
| 	data := map[string]any{}
 | |
| 	ctx := &Context{fs: &ffs.FileSystem{
 | |
| 		Glob: func(s string) ([]string, error) {
 | |
| 			return nil, nil
 | |
| 		},
 | |
| 		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)
 | |
| 	}
 | |
| 	actual := buf.String()
 | |
| 	if !reflect.DeepEqual(actual, expected) {
 | |
| 		t.Errorf("unexpected result: expected=%v, actual=%v", expected, actual)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestRenderTemplate_Defaulting(t *testing.T) {
 | |
| 	valuesYamlContent := `foo:
 | |
|   bar: {{ . | getOrNil "foo.bar" | default "DEFAULT" }}
 | |
| `
 | |
| 	expected := `foo:
 | |
|   bar: DEFAULT
 | |
| `
 | |
| 	expectedFilename := "values.yaml"
 | |
| 	data := map[string]any{}
 | |
| 	ctx := &Context{fs: &ffs.FileSystem{
 | |
| 		Glob: func(s string) ([]string, error) {
 | |
| 			return nil, nil
 | |
| 		},
 | |
| 		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)
 | |
| 	}
 | |
| 	actual := buf.String()
 | |
| 	if !reflect.DeepEqual(actual, expected) {
 | |
| 		t.Errorf("unexpected result: expected=%v, actual=%v", expected, actual)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func renderTemplateToString(s string, data ...any) (string, error) {
 | |
| 	ctx := &Context{fs: &ffs.FileSystem{
 | |
| 		Glob: func(s string) ([]string, error) {
 | |
| 			return nil, nil
 | |
| 		},
 | |
| 		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
 | |
| 	}
 | |
| 	return tplString.String(), nil
 | |
| }
 | |
| 
 | |
| func Test_renderTemplateToString(t *testing.T) {
 | |
| 	type args struct {
 | |
| 		s    string
 | |
| 		envs map[string]string
 | |
| 		data any
 | |
| 	}
 | |
| 	tests := []struct {
 | |
| 		name    string
 | |
| 		args    args
 | |
| 		want    string
 | |
| 		wantErr bool
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "simple replacement",
 | |
| 			args: args{
 | |
| 				s: "{{ env \"HF_TEST_VAR\" }}",
 | |
| 				envs: map[string]string{
 | |
| 					"HF_TEST_VAR": "content",
 | |
| 				},
 | |
| 			},
 | |
| 			want:    "content",
 | |
| 			wantErr: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "two replacements",
 | |
| 			args: args{
 | |
| 				s: "{{ env \"HF_TEST_ALPHA\" }}{{ env \"HF_TEST_BETA\" }}",
 | |
| 				envs: map[string]string{
 | |
| 					"HF_TEST_ALPHA": "first",
 | |
| 					"HF_TEST_BETA":  "second",
 | |
| 				},
 | |
| 			},
 | |
| 			want:    "firstsecond",
 | |
| 			wantErr: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "replacement and comment",
 | |
| 			args: args{
 | |
| 				s: "{{ env \"HF_TEST_ALPHA\" }}{{/* comment */}}",
 | |
| 				envs: map[string]string{
 | |
| 					"HF_TEST_ALPHA": "first",
 | |
| 				},
 | |
| 			},
 | |
| 			want:    "first",
 | |
| 			wantErr: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "global template function",
 | |
| 			args: args{
 | |
| 				s: "{{ env \"HF_TEST_ALPHA\" | len }}",
 | |
| 				envs: map[string]string{
 | |
| 					"HF_TEST_ALPHA": "abcdefg",
 | |
| 				},
 | |
| 			},
 | |
| 			want:    "7",
 | |
| 			wantErr: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "get",
 | |
| 			args: args{
 | |
| 				s:    `{{ . | get "Foo" }}, {{ . | get "Bar" "2" }}`,
 | |
| 				envs: map[string]string{},
 | |
| 				data: map[string]any{
 | |
| 					"Foo": "1",
 | |
| 				},
 | |
| 			},
 | |
| 			want:    "1, 2",
 | |
| 			wantErr: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "env var not set",
 | |
| 			args: args{
 | |
| 				s: "{{ env \"HF_TEST_NONE\" }}",
 | |
| 				envs: map[string]string{
 | |
| 					"HF_TEST_THIS": "first",
 | |
| 				},
 | |
| 			},
 | |
| 			want: "",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "undefined function",
 | |
| 			args: args{
 | |
| 				s: "{{ env foo }}",
 | |
| 				envs: map[string]string{
 | |
| 					"foo": "bar",
 | |
| 				},
 | |
| 			},
 | |
| 			wantErr: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "required env var",
 | |
| 			args: args{
 | |
| 				s: "{{ requiredEnv \"HF_TEST\" }}",
 | |
| 				envs: map[string]string{
 | |
| 					"HF_TEST": "value",
 | |
| 				},
 | |
| 			},
 | |
| 			want:    "value",
 | |
| 			wantErr: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name: "required env var not set",
 | |
| 			args: args{
 | |
| 				s:    "{{ requiredEnv \"HF_TEST_NONE\" }}",
 | |
| 				envs: map[string]string{},
 | |
| 			},
 | |
| 			wantErr: true,
 | |
| 		},
 | |
| 	}
 | |
| 	for i := range tests {
 | |
| 		tt := tests[i]
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			for k, v := range tt.args.envs {
 | |
| 				t.Setenv(k, v)
 | |
| 			}
 | |
| 			got, err := renderTemplateToString(tt.args.s, tt.args.data)
 | |
| 			if (err != nil) != tt.wantErr {
 | |
| 				t.Errorf("renderTemplateToString() for %s error = %v, wantErr %v", tt.name, err, tt.wantErr)
 | |
| 				return
 | |
| 			}
 | |
| 			if got != tt.want {
 | |
| 				t.Errorf("renderTemplateToString() for %s = %v, want %v", tt.name, got, tt.want)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestRenderTemplate_Required(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name    string
 | |
| 		s       string
 | |
| 		data    map[string]any
 | |
| 		want    string
 | |
| 		wantErr bool
 | |
| 	}{
 | |
| 		{
 | |
| 			name: ".foo is existed",
 | |
| 			s:    `{{ required ".foo.bar is required" .foo }}`,
 | |
| 			data: map[string]any{
 | |
| 				"foo": "bar",
 | |
| 			},
 | |
| 			want:    "bar",
 | |
| 			wantErr: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name: ".foo.bar is existed",
 | |
| 			s:    `{{ required "foo.bar is required" .foo.bar }}`,
 | |
| 			data: map[string]any{
 | |
| 				"foo": map[string]any{
 | |
| 					"bar": "FOO_BAR",
 | |
| 				},
 | |
| 			},
 | |
| 			want:    "FOO_BAR",
 | |
| 			wantErr: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name: ".foo.bar is existed but value is nil",
 | |
| 			s:    `{{ required "foo.bar is required" .foo.bar }}`,
 | |
| 			data: map[string]any{
 | |
| 				"foo": map[string]any{
 | |
| 					"bar": nil,
 | |
| 				},
 | |
| 			},
 | |
| 			wantErr: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name: ".foo.bar is existed but value is empty string",
 | |
| 			s:    `{{ required "foo.bar is required" .foo.bar }}`,
 | |
| 			data: map[string]any{
 | |
| 				"foo": map[string]any{
 | |
| 					"bar": "",
 | |
| 				},
 | |
| 			},
 | |
| 			wantErr: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name: ".foo is nil",
 | |
| 			s:    `{{ required "foo is required" .foo }}`,
 | |
| 			data: map[string]any{
 | |
| 				"foo": nil,
 | |
| 			},
 | |
| 			wantErr: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name: ".foo is a empty string",
 | |
| 			s:    `{{ required "foo is required" .foo }}`,
 | |
| 			data: map[string]any{
 | |
| 				"foo": "",
 | |
| 			},
 | |
| 			wantErr: true,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		got, err := renderTemplateToString(tt.s, tt.data)
 | |
| 		if (err != nil) != tt.wantErr {
 | |
| 			t.Errorf("renderTemplateToString() for %s error = %v, wantErr %v", tt.name, err, tt.wantErr)
 | |
| 			return
 | |
| 		}
 | |
| 		if got != tt.want {
 | |
| 			t.Errorf("renderTemplateToString() for %s = %v, want %v", tt.name, got, tt.want)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| func TestContext_helperTPLs(t *testing.T) {
 | |
| 	c := &Context{
 | |
| 		fs: &ffs.FileSystem{
 | |
| 			Glob: func(s string) ([]string, error) {
 | |
| 				return []string{
 | |
| 					"/helmfiletmpl/_template1.tpl",
 | |
| 					"/helmfiletmpl/_template2.tpl",
 | |
| 				}, nil
 | |
| 			},
 | |
| 			ReadFile: func(filename string) ([]byte, error) {
 | |
| 				switch filename {
 | |
| 				case "/helmfiletmpl/_template1.tpl":
 | |
| 					return []byte("Template 1 content"), nil
 | |
| 				case "/helmfiletmpl/_template2.tpl":
 | |
| 					return []byte("Template 2 content"), nil
 | |
| 				default:
 | |
| 					return nil, fmt.Errorf("unexpected filename: %s", filename)
 | |
| 				}
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	want := []tplInfo{
 | |
| 		{
 | |
| 			name:    "/helmfiletmpl/_template1.tpl",
 | |
| 			content: "Template 1 content",
 | |
| 		},
 | |
| 		{
 | |
| 			name:    "/helmfiletmpl/_template2.tpl",
 | |
| 			content: "Template 2 content",
 | |
| 		},
 | |
| 	}
 | |
| 	got, err := c.helperTPLs()
 | |
| 	if err != nil {
 | |
| 		t.Errorf("unexpected error: %v", err)
 | |
| 	}
 | |
| 	if !reflect.DeepEqual(got, want) {
 | |
| 		t.Errorf("unexpected result: got=%v, want=%v", got, want)
 | |
| 	}
 | |
| }
 | |
| func TestContext_RenderTemplateToBuffer(t *testing.T) {
 | |
| 	c := &Context{
 | |
| 		basePath: "/helmfile",
 | |
| 		fs: &ffs.FileSystem{
 | |
| 			Glob: func(s string) ([]string, error) {
 | |
| 				return []string{
 | |
| 					"/helmfile/_template1.tpl",
 | |
| 				}, nil
 | |
| 			},
 | |
| 			ReadFile: func(filename string) ([]byte, error) {
 | |
| 				if filename == "/helmfile/_template1.tpl" {
 | |
| 					return []byte("{{- define \"name\" -}}\n{{ .Name }}\n{{- end }}"), nil
 | |
| 				}
 | |
| 				return nil, fmt.Errorf("unexpected filename: %s", filename)
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 	s := "Hello, {{ include \"name\" . }}!"
 | |
| 	data := map[string]interface{}{
 | |
| 		"Name": "Alice",
 | |
| 	}
 | |
| 	expected := "Hello, Alice!"
 | |
| 
 | |
| 	buf, err := c.RenderTemplateToBuffer(s, data)
 | |
| 	if err != nil {
 | |
| 		t.Errorf("unexpected error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	actual := buf.String()
 | |
| 	if actual != expected {
 | |
| 		t.Errorf("unexpected result: expected=%s, actual=%s", expected, actual)
 | |
| 	}
 | |
| }
 |