github.com/cilium/cilium@v1.16.2/pkg/command/map_string_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package command
     5  
     6  import (
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/spf13/viper"
    11  	"github.com/stretchr/testify/assert"
    12  )
    13  
    14  func TestGetStringMapString(t *testing.T) {
    15  	expectedResult := map[string]string{
    16  		"k1": "v1",
    17  		"k2": "v2",
    18  	}
    19  	type args struct {
    20  		key   string
    21  		value string
    22  	}
    23  	tests := []struct {
    24  		name    string
    25  		args    args
    26  		want    map[string]string
    27  		wantErr assert.ErrorAssertionFunc
    28  	}{
    29  		{
    30  			name: "valid json format",
    31  			args: args{
    32  				key:   "FOO_BAR",
    33  				value: `{"k1":"v1","k2":"v2"}`,
    34  			},
    35  			want:    expectedResult,
    36  			wantErr: assert.NoError,
    37  		},
    38  		{
    39  			name: "valid empty json",
    40  			args: args{
    41  				key:   "FOO_BAR",
    42  				value: "{}",
    43  			},
    44  			want:    map[string]string{},
    45  			wantErr: assert.NoError,
    46  		},
    47  		{
    48  			name: "invalid json format with extra comma at the end",
    49  			args: args{
    50  				key:   "FOO_BAR",
    51  				value: `{"k1":"v1","k2":"v2",}`,
    52  			},
    53  			want:    map[string]string{},
    54  			wantErr: assertErrorString("invalid character '}' looking for beginning of object key string"),
    55  		},
    56  		{
    57  			name: "valid single kv format",
    58  			args: args{
    59  				key:   "FOO_BAR",
    60  				value: "k1=v1",
    61  			},
    62  			want:    map[string]string{"k1": "v1"},
    63  			wantErr: assert.NoError,
    64  		},
    65  		{
    66  			name: "valid kv format",
    67  			args: args{
    68  				key:   "FOO_BAR",
    69  				value: "k1=v1,k2=v2",
    70  			},
    71  			want:    expectedResult,
    72  			wantErr: assert.NoError,
    73  		},
    74  		{
    75  			name: "valid kv format with @",
    76  			args: args{
    77  				key:   "FOO_BAR",
    78  				value: "k1=v1,k2=test@test.com",
    79  			},
    80  			want: map[string]string{
    81  				"k1": "v1",
    82  				"k2": "test@test.com",
    83  			},
    84  			wantErr: assert.NoError,
    85  		},
    86  		{
    87  			name: "valid kv format with empty value",
    88  			args: args{
    89  				key:   "FOO_BAR",
    90  				value: "k1=,k2=v2",
    91  			},
    92  			want: map[string]string{
    93  				"k1": "",
    94  				"k2": "v2",
    95  			},
    96  			wantErr: assert.NoError,
    97  		},
    98  		{
    99  			name: "valid kv format with a single key and commas in value",
   100  			args: args{
   101  				key:   "API_RATE_LIMIT",
   102  				value: "endpoint-create=rate-limit:10/s,rate-burst:10,parallel-requests:10,auto-adjust:true",
   103  			},
   104  			want: map[string]string{
   105  				"endpoint-create": "rate-limit:10/s,rate-burst:10,parallel-requests:10,auto-adjust:true",
   106  			},
   107  			wantErr: assert.NoError,
   108  		},
   109  
   110  		{
   111  			name: "valid kv format with multiple keys with commas in value",
   112  			args: args{
   113  				key:   "API_RATE_LIMIT",
   114  				value: "endpoint-create=rate-limit:10/s,rate-burst:10,parallel-requests:10,auto-adjust:true,endpoint-delete=rate-limit:10/s,rate-burst:10,parallel-requests:10,auto-adjust:true",
   115  			},
   116  			want: map[string]string{
   117  				"endpoint-create": "rate-limit:10/s,rate-burst:10,parallel-requests:10,auto-adjust:true",
   118  				"endpoint-delete": "rate-limit:10/s,rate-burst:10,parallel-requests:10,auto-adjust:true",
   119  			},
   120  			wantErr: assert.NoError,
   121  		},
   122  		{
   123  			name: "another valid kv format with comma in value",
   124  			args: args{
   125  				key:   "AWS_INSTANCE_LIMIT_MAPPING",
   126  				value: "c6a.2xlarge=4,15,15,m4.large=1,5,10",
   127  			},
   128  			want: map[string]string{
   129  				"c6a.2xlarge": "4,15,15",
   130  				"m4.large":    "1,5,10",
   131  			},
   132  			wantErr: assert.NoError,
   133  		},
   134  		{
   135  			name: "valid kv format with forward slash",
   136  			args: args{
   137  				key:   "FOO_BAR",
   138  				value: "kubernetes.io/cluster/piano-eks-general-blue-01=owned,kubernetes.io/role/internal-elb=1",
   139  			},
   140  			want: map[string]string{
   141  				"kubernetes.io/cluster/piano-eks-general-blue-01": "owned",
   142  				"kubernetes.io/role/internal-elb":                 "1",
   143  			},
   144  			wantErr: assert.NoError,
   145  		},
   146  		{
   147  			name: "valid kv format with hyphens",
   148  			args: args{
   149  				key:   "FOO_BAR",
   150  				value: "cluster=my-cluster",
   151  			},
   152  			want: map[string]string{
   153  				"cluster": "my-cluster",
   154  			},
   155  			wantErr: assert.NoError,
   156  		},
   157  		{
   158  			name: "valid kv format with space",
   159  			args: args{
   160  				key:   "FOO_BAR",
   161  				value: "cluster=my cluster",
   162  			},
   163  			want: map[string]string{
   164  				"cluster": "my cluster",
   165  			},
   166  			wantErr: assert.NoError,
   167  		},
   168  		{
   169  			name: "valid kv format from issue #20666",
   170  			args: args{
   171  				key:   "FOO_BAR",
   172  				value: "a=b,c=d,E=F,G=h",
   173  			},
   174  			want: map[string]string{
   175  				"a": "b",
   176  				"c": "d",
   177  				"E": "F",
   178  				"G": "h",
   179  			},
   180  			wantErr: assert.NoError,
   181  		},
   182  		{
   183  			name: "valid kv format for cluster-pool-map",
   184  			args: args{
   185  				key:   "CLUSTER_POOL_MAP",
   186  				value: "mars=ipv4-cidrs:172.16.0.0/16,172.17.0.0/16;ipv4-mask-size:24,jupiter=ipv4-cidrs:192.168.0.0/19;ipv4-mask-size:26",
   187  			},
   188  			want: map[string]string{
   189  				"mars":    "ipv4-cidrs:172.16.0.0/16,172.17.0.0/16;ipv4-mask-size:24",
   190  				"jupiter": "ipv4-cidrs:192.168.0.0/19;ipv4-mask-size:26",
   191  			},
   192  			wantErr: assert.NoError,
   193  		},
   194  		{
   195  			name: "invalid kv format with extra comma",
   196  			args: args{
   197  				key:   "FOO_BAR",
   198  				value: "k1=v1,k2=v2,",
   199  			},
   200  			want:    map[string]string{},
   201  			wantErr: assertErrorString("'k1=v1,k2=v2,' is not formatted as key=value,key1=value1"),
   202  		},
   203  		{
   204  			name: "invalid kv format with extra equal",
   205  			args: args{
   206  				key:   "FOO_BAR",
   207  				value: "k1=v1,k2==v2",
   208  			},
   209  			want:    map[string]string{},
   210  			wantErr: assertErrorString("'k1=v1,k2==v2' is not formatted as key=value,key1=value1"),
   211  		},
   212  		{
   213  			name: "invalid kv format with wrong space in between",
   214  			args: args{
   215  				key:   "FOO_BAR",
   216  				value: "k1=v1, k2=v2",
   217  			},
   218  			want:    map[string]string{},
   219  			wantErr: assertErrorString("'k1=v1, k2=v2' is not formatted as key=value,key1=value1"),
   220  		},
   221  		{
   222  			name: "malformed json format",
   223  			args: args{
   224  				key:   "FOO_BAR",
   225  				value: `{"k1": "v1",=sdlkfj`,
   226  			},
   227  			want:    map[string]string{},
   228  			wantErr: assertErrorString("invalid character '=' looking for beginning of object key string"),
   229  		},
   230  		{
   231  			name: "staring with valid json beginning value e.g. t, f, n, 0, -, \"",
   232  			args: args{
   233  				key:   "FOO_BAR",
   234  				value: "this is a sentence used in test",
   235  			},
   236  			want:    map[string]string{},
   237  			wantErr: assertErrorString("'this is a sentence used in test' is not formatted as key=value,key1=value1"),
   238  		},
   239  	}
   240  
   241  	for _, tt := range tests {
   242  		t.Run(tt.name, func(t *testing.T) {
   243  			vp := viper.New()
   244  			vp.AutomaticEnv()
   245  			t.Setenv(strings.ToUpper(tt.args.key), tt.args.value)
   246  			v, err := GetStringMapStringE(vp, strings.ToLower(tt.args.key))
   247  			tt.wantErr(t, err)
   248  			assert.Equal(t, tt.want, v)
   249  		})
   250  	}
   251  }
   252  
   253  func TestGetStringMapStringConversion(t *testing.T) {
   254  	vp := viper.New()
   255  	vp.Set("foo_bar", struct{}{})
   256  	v, err := GetStringMapStringE(vp, "foo_bar")
   257  	assert.Error(t, err)
   258  	assert.Contains(t, err.Error(), "unable to cast struct {}{} of type struct {} to map[string]string")
   259  	assert.Equal(t, map[string]string{}, v)
   260  }
   261  
   262  func Test_isValidKeyValuePair(t *testing.T) {
   263  	type args struct {
   264  		str string
   265  	}
   266  	tests := []struct {
   267  		name string
   268  		args args
   269  		want bool
   270  	}{
   271  		{
   272  			name: "valid format with one pair",
   273  			args: args{
   274  				str: "k1=v1",
   275  			},
   276  			want: true,
   277  		},
   278  		{
   279  			name: "valid format with hyphen in k and v",
   280  			args: args{
   281  				str: "k-1=v-1,k-2=v-2",
   282  			},
   283  			want: true,
   284  		},
   285  		{
   286  			name: "valid format with multiple hyphens",
   287  			args: args{
   288  				str: "Cluster=piano-eks-general-blue-01",
   289  			},
   290  			want: true,
   291  		},
   292  		{
   293  			name: "valid format with colon",
   294  			args: args{
   295  				str: "consul.address=127.0.0.1:8500",
   296  			},
   297  			want: true,
   298  		},
   299  		{
   300  			name: "valid format with forward slash",
   301  			args: args{
   302  				str: "kubernetes.io/cluster/piano-eks-general-blue-01=owned",
   303  			},
   304  			want: true,
   305  		},
   306  		{
   307  			name: "valid format with multiple pairs",
   308  			args: args{
   309  				str: "k1=v1,k2=v2,k3=v3,k4=v4,k4=v4,k4=v4",
   310  			},
   311  			want: true,
   312  		},
   313  		{
   314  			name: "valid format with multiple pairs with commas",
   315  			args: args{
   316  				str: "k1=v,1,k2=v2,,k3=,v3,k4=v,4,k4=v4,k4=v,4",
   317  			},
   318  			want: true,
   319  		},
   320  		{
   321  			name: "empty value",
   322  			args: args{
   323  				str: "",
   324  			},
   325  			want: true,
   326  		},
   327  		{
   328  			name: "space in between",
   329  			args: args{
   330  				str: "k1=v1, k2=v2",
   331  			},
   332  			want: false,
   333  		},
   334  		{
   335  			name: "insufficient value",
   336  			args: args{
   337  				str: "k1=v1,k2,=v2",
   338  			},
   339  			want: false,
   340  		},
   341  		{
   342  			name: "no pair at all",
   343  			args: args{
   344  				str: "here-is-the-test",
   345  			},
   346  			want: false,
   347  		},
   348  		{
   349  			name: "ending with comma",
   350  			args: args{
   351  				str: "k1=v1,k2=v2,",
   352  			},
   353  			want: false,
   354  		},
   355  		{
   356  			name: "ending with equal",
   357  			args: args{
   358  				str: "k1=v1,k2=v2=",
   359  			},
   360  			want: false,
   361  		},
   362  		{
   363  			name: "kv separator as space",
   364  			args: args{
   365  				str: "k1=v1 k2=v2=",
   366  			},
   367  			want: false,
   368  		},
   369  		{
   370  			name: "space in key",
   371  			args: args{
   372  				str: "k1=v1, k2=v2",
   373  			},
   374  			want: false,
   375  		},
   376  		{
   377  			name: "value starts with space",
   378  			args: args{
   379  				str: "k1= v1,k2=v2",
   380  			},
   381  			want: false,
   382  		},
   383  		{
   384  			name: "last value starts with space",
   385  			args: args{
   386  				str: "k1=v1,k2= v2",
   387  			},
   388  			want: false,
   389  		},
   390  		{
   391  			name: "value ends with space",
   392  			args: args{
   393  				str: "k1=v1 ,k2=v2",
   394  			},
   395  			want: false,
   396  		},
   397  		{
   398  			name: "last value ends with space",
   399  			args: args{
   400  				str: "k1=v1,k2=v2 ",
   401  			},
   402  			want: false,
   403  		},
   404  	}
   405  	for _, tt := range tests {
   406  		t.Run(tt.name, func(t *testing.T) {
   407  			assert.Equalf(t, tt.want, isValidKeyValuePair(tt.args.str), "isValidKeyValuePair(%v)", tt.args.str)
   408  		})
   409  	}
   410  }
   411  
   412  func assertErrorString(errString string) assert.ErrorAssertionFunc {
   413  	return func(t assert.TestingT, err error, msgAndArgs ...interface{}) bool {
   414  		return assert.EqualError(t, err, errString, msgAndArgs)
   415  	}
   416  }