...

Source file src/k8s.io/kubectl/pkg/explain/v2/funcs_test.go

Documentation: k8s.io/kubectl/pkg/explain/v2

     1  /*
     2  Copyright 2022 The Kubernetes 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 v2_test
    18  
    19  import (
    20  	"bytes"
    21  	"testing"
    22  	"text/template"
    23  
    24  	"github.com/stretchr/testify/require"
    25  	"k8s.io/apimachinery/pkg/runtime/schema"
    26  	v2 "k8s.io/kubectl/pkg/explain/v2"
    27  )
    28  
    29  func TestFuncs(t *testing.T) {
    30  	testcases := []struct {
    31  		Name     string
    32  		FuncName string
    33  		Source   string
    34  		Context  any
    35  		Expect   string
    36  		Error    string
    37  	}{
    38  		{
    39  			Name:     "err",
    40  			FuncName: "fail",
    41  			Source:   `{{fail .}}`,
    42  			Context:  "this is a test",
    43  			Error:    "this is a test",
    44  		},
    45  		{
    46  			Name:     "basic",
    47  			FuncName: "wrap",
    48  			Source:   `{{wrap 3 .}}`,
    49  			Context:  "this is a really good test",
    50  			Expect:   "this\nis\na\nreally\ngood\ntest",
    51  		},
    52  		{
    53  			Name:     "basic",
    54  			FuncName: "split",
    55  			Source:   `{{split . "/"}}`,
    56  			Context:  "this/is/a/slash/separated/thing",
    57  			Expect:   "[this is a slash separated thing]",
    58  		},
    59  		{
    60  			Name:     "basic",
    61  			FuncName: "join",
    62  			Source:   `{{join "/" "this" "is" "a" "slash" "separated" "thing"}}`,
    63  			Expect:   "this/is/a/slash/separated/thing",
    64  		},
    65  		{
    66  			Name:     "basic",
    67  			FuncName: "include",
    68  			Source:   `{{define "myTemplate"}}{{.}}{{end}}{{$var := include "myTemplate" .}}{{$var}}`,
    69  			Context:  "hello, world!",
    70  			Expect:   "hello, world!",
    71  		},
    72  		{
    73  			Name:     "nil",
    74  			FuncName: "first",
    75  			Source:   `{{first .}}`,
    76  			Context:  nil,
    77  			Error:    "list is empty",
    78  		},
    79  		{
    80  			Name:     "empty",
    81  			FuncName: "first",
    82  			Source:   `{{first .}}`,
    83  			Context:  []string{},
    84  			Error:    "list is empty",
    85  		},
    86  		{
    87  			Name:     "basic",
    88  			FuncName: "first",
    89  			Source:   `{{first .}}`,
    90  			Context:  []string{"first", "second", "third"},
    91  			Expect:   "first",
    92  		},
    93  		{
    94  			Name:     "wrongtype",
    95  			FuncName: "first",
    96  			Source:   `{{first .}}`,
    97  			Context:  "test",
    98  			Error:    "first cannot be used on type: string",
    99  		},
   100  		{
   101  			Name:     "nil",
   102  			FuncName: "last",
   103  			Source:   `{{last .}}`,
   104  			Context:  nil,
   105  			Error:    "list is empty",
   106  		},
   107  		{
   108  			Name:     "empty",
   109  			FuncName: "last",
   110  			Source:   `{{last .}}`,
   111  			Context:  []string{},
   112  			Error:    "list is empty",
   113  		},
   114  		{
   115  			Name:     "basic",
   116  			FuncName: "last",
   117  			Source:   `{{last .}}`,
   118  			Context:  []string{"first", "second", "third"},
   119  			Expect:   "third",
   120  		},
   121  		{
   122  			Name:     "wrongtype",
   123  			FuncName: "last",
   124  			Source:   `{{last .}}`,
   125  			Context:  "test",
   126  			Error:    "last cannot be used on type: string",
   127  		},
   128  		{
   129  			Name:     "none",
   130  			FuncName: "indent",
   131  			Source:   `{{indent 0 .}}`,
   132  			Context:  "this is a string",
   133  			Expect:   "this is a string",
   134  		},
   135  		{
   136  			Name:     "some",
   137  			FuncName: "indent",
   138  			Source:   `{{indent 2 .}}`,
   139  			Context:  "this is a string",
   140  			Expect:   "  this is a string",
   141  		},
   142  		{
   143  			Name:     "empty",
   144  			FuncName: "dict",
   145  			Source:   `{{dict | toJson}}`,
   146  			Expect:   "{}",
   147  		},
   148  		{
   149  			Name:     "single value",
   150  			FuncName: "dict",
   151  			Source:   `{{dict "key" "value" | toJson}}`,
   152  			Expect:   `{"key":"value"}`,
   153  		},
   154  		{
   155  			Name:     "twoValues",
   156  			FuncName: "dict",
   157  			Source:   `{{dict "key1" "val1" "key2" "val2" | toJson}}`,
   158  			Expect:   `{"key1":"val1","key2":"val2"}`,
   159  		},
   160  		{
   161  			Name:     "oddNumberArgs",
   162  			FuncName: "dict",
   163  			Source:   `{{dict "key1" 1 "key2" | toJson}}`,
   164  			Error:    "error calling dict: expected even # of arguments",
   165  		},
   166  		{
   167  			Name:     "IntegerValue",
   168  			FuncName: "dict",
   169  			Source:   `{{dict "key1" 1 | toJson}}`,
   170  			Expect:   `{"key1":1}`,
   171  		},
   172  		{
   173  			Name:     "MixedValues",
   174  			FuncName: "dict",
   175  			Source:   `{{dict "key1" 1 "key2" "val2" "key3" (dict "key1" "val1") | toJson}}`,
   176  			Expect:   `{"key1":1,"key2":"val2","key3":{"key1":"val1"}}`,
   177  		},
   178  		{
   179  			Name:     "nil",
   180  			FuncName: "contains",
   181  			Source:   `{{contains . "value"}}`,
   182  			Context:  nil,
   183  			Expect:   `false`,
   184  		},
   185  		{
   186  			Name:     "empty",
   187  			FuncName: "contains",
   188  			Source:   `{{contains . "value"}}`,
   189  			Context:  []string{},
   190  			Expect:   `false`,
   191  		},
   192  		{
   193  			Name:     "basic",
   194  			FuncName: "contains",
   195  			Source:   `{{contains . "value"}}`,
   196  			Context:  []string{"value"},
   197  			Expect:   `true`,
   198  		},
   199  		{
   200  			Name:     "struct",
   201  			FuncName: "contains",
   202  			Source:   `{{contains $.haystack $.needle}}`,
   203  			Context: map[string]any{
   204  				"needle": schema.GroupVersionKind{Group: "testgroup.k8s.io", Version: "v1", Kind: "Kind"},
   205  				"haystack": []schema.GroupVersionKind{
   206  					{Group: "randomgroup.k8s.io", Version: "v1", Kind: "OtherKind"},
   207  					{Group: "testgroup.k8s.io", Version: "v1", Kind: "OtherKind"},
   208  					{Group: "testgroup.k8s.io", Version: "v1", Kind: "Kind"},
   209  				},
   210  			},
   211  			Expect: `true`,
   212  		},
   213  		{
   214  			Name:     "nil",
   215  			FuncName: "set",
   216  			Source:   `{{set nil "key" "value" | toJson}}`,
   217  			Expect:   `{"key":"value"}`,
   218  		},
   219  		{
   220  			Name:     "empty",
   221  			FuncName: "set",
   222  			Source:   `{{set (dict) "key" "value" | toJson}}`,
   223  			Expect:   `{"key":"value"}`,
   224  		},
   225  		{
   226  			Name:     "OddArgs",
   227  			FuncName: "set",
   228  			Source:   `{{set (dict) "key" "value" "key2" | toJson}}`,
   229  			Error:    `expected even number of arguments`,
   230  		},
   231  		{
   232  			Name:     "NonStringKey",
   233  			FuncName: "set",
   234  			Source:   `{{set (dict) 1 "value" | toJson}}`,
   235  			Error:    `keys must be strings`,
   236  		},
   237  		{
   238  			Name:     "NilKey",
   239  			FuncName: "set",
   240  			Source:   `{{set (dict) nil "value" | toJson}}`,
   241  			Error:    `keys must be strings`,
   242  		},
   243  		{
   244  			Name:     "NilValue",
   245  			FuncName: "set",
   246  			Source:   `{{set (dict) "key" nil | toJson}}`,
   247  			Expect:   `{"key":null}`,
   248  		},
   249  		{
   250  			Name:     "OverwriteKey",
   251  			FuncName: "set",
   252  			Source:   `{{set (dict "key1" "val1" "key2" "val2") "key1" nil | toJson}}`,
   253  			Expect:   `{"key1":null,"key2":"val2"}`,
   254  		},
   255  		{
   256  			Name:     "OverwriteKeyWithLefover",
   257  			FuncName: "set",
   258  			Source:   `{{set (dict "key1" "val1" "key2" "val2" "key3" "val3") "key1" nil | toJson}}`,
   259  			Expect:   `{"key1":null,"key2":"val2","key3":"val3"}`,
   260  		},
   261  		{
   262  			Name:     "basic",
   263  			FuncName: "add",
   264  			Source:   `{{add 1 2}}`,
   265  			Expect:   `3`,
   266  		},
   267  		{
   268  			Name:     "basic",
   269  			FuncName: "sub",
   270  			Source:   `{{sub 1 2}}`,
   271  			Expect:   `-1`,
   272  		},
   273  		{
   274  			Name:     "basic",
   275  			FuncName: "mul",
   276  			Source:   `{{mul 2 3}}`,
   277  			Expect:   `6`,
   278  		},
   279  		{
   280  			Name:     "basic",
   281  			FuncName: "resolveRef",
   282  			Source:   `{{resolveRef "#/components/schemas/myTypeName" . | toJson}}`,
   283  			Context: map[string]any{
   284  				"components": map[string]any{
   285  					"schemas": map[string]any{
   286  						"myTypeName": map[string]any{
   287  							"key": "val",
   288  						},
   289  					},
   290  				},
   291  			},
   292  			Expect: `{"key":"val"}`,
   293  		},
   294  		{
   295  			Name:     "basicNameWithDots",
   296  			FuncName: "resolveRef",
   297  			Source:   `{{resolveRef "#/components/schemas/myTypeName.with.dots" . | toJson}}`,
   298  			Context: map[string]any{
   299  				"components": map[string]any{
   300  					"schemas": map[string]any{
   301  						"myTypeName.with.dots": map[string]any{
   302  							"key": "val",
   303  						},
   304  					},
   305  				},
   306  			},
   307  			Expect: `{"key":"val"}`,
   308  		},
   309  		{
   310  			Name:     "notFound",
   311  			FuncName: "resolveRef",
   312  			Source:   `{{resolveRef "#/components/schemas/otherTypeName" . | toJson}}`,
   313  			Context: map[string]any{
   314  				"components": map[string]any{
   315  					"schemas": map[string]any{
   316  						"myTypeName": map[string]any{
   317  							"key": "val",
   318  						},
   319  					},
   320  				},
   321  			},
   322  			Expect: `null`,
   323  		},
   324  		{
   325  			Name:     "url",
   326  			FuncName: "resolveRef",
   327  			Source:   `{{resolveRef "http://swagger.com/swagger.json#/components/schemas/myTypeName" . | toJson}}`,
   328  			Context: map[string]any{
   329  				"components": map[string]any{
   330  					"schemas": map[string]any{
   331  						"myTypeName": map[string]any{
   332  							"key": "val",
   333  						},
   334  					},
   335  				},
   336  			},
   337  			Expect: `null`,
   338  		},
   339  	}
   340  
   341  	for _, tcase := range testcases {
   342  		t.Run(tcase.FuncName+"/"+tcase.Name, func(t *testing.T) {
   343  
   344  			tmpl, err := v2.WithBuiltinTemplateFuncs(template.New("me")).Parse(tcase.Source)
   345  			require.NoError(t, err)
   346  
   347  			buf := bytes.NewBuffer(nil)
   348  			err = tmpl.Execute(buf, tcase.Context)
   349  
   350  			if len(tcase.Error) > 0 {
   351  				require.ErrorContains(t, err, tcase.Error)
   352  			} else if output := buf.String(); len(tcase.Expect) > 0 {
   353  				require.NoError(t, err)
   354  				require.Contains(t, output, tcase.Expect)
   355  			}
   356  		})
   357  	}
   358  }
   359  

View as plain text