go.uber.org/yarpc@v1.72.1/internal/config/attributemap_test.go (about)

     1  // Copyright (c) 2022 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package config
    22  
    23  import (
    24  	"testing"
    25  
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/stretchr/testify/require"
    28  )
    29  
    30  func TestAttributeMapPopBool(t *testing.T) {
    31  	tests := []struct {
    32  		desc     string
    33  		give     AttributeMap
    34  		giveName string
    35  
    36  		want       bool
    37  		wantErrors []string
    38  	}{
    39  		{
    40  			desc: "true decode",
    41  			give: AttributeMap{
    42  				"true": true,
    43  			},
    44  			giveName: "true",
    45  			want:     true,
    46  		},
    47  		{
    48  			desc: "false decode",
    49  			give: AttributeMap{
    50  				"false": false,
    51  			},
    52  			giveName: "false",
    53  			want:     false,
    54  		},
    55  		{
    56  			desc: "invalid decode",
    57  			give: AttributeMap{
    58  				"test": "teeeeeest",
    59  			},
    60  			giveName:   "test",
    61  			wantErrors: []string{"failed to read attribute", `"test"`, `teeeeeest`},
    62  		},
    63  		{
    64  			desc: "named string decode",
    65  			give: AttributeMap{
    66  				"test": "true",
    67  			},
    68  			giveName: "test",
    69  			want:     true,
    70  		},
    71  		{
    72  			desc: "named string decode false",
    73  			give: AttributeMap{
    74  				"test": "false",
    75  			},
    76  			giveName: "test",
    77  			want:     false,
    78  		},
    79  		{
    80  			desc:     "missing decode",
    81  			give:     AttributeMap{},
    82  			giveName: "test",
    83  			want:     false,
    84  		},
    85  	}
    86  
    87  	for _, tt := range tests {
    88  		t.Run(tt.desc, func(t *testing.T) {
    89  			res, err := tt.give.PopBool(tt.giveName)
    90  
    91  			if len(tt.wantErrors) > 0 {
    92  				require.Error(t, err)
    93  				for _, msg := range tt.wantErrors {
    94  					assert.Contains(t, err.Error(), msg)
    95  				}
    96  				return
    97  			}
    98  			require.NoError(t, err)
    99  
   100  			assert.Equal(t, tt.want, res)
   101  		})
   102  	}
   103  }
   104  
   105  func TestAttributeMapPopString(t *testing.T) {
   106  	tests := []struct {
   107  		desc     string
   108  		give     AttributeMap
   109  		giveName string
   110  
   111  		want       string
   112  		wantErrors []string
   113  	}{
   114  		{
   115  			desc: "value decode",
   116  			give: AttributeMap{
   117  				"test": "value",
   118  			},
   119  			giveName: "test",
   120  			want:     "value",
   121  		},
   122  		{
   123  			desc: "invalid decode",
   124  			give: AttributeMap{
   125  				"test": []byte("value"),
   126  			},
   127  			giveName:   "test",
   128  			wantErrors: []string{`failed to read attribute "test"`},
   129  		},
   130  		{
   131  			desc:     "invalid name",
   132  			give:     AttributeMap{},
   133  			giveName: "test",
   134  			want:     "", // we default to the Nil value
   135  		},
   136  	}
   137  
   138  	for _, tt := range tests {
   139  		t.Run(tt.desc, func(t *testing.T) {
   140  			res, err := tt.give.PopString(tt.giveName)
   141  
   142  			if len(tt.wantErrors) > 0 {
   143  				require.Error(t, err)
   144  				for _, msg := range tt.wantErrors {
   145  					assert.Contains(t, err.Error(), msg)
   146  				}
   147  				return
   148  			}
   149  			require.NoError(t, err)
   150  
   151  			assert.Equal(t, tt.want, res)
   152  		})
   153  	}
   154  }
   155  
   156  func TestAttributeMapPop(t *testing.T) {
   157  	type someStruct struct {
   158  		SomeString    string `config:",interpolate"`
   159  		AnotherString string
   160  	}
   161  
   162  	tests := []struct {
   163  		desc     string
   164  		give     AttributeMap
   165  		giveName string
   166  		giveInto interface{}
   167  
   168  		env map[string]string
   169  
   170  		want        interface{}
   171  		wantMissing bool
   172  		wantErrors  []string
   173  	}{
   174  		{
   175  			desc: "bad string",
   176  			give: AttributeMap{
   177  				"test": map[string]interface{}{
   178  					"someString": "hello ${NAME:world",
   179  				},
   180  			},
   181  			giveInto: &someStruct{},
   182  			giveName: "test",
   183  			wantErrors: []string{
   184  				`failed to read attribute "test"`,
   185  				`failed to parse "hello ${NAME:world" for interpolation`,
   186  			},
   187  		},
   188  		{
   189  			desc: "bad string uninterpolated field",
   190  			give: AttributeMap{
   191  				"test": map[string]interface{}{
   192  					"anotherString": "hello ${NAME:world",
   193  				},
   194  			},
   195  			giveInto: &someStruct{},
   196  			giveName: "test",
   197  			want:     &someStruct{AnotherString: "hello ${NAME:world"},
   198  		},
   199  		{
   200  			desc: "string interpolation",
   201  			give: AttributeMap{
   202  				"test": map[string]interface{}{
   203  					"someString": "hello ${NAME:world}",
   204  				},
   205  			},
   206  			giveInto: &someStruct{},
   207  			giveName: "test",
   208  			env:      map[string]string{"NAME": "foo"},
   209  			want:     &someStruct{SomeString: "hello foo"},
   210  		},
   211  		{
   212  			desc:        "missing field",
   213  			give:        AttributeMap{},
   214  			giveInto:    &someStruct{},
   215  			giveName:    "test",
   216  			want:        &someStruct{},
   217  			wantMissing: true,
   218  		},
   219  	}
   220  
   221  	for _, tt := range tests {
   222  		t.Run(tt.desc, func(t *testing.T) {
   223  			ok, err := tt.give.Pop(tt.giveName, tt.giveInto, InterpolateWith(mapVariableResolver(tt.env)))
   224  
   225  			if len(tt.wantErrors) > 0 {
   226  				require.Error(t, err)
   227  				for _, msg := range tt.wantErrors {
   228  					assert.Contains(t, err.Error(), msg)
   229  				}
   230  				return
   231  			}
   232  			require.NoError(t, err)
   233  
   234  			assert.Equal(t, tt.wantMissing, !ok)
   235  			assert.Equal(t, tt.want, tt.giveInto)
   236  		})
   237  	}
   238  }
   239  
   240  func TestAttributeMapKeys(t *testing.T) {
   241  	tests := []struct {
   242  		desc string
   243  		give AttributeMap
   244  
   245  		wantKeys []string
   246  	}{
   247  		{
   248  			desc:     "no keys",
   249  			give:     AttributeMap{},
   250  			wantKeys: []string{},
   251  		},
   252  		{
   253  			desc: "one key",
   254  			give: AttributeMap{
   255  				"test": map[string]interface{}{
   256  					"anotherString": "hello ${NAME:world",
   257  				},
   258  			},
   259  			wantKeys: []string{"test"},
   260  		},
   261  		{
   262  			desc: "multi keys",
   263  			give: AttributeMap{
   264  				"test": map[string]interface{}{
   265  					"anotherString": "hello ${NAME:world",
   266  				},
   267  				"test1": true,
   268  				"test2": "teeeeest",
   269  				"test3": 1234,
   270  			},
   271  			wantKeys: []string{"test", "test1", "test2", "test3"},
   272  		},
   273  	}
   274  
   275  	for _, tt := range tests {
   276  		t.Run(tt.desc, func(t *testing.T) {
   277  			keys := tt.give.Keys()
   278  
   279  			require.Equal(t, len(tt.wantKeys), len(keys))
   280  
   281  			keyMap := make(map[string]struct{}, len(tt.wantKeys))
   282  			for _, key := range keys {
   283  				keyMap[key] = struct{}{}
   284  			}
   285  
   286  			for _, wantKey := range tt.wantKeys {
   287  				_, ok := keyMap[wantKey]
   288  				assert.True(t, ok, "key %q was not in map", wantKey)
   289  			}
   290  		})
   291  	}
   292  }
   293  
   294  func TestAttributeMapDecode(t *testing.T) {
   295  	type someStruct struct {
   296  		SomeString    string `config:",interpolate"`
   297  		AnotherString string
   298  	}
   299  
   300  	tests := []struct {
   301  		desc     string
   302  		give     AttributeMap
   303  		giveInto interface{}
   304  
   305  		env map[string]string
   306  
   307  		want       interface{}
   308  		wantErrors []string
   309  	}{
   310  		{
   311  			desc: "bad string",
   312  			give: AttributeMap{
   313  				"someString": "hello ${NAME:world",
   314  			},
   315  			giveInto: &someStruct{},
   316  			wantErrors: []string{
   317  				`failed to parse "hello ${NAME:world" for interpolation`,
   318  			},
   319  		},
   320  		{
   321  			desc: "bad string uninterpolated field",
   322  			give: AttributeMap{
   323  				"anotherString": "hello ${NAME:world",
   324  			},
   325  			giveInto: &someStruct{},
   326  			want:     &someStruct{AnotherString: "hello ${NAME:world"},
   327  		},
   328  		{
   329  			desc: "string interpolation",
   330  			give: AttributeMap{
   331  				"someString": "hello ${NAME:world}",
   332  			},
   333  			giveInto: &someStruct{},
   334  			env:      map[string]string{"NAME": "foo"},
   335  			want:     &someStruct{SomeString: "hello foo"},
   336  		},
   337  	}
   338  
   339  	for _, tt := range tests {
   340  		t.Run(tt.desc, func(t *testing.T) {
   341  			err := tt.give.Decode(tt.giveInto, InterpolateWith(mapVariableResolver(tt.env)))
   342  
   343  			if len(tt.wantErrors) > 0 {
   344  				require.Error(t, err)
   345  				for _, msg := range tt.wantErrors {
   346  					assert.Contains(t, err.Error(), msg)
   347  				}
   348  				return
   349  			}
   350  			require.NoError(t, err)
   351  
   352  			assert.Equal(t, tt.want, tt.giveInto)
   353  		})
   354  	}
   355  }