github.com/koderover/helm@v2.17.0+incompatible/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  	"k8s.io/helm/pkg/chartutil"
    26  	"k8s.io/helm/pkg/proto/hapi/chart"
    27  
    28  	"github.com/golang/protobuf/ptypes/any"
    29  )
    30  
    31  func TestSortTemplates(t *testing.T) {
    32  	tpls := map[string]renderable{
    33  		"/mychart/templates/foo.tpl":                                 {},
    34  		"/mychart/templates/charts/foo/charts/bar/templates/foo.tpl": {},
    35  		"/mychart/templates/bar.tpl":                                 {},
    36  		"/mychart/templates/charts/foo/templates/bar.tpl":            {},
    37  		"/mychart/templates/_foo.tpl":                                {},
    38  		"/mychart/templates/charts/foo/templates/foo.tpl":            {},
    39  		"/mychart/templates/charts/bar/templates/foo.tpl":            {},
    40  	}
    41  	got := sortTemplates(tpls)
    42  	if len(got) != len(tpls) {
    43  		t.Fatal("Sorted results are missing templates")
    44  	}
    45  
    46  	expect := []string{
    47  		"/mychart/templates/charts/foo/charts/bar/templates/foo.tpl",
    48  		"/mychart/templates/charts/foo/templates/foo.tpl",
    49  		"/mychart/templates/charts/foo/templates/bar.tpl",
    50  		"/mychart/templates/charts/bar/templates/foo.tpl",
    51  		"/mychart/templates/foo.tpl",
    52  		"/mychart/templates/bar.tpl",
    53  		"/mychart/templates/_foo.tpl",
    54  	}
    55  	for i, e := range expect {
    56  		if got[i] != e {
    57  			t.Errorf("expected %q, got %q at index %d\n\tExp: %#v\n\tGot: %#v", e, got[i], i, expect, got)
    58  		}
    59  	}
    60  }
    61  
    62  func TestEngine(t *testing.T) {
    63  	e := New()
    64  
    65  	// Forbidden because they allow access to the host OS.
    66  	forbidden := []string{"env", "expandenv"}
    67  	for _, f := range forbidden {
    68  		if _, ok := e.FuncMap[f]; ok {
    69  			t.Errorf("Forbidden function %s exists in FuncMap.", f)
    70  		}
    71  	}
    72  }
    73  
    74  func TestFuncMap(t *testing.T) {
    75  	fns := FuncMap()
    76  	forbidden := []string{"env", "expandenv"}
    77  	for _, f := range forbidden {
    78  		if _, ok := fns[f]; ok {
    79  			t.Errorf("Forbidden function %s exists in FuncMap.", f)
    80  		}
    81  	}
    82  
    83  	// Test for Engine-specific template functions.
    84  	expect := []string{"include", "required", "tpl", "toYaml", "fromYaml", "toToml", "toJson", "fromJson"}
    85  	for _, f := range expect {
    86  		if _, ok := fns[f]; !ok {
    87  			t.Errorf("Expected add-on function %q", f)
    88  		}
    89  	}
    90  }
    91  
    92  func TestRender(t *testing.T) {
    93  	c := &chart.Chart{
    94  		Metadata: &chart.Metadata{
    95  			Name:    "moby",
    96  			Version: "1.2.3",
    97  		},
    98  		Templates: []*chart.Template{
    99  			{Name: "templates/test1", Data: []byte("{{.outer | title }} {{.inner | title}}")},
   100  			{Name: "templates/test2", Data: []byte("{{.global.callme | lower }}")},
   101  			{Name: "templates/test3", Data: []byte("{{.noValue}}")},
   102  		},
   103  		Values: &chart.Config{
   104  			Raw: "outer: DEFAULT\ninner: DEFAULT",
   105  		},
   106  	}
   107  
   108  	vals := &chart.Config{
   109  		Raw: `
   110  outer: spouter
   111  inner: inn
   112  global:
   113    callme: Ishmael
   114  `}
   115  
   116  	e := New()
   117  	v, err := chartutil.CoalesceValues(c, vals)
   118  	if err != nil {
   119  		t.Fatalf("Failed to coalesce values: %s", err)
   120  	}
   121  	out, err := e.Render(c, v)
   122  	if err != nil {
   123  		t.Errorf("Failed to render templates: %s", err)
   124  	}
   125  
   126  	expect := "Spouter Inn"
   127  	if out["moby/templates/test1"] != expect {
   128  		t.Errorf("Expected %q, got %q", expect, out["test1"])
   129  	}
   130  
   131  	expect = "ishmael"
   132  	if out["moby/templates/test2"] != expect {
   133  		t.Errorf("Expected %q, got %q", expect, out["test2"])
   134  	}
   135  	expect = ""
   136  	if out["moby/templates/test3"] != expect {
   137  		t.Errorf("Expected %q, got %q", expect, out["test3"])
   138  	}
   139  
   140  	if _, err := e.Render(c, v); err != nil {
   141  		t.Errorf("Unexpected error: %s", err)
   142  	}
   143  }
   144  
   145  func TestRenderInternals(t *testing.T) {
   146  	// Test the internals of the rendering tool.
   147  	e := New()
   148  
   149  	vals := chartutil.Values{"Name": "one", "Value": "two"}
   150  	tpls := map[string]renderable{
   151  		"one": {tpl: `Hello {{title .Name}}`, vals: vals},
   152  		"two": {tpl: `Goodbye {{upper .Value}}`, vals: vals},
   153  		// Test whether a template can reliably reference another template
   154  		// without regard for ordering.
   155  		"three": {tpl: `{{template "two" dict "Value" "three"}}`, vals: vals},
   156  	}
   157  
   158  	out, err := e.render(tpls)
   159  	if err != nil {
   160  		t.Fatalf("Failed template rendering: %s", err)
   161  	}
   162  
   163  	if len(out) != 3 {
   164  		t.Fatalf("Expected 3 templates, got %d", len(out))
   165  	}
   166  
   167  	if out["one"] != "Hello One" {
   168  		t.Errorf("Expected 'Hello One', got %q", out["one"])
   169  	}
   170  
   171  	if out["two"] != "Goodbye TWO" {
   172  		t.Errorf("Expected 'Goodbye TWO'. got %q", out["two"])
   173  	}
   174  
   175  	if out["three"] != "Goodbye THREE" {
   176  		t.Errorf("Expected 'Goodbye THREE'. got %q", out["two"])
   177  	}
   178  }
   179  
   180  func TestParallelRenderInternals(t *testing.T) {
   181  	// Make sure that we can use one Engine to run parallel template renders.
   182  	e := New()
   183  	var wg sync.WaitGroup
   184  	for i := 0; i < 20; i++ {
   185  		wg.Add(1)
   186  		go func(i int) {
   187  			fname := "my/file/name"
   188  			tt := fmt.Sprintf("expect-%d", i)
   189  			v := chartutil.Values{"val": tt}
   190  			tpls := map[string]renderable{fname: {tpl: `{{.val}}`, vals: v}}
   191  			out, err := e.render(tpls)
   192  			if err != nil {
   193  				t.Errorf("Failed to render %s: %s", tt, err)
   194  			}
   195  			if out[fname] != tt {
   196  				t.Errorf("Expected %q, got %q", tt, out[fname])
   197  			}
   198  			wg.Done()
   199  		}(i)
   200  	}
   201  	wg.Wait()
   202  }
   203  
   204  func TestAllTemplates(t *testing.T) {
   205  	ch1 := &chart.Chart{
   206  		Metadata: &chart.Metadata{Name: "ch1"},
   207  		Templates: []*chart.Template{
   208  			{Name: "templates/foo", Data: []byte("foo")},
   209  			{Name: "templates/bar", Data: []byte("bar")},
   210  		},
   211  		Dependencies: []*chart.Chart{
   212  			{
   213  				Metadata: &chart.Metadata{Name: "laboratory mice"},
   214  				Templates: []*chart.Template{
   215  					{Name: "templates/pinky", Data: []byte("pinky")},
   216  					{Name: "templates/brain", Data: []byte("brain")},
   217  				},
   218  				Dependencies: []*chart.Chart{{
   219  					Metadata: &chart.Metadata{Name: "same thing we do every night"},
   220  					Templates: []*chart.Template{
   221  						{Name: "templates/innermost", Data: []byte("innermost")},
   222  					}},
   223  				},
   224  			},
   225  		},
   226  	}
   227  
   228  	var v chartutil.Values
   229  	tpls := allTemplates(ch1, v)
   230  	if len(tpls) != 5 {
   231  		t.Errorf("Expected 5 charts, got %d", len(tpls))
   232  	}
   233  }
   234  
   235  func TestRenderDependency(t *testing.T) {
   236  	e := New()
   237  	deptpl := `{{define "myblock"}}World{{end}}`
   238  	toptpl := `Hello {{template "myblock"}}`
   239  	ch := &chart.Chart{
   240  		Metadata: &chart.Metadata{Name: "outerchart"},
   241  		Templates: []*chart.Template{
   242  			{Name: "templates/outer", Data: []byte(toptpl)},
   243  		},
   244  		Dependencies: []*chart.Chart{
   245  			{
   246  				Metadata: &chart.Metadata{Name: "innerchart"},
   247  				Templates: []*chart.Template{
   248  					{Name: "templates/inner", Data: []byte(deptpl)},
   249  				},
   250  			},
   251  		},
   252  	}
   253  
   254  	out, err := e.Render(ch, map[string]interface{}{})
   255  
   256  	if err != nil {
   257  		t.Fatalf("failed to render chart: %s", err)
   258  	}
   259  
   260  	if len(out) != 2 {
   261  		t.Errorf("Expected 2, got %d", len(out))
   262  	}
   263  
   264  	expect := "Hello World"
   265  	if out["outerchart/templates/outer"] != expect {
   266  		t.Errorf("Expected %q, got %q", expect, out["outer"])
   267  	}
   268  
   269  }
   270  
   271  func TestRenderNestedValues(t *testing.T) {
   272  	e := New()
   273  
   274  	innerpath := "templates/inner.tpl"
   275  	outerpath := "templates/outer.tpl"
   276  	// Ensure namespacing rules are working.
   277  	deepestpath := "templates/inner.tpl"
   278  	checkrelease := "templates/release.tpl"
   279  
   280  	deepest := &chart.Chart{
   281  		Metadata: &chart.Metadata{Name: "deepest"},
   282  		Templates: []*chart.Template{
   283  			{Name: deepestpath, Data: []byte(`And this same {{.Values.what}} that smiles {{.Values.global.when}}`)},
   284  			{Name: checkrelease, Data: []byte(`Tomorrow will be {{default "happy" .Release.Name }}`)},
   285  		},
   286  		Values: &chart.Config{Raw: `what: "milkshake"`},
   287  	}
   288  
   289  	inner := &chart.Chart{
   290  		Metadata: &chart.Metadata{Name: "herrick"},
   291  		Templates: []*chart.Template{
   292  			{Name: innerpath, Data: []byte(`Old {{.Values.who}} is still a-flyin'`)},
   293  		},
   294  		Values:       &chart.Config{Raw: `who: "Robert"`},
   295  		Dependencies: []*chart.Chart{deepest},
   296  	}
   297  
   298  	outer := &chart.Chart{
   299  		Metadata: &chart.Metadata{Name: "top"},
   300  		Templates: []*chart.Template{
   301  			{Name: outerpath, Data: []byte(`Gather ye {{.Values.what}} while ye may`)},
   302  		},
   303  		Values: &chart.Config{
   304  			Raw: `
   305  what: stinkweed
   306  who: me
   307  herrick:
   308      who: time`,
   309  		},
   310  		Dependencies: []*chart.Chart{inner},
   311  	}
   312  
   313  	injValues := chart.Config{
   314  		Raw: `
   315  what: rosebuds
   316  herrick:
   317    deepest:
   318      what: flower
   319  global:
   320    when: to-day`,
   321  	}
   322  
   323  	tmp, err := chartutil.CoalesceValues(outer, &injValues)
   324  	if err != nil {
   325  		t.Fatalf("Failed to coalesce values: %s", err)
   326  	}
   327  
   328  	inject := chartutil.Values{
   329  		"Values": tmp,
   330  		"Chart":  outer.Metadata,
   331  		"Release": chartutil.Values{
   332  			"Name": "dyin",
   333  		},
   334  	}
   335  
   336  	t.Logf("Calculated values: %v", inject)
   337  
   338  	out, err := e.Render(outer, inject)
   339  	if err != nil {
   340  		t.Fatalf("failed to render templates: %s", err)
   341  	}
   342  
   343  	fullouterpath := "top/" + outerpath
   344  	if out[fullouterpath] != "Gather ye rosebuds while ye may" {
   345  		t.Errorf("Unexpected outer: %q", out[fullouterpath])
   346  	}
   347  
   348  	fullinnerpath := "top/charts/herrick/" + innerpath
   349  	if out[fullinnerpath] != "Old time is still a-flyin'" {
   350  		t.Errorf("Unexpected inner: %q", out[fullinnerpath])
   351  	}
   352  
   353  	fulldeepestpath := "top/charts/herrick/charts/deepest/" + deepestpath
   354  	if out[fulldeepestpath] != "And this same flower that smiles to-day" {
   355  		t.Errorf("Unexpected deepest: %q", out[fulldeepestpath])
   356  	}
   357  
   358  	fullcheckrelease := "top/charts/herrick/charts/deepest/" + checkrelease
   359  	if out[fullcheckrelease] != "Tomorrow will be dyin" {
   360  		t.Errorf("Unexpected release: %q", out[fullcheckrelease])
   361  	}
   362  }
   363  
   364  func TestRenderBuiltinValues(t *testing.T) {
   365  	inner := &chart.Chart{
   366  		Metadata: &chart.Metadata{Name: "Latium"},
   367  		Templates: []*chart.Template{
   368  			{Name: "templates/Lavinia", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)},
   369  			{Name: "templates/From", Data: []byte(`{{.Files.author | printf "%s"}} {{.Files.Get "book/title.txt"}}`)},
   370  		},
   371  		Values:       &chart.Config{Raw: ``},
   372  		Dependencies: []*chart.Chart{},
   373  		Files: []*any.Any{
   374  			{TypeUrl: "author", Value: []byte("Virgil")},
   375  			{TypeUrl: "book/title.txt", Value: []byte("Aeneid")},
   376  		},
   377  	}
   378  
   379  	outer := &chart.Chart{
   380  		Metadata: &chart.Metadata{Name: "Troy"},
   381  		Templates: []*chart.Template{
   382  			{Name: "templates/Aeneas", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)},
   383  		},
   384  		Values:       &chart.Config{Raw: ``},
   385  		Dependencies: []*chart.Chart{inner},
   386  	}
   387  
   388  	inject := chartutil.Values{
   389  		"Values": &chart.Config{Raw: ""},
   390  		"Chart":  outer.Metadata,
   391  		"Release": chartutil.Values{
   392  			"Name": "Aeneid",
   393  		},
   394  	}
   395  
   396  	t.Logf("Calculated values: %v", outer)
   397  
   398  	out, err := New().Render(outer, inject)
   399  	if err != nil {
   400  		t.Fatalf("failed to render templates: %s", err)
   401  	}
   402  
   403  	expects := map[string]string{
   404  		"Troy/charts/Latium/templates/Lavinia": "Troy/charts/Latium/templates/LaviniaLatiumAeneid",
   405  		"Troy/templates/Aeneas":                "Troy/templates/AeneasTroyAeneid",
   406  		"Troy/charts/Latium/templates/From":    "Virgil Aeneid",
   407  	}
   408  	for file, expect := range expects {
   409  		if out[file] != expect {
   410  			t.Errorf("Expected %q, got %q", expect, out[file])
   411  		}
   412  	}
   413  
   414  }
   415  
   416  func TestAlterFuncMap(t *testing.T) {
   417  	c := &chart.Chart{
   418  		Metadata: &chart.Metadata{Name: "conrad"},
   419  		Templates: []*chart.Template{
   420  			{Name: "templates/quote", Data: []byte(`{{include "conrad/templates/_partial" . | indent 2}} dead.`)},
   421  			{Name: "templates/_partial", Data: []byte(`{{.Release.Name}} - he`)},
   422  		},
   423  		Values:       &chart.Config{Raw: ``},
   424  		Dependencies: []*chart.Chart{},
   425  	}
   426  
   427  	// Check nested reference in include FuncMap
   428  	d := &chart.Chart{
   429  		Metadata: &chart.Metadata{Name: "nested"},
   430  		Templates: []*chart.Template{
   431  			{Name: "templates/quote", Data: []byte(`{{include "nested/templates/quote" . | indent 2}} dead.`)},
   432  			{Name: "templates/_partial", Data: []byte(`{{.Release.Name}} - he`)},
   433  		},
   434  	}
   435  
   436  	v := chartutil.Values{
   437  		"Values": &chart.Config{Raw: ""},
   438  		"Chart":  c.Metadata,
   439  		"Release": chartutil.Values{
   440  			"Name": "Mistah Kurtz",
   441  		},
   442  	}
   443  
   444  	out, err := New().Render(c, v)
   445  	if err != nil {
   446  		t.Fatal(err)
   447  	}
   448  
   449  	expect := "  Mistah Kurtz - he dead."
   450  	if got := out["conrad/templates/quote"]; got != expect {
   451  		t.Errorf("Expected %q, got %q (%v)", expect, got, out)
   452  	}
   453  
   454  	_, err = New().Render(d, v)
   455  	expectErrName := "nested/templates/quote"
   456  	if err == nil {
   457  		t.Errorf("Expected err of nested reference name: %v", expectErrName)
   458  	}
   459  
   460  	reqChart := &chart.Chart{
   461  		Metadata: &chart.Metadata{Name: "conan"},
   462  		Templates: []*chart.Template{
   463  			{Name: "templates/quote", Data: []byte(`All your base are belong to {{ required "A valid 'who' is required" .Values.who }}`)},
   464  			{Name: "templates/bases", Data: []byte(`All {{ required "A valid 'bases' is required" .Values.bases }} of them!`)},
   465  		},
   466  		Values:       &chart.Config{Raw: ``},
   467  		Dependencies: []*chart.Chart{},
   468  	}
   469  
   470  	reqValues := chartutil.Values{
   471  		"Values": chartutil.Values{
   472  			"who":   "us",
   473  			"bases": 2,
   474  		},
   475  		"Chart": reqChart.Metadata,
   476  		"Release": chartutil.Values{
   477  			"Name": "That 90s meme",
   478  		},
   479  	}
   480  
   481  	outReq, err := New().Render(reqChart, reqValues)
   482  	if err != nil {
   483  		t.Fatal(err)
   484  	}
   485  	expectStr := "All your base are belong to us"
   486  	if gotStr := outReq["conan/templates/quote"]; gotStr != expectStr {
   487  		t.Errorf("Expected %q, got %q (%v)", expectStr, gotStr, outReq)
   488  	}
   489  	expectNum := "All 2 of them!"
   490  	if gotNum := outReq["conan/templates/bases"]; gotNum != expectNum {
   491  		t.Errorf("Expected %q, got %q (%v)", expectNum, gotNum, outReq)
   492  	}
   493  
   494  	// test required without passing in needed values with lint mode on
   495  	// verifies lint replaces required with an empty string (should not fail)
   496  	lintValues := chartutil.Values{
   497  		"Values": chartutil.Values{
   498  			"who": "us",
   499  		},
   500  		"Chart": reqChart.Metadata,
   501  		"Release": chartutil.Values{
   502  			"Name": "That 90s meme",
   503  		},
   504  	}
   505  	e := New()
   506  	e.LintMode = true
   507  	outReq, err = e.Render(reqChart, lintValues)
   508  	if err != nil {
   509  		t.Fatal(err)
   510  	}
   511  	expectStr = "All your base are belong to us"
   512  	if gotStr := outReq["conan/templates/quote"]; gotStr != expectStr {
   513  		t.Errorf("Expected %q, got %q (%v)", expectStr, gotStr, outReq)
   514  	}
   515  	expectNum = "All  of them!"
   516  	if gotNum := outReq["conan/templates/bases"]; gotNum != expectNum {
   517  		t.Errorf("Expected %q, got %q (%v)", expectNum, gotNum, outReq)
   518  	}
   519  
   520  	tplChart := &chart.Chart{
   521  		Metadata: &chart.Metadata{Name: "TplFunction"},
   522  		Templates: []*chart.Template{
   523  			{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value}}" .}}`)},
   524  		},
   525  		Values:       &chart.Config{Raw: ``},
   526  		Dependencies: []*chart.Chart{},
   527  	}
   528  
   529  	tplValues := chartutil.Values{
   530  		"Values": chartutil.Values{
   531  			"value": "myvalue",
   532  		},
   533  		"Chart": tplChart.Metadata,
   534  		"Release": chartutil.Values{
   535  			"Name": "TestRelease",
   536  		},
   537  	}
   538  
   539  	outTpl, err := New().Render(tplChart, tplValues)
   540  	if err != nil {
   541  		t.Fatal(err)
   542  	}
   543  
   544  	expectTplStr := "Evaluate tpl Value: myvalue"
   545  	if gotStrTpl := outTpl["TplFunction/templates/base"]; gotStrTpl != expectTplStr {
   546  		t.Errorf("Expected %q, got %q (%v)", expectTplStr, gotStrTpl, outTpl)
   547  	}
   548  
   549  	tplChartWithFunction := &chart.Chart{
   550  		Metadata: &chart.Metadata{Name: "TplFunction"},
   551  		Templates: []*chart.Template{
   552  			{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value | quote}}" .}}`)},
   553  		},
   554  		Values:       &chart.Config{Raw: ``},
   555  		Dependencies: []*chart.Chart{},
   556  	}
   557  
   558  	tplValuesWithFunction := chartutil.Values{
   559  		"Values": chartutil.Values{
   560  			"value": "myvalue",
   561  		},
   562  		"Chart": tplChartWithFunction.Metadata,
   563  		"Release": chartutil.Values{
   564  			"Name": "TestRelease",
   565  		},
   566  	}
   567  
   568  	outTplWithFunction, err := New().Render(tplChartWithFunction, tplValuesWithFunction)
   569  	if err != nil {
   570  		t.Fatal(err)
   571  	}
   572  
   573  	expectTplStrWithFunction := "Evaluate tpl Value: \"myvalue\""
   574  	if gotStrTplWithFunction := outTplWithFunction["TplFunction/templates/base"]; gotStrTplWithFunction != expectTplStrWithFunction {
   575  		t.Errorf("Expected %q, got %q (%v)", expectTplStrWithFunction, gotStrTplWithFunction, outTplWithFunction)
   576  	}
   577  
   578  	tplChartWithInclude := &chart.Chart{
   579  		Metadata: &chart.Metadata{Name: "TplFunction"},
   580  		Templates: []*chart.Template{
   581  			{Name: "templates/base", Data: []byte(`{{ tpl "{{include ` + "`" + `TplFunction/templates/_partial` + "`" + ` .  | quote }}" .}}`)},
   582  			{Name: "templates/_partial", Data: []byte(`{{.Template.Name}}`)},
   583  		},
   584  		Values:       &chart.Config{Raw: ``},
   585  		Dependencies: []*chart.Chart{},
   586  	}
   587  	tplValueWithInclude := chartutil.Values{
   588  		"Values": chartutil.Values{
   589  			"value": "myvalue",
   590  		},
   591  		"Chart": tplChartWithInclude.Metadata,
   592  		"Release": chartutil.Values{
   593  			"Name": "TestRelease",
   594  		},
   595  	}
   596  
   597  	outTplWithInclude, err := New().Render(tplChartWithInclude, tplValueWithInclude)
   598  	if err != nil {
   599  		t.Fatal(err)
   600  	}
   601  
   602  	expectedTplStrWithInclude := "\"TplFunction/templates/base\""
   603  	if gotStrTplWithInclude := outTplWithInclude["TplFunction/templates/base"]; gotStrTplWithInclude != expectedTplStrWithInclude {
   604  		t.Errorf("Expected %q, got %q (%v)", expectedTplStrWithInclude, gotStrTplWithInclude, outTplWithInclude)
   605  	}
   606  
   607  }
   608  
   609  func TestRenderRecursionLimit(t *testing.T) {
   610  	// endless recursion should produce an error
   611  	c := &chart.Chart{
   612  		Metadata: &chart.Metadata{Name: "bad"},
   613  		Templates: []*chart.Template{
   614  			{Name: "templates/base", Data: []byte(`{{include "recursion" . }}`)},
   615  			{Name: "templates/recursion", Data: []byte(`{{define "recursion"}}{{include "recursion" . }}{{end}}`)},
   616  		},
   617  	}
   618  	v := chartutil.Values{
   619  		"Values": "",
   620  		"Chart":  c.Metadata,
   621  		"Release": chartutil.Values{
   622  			"Name": "TestRelease",
   623  		},
   624  	}
   625  	expectErr := "rendering template has a nested reference name: recursion: unable to execute template"
   626  
   627  	e := New()
   628  	_, err := e.Render(c, v)
   629  	if err == nil || !strings.HasSuffix(err.Error(), expectErr) {
   630  		t.Errorf("Expected err with suffix: %s", expectErr)
   631  	}
   632  
   633  	// calling the same function many times is ok
   634  	times := 4000
   635  	phrase := "All work and no play makes Jack a dull boy"
   636  	printFunc := `{{define "overlook"}}{{printf "` + phrase + `\n"}}{{end}}`
   637  	var repeatedIncl string
   638  	for i := 0; i < times; i++ {
   639  		repeatedIncl += `{{include "overlook" . }}`
   640  	}
   641  
   642  	d := &chart.Chart{
   643  		Metadata: &chart.Metadata{Name: "overlook"},
   644  		Templates: []*chart.Template{
   645  			{Name: "templates/quote", Data: []byte(repeatedIncl)},
   646  			{Name: "templates/_function", Data: []byte(printFunc)},
   647  		},
   648  	}
   649  
   650  	out, err := e.Render(d, v)
   651  	if err != nil {
   652  		t.Fatal(err)
   653  	}
   654  
   655  	var expect string
   656  	for i := 0; i < times; i++ {
   657  		expect += phrase + "\n"
   658  	}
   659  	if got := out["overlook/templates/quote"]; got != expect {
   660  		t.Errorf("Expected %q, got %q (%v)", expect, got, out)
   661  	}
   662  }