github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/config/types_test.go (about)

     1  package config_test
     2  
     3  import (
     4  	"errors"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/go-test/deep"
     9  	"github.com/mitchellh/mapstructure"
    10  	"github.com/treeverse/lakefs/pkg/config"
    11  	"github.com/treeverse/lakefs/pkg/testutil"
    12  )
    13  
    14  type StringsStruct struct {
    15  	S config.Strings
    16  	I int
    17  }
    18  
    19  func TestStrings(t *testing.T) {
    20  	cases := []struct {
    21  		Name     string
    22  		Source   map[string]interface{}
    23  		Expected StringsStruct
    24  	}{
    25  		{
    26  			Name: "single string",
    27  			Source: map[string]interface{}{
    28  				"s": "value",
    29  			},
    30  			Expected: StringsStruct{
    31  				S: config.Strings{"value"},
    32  			},
    33  		}, {
    34  			Name: "comma-separated string",
    35  			Source: map[string]interface{}{
    36  				"s": "the,quick,brown",
    37  			},
    38  			Expected: StringsStruct{
    39  				S: config.Strings{"the", "quick", "brown"},
    40  			},
    41  		}, {
    42  			Name: "multiple strings",
    43  			Source: map[string]interface{}{
    44  				"s": []string{"the", "quick", "brown"},
    45  			},
    46  			Expected: StringsStruct{
    47  				S: config.Strings{"the", "quick", "brown"},
    48  			},
    49  		}, {
    50  			Name: "other values",
    51  			Source: map[string]interface{}{
    52  				"s": []string{"yes"},
    53  				"i": 17,
    54  			},
    55  			Expected: StringsStruct{
    56  				S: config.Strings{"yes"},
    57  				I: 17,
    58  			},
    59  		},
    60  	}
    61  	for _, c := range cases {
    62  		t.Run(c.Name, func(t *testing.T) {
    63  			var s StringsStruct
    64  
    65  			dc := mapstructure.DecoderConfig{
    66  				DecodeHook: config.DecodeStrings,
    67  				Result:     &s,
    68  			}
    69  			decoder, err := mapstructure.NewDecoder(&dc)
    70  			testutil.MustDo(t, "new decoder", err)
    71  			err = decoder.Decode(c.Source)
    72  			testutil.MustDo(t, "decode", err)
    73  			if diffs := deep.Equal(s, c.Expected); diffs != nil {
    74  				t.Error(diffs)
    75  			}
    76  		})
    77  	}
    78  }
    79  
    80  type OnlyStringStruct struct {
    81  	S config.OnlyString
    82  }
    83  
    84  // errorsMatch returns true if errors.Is(err, target), or if the error message of err
    85  // contains the error message of target as a substring.  It is needed until
    86  // mapstructure wraps errors returned from string functions (see PR
    87  // mitchellh/mapstructure#282).
    88  func errorsMatch(err, target error) bool {
    89  	return errors.Is(err, target) ||
    90  		target != nil && err != nil && strings.Contains(err.Error(), target.Error())
    91  }
    92  
    93  func TestOnlyString(t *testing.T) {
    94  	cases := []struct {
    95  		Name     string
    96  		Source   map[string]interface{}
    97  		Expected *OnlyStringStruct
    98  		Err      error
    99  	}{
   100  		{
   101  			Name:     "String",
   102  			Source:   map[string]interface{}{"S": "string"},
   103  			Expected: &OnlyStringStruct{S: "string"},
   104  		}, {
   105  			Name:   "Number",
   106  			Source: map[string]interface{}{"S": 17},
   107  			Err:    config.ErrMustBeString,
   108  		}, {
   109  			Name:   "Struct",
   110  			Source: map[string]interface{}{"S": OnlyStringStruct{S: "string nested in struct"}},
   111  			Err:    config.ErrMustBeString,
   112  		},
   113  	}
   114  	for _, c := range cases {
   115  		t.Run(c.Name, func(t *testing.T) {
   116  			var o OnlyStringStruct
   117  
   118  			dc := mapstructure.DecoderConfig{
   119  				DecodeHook: config.DecodeOnlyString,
   120  				Result:     &o,
   121  			}
   122  			decoder, err := mapstructure.NewDecoder(&dc)
   123  			testutil.MustDo(t, "new decoder", err)
   124  			err = decoder.Decode(c.Source)
   125  			if c.Err != nil && !errorsMatch(err, c.Err) {
   126  				t.Errorf("Got value %+v, error %v when expecting error %v", o, err, c.Err)
   127  			}
   128  			if c.Err == nil && err != nil {
   129  				t.Errorf("Got error %v when expecting to succeed", err)
   130  			}
   131  			if c.Expected != nil {
   132  				if diffs := deep.Equal(o, *c.Expected); diffs != nil {
   133  					t.Errorf("Got unexpected value: %s", diffs)
   134  				}
   135  			}
   136  		})
   137  	}
   138  }