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 }