github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/pkg/engine/engine_test.go (about)

     1  /*
     2  Copyright The Helm Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package engine
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  	"sync"
    23  	"testing"
    24  
    25  	"github.com/stefanmcshane/helm/pkg/chart"
    26  	"github.com/stefanmcshane/helm/pkg/chartutil"
    27  )
    28  
    29  func TestSortTemplates(t *testing.T) {
    30  	tpls := map[string]renderable{
    31  		"/mychart/templates/foo.tpl":                                 {},
    32  		"/mychart/templates/charts/foo/charts/bar/templates/foo.tpl": {},
    33  		"/mychart/templates/bar.tpl":                                 {},
    34  		"/mychart/templates/charts/foo/templates/bar.tpl":            {},
    35  		"/mychart/templates/_foo.tpl":                                {},
    36  		"/mychart/templates/charts/foo/templates/foo.tpl":            {},
    37  		"/mychart/templates/charts/bar/templates/foo.tpl":            {},
    38  	}
    39  	got := sortTemplates(tpls)
    40  	if len(got) != len(tpls) {
    41  		t.Fatal("Sorted results are missing templates")
    42  	}
    43  
    44  	expect := []string{
    45  		"/mychart/templates/charts/foo/charts/bar/templates/foo.tpl",
    46  		"/mychart/templates/charts/foo/templates/foo.tpl",
    47  		"/mychart/templates/charts/foo/templates/bar.tpl",
    48  		"/mychart/templates/charts/bar/templates/foo.tpl",
    49  		"/mychart/templates/foo.tpl",
    50  		"/mychart/templates/bar.tpl",
    51  		"/mychart/templates/_foo.tpl",
    52  	}
    53  	for i, e := range expect {
    54  		if got[i] != e {
    55  			t.Fatalf("\n\tExp:\n%s\n\tGot:\n%s",
    56  				strings.Join(expect, "\n"),
    57  				strings.Join(got, "\n"),
    58  			)
    59  		}
    60  	}
    61  }
    62  
    63  func TestFuncMap(t *testing.T) {
    64  	fns := funcMap()
    65  	forbidden := []string{"env", "expandenv"}
    66  	for _, f := range forbidden {
    67  		if _, ok := fns[f]; ok {
    68  			t.Errorf("Forbidden function %s exists in FuncMap.", f)
    69  		}
    70  	}
    71  
    72  	// Test for Engine-specific template functions.
    73  	expect := []string{"include", "required", "tpl", "toYaml", "fromYaml", "toToml", "toJson", "fromJson", "lookup"}
    74  	for _, f := range expect {
    75  		if _, ok := fns[f]; !ok {
    76  			t.Errorf("Expected add-on function %q", f)
    77  		}
    78  	}
    79  }
    80  
    81  func TestRender(t *testing.T) {
    82  	c := &chart.Chart{
    83  		Metadata: &chart.Metadata{
    84  			Name:    "moby",
    85  			Version: "1.2.3",
    86  		},
    87  		Templates: []*chart.File{
    88  			{Name: "templates/test1", Data: []byte("{{.Values.outer | title }} {{.Values.inner | title}}")},
    89  			{Name: "templates/test2", Data: []byte("{{.Values.global.callme | lower }}")},
    90  			{Name: "templates/test3", Data: []byte("{{.noValue}}")},
    91  			{Name: "templates/test4", Data: []byte("{{toJson .Values}}")},
    92  		},
    93  		Values: map[string]interface{}{"outer": "DEFAULT", "inner": "DEFAULT"},
    94  	}
    95  
    96  	vals := map[string]interface{}{
    97  		"Values": map[string]interface{}{
    98  			"outer": "spouter",
    99  			"inner": "inn",
   100  			"global": map[string]interface{}{
   101  				"callme": "Ishmael",
   102  			},
   103  		},
   104  	}
   105  
   106  	v, err := chartutil.CoalesceValues(c, vals)
   107  	if err != nil {
   108  		t.Fatalf("Failed to coalesce values: %s", err)
   109  	}
   110  	out, err := Render(c, v)
   111  	if err != nil {
   112  		t.Errorf("Failed to render templates: %s", err)
   113  	}
   114  
   115  	expect := map[string]string{
   116  		"moby/templates/test1": "Spouter Inn",
   117  		"moby/templates/test2": "ishmael",
   118  		"moby/templates/test3": "",
   119  		"moby/templates/test4": `{"global":{"callme":"Ishmael"},"inner":"inn","outer":"spouter"}`,
   120  	}
   121  
   122  	for name, data := range expect {
   123  		if out[name] != data {
   124  			t.Errorf("Expected %q, got %q", data, out[name])
   125  		}
   126  	}
   127  }
   128  
   129  func TestRenderRefsOrdering(t *testing.T) {
   130  	parentChart := &chart.Chart{
   131  		Metadata: &chart.Metadata{
   132  			Name:    "parent",
   133  			Version: "1.2.3",
   134  		},
   135  		Templates: []*chart.File{
   136  			{Name: "templates/_helpers.tpl", Data: []byte(`{{- define "test" -}}parent value{{- end -}}`)},
   137  			{Name: "templates/test.yaml", Data: []byte(`{{ tpl "{{ include \"test\" . }}" . }}`)},
   138  		},
   139  	}
   140  	childChart := &chart.Chart{
   141  		Metadata: &chart.Metadata{
   142  			Name:    "child",
   143  			Version: "1.2.3",
   144  		},
   145  		Templates: []*chart.File{
   146  			{Name: "templates/_helpers.tpl", Data: []byte(`{{- define "test" -}}child value{{- end -}}`)},
   147  		},
   148  	}
   149  	parentChart.AddDependency(childChart)
   150  
   151  	expect := map[string]string{
   152  		"parent/templates/test.yaml": "parent value",
   153  	}
   154  
   155  	for i := 0; i < 100; i++ {
   156  		out, err := Render(parentChart, chartutil.Values{})
   157  		if err != nil {
   158  			t.Fatalf("Failed to render templates: %s", err)
   159  		}
   160  
   161  		for name, data := range expect {
   162  			if out[name] != data {
   163  				t.Fatalf("Expected %q, got %q (iteration %d)", data, out[name], i+1)
   164  			}
   165  		}
   166  	}
   167  }
   168  
   169  func TestRenderInternals(t *testing.T) {
   170  	// Test the internals of the rendering tool.
   171  
   172  	vals := chartutil.Values{"Name": "one", "Value": "two"}
   173  	tpls := map[string]renderable{
   174  		"one": {tpl: `Hello {{title .Name}}`, vals: vals},
   175  		"two": {tpl: `Goodbye {{upper .Value}}`, vals: vals},
   176  		// Test whether a template can reliably reference another template
   177  		// without regard for ordering.
   178  		"three": {tpl: `{{template "two" dict "Value" "three"}}`, vals: vals},
   179  	}
   180  
   181  	out, err := new(Engine).render(tpls)
   182  	if err != nil {
   183  		t.Fatalf("Failed template rendering: %s", err)
   184  	}
   185  
   186  	if len(out) != 3 {
   187  		t.Fatalf("Expected 3 templates, got %d", len(out))
   188  	}
   189  
   190  	if out["one"] != "Hello One" {
   191  		t.Errorf("Expected 'Hello One', got %q", out["one"])
   192  	}
   193  
   194  	if out["two"] != "Goodbye TWO" {
   195  		t.Errorf("Expected 'Goodbye TWO'. got %q", out["two"])
   196  	}
   197  
   198  	if out["three"] != "Goodbye THREE" {
   199  		t.Errorf("Expected 'Goodbye THREE'. got %q", out["two"])
   200  	}
   201  }
   202  
   203  func TestParallelRenderInternals(t *testing.T) {
   204  	// Make sure that we can use one Engine to run parallel template renders.
   205  	e := new(Engine)
   206  	var wg sync.WaitGroup
   207  	for i := 0; i < 20; i++ {
   208  		wg.Add(1)
   209  		go func(i int) {
   210  			tt := fmt.Sprintf("expect-%d", i)
   211  			tpls := map[string]renderable{
   212  				"t": {
   213  					tpl:  `{{.val}}`,
   214  					vals: map[string]interface{}{"val": tt},
   215  				},
   216  			}
   217  			out, err := e.render(tpls)
   218  			if err != nil {
   219  				t.Errorf("Failed to render %s: %s", tt, err)
   220  			}
   221  			if out["t"] != tt {
   222  				t.Errorf("Expected %q, got %q", tt, out["t"])
   223  			}
   224  			wg.Done()
   225  		}(i)
   226  	}
   227  	wg.Wait()
   228  }
   229  
   230  func TestParseErrors(t *testing.T) {
   231  	vals := chartutil.Values{"Values": map[string]interface{}{}}
   232  
   233  	tplsUndefinedFunction := map[string]renderable{
   234  		"undefined_function": {tpl: `{{foo}}`, vals: vals},
   235  	}
   236  	_, err := new(Engine).render(tplsUndefinedFunction)
   237  	if err == nil {
   238  		t.Fatalf("Expected failures while rendering: %s", err)
   239  	}
   240  	expected := `parse error at (undefined_function:1): function "foo" not defined`
   241  	if err.Error() != expected {
   242  		t.Errorf("Expected '%s', got %q", expected, err.Error())
   243  	}
   244  }
   245  
   246  func TestExecErrors(t *testing.T) {
   247  	vals := chartutil.Values{"Values": map[string]interface{}{}}
   248  	cases := []struct {
   249  		name     string
   250  		tpls     map[string]renderable
   251  		expected string
   252  	}{
   253  		{
   254  			name: "MissingRequired",
   255  			tpls: map[string]renderable{
   256  				"missing_required": {tpl: `{{required "foo is required" .Values.foo}}`, vals: vals},
   257  			},
   258  			expected: `execution error at (missing_required:1:2): foo is required`,
   259  		},
   260  		{
   261  			name: "MissingRequiredWithColons",
   262  			tpls: map[string]renderable{
   263  				"missing_required_with_colons": {tpl: `{{required ":this: message: has many: colons:" .Values.foo}}`, vals: vals},
   264  			},
   265  			expected: `execution error at (missing_required_with_colons:1:2): :this: message: has many: colons:`,
   266  		},
   267  		{
   268  			name: "Issue6044",
   269  			tpls: map[string]renderable{
   270  				"issue6044": {
   271  					vals: vals,
   272  					tpl: `{{ $someEmptyValue := "" }}
   273  {{ $myvar := "abc" }}
   274  {{- required (printf "%s: something is missing" $myvar) $someEmptyValue | repeat 0 }}`,
   275  				},
   276  			},
   277  			expected: `execution error at (issue6044:3:4): abc: something is missing`,
   278  		},
   279  		{
   280  			name: "MissingRequiredWithNewlines",
   281  			tpls: map[string]renderable{
   282  				"issue9981": {tpl: `{{required "foo is required\nmore info after the break" .Values.foo}}`, vals: vals},
   283  			},
   284  			expected: `execution error at (issue9981:1:2): foo is required
   285  more info after the break`,
   286  		},
   287  		{
   288  			name: "FailWithNewlines",
   289  			tpls: map[string]renderable{
   290  				"issue9981": {tpl: `{{fail "something is wrong\nlinebreak"}}`, vals: vals},
   291  			},
   292  			expected: `execution error at (issue9981:1:2): something is wrong
   293  linebreak`,
   294  		},
   295  	}
   296  
   297  	for _, tt := range cases {
   298  		t.Run(tt.name, func(t *testing.T) {
   299  			_, err := new(Engine).render(tt.tpls)
   300  			if err == nil {
   301  				t.Fatalf("Expected failures while rendering: %s", err)
   302  			}
   303  			if err.Error() != tt.expected {
   304  				t.Errorf("Expected %q, got %q", tt.expected, err.Error())
   305  			}
   306  		})
   307  	}
   308  }
   309  
   310  func TestFailErrors(t *testing.T) {
   311  	vals := chartutil.Values{"Values": map[string]interface{}{}}
   312  
   313  	failtpl := `All your base are belong to us{{ fail "This is an error" }}`
   314  	tplsFailed := map[string]renderable{
   315  		"failtpl": {tpl: failtpl, vals: vals},
   316  	}
   317  	_, err := new(Engine).render(tplsFailed)
   318  	if err == nil {
   319  		t.Fatalf("Expected failures while rendering: %s", err)
   320  	}
   321  	expected := `execution error at (failtpl:1:33): This is an error`
   322  	if err.Error() != expected {
   323  		t.Errorf("Expected '%s', got %q", expected, err.Error())
   324  	}
   325  
   326  	var e Engine
   327  	e.LintMode = true
   328  	out, err := e.render(tplsFailed)
   329  	if err != nil {
   330  		t.Fatal(err)
   331  	}
   332  
   333  	expectStr := "All your base are belong to us"
   334  	if gotStr := out["failtpl"]; gotStr != expectStr {
   335  		t.Errorf("Expected %q, got %q (%v)", expectStr, gotStr, out)
   336  	}
   337  }
   338  
   339  func TestAllTemplates(t *testing.T) {
   340  	ch1 := &chart.Chart{
   341  		Metadata: &chart.Metadata{Name: "ch1"},
   342  		Templates: []*chart.File{
   343  			{Name: "templates/foo", Data: []byte("foo")},
   344  			{Name: "templates/bar", Data: []byte("bar")},
   345  		},
   346  	}
   347  	dep1 := &chart.Chart{
   348  		Metadata: &chart.Metadata{Name: "laboratory mice"},
   349  		Templates: []*chart.File{
   350  			{Name: "templates/pinky", Data: []byte("pinky")},
   351  			{Name: "templates/brain", Data: []byte("brain")},
   352  		},
   353  	}
   354  	ch1.AddDependency(dep1)
   355  
   356  	dep2 := &chart.Chart{
   357  		Metadata: &chart.Metadata{Name: "same thing we do every night"},
   358  		Templates: []*chart.File{
   359  			{Name: "templates/innermost", Data: []byte("innermost")},
   360  		},
   361  	}
   362  	dep1.AddDependency(dep2)
   363  
   364  	tpls := allTemplates(ch1, chartutil.Values{})
   365  	if len(tpls) != 5 {
   366  		t.Errorf("Expected 5 charts, got %d", len(tpls))
   367  	}
   368  }
   369  
   370  func TestChartValuesContainsIsRoot(t *testing.T) {
   371  	ch1 := &chart.Chart{
   372  		Metadata: &chart.Metadata{Name: "parent"},
   373  		Templates: []*chart.File{
   374  			{Name: "templates/isroot", Data: []byte("{{.Chart.IsRoot}}")},
   375  		},
   376  	}
   377  	dep1 := &chart.Chart{
   378  		Metadata: &chart.Metadata{Name: "child"},
   379  		Templates: []*chart.File{
   380  			{Name: "templates/isroot", Data: []byte("{{.Chart.IsRoot}}")},
   381  		},
   382  	}
   383  	ch1.AddDependency(dep1)
   384  
   385  	out, err := Render(ch1, chartutil.Values{})
   386  	if err != nil {
   387  		t.Fatalf("failed to render templates: %s", err)
   388  	}
   389  	expects := map[string]string{
   390  		"parent/charts/child/templates/isroot": "false",
   391  		"parent/templates/isroot":              "true",
   392  	}
   393  	for file, expect := range expects {
   394  		if out[file] != expect {
   395  			t.Errorf("Expected %q, got %q", expect, out[file])
   396  		}
   397  	}
   398  }
   399  
   400  func TestRenderDependency(t *testing.T) {
   401  	deptpl := `{{define "myblock"}}World{{end}}`
   402  	toptpl := `Hello {{template "myblock"}}`
   403  	ch := &chart.Chart{
   404  		Metadata: &chart.Metadata{Name: "outerchart"},
   405  		Templates: []*chart.File{
   406  			{Name: "templates/outer", Data: []byte(toptpl)},
   407  		},
   408  	}
   409  	ch.AddDependency(&chart.Chart{
   410  		Metadata: &chart.Metadata{Name: "innerchart"},
   411  		Templates: []*chart.File{
   412  			{Name: "templates/inner", Data: []byte(deptpl)},
   413  		},
   414  	})
   415  
   416  	out, err := Render(ch, map[string]interface{}{})
   417  	if err != nil {
   418  		t.Fatalf("failed to render chart: %s", err)
   419  	}
   420  
   421  	if len(out) != 2 {
   422  		t.Errorf("Expected 2, got %d", len(out))
   423  	}
   424  
   425  	expect := "Hello World"
   426  	if out["outerchart/templates/outer"] != expect {
   427  		t.Errorf("Expected %q, got %q", expect, out["outer"])
   428  	}
   429  
   430  }
   431  
   432  func TestRenderNestedValues(t *testing.T) {
   433  	innerpath := "templates/inner.tpl"
   434  	outerpath := "templates/outer.tpl"
   435  	// Ensure namespacing rules are working.
   436  	deepestpath := "templates/inner.tpl"
   437  	checkrelease := "templates/release.tpl"
   438  	// Ensure subcharts scopes are working.
   439  	subchartspath := "templates/subcharts.tpl"
   440  
   441  	deepest := &chart.Chart{
   442  		Metadata: &chart.Metadata{Name: "deepest"},
   443  		Templates: []*chart.File{
   444  			{Name: deepestpath, Data: []byte(`And this same {{.Values.what}} that smiles {{.Values.global.when}}`)},
   445  			{Name: checkrelease, Data: []byte(`Tomorrow will be {{default "happy" .Release.Name }}`)},
   446  		},
   447  		Values: map[string]interface{}{"what": "milkshake", "where": "here"},
   448  	}
   449  
   450  	inner := &chart.Chart{
   451  		Metadata: &chart.Metadata{Name: "herrick"},
   452  		Templates: []*chart.File{
   453  			{Name: innerpath, Data: []byte(`Old {{.Values.who}} is still a-flyin'`)},
   454  		},
   455  		Values: map[string]interface{}{"who": "Robert", "what": "glasses"},
   456  	}
   457  	inner.AddDependency(deepest)
   458  
   459  	outer := &chart.Chart{
   460  		Metadata: &chart.Metadata{Name: "top"},
   461  		Templates: []*chart.File{
   462  			{Name: outerpath, Data: []byte(`Gather ye {{.Values.what}} while ye may`)},
   463  			{Name: subchartspath, Data: []byte(`The glorious Lamp of {{.Subcharts.herrick.Subcharts.deepest.Values.where}}, the {{.Subcharts.herrick.Values.what}}`)},
   464  		},
   465  		Values: map[string]interface{}{
   466  			"what": "stinkweed",
   467  			"who":  "me",
   468  			"herrick": map[string]interface{}{
   469  				"who":  "time",
   470  				"what": "Sun",
   471  			},
   472  		},
   473  	}
   474  	outer.AddDependency(inner)
   475  
   476  	injValues := map[string]interface{}{
   477  		"what": "rosebuds",
   478  		"herrick": map[string]interface{}{
   479  			"deepest": map[string]interface{}{
   480  				"what":  "flower",
   481  				"where": "Heaven",
   482  			},
   483  		},
   484  		"global": map[string]interface{}{
   485  			"when": "to-day",
   486  		},
   487  	}
   488  
   489  	tmp, err := chartutil.CoalesceValues(outer, injValues)
   490  	if err != nil {
   491  		t.Fatalf("Failed to coalesce values: %s", err)
   492  	}
   493  
   494  	inject := chartutil.Values{
   495  		"Values": tmp,
   496  		"Chart":  outer.Metadata,
   497  		"Release": chartutil.Values{
   498  			"Name": "dyin",
   499  		},
   500  	}
   501  
   502  	t.Logf("Calculated values: %v", inject)
   503  
   504  	out, err := Render(outer, inject)
   505  	if err != nil {
   506  		t.Fatalf("failed to render templates: %s", err)
   507  	}
   508  
   509  	fullouterpath := "top/" + outerpath
   510  	if out[fullouterpath] != "Gather ye rosebuds while ye may" {
   511  		t.Errorf("Unexpected outer: %q", out[fullouterpath])
   512  	}
   513  
   514  	fullinnerpath := "top/charts/herrick/" + innerpath
   515  	if out[fullinnerpath] != "Old time is still a-flyin'" {
   516  		t.Errorf("Unexpected inner: %q", out[fullinnerpath])
   517  	}
   518  
   519  	fulldeepestpath := "top/charts/herrick/charts/deepest/" + deepestpath
   520  	if out[fulldeepestpath] != "And this same flower that smiles to-day" {
   521  		t.Errorf("Unexpected deepest: %q", out[fulldeepestpath])
   522  	}
   523  
   524  	fullcheckrelease := "top/charts/herrick/charts/deepest/" + checkrelease
   525  	if out[fullcheckrelease] != "Tomorrow will be dyin" {
   526  		t.Errorf("Unexpected release: %q", out[fullcheckrelease])
   527  	}
   528  
   529  	fullchecksubcharts := "top/" + subchartspath
   530  	if out[fullchecksubcharts] != "The glorious Lamp of Heaven, the Sun" {
   531  		t.Errorf("Unexpected subcharts: %q", out[fullchecksubcharts])
   532  	}
   533  }
   534  
   535  func TestRenderBuiltinValues(t *testing.T) {
   536  	inner := &chart.Chart{
   537  		Metadata: &chart.Metadata{Name: "Latium"},
   538  		Templates: []*chart.File{
   539  			{Name: "templates/Lavinia", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)},
   540  			{Name: "templates/From", Data: []byte(`{{.Files.author | printf "%s"}} {{.Files.Get "book/title.txt"}}`)},
   541  		},
   542  		Files: []*chart.File{
   543  			{Name: "author", Data: []byte("Virgil")},
   544  			{Name: "book/title.txt", Data: []byte("Aeneid")},
   545  		},
   546  	}
   547  
   548  	outer := &chart.Chart{
   549  		Metadata: &chart.Metadata{Name: "Troy"},
   550  		Templates: []*chart.File{
   551  			{Name: "templates/Aeneas", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)},
   552  			{Name: "templates/Amata", Data: []byte(`{{.Subcharts.Latium.Chart.Name}} {{.Subcharts.Latium.Files.author | printf "%s"}}`)},
   553  		},
   554  	}
   555  	outer.AddDependency(inner)
   556  
   557  	inject := chartutil.Values{
   558  		"Values": "",
   559  		"Chart":  outer.Metadata,
   560  		"Release": chartutil.Values{
   561  			"Name": "Aeneid",
   562  		},
   563  	}
   564  
   565  	t.Logf("Calculated values: %v", outer)
   566  
   567  	out, err := Render(outer, inject)
   568  	if err != nil {
   569  		t.Fatalf("failed to render templates: %s", err)
   570  	}
   571  
   572  	expects := map[string]string{
   573  		"Troy/charts/Latium/templates/Lavinia": "Troy/charts/Latium/templates/LaviniaLatiumAeneid",
   574  		"Troy/templates/Aeneas":                "Troy/templates/AeneasTroyAeneid",
   575  		"Troy/templates/Amata":                 "Latium Virgil",
   576  		"Troy/charts/Latium/templates/From":    "Virgil Aeneid",
   577  	}
   578  	for file, expect := range expects {
   579  		if out[file] != expect {
   580  			t.Errorf("Expected %q, got %q", expect, out[file])
   581  		}
   582  	}
   583  
   584  }
   585  
   586  func TestAlterFuncMap_include(t *testing.T) {
   587  	c := &chart.Chart{
   588  		Metadata: &chart.Metadata{Name: "conrad"},
   589  		Templates: []*chart.File{
   590  			{Name: "templates/quote", Data: []byte(`{{include "conrad/templates/_partial" . | indent 2}} dead.`)},
   591  			{Name: "templates/_partial", Data: []byte(`{{.Release.Name}} - he`)},
   592  		},
   593  	}
   594  
   595  	// Check nested reference in include FuncMap
   596  	d := &chart.Chart{
   597  		Metadata: &chart.Metadata{Name: "nested"},
   598  		Templates: []*chart.File{
   599  			{Name: "templates/quote", Data: []byte(`{{include "nested/templates/quote" . | indent 2}} dead.`)},
   600  			{Name: "templates/_partial", Data: []byte(`{{.Release.Name}} - he`)},
   601  		},
   602  	}
   603  
   604  	v := chartutil.Values{
   605  		"Values": "",
   606  		"Chart":  c.Metadata,
   607  		"Release": chartutil.Values{
   608  			"Name": "Mistah Kurtz",
   609  		},
   610  	}
   611  
   612  	out, err := Render(c, v)
   613  	if err != nil {
   614  		t.Fatal(err)
   615  	}
   616  
   617  	expect := "  Mistah Kurtz - he dead."
   618  	if got := out["conrad/templates/quote"]; got != expect {
   619  		t.Errorf("Expected %q, got %q (%v)", expect, got, out)
   620  	}
   621  
   622  	_, err = Render(d, v)
   623  	expectErrName := "nested/templates/quote"
   624  	if err == nil {
   625  		t.Errorf("Expected err of nested reference name: %v", expectErrName)
   626  	}
   627  }
   628  
   629  func TestAlterFuncMap_require(t *testing.T) {
   630  	c := &chart.Chart{
   631  		Metadata: &chart.Metadata{Name: "conan"},
   632  		Templates: []*chart.File{
   633  			{Name: "templates/quote", Data: []byte(`All your base are belong to {{ required "A valid 'who' is required" .Values.who }}`)},
   634  			{Name: "templates/bases", Data: []byte(`All {{ required "A valid 'bases' is required" .Values.bases }} of them!`)},
   635  		},
   636  	}
   637  
   638  	v := chartutil.Values{
   639  		"Values": chartutil.Values{
   640  			"who":   "us",
   641  			"bases": 2,
   642  		},
   643  		"Chart": c.Metadata,
   644  		"Release": chartutil.Values{
   645  			"Name": "That 90s meme",
   646  		},
   647  	}
   648  
   649  	out, err := Render(c, v)
   650  	if err != nil {
   651  		t.Fatal(err)
   652  	}
   653  
   654  	expectStr := "All your base are belong to us"
   655  	if gotStr := out["conan/templates/quote"]; gotStr != expectStr {
   656  		t.Errorf("Expected %q, got %q (%v)", expectStr, gotStr, out)
   657  	}
   658  	expectNum := "All 2 of them!"
   659  	if gotNum := out["conan/templates/bases"]; gotNum != expectNum {
   660  		t.Errorf("Expected %q, got %q (%v)", expectNum, gotNum, out)
   661  	}
   662  
   663  	// test required without passing in needed values with lint mode on
   664  	// verifies lint replaces required with an empty string (should not fail)
   665  	lintValues := chartutil.Values{
   666  		"Values": chartutil.Values{
   667  			"who": "us",
   668  		},
   669  		"Chart": c.Metadata,
   670  		"Release": chartutil.Values{
   671  			"Name": "That 90s meme",
   672  		},
   673  	}
   674  	var e Engine
   675  	e.LintMode = true
   676  	out, err = e.Render(c, lintValues)
   677  	if err != nil {
   678  		t.Fatal(err)
   679  	}
   680  
   681  	expectStr = "All your base are belong to us"
   682  	if gotStr := out["conan/templates/quote"]; gotStr != expectStr {
   683  		t.Errorf("Expected %q, got %q (%v)", expectStr, gotStr, out)
   684  	}
   685  	expectNum = "All  of them!"
   686  	if gotNum := out["conan/templates/bases"]; gotNum != expectNum {
   687  		t.Errorf("Expected %q, got %q (%v)", expectNum, gotNum, out)
   688  	}
   689  }
   690  
   691  func TestAlterFuncMap_tpl(t *testing.T) {
   692  	c := &chart.Chart{
   693  		Metadata: &chart.Metadata{Name: "TplFunction"},
   694  		Templates: []*chart.File{
   695  			{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value}}" .}}`)},
   696  		},
   697  	}
   698  
   699  	v := chartutil.Values{
   700  		"Values": chartutil.Values{
   701  			"value": "myvalue",
   702  		},
   703  		"Chart": c.Metadata,
   704  		"Release": chartutil.Values{
   705  			"Name": "TestRelease",
   706  		},
   707  	}
   708  
   709  	out, err := Render(c, v)
   710  	if err != nil {
   711  		t.Fatal(err)
   712  	}
   713  
   714  	expect := "Evaluate tpl Value: myvalue"
   715  	if got := out["TplFunction/templates/base"]; got != expect {
   716  		t.Errorf("Expected %q, got %q (%v)", expect, got, out)
   717  	}
   718  }
   719  
   720  func TestAlterFuncMap_tplfunc(t *testing.T) {
   721  	c := &chart.Chart{
   722  		Metadata: &chart.Metadata{Name: "TplFunction"},
   723  		Templates: []*chart.File{
   724  			{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value | quote}}" .}}`)},
   725  		},
   726  	}
   727  
   728  	v := chartutil.Values{
   729  		"Values": chartutil.Values{
   730  			"value": "myvalue",
   731  		},
   732  		"Chart": c.Metadata,
   733  		"Release": chartutil.Values{
   734  			"Name": "TestRelease",
   735  		},
   736  	}
   737  
   738  	out, err := Render(c, v)
   739  	if err != nil {
   740  		t.Fatal(err)
   741  	}
   742  
   743  	expect := "Evaluate tpl Value: \"myvalue\""
   744  	if got := out["TplFunction/templates/base"]; got != expect {
   745  		t.Errorf("Expected %q, got %q (%v)", expect, got, out)
   746  	}
   747  }
   748  
   749  func TestAlterFuncMap_tplinclude(t *testing.T) {
   750  	c := &chart.Chart{
   751  		Metadata: &chart.Metadata{Name: "TplFunction"},
   752  		Templates: []*chart.File{
   753  			{Name: "templates/base", Data: []byte(`{{ tpl "{{include ` + "`" + `TplFunction/templates/_partial` + "`" + ` .  | quote }}" .}}`)},
   754  			{Name: "templates/_partial", Data: []byte(`{{.Template.Name}}`)},
   755  		},
   756  	}
   757  	v := chartutil.Values{
   758  		"Values": chartutil.Values{
   759  			"value": "myvalue",
   760  		},
   761  		"Chart": c.Metadata,
   762  		"Release": chartutil.Values{
   763  			"Name": "TestRelease",
   764  		},
   765  	}
   766  
   767  	out, err := Render(c, v)
   768  	if err != nil {
   769  		t.Fatal(err)
   770  	}
   771  
   772  	expect := "\"TplFunction/templates/base\""
   773  	if got := out["TplFunction/templates/base"]; got != expect {
   774  		t.Errorf("Expected %q, got %q (%v)", expect, got, out)
   775  	}
   776  
   777  }
   778  
   779  func TestRenderRecursionLimit(t *testing.T) {
   780  	// endless recursion should produce an error
   781  	c := &chart.Chart{
   782  		Metadata: &chart.Metadata{Name: "bad"},
   783  		Templates: []*chart.File{
   784  			{Name: "templates/base", Data: []byte(`{{include "recursion" . }}`)},
   785  			{Name: "templates/recursion", Data: []byte(`{{define "recursion"}}{{include "recursion" . }}{{end}}`)},
   786  		},
   787  	}
   788  	v := chartutil.Values{
   789  		"Values": "",
   790  		"Chart":  c.Metadata,
   791  		"Release": chartutil.Values{
   792  			"Name": "TestRelease",
   793  		},
   794  	}
   795  	expectErr := "rendering template has a nested reference name: recursion: unable to execute template"
   796  
   797  	_, err := Render(c, v)
   798  	if err == nil || !strings.HasSuffix(err.Error(), expectErr) {
   799  		t.Errorf("Expected err with suffix: %s", expectErr)
   800  	}
   801  
   802  	// calling the same function many times is ok
   803  	times := 4000
   804  	phrase := "All work and no play makes Jack a dull boy"
   805  	printFunc := `{{define "overlook"}}{{printf "` + phrase + `\n"}}{{end}}`
   806  	var repeatedIncl string
   807  	for i := 0; i < times; i++ {
   808  		repeatedIncl += `{{include "overlook" . }}`
   809  	}
   810  
   811  	d := &chart.Chart{
   812  		Metadata: &chart.Metadata{Name: "overlook"},
   813  		Templates: []*chart.File{
   814  			{Name: "templates/quote", Data: []byte(repeatedIncl)},
   815  			{Name: "templates/_function", Data: []byte(printFunc)},
   816  		},
   817  	}
   818  
   819  	out, err := Render(d, v)
   820  	if err != nil {
   821  		t.Fatal(err)
   822  	}
   823  
   824  	var expect string
   825  	for i := 0; i < times; i++ {
   826  		expect += phrase + "\n"
   827  	}
   828  	if got := out["overlook/templates/quote"]; got != expect {
   829  		t.Errorf("Expected %q, got %q (%v)", expect, got, out)
   830  	}
   831  
   832  }