...

Source file src/helm.sh/helm/v3/pkg/strvals/literal_parser_test.go

Documentation: helm.sh/helm/v3/pkg/strvals

     1  /*
     2  Copyright The Helm Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package strvals
    17  
    18  import (
    19  	"fmt"
    20  	"testing"
    21  
    22  	"sigs.k8s.io/yaml"
    23  )
    24  
    25  func TestParseLiteral(t *testing.T) {
    26  	cases := []struct {
    27  		str    string
    28  		expect map[string]interface{}
    29  		err    bool
    30  	}{
    31  		{
    32  			str: "name",
    33  			err: true,
    34  		},
    35  		{
    36  			str:    "name=",
    37  			expect: map[string]interface{}{"name": ""},
    38  		},
    39  		{
    40  			str:    "name=value",
    41  			expect: map[string]interface{}{"name": "value"},
    42  			err:    false,
    43  		},
    44  		{
    45  			str:    "long_int_string=1234567890",
    46  			expect: map[string]interface{}{"long_int_string": "1234567890"},
    47  			err:    false,
    48  		},
    49  		{
    50  			str:    "boolean=true",
    51  			expect: map[string]interface{}{"boolean": "true"},
    52  			err:    false,
    53  		},
    54  		{
    55  			str:    "is_null=null",
    56  			expect: map[string]interface{}{"is_null": "null"},
    57  			err:    false,
    58  		},
    59  		{
    60  			str:    "zero=0",
    61  			expect: map[string]interface{}{"zero": "0"},
    62  			err:    false,
    63  		},
    64  		{
    65  			str:    "name1=null,name2=value2",
    66  			expect: map[string]interface{}{"name1": "null,name2=value2"},
    67  			err:    false,
    68  		},
    69  		{
    70  			str:    "name1=value,,,tail",
    71  			expect: map[string]interface{}{"name1": "value,,,tail"},
    72  			err:    false,
    73  		},
    74  		{
    75  			str:    "leading_zeros=00009",
    76  			expect: map[string]interface{}{"leading_zeros": "00009"},
    77  			err:    false,
    78  		},
    79  		{
    80  			str:    "name=one two three",
    81  			expect: map[string]interface{}{"name": "one two three"},
    82  			err:    false,
    83  		},
    84  		{
    85  			str:    "outer.inner=value",
    86  			expect: map[string]interface{}{"outer": map[string]interface{}{"inner": "value"}},
    87  			err:    false,
    88  		},
    89  		{
    90  			str:    "outer.middle.inner=value",
    91  			expect: map[string]interface{}{"outer": map[string]interface{}{"middle": map[string]interface{}{"inner": "value"}}},
    92  			err:    false,
    93  		},
    94  		{
    95  			str: "name1.name2",
    96  			err: true,
    97  		},
    98  		{
    99  			str:    "name1.name2=",
   100  			expect: map[string]interface{}{"name1": map[string]interface{}{"name2": ""}},
   101  			err:    false,
   102  		},
   103  		{
   104  			str: "name1.=name2",
   105  			err: true,
   106  		},
   107  		{
   108  			str: "name1.,name2",
   109  			err: true,
   110  		},
   111  		{
   112  			str:    "name1={value1,value2}",
   113  			expect: map[string]interface{}{"name1": "{value1,value2}"},
   114  		},
   115  
   116  		// List support
   117  		{
   118  			str:    "list[0]=foo",
   119  			expect: map[string]interface{}{"list": []string{"foo"}},
   120  			err:    false,
   121  		},
   122  		{
   123  			str: "list[0].foo=bar",
   124  			expect: map[string]interface{}{
   125  				"list": []interface{}{
   126  					map[string]interface{}{"foo": "bar"},
   127  				},
   128  			},
   129  			err: false,
   130  		},
   131  		{
   132  			str: "list[-30].hello=world",
   133  			err: true,
   134  		},
   135  		{
   136  			str:    "list[3]=bar",
   137  			expect: map[string]interface{}{"list": []interface{}{nil, nil, nil, "bar"}},
   138  			err:    false,
   139  		},
   140  		{
   141  			str: "illegal[0]name.foo=bar",
   142  			err: true,
   143  		},
   144  		{
   145  			str:    "noval[0]",
   146  			expect: map[string]interface{}{"noval": []interface{}{}},
   147  			err:    false,
   148  		},
   149  		{
   150  			str:    "noval[0]=",
   151  			expect: map[string]interface{}{"noval": []interface{}{""}},
   152  			err:    false,
   153  		},
   154  		{
   155  			str:    "nested[0][0]=1",
   156  			expect: map[string]interface{}{"nested": []interface{}{[]interface{}{"1"}}},
   157  			err:    false,
   158  		},
   159  		{
   160  			str:    "nested[1][1]=1",
   161  			expect: map[string]interface{}{"nested": []interface{}{nil, []interface{}{nil, "1"}}},
   162  			err:    false,
   163  		},
   164  		{
   165  			str: "name1.name2[0].foo=bar",
   166  			expect: map[string]interface{}{
   167  				"name1": map[string]interface{}{
   168  					"name2": []map[string]interface{}{{"foo": "bar"}},
   169  				},
   170  			},
   171  		},
   172  		{
   173  			str: "name1.name2[1].foo=bar",
   174  			expect: map[string]interface{}{
   175  				"name1": map[string]interface{}{
   176  					"name2": []map[string]interface{}{nil, {"foo": "bar"}},
   177  				},
   178  			},
   179  		},
   180  		{
   181  			str: "name1.name2[1].foo=bar",
   182  			expect: map[string]interface{}{
   183  				"name1": map[string]interface{}{
   184  					"name2": []map[string]interface{}{nil, {"foo": "bar"}},
   185  				},
   186  			},
   187  		},
   188  		{
   189  			str:    "]={}].",
   190  			expect: map[string]interface{}{"]": "{}]."},
   191  			err:    false,
   192  		},
   193  
   194  		// issue test cases: , = $ ( ) { } . \ \\
   195  		{
   196  			str:    "name=val,val",
   197  			expect: map[string]interface{}{"name": "val,val"},
   198  			err:    false,
   199  		},
   200  		{
   201  			str:    "name=val.val",
   202  			expect: map[string]interface{}{"name": "val.val"},
   203  			err:    false,
   204  		},
   205  		{
   206  			str:    "name=val=val",
   207  			expect: map[string]interface{}{"name": "val=val"},
   208  			err:    false,
   209  		},
   210  		{
   211  			str:    "name=val$val",
   212  			expect: map[string]interface{}{"name": "val$val"},
   213  			err:    false,
   214  		},
   215  		{
   216  			str:    "name=(value",
   217  			expect: map[string]interface{}{"name": "(value"},
   218  			err:    false,
   219  		},
   220  		{
   221  			str:    "name=value)",
   222  			expect: map[string]interface{}{"name": "value)"},
   223  			err:    false,
   224  		},
   225  		{
   226  			str:    "name=(value)",
   227  			expect: map[string]interface{}{"name": "(value)"},
   228  			err:    false,
   229  		},
   230  		{
   231  			str:    "name={value",
   232  			expect: map[string]interface{}{"name": "{value"},
   233  			err:    false,
   234  		},
   235  		{
   236  			str:    "name=value}",
   237  			expect: map[string]interface{}{"name": "value}"},
   238  			err:    false,
   239  		},
   240  		{
   241  			str:    "name={value}",
   242  			expect: map[string]interface{}{"name": "{value}"},
   243  			err:    false,
   244  		},
   245  		{
   246  			str:    "name={value1,value2}",
   247  			expect: map[string]interface{}{"name": "{value1,value2}"},
   248  			err:    false,
   249  		},
   250  		{
   251  			str:    `name=val\val`,
   252  			expect: map[string]interface{}{"name": `val\val`},
   253  			err:    false,
   254  		},
   255  		{
   256  			str:    `name=val\\val`,
   257  			expect: map[string]interface{}{"name": `val\\val`},
   258  			err:    false,
   259  		},
   260  		{
   261  			str:    `name=val\\\val`,
   262  			expect: map[string]interface{}{"name": `val\\\val`},
   263  			err:    false,
   264  		},
   265  		{
   266  			str:    `name={val,.?*v\0a!l)some`,
   267  			expect: map[string]interface{}{"name": `{val,.?*v\0a!l)some`},
   268  			err:    false,
   269  		},
   270  		{
   271  			str:    `name=em%GT)tqUDqz,i-\h+Mbqs-!:.m\\rE=mkbM#rR}@{-k@`,
   272  			expect: map[string]interface{}{"name": `em%GT)tqUDqz,i-\h+Mbqs-!:.m\\rE=mkbM#rR}@{-k@`},
   273  		},
   274  	}
   275  
   276  	for _, tt := range cases {
   277  		got, err := ParseLiteral(tt.str)
   278  		if err != nil {
   279  			if !tt.err {
   280  				t.Fatalf("%s: %s", tt.str, err)
   281  			}
   282  			continue
   283  		}
   284  
   285  		if tt.err {
   286  			t.Errorf("%s: Expected error. Got nil", tt.str)
   287  		}
   288  
   289  		y1, err := yaml.Marshal(tt.expect)
   290  		if err != nil {
   291  			t.Fatal(err)
   292  		}
   293  
   294  		y2, err := yaml.Marshal(got)
   295  		if err != nil {
   296  			t.Fatalf("Error serializing parsed value: %s", err)
   297  		}
   298  
   299  		if string(y1) != string(y2) {
   300  			t.Errorf("%s: Expected:\n%s\nGot:\n%s", tt.str, y1, y2)
   301  		}
   302  	}
   303  }
   304  
   305  func TestParseLiteralInto(t *testing.T) {
   306  	tests := []struct {
   307  		input  string
   308  		input2 string
   309  		got    map[string]interface{}
   310  		expect map[string]interface{}
   311  		err    bool
   312  	}{
   313  		{
   314  			input: "outer.inner1=value1,outer.inner3=value3,outer.inner4=4",
   315  			got: map[string]interface{}{
   316  				"outer": map[string]interface{}{
   317  					"inner1": "overwrite",
   318  					"inner2": "value2",
   319  				},
   320  			},
   321  			expect: map[string]interface{}{
   322  				"outer": map[string]interface{}{
   323  					"inner1": "value1,outer.inner3=value3,outer.inner4=4",
   324  					"inner2": "value2",
   325  				}},
   326  			err: false,
   327  		},
   328  		{
   329  			input:  "listOuter[0][0].type=listValue",
   330  			input2: "listOuter[0][0].status=alive",
   331  			got:    map[string]interface{}{},
   332  			expect: map[string]interface{}{
   333  				"listOuter": [][]interface{}{{map[string]string{
   334  					"type":   "listValue",
   335  					"status": "alive",
   336  				}}},
   337  			},
   338  			err: false,
   339  		},
   340  		{
   341  			input:  "listOuter[0][0].type=listValue",
   342  			input2: "listOuter[1][0].status=alive",
   343  			got:    map[string]interface{}{},
   344  			expect: map[string]interface{}{
   345  				"listOuter": [][]interface{}{
   346  					{
   347  						map[string]string{"type": "listValue"},
   348  					},
   349  					{
   350  						map[string]string{"status": "alive"},
   351  					},
   352  				},
   353  			},
   354  			err: false,
   355  		},
   356  		{
   357  			input:  "listOuter[0][1][0].type=listValue",
   358  			input2: "listOuter[0][0][1].status=alive",
   359  			got: map[string]interface{}{
   360  				"listOuter": []interface{}{
   361  					[]interface{}{
   362  						[]interface{}{
   363  							map[string]string{"exited": "old"},
   364  						},
   365  					},
   366  				},
   367  			},
   368  			expect: map[string]interface{}{
   369  				"listOuter": [][][]interface{}{
   370  					{
   371  						{
   372  							map[string]string{"exited": "old"},
   373  							map[string]string{"status": "alive"},
   374  						},
   375  						{
   376  							map[string]string{"type": "listValue"},
   377  						},
   378  					},
   379  				},
   380  			},
   381  			err: false,
   382  		},
   383  	}
   384  
   385  	for _, tt := range tests {
   386  		if err := ParseLiteralInto(tt.input, tt.got); err != nil {
   387  			t.Fatal(err)
   388  		}
   389  		if tt.err {
   390  			t.Errorf("%s: Expected error. Got nil", tt.input)
   391  		}
   392  
   393  		if tt.input2 != "" {
   394  			if err := ParseLiteralInto(tt.input2, tt.got); err != nil {
   395  				t.Fatal(err)
   396  			}
   397  			if tt.err {
   398  				t.Errorf("%s: Expected error. Got nil", tt.input2)
   399  			}
   400  		}
   401  
   402  		y1, err := yaml.Marshal(tt.expect)
   403  		if err != nil {
   404  			t.Fatal(err)
   405  		}
   406  
   407  		y2, err := yaml.Marshal(tt.got)
   408  		if err != nil {
   409  			t.Fatalf("Error serializing parsed value: %s", err)
   410  		}
   411  
   412  		if string(y1) != string(y2) {
   413  			t.Errorf("%s: Expected:\n%s\nGot:\n%s", tt.input, y1, y2)
   414  		}
   415  	}
   416  }
   417  
   418  func TestParseLiteralNestedLevels(t *testing.T) {
   419  	var keyMultipleNestedLevels string
   420  
   421  	for i := 1; i <= MaxNestedNameLevel+2; i++ {
   422  		tmpStr := fmt.Sprintf("name%d", i)
   423  		if i <= MaxNestedNameLevel+1 {
   424  			tmpStr = tmpStr + "."
   425  		}
   426  		keyMultipleNestedLevels += tmpStr
   427  	}
   428  
   429  	tests := []struct {
   430  		str    string
   431  		expect map[string]interface{}
   432  		err    bool
   433  		errStr string
   434  	}{
   435  		{
   436  			"outer.middle.inner=value",
   437  			map[string]interface{}{"outer": map[string]interface{}{"middle": map[string]interface{}{"inner": "value"}}},
   438  			false,
   439  			"",
   440  		},
   441  		{
   442  			str:    keyMultipleNestedLevels + "=value",
   443  			err:    true,
   444  			errStr: fmt.Sprintf("value name nested level is greater than maximum supported nested level of %d", MaxNestedNameLevel),
   445  		},
   446  	}
   447  
   448  	for _, tt := range tests {
   449  		got, err := ParseLiteral(tt.str)
   450  		if err != nil {
   451  			if tt.err {
   452  				if tt.errStr != "" {
   453  					if err.Error() != tt.errStr {
   454  						t.Errorf("Expected error: %s. Got error: %s", tt.errStr, err.Error())
   455  					}
   456  				}
   457  				continue
   458  			}
   459  			t.Fatalf("%s: %s", tt.str, err)
   460  		}
   461  
   462  		if tt.err {
   463  			t.Errorf("%s: Expected error. Got nil", tt.str)
   464  		}
   465  
   466  		y1, err := yaml.Marshal(tt.expect)
   467  		if err != nil {
   468  			t.Fatal(err)
   469  		}
   470  
   471  		y2, err := yaml.Marshal(got)
   472  		if err != nil {
   473  			t.Fatalf("Error serializing parsed value: %s", err)
   474  		}
   475  
   476  		if string(y1) != string(y2) {
   477  			t.Errorf("%s: Expected:\n%s\nGot:\n%s", tt.str, y1, y2)
   478  		}
   479  	}
   480  }
   481  

View as plain text