1 package jreader
2
3 import (
4 "errors"
5 "fmt"
6 "testing"
7
8 "github.com/launchdarkly/go-jsonstream/v3/internal/commontest"
9
10 "github.com/stretchr/testify/require"
11 )
12
13
14
15
16
17 type readerTestContext struct {
18 input []byte
19 r *Reader
20 }
21
22 type readerValueTestFactory struct{}
23 type readerErrorTestFactory struct{}
24
25
26
27
28
29
30
31
32
33 const (
34 defaultVariant commontest.ValueVariant = ""
35 nullableValue commontest.ValueVariant = "nullable"
36 numberAsInt commontest.ValueVariant = "int"
37 nullableNumberAsInt commontest.ValueVariant = "nullable int"
38 nullableBoolIsNull commontest.ValueVariant = "nullable bool is"
39 nullableIntIsNull commontest.ValueVariant = "nullable int is"
40 nullableFloatIsNull commontest.ValueVariant = "nullable float is"
41 nullableStringIsNull commontest.ValueVariant = "nullable string is"
42 nullableArrayIsNull commontest.ValueVariant = "nullable array is"
43 nullableObjectIsNull commontest.ValueVariant = "nullable object is"
44 )
45
46 var variantsForNullValues = []commontest.ValueVariant{defaultVariant, nullableBoolIsNull, nullableIntIsNull,
47 nullableFloatIsNull, nullableStringIsNull, nullableArrayIsNull, nullableObjectIsNull,
48 commontest.UntypedVariant, commontest.SkipValueVariant}
49 var variantsForInts = []commontest.ValueVariant{defaultVariant, numberAsInt, nullableValue, nullableNumberAsInt,
50 commontest.UntypedVariant, commontest.SkipValueVariant}
51 var variantsForFloats = []commontest.ValueVariant{defaultVariant, nullableValue,
52 commontest.UntypedVariant, commontest.SkipValueVariant}
53 var variantsForNonNullValues = []commontest.ValueVariant{defaultVariant, nullableValue,
54 commontest.UntypedVariant, commontest.SkipValueVariant}
55 var shouldHaveBeenNullError = errors.New("should have been null")
56 var shouldNotHaveBeenNullError = errors.New("should not have been null")
57
58 func TestReader(t *testing.T) {
59 ts := commontest.ReaderTestSuite{
60 ContextFactory: func(input []byte) commontest.TestContext {
61 r := NewReader(input)
62 return &readerTestContext{input: input, r: &r}
63 },
64 ValueTestFactory: readerValueTestFactory{},
65 ReadErrorTestFactory: readerErrorTestFactory{},
66 }
67 ts.Run(t)
68 }
69
70 func (c readerTestContext) JSONData() []byte { return c.input }
71
72 func (f readerValueTestFactory) EOF() commontest.Action {
73 return func(c commontest.TestContext) error {
74 return c.(*readerTestContext).r.RequireEOF()
75 }
76 }
77
78 func (f readerValueTestFactory) Variants(value commontest.AnyValue) []commontest.ValueVariant {
79 switch value.Kind {
80 case commontest.NullValue:
81 return variantsForNullValues
82 case commontest.NumberValue:
83 if float64(int(value.Number)) == value.Number {
84 return variantsForInts
85 }
86 return variantsForFloats
87 default:
88 return variantsForNonNullValues
89 }
90 }
91
92 func (f readerValueTestFactory) Value(value commontest.AnyValue, variant commontest.ValueVariant) commontest.Action {
93 return func(c commontest.TestContext) error {
94 ctx := c.(*readerTestContext)
95 r := ctx.r
96
97 if variant == commontest.SkipValueVariant {
98 return r.SkipValue()
99 }
100 if variant == commontest.UntypedVariant {
101 return assertReadAnyValue(ctx, r, value)
102 }
103
104 switch value.Kind {
105 case commontest.NullValue:
106 return assertReadNull(r, variant)
107
108 case commontest.BoolValue:
109 switch variant {
110 case nullableValue:
111 gotVal, nonNull := r.BoolOrNull()
112 return commontest.AssertNoErrors(r.Error(),
113 commontest.AssertTrue(nonNull, shouldNotHaveBeenNullError.Error()),
114 commontest.AssertEqual(value.Bool, gotVal))
115 default:
116 gotVal := r.Bool()
117 return commontest.AssertNoErrors(r.Error(),
118 commontest.AssertEqual(value.Bool, gotVal))
119 }
120
121 case commontest.NumberValue:
122 switch variant {
123 case nullableNumberAsInt:
124 gotVal, nonNull := r.IntOrNull()
125 return commontest.AssertNoErrors(r.Error(),
126 commontest.AssertTrue(nonNull, shouldNotHaveBeenNullError.Error()),
127 commontest.AssertEqual(int(value.Number), gotVal))
128 case numberAsInt:
129 gotVal := r.Int()
130 return commontest.AssertNoErrors(r.Error(),
131 commontest.AssertEqual(int(value.Number), gotVal))
132 case nullableValue:
133 gotVal, nonNull := r.Float64OrNull()
134 return commontest.AssertNoErrors(r.Error(),
135 commontest.AssertTrue(nonNull, shouldNotHaveBeenNullError.Error()),
136 commontest.AssertEqual(value.Number, gotVal))
137 default:
138 gotVal := r.Float64()
139 return commontest.AssertNoErrors(r.Error(),
140 commontest.AssertEqual(value.Number, gotVal))
141 }
142
143 case commontest.StringValue:
144 switch variant {
145 case nullableValue:
146 gotVal, nonNull := r.StringOrNull()
147 return commontest.AssertNoErrors(r.Error(),
148 commontest.AssertTrue(nonNull, shouldNotHaveBeenNullError.Error()),
149 commontest.AssertEqual(value.String, gotVal))
150 default:
151 gotVal := r.String()
152 return commontest.AssertNoErrors(r.Error(),
153 commontest.AssertEqual(value.String, gotVal))
154 }
155
156 case commontest.ArrayValue:
157 var arr ArrayState
158 if variant == nullableValue {
159 arr = r.ArrayOrNull()
160 } else {
161 arr = r.Array()
162 }
163 if r.Error() != nil {
164 return r.Error()
165 }
166 if err := commontest.AssertTrue(arr.IsDefined(), shouldNotHaveBeenNullError.Error()); err != nil {
167 return err
168 }
169 return assertReadArray(ctx, &arr, value)
170
171 case commontest.ObjectValue:
172 var obj ObjectState
173 if variant == nullableValue {
174 obj = r.ObjectOrNull()
175 } else {
176 obj = r.Object()
177 }
178 if r.Error() != nil {
179 return r.Error()
180 }
181 if err := commontest.AssertTrue(obj.IsDefined(), shouldNotHaveBeenNullError.Error()); err != nil {
182 return err
183 }
184 return assertReadObject(ctx, &obj, value)
185 }
186 return nil
187 }
188 }
189
190 func assertReadNull(r *Reader, variant commontest.ValueVariant) error {
191 var gotVal, expectVal interface{}
192 var nonNull bool
193 switch variant {
194 case defaultVariant:
195 return r.Null()
196 case nullableBoolIsNull:
197 gotVal, nonNull = r.BoolOrNull()
198 expectVal = false
199 case nullableIntIsNull:
200 gotVal, nonNull = r.IntOrNull()
201 expectVal = 0
202 case nullableFloatIsNull:
203 gotVal, nonNull = r.Float64OrNull()
204 expectVal = float64(0)
205 case nullableStringIsNull:
206 gotVal, nonNull = r.StringOrNull()
207 expectVal = ""
208 case nullableArrayIsNull:
209 arr := r.ArrayOrNull()
210 if r.Error() != nil {
211 return r.Error()
212 }
213 if arr.IsDefined() {
214 return TypeError{Expected: NullValue, Actual: ArrayValue}
215 }
216 return nil
217 case nullableObjectIsNull:
218 obj := r.ObjectOrNull()
219 if r.Error() != nil {
220 return r.Error()
221 }
222 if obj.IsDefined() {
223 return TypeError{Expected: NullValue, Actual: ObjectValue}
224 }
225 return nil
226 }
227 return commontest.AssertNoErrors(
228 r.Error(),
229 commontest.AssertTrue(!nonNull, shouldHaveBeenNullError.Error()),
230 commontest.AssertEqual(expectVal, gotVal))
231 }
232
233 func assertReadArray(ctx *readerTestContext, arr *ArrayState, value commontest.AnyValue) error {
234 if err := commontest.AssertTrue(arr.IsDefined(), shouldNotHaveBeenNullError.Error()); err != nil {
235 return err
236 }
237 for _, e := range value.Array {
238 if err := commontest.AssertTrue(arr.Next(), "array ended too soon"); err != nil {
239 return err
240 }
241 if err := e(ctx); err != nil {
242 return err
243 }
244 }
245 return commontest.AssertTrue(!arr.Next(), "expected end of array")
246 }
247
248 func assertReadObject(ctx *readerTestContext, obj *ObjectState, value commontest.AnyValue) error {
249 if err := commontest.AssertTrue(obj.IsDefined(), "should not have been null"); err != nil {
250 return err
251 }
252 for _, p := range value.Object {
253 if err := commontest.AssertNoErrors(
254 commontest.AssertTrue(obj.Next(), "object ended too soon"),
255 commontest.AssertEqual(p.Name, string(obj.Name())),
256 ); err != nil {
257 return err
258 }
259 if err := p.Action(ctx); err != nil {
260 return err
261 }
262 }
263 return commontest.AssertTrue(!obj.Next(), "expected end of object")
264 }
265
266 func assertReadAnyValue(ctx *readerTestContext, r *Reader, value commontest.AnyValue) error {
267 av := r.Any()
268 if r.Error() != nil {
269 return r.Error()
270 }
271
272 switch value.Kind {
273 case commontest.NullValue:
274 return commontest.AssertEqual(NullValue, av.Kind)
275
276 case commontest.BoolValue:
277 return commontest.AssertNoErrors(commontest.AssertEqual(BoolValue, av.Kind),
278 commontest.AssertEqual(value.Bool, av.Bool))
279
280 case commontest.NumberValue:
281 return commontest.AssertNoErrors(commontest.AssertEqual(NumberValue, av.Kind),
282 commontest.AssertEqual(value.Number, av.Number))
283
284 case commontest.StringValue:
285 return commontest.AssertNoErrors(commontest.AssertEqual(StringValue, av.Kind),
286 commontest.AssertEqual(value.String, av.String))
287
288 case commontest.ArrayValue:
289 if err := commontest.AssertEqual(ArrayValue, av.Kind); err != nil {
290 return err
291 }
292 return assertReadArray(ctx, &av.Array, value)
293
294 case commontest.ObjectValue:
295 if err := commontest.AssertEqual(ObjectValue, av.Kind); err != nil {
296 return err
297 }
298 return assertReadObject(ctx, &av.Object, value)
299 }
300 return nil
301 }
302
303 func (f readerErrorTestFactory) ExpectEOFError(err error) error {
304 return tokenReaderErrorTestFactory{}.ExpectEOFError(err)
305 }
306
307 func (f readerErrorTestFactory) ExpectWrongTypeError(err error, expected commontest.ValueKind,
308 variant commontest.ValueVariant, actual commontest.ValueKind) error {
309
310
311 expectedError := TypeError{
312 Expected: valueKindFromTestValueKind(expected),
313 Actual: valueKindFromTestValueKind(actual),
314 }
315 switch variant {
316 case nullableValue, nullableNumberAsInt:
317 expectedError.Nullable = true
318 case nullableBoolIsNull:
319 expectedError.Nullable = true
320 expectedError.Expected = BoolValue
321 case nullableIntIsNull, nullableFloatIsNull:
322 expectedError.Nullable = true
323 expectedError.Expected = NumberValue
324 case nullableStringIsNull:
325 expectedError.Nullable = true
326 expectedError.Expected = StringValue
327 case nullableArrayIsNull:
328 expectedError.Nullable = true
329 expectedError.Expected = ArrayValue
330 case nullableObjectIsNull:
331 expectedError.Nullable = true
332 expectedError.Expected = ObjectValue
333 }
334 if expectedError.Nullable && valueKindFromTestValueKind(actual) == expectedError.Expected {
335 return commontest.AssertEqual(shouldHaveBeenNullError, err)
336 }
337 if expectedError.Nullable && actual == commontest.NullValue {
338 return commontest.AssertEqual(shouldNotHaveBeenNullError, err)
339 }
340 if te, ok := err.(TypeError); ok {
341 expectedError.Offset = te.Offset
342 if te == expectedError {
343 return nil
344 }
345 }
346 return fmt.Errorf("expected %T %+v, got %T %+v", expectedError, expectedError, err, err)
347 }
348
349 func (f readerErrorTestFactory) ExpectSyntaxError(err error) error {
350 return tokenReaderErrorTestFactory{}.ExpectSyntaxError(err)
351 }
352
353 func TestReaderAddErrorDoesNotOverridePreviousError(t *testing.T) {
354 fakeError := errors.New("sorry")
355 r := NewReader([]byte(`"not a bool"`))
356 _ = r.Bool()
357 r.AddError(fakeError)
358 require.Error(t, r.Error())
359 require.NotEqual(t, fakeError, r.Error())
360 require.IsType(t, TypeError{}, r.Error())
361 }
362
363 func TestReaderSetErrorOverridesPreviousError(t *testing.T) {
364 fakeError := errors.New("sorry")
365 r := NewReader([]byte(`"not a bool"`))
366 _ = r.Bool()
367 r.ReplaceError(fakeError)
368 require.Error(t, r.Error())
369 require.Equal(t, fakeError, r.Error())
370 }
371
372 func TestReaderSkipValue(t *testing.T) {
373 t.Run("Next() skips array element if it was not read", func(t *testing.T) {
374 data := []byte(`["a", ["b1", "b2"], "c"]`)
375 r := NewReader(data)
376 arr := r.Array()
377 require.NoError(t, r.Error())
378
379 require.True(t, arr.Next())
380 val1 := r.String()
381 require.NoError(t, r.Error())
382 require.Equal(t, "a", val1)
383
384 require.True(t, arr.Next())
385
386 require.True(t, arr.Next())
387 val3 := r.String()
388 require.NoError(t, r.Error())
389 require.Equal(t, "c", val3)
390
391 require.False(t, arr.Next())
392 })
393
394 t.Run("Next() skips property value if it was not read", func(t *testing.T) {
395 data := []byte(`{"a":1, "b":{"b1":2, "b2":3}, "c":4}`)
396 r := NewReader(data)
397 obj := r.Object()
398 require.NoError(t, r.Error())
399
400 require.True(t, obj.Next())
401 require.Equal(t, "a", string(obj.Name()))
402 val1 := r.Int()
403 require.NoError(t, r.Error())
404 require.Equal(t, 1, val1)
405
406 require.True(t, obj.Next())
407
408 require.True(t, obj.Next())
409 require.Equal(t, "c", string(obj.Name()))
410 val3 := r.Int()
411 require.NoError(t, r.Error())
412 require.Equal(t, 4, val3)
413
414 require.False(t, obj.Next())
415 })
416 }
417
View as plain text