github.com/abemedia/go-don@v0.2.2-0.20240329015135-be88e32bb73b/decoder/decode_test.go (about)

     1  package decoder_test
     2  
     3  import (
     4  	"errors"
     5  	"reflect"
     6  	"strconv"
     7  	"testing"
     8  
     9  	"github.com/abemedia/go-don/decoder"
    10  	"github.com/google/go-cmp/cmp"
    11  )
    12  
    13  type unmarshaler string
    14  
    15  func (h *unmarshaler) UnmarshalText(b []byte) error {
    16  	*h = unmarshaler(":" + string(b) + ":")
    17  	return nil
    18  }
    19  
    20  func TestDecode(t *testing.T) {
    21  	type child struct {
    22  		String string `field:"string"`
    23  	}
    24  
    25  	type test struct {
    26  		Unmarshaler    unmarshaler  `field:"string"`
    27  		UnmarshalerPtr *unmarshaler `field:"string"`
    28  		String         string       `field:"string"`
    29  		StringPtr      *string      `field:"string"`
    30  		Int            int          `field:"number"`
    31  		Int8           int8         `field:"number"`
    32  		Int16          int16        `field:"number"`
    33  		Int32          int32        `field:"number"`
    34  		Int64          int64        `field:"number"`
    35  		Uint           uint         `field:"number"`
    36  		Uint8          uint8        `field:"number"`
    37  		Uint16         uint16       `field:"number"`
    38  		Uint32         uint32       `field:"number"`
    39  		Uint64         uint64       `field:"number"`
    40  		Float32        float32      `field:"number"`
    41  		Float64        float64      `field:"number"`
    42  		Bool           bool         `field:"bool"`
    43  		Bytes          []byte       `field:"string"`
    44  		Strings        []string     `field:"strings"`
    45  		Nested         child
    46  		NestedPtr      *child
    47  		unexported     string `field:"string"` //nolint:unused
    48  	}
    49  
    50  	in := decoder.Map{
    51  		"string":  {"string"},
    52  		"strings": {"string", "string"},
    53  		"number":  {"1"},
    54  		"bool":    {"true"},
    55  	}
    56  
    57  	s := "string"
    58  	u := unmarshaler(":string:")
    59  	expected := &test{
    60  		Unmarshaler:    ":string:",
    61  		UnmarshalerPtr: &u,
    62  		String:         "string",
    63  		StringPtr:      &s,
    64  		Int:            1,
    65  		Int8:           1,
    66  		Int16:          1,
    67  		Int32:          1,
    68  		Int64:          1,
    69  		Uint:           1,
    70  		Uint8:          1,
    71  		Uint16:         1,
    72  		Uint32:         1,
    73  		Uint64:         1,
    74  		Float32:        1,
    75  		Float64:        1,
    76  		Bool:           true,
    77  		Bytes:          []byte("string"),
    78  		Strings:        []string{"string", "string"},
    79  		Nested: child{
    80  			String: "string",
    81  		},
    82  		NestedPtr: &child{
    83  			String: "string",
    84  		},
    85  	}
    86  
    87  	exportAll := cmp.Exporter(func(t reflect.Type) bool { return true })
    88  
    89  	t.Run("Decoder", func(t *testing.T) {
    90  		dec := decoder.New("field")
    91  		actual := &test{}
    92  		if err := dec.Decode(in, actual); err != nil {
    93  			t.Fatal(err)
    94  		}
    95  		if diff := cmp.Diff(expected, actual, exportAll); diff != "" {
    96  			t.Errorf(diff)
    97  		}
    98  	})
    99  
   100  	t.Run("CachedDecoder", func(t *testing.T) {
   101  		dec, err := decoder.NewCached(test{}, "field")
   102  		if err != nil {
   103  			t.Fatal(err)
   104  		}
   105  
   106  		actual := &test{}
   107  		if err := dec.Decode(in, actual); err != nil {
   108  			t.Fatal(err)
   109  		}
   110  		if diff := cmp.Diff(expected, actual, exportAll); diff != "" {
   111  			t.Errorf(diff)
   112  		}
   113  
   114  		actual = &test{}
   115  		val := reflect.ValueOf(actual).Elem()
   116  		if err = dec.DecodeValue(in, val); err != nil {
   117  			t.Fatal(err)
   118  		}
   119  		if diff := cmp.Diff(expected, actual, exportAll); diff != "" {
   120  			t.Errorf(diff)
   121  		}
   122  	})
   123  
   124  	t.Run("CachedDecoder_NilPointer", func(t *testing.T) {
   125  		dec, err := decoder.NewCached(&test{}, "field")
   126  		if err != nil {
   127  			t.Fatal(err)
   128  		}
   129  
   130  		var actual *test
   131  		if err := dec.Decode(in, &actual); err != nil {
   132  			t.Fatal(err)
   133  		}
   134  		if diff := cmp.Diff(expected, actual, exportAll); diff != "" {
   135  			t.Errorf(diff)
   136  		}
   137  	})
   138  }
   139  
   140  func TestDecodeError(t *testing.T) {
   141  	data := decoder.Map{"test": {"test"}}
   142  
   143  	tests := []struct {
   144  		target any
   145  		error  error
   146  	}{
   147  		{
   148  			target: nil,
   149  			error:  decoder.ErrUnsupportedType,
   150  		},
   151  		{
   152  			target: "",
   153  			error:  decoder.ErrUnsupportedType,
   154  		},
   155  		{
   156  			target: new(string),
   157  			error:  decoder.ErrUnsupportedType,
   158  		},
   159  		{
   160  			target: new(int),
   161  			error:  decoder.ErrUnsupportedType,
   162  		},
   163  		{
   164  			target: &struct {
   165  				Test string `json:"test"`
   166  			}{},
   167  			error: decoder.ErrTagNotFound,
   168  		},
   169  		{
   170  			target: &struct {
   171  				Test chan string `field:"test"`
   172  			}{},
   173  			error: decoder.ErrUnsupportedType,
   174  		},
   175  		{
   176  			target: &struct {
   177  				Test  string `field:"test"`
   178  				Child struct {
   179  					Test chan string `field:"test"`
   180  				}
   181  			}{},
   182  			error: decoder.ErrUnsupportedType,
   183  		},
   184  		{
   185  			target: &struct {
   186  				Test int `field:"test"`
   187  			}{},
   188  			error: strconv.ErrSyntax,
   189  		},
   190  		{
   191  			target: &struct {
   192  				Test uint `field:"test"`
   193  			}{},
   194  			error: strconv.ErrSyntax,
   195  		},
   196  		{
   197  			target: &struct {
   198  				Test float64 `field:"test"`
   199  			}{},
   200  			error: strconv.ErrSyntax,
   201  		},
   202  		{
   203  			target: &struct {
   204  				Test bool `field:"test"`
   205  			}{},
   206  			error: strconv.ErrSyntax,
   207  		},
   208  	}
   209  
   210  	t.Run("Decoder", func(t *testing.T) {
   211  		for _, test := range tests {
   212  			dec := decoder.New("field")
   213  			err := dec.Decode(data, test.target)
   214  			if errors.Is(test.error, decoder.ErrTagNotFound) {
   215  				if err != nil {
   216  					t.Errorf("should silently ignore error %q for %T", test.error, test.target)
   217  				}
   218  			} else {
   219  				if !errors.Is(err, test.error) {
   220  					t.Errorf("should return %q for %T: %q", test.error, test.target, err)
   221  				}
   222  			}
   223  		}
   224  	})
   225  
   226  	t.Run("CachedDecoder", func(t *testing.T) {
   227  		for _, test := range tests {
   228  			dec, err := decoder.NewCached(test.target, "field")
   229  			if err != nil {
   230  				if !errors.Is(err, test.error) {
   231  					t.Errorf("should return %q for %T: %q", test.error, test.target, err)
   232  				}
   233  				continue
   234  			}
   235  			err = dec.Decode(data, &test.target)
   236  			if !errors.Is(err, test.error) {
   237  				t.Errorf("should return %q for %T: %q", test.error, test.target, err)
   238  			}
   239  		}
   240  	})
   241  }