github.com/yandex/pandora@v0.5.32/core/config/config_test.go (about)

     1  package config
     2  
     3  import (
     4  	"net"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/facebookgo/stack"
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  	"github.com/yandex/pandora/lib/confutil"
    12  )
    13  
    14  type M map[string]interface{}
    15  
    16  type IPStruct struct {
    17  	Val string `validate:"ip"`
    18  }
    19  
    20  func TestDecodeValidate(t *testing.T) {
    21  	var data IPStruct
    22  	err := DecodeAndValidate(M{"val": "192.300.200.100"}, &data)
    23  	assert.Error(t, err)
    24  }
    25  
    26  type NoTagStruct struct {
    27  	Val string
    28  }
    29  
    30  const testVal = "test"
    31  
    32  func TestNilInputIsEmptyInput(t *testing.T) {
    33  	require.NotPanics(t, func() {
    34  		var data NoTagStruct
    35  		err := Decode(nil, &data)
    36  		assert.NoError(t, err)
    37  	})
    38  }
    39  
    40  func TestNilResultNotPanic(t *testing.T) {
    41  	require.NotPanics(t, func() {
    42  		err := Decode(M{"val": testVal}, (*NoTagStruct)(nil))
    43  		assert.Error(t, err)
    44  	})
    45  }
    46  
    47  func TestFieldNameDecode(t *testing.T) {
    48  	var data NoTagStruct
    49  	err := Decode(M{"val": testVal}, &data)
    50  	require.NoError(t, err)
    51  	assert.Equal(t, testVal, data.Val)
    52  }
    53  
    54  type TagStruct struct {
    55  	Val string `config:"valAlias"`
    56  }
    57  
    58  func TestTagDecode(t *testing.T) {
    59  	var data TagStruct
    60  	err := Decode(M{"ValAlias": testVal}, &data)
    61  	require.NoError(t, err)
    62  	assert.Equal(t, testVal, data.Val)
    63  }
    64  
    65  func TestErrorUnused(t *testing.T) {
    66  	var data NoTagStruct
    67  	err := Decode(M{"val": testVal, "unused": testVal}, &data)
    68  	require.Error(t, err)
    69  
    70  	err = Decode(M{"vval": testVal}, &data)
    71  	assert.Error(t, err)
    72  }
    73  
    74  func TestNoWeakTypedInput(t *testing.T) {
    75  	var data NoTagStruct
    76  	err := Decode(M{"val": 123}, &data)
    77  	assert.Error(t, err)
    78  }
    79  
    80  type TimeoutStruct struct {
    81  	Timeout time.Duration `validate:"min-time=1s,max-time=20m"`
    82  }
    83  
    84  func TestValidDurationDecode(t *testing.T) {
    85  	var data TimeoutStruct
    86  	expectedDuration := time.Second * 666
    87  
    88  	err := DecodeAndValidate(M{"timeout": "666s"}, &data)
    89  	require.NoError(t, err)
    90  	assert.Equal(t, expectedDuration, data.Timeout)
    91  }
    92  
    93  func TestInvalidDurationError(t *testing.T) {
    94  	var data TimeoutStruct
    95  
    96  	invalidTimeouts := []string{"ssss", "1ss", "1", "1s1", "1.s", "0x50"}
    97  	for _, invalid := range invalidTimeouts {
    98  		err := DecodeAndValidate(M{"timeout": "ssss"}, &data)
    99  		assert.Error(t, err, "invalid case: ", invalid)
   100  	}
   101  }
   102  
   103  type Level1 struct {
   104  	Val1 string
   105  	Val2 string
   106  }
   107  
   108  type Level2 struct {
   109  	Val1 Level1
   110  	Val2 Level1
   111  }
   112  
   113  func TestNestedStructDecode(t *testing.T) {
   114  	const (
   115  		iniVal1 = "val1"
   116  		iniVal2 = "val2"
   117  		newVal  = "newVal"
   118  	)
   119  	l2 := Level2{
   120  		Level1{
   121  			iniVal1,
   122  			iniVal2,
   123  		},
   124  		Level1{
   125  			iniVal1,
   126  			iniVal2,
   127  		},
   128  	}
   129  
   130  	err := DecodeAndValidate(M{
   131  		"val1": M{"val1": newVal},
   132  		"val2": M{"val1": ""},
   133  	}, &l2)
   134  	require.NoError(t, err)
   135  	assert.Equal(t, newVal, l2.Val1.Val1)
   136  	assert.Equal(t, iniVal2, l2.Val1.Val2, "one field in intput, but entire struct rewrited")
   137  	assert.Equal(t, "", l2.Val2.Val1, "zero value not override default")
   138  }
   139  
   140  type MultiStrings struct {
   141  	A string
   142  	B string
   143  }
   144  
   145  type SingleString struct {
   146  	B string
   147  }
   148  
   149  func TestMapFlat(t *testing.T) {
   150  	a := &MultiStrings{}
   151  	Map(a, &SingleString{B: "b"})
   152  	assert.Equal(t, &MultiStrings{B: "b"}, a)
   153  
   154  	a = &MultiStrings{A: "a", B: "not b"}
   155  	Map(a, &SingleString{B: "b"})
   156  	assert.Equal(t, &MultiStrings{A: "a", B: "b"}, a)
   157  }
   158  
   159  func TestMapRecursive(t *testing.T) {
   160  	type N struct {
   161  		MultiStrings
   162  		A string
   163  	}
   164  	type M struct {
   165  		MultiStrings
   166  	}
   167  	n := &N{MultiStrings: MultiStrings{B: "b"}, A: "a"}
   168  	Map(n, &M{MultiStrings: MultiStrings{A: "a"}})
   169  	assert.Equal(t, &N{A: "a", MultiStrings: MultiStrings{A: "a", B: ""}}, n)
   170  }
   171  
   172  func TestMapTagged(t *testing.T) {
   173  	type N struct {
   174  		MultiStrings
   175  		A string
   176  	}
   177  	type M struct {
   178  		SomeOtherFieldName MultiStrings `map:"MultiStrings"`
   179  	}
   180  	n := &N{MultiStrings: MultiStrings{B: "b"}, A: "a"}
   181  	Map(n, &M{SomeOtherFieldName: MultiStrings{A: "a"}})
   182  	assert.Equal(t, &N{A: "a", MultiStrings: MultiStrings{A: "a", B: ""}}, n)
   183  }
   184  func TestDeltaUpdate(t *testing.T) {
   185  	var l2 Level2
   186  	err := Decode(M{
   187  		"val1": M{"val1": "val1", "val2": "val2"},
   188  		"val2": M{"val1": "val3"},
   189  	}, &l2)
   190  	require.NoError(t, err)
   191  	assert.Equal(t, "val1", l2.Val1.Val1)
   192  	assert.Equal(t, "val2", l2.Val1.Val2)
   193  	assert.Equal(t, "val3", l2.Val2.Val1)
   194  	assert.Equal(t, "", l2.Val2.Val2)
   195  
   196  	err = DecodeAndValidate(M{
   197  		"val1": M{"val1": "val4"},
   198  		"val2": M{"val2": "val5"},
   199  	}, &l2)
   200  	require.NoError(t, err)
   201  	assert.Equal(t, "val4", l2.Val1.Val1)
   202  	assert.Equal(t, "val2", l2.Val1.Val2)
   203  	assert.Equal(t, "val3", l2.Val2.Val1)
   204  	assert.Equal(t, "val5", l2.Val2.Val2)
   205  }
   206  
   207  func TestNextSquash(t *testing.T) {
   208  	data := &struct {
   209  		Level1 struct {
   210  			Level2 struct {
   211  				Foo string
   212  			} `config:",squash"`
   213  		} `config:",squash"`
   214  	}{}
   215  
   216  	defer func() {
   217  		r := recover()
   218  		if r == nil {
   219  			return
   220  		}
   221  		t.Fatalf("panic: %s\n %s", r, stack.Callers(3))
   222  	}()
   223  
   224  	err := Decode(M{
   225  		"foo": "baz",
   226  	}, &data)
   227  	require.NoError(t, err)
   228  	assert.Equal(t, "baz", data.Level1.Level2.Foo)
   229  }
   230  
   231  func TestConfigEnvVarReplacement(t *testing.T) {
   232  	confutil.RegisterTagResolver("", confutil.EnvTagResolver)
   233  	confutil.RegisterTagResolver("ENV", confutil.EnvTagResolver)
   234  
   235  	t.Setenv("ENV_VAR_1", "value1")
   236  	t.Setenv("VAR_2", "value2")
   237  	t.Setenv("INT_VAR_3", "15")
   238  	t.Setenv("IP_SEQ", "1.2")
   239  	t.Setenv("DURATION", "30s")
   240  	var l1 struct {
   241  		Val1 string
   242  		Val2 string
   243  		Val3 int
   244  		Val4 net.IP
   245  		Val5 time.Duration
   246  	}
   247  
   248  	err := Decode(M{
   249  		"val1": "aa-${ENV_VAR_1}",
   250  		"val2": "${ENV:VAR_2}",
   251  		"val3": "${INT_VAR_3}",
   252  		"val4": "1.1.${ENV:IP_SEQ}",
   253  		"val5": "${DURATION}",
   254  	}, &l1)
   255  	assert.NoError(t, err)
   256  	assert.Equal(t, "aa-value1", l1.Val1)
   257  	assert.Equal(t, "value2", l1.Val2)
   258  	assert.Equal(t, 15, l1.Val3)
   259  	assert.Equal(t, net.IPv4(1, 1, 1, 2), l1.Val4)
   260  	assert.Equal(t, 30*time.Second, l1.Val5)
   261  }