...

Source file src/github.com/launchdarkly/go-jsonstream/v3/jreader/reader_test.go

Documentation: github.com/launchdarkly/go-jsonstream/v3/jreader

     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  // This uses the framework defined in the commontest package to exercise Reader with a large number
    14  // of valid and invalid JSON inputs. All we need to implement here is the logic for calling the
    15  // appropriate Reader methods that correspond to the commontest abstractions.
    16  
    17  type readerTestContext struct {
    18  	input []byte
    19  	r     *Reader
    20  }
    21  
    22  type readerValueTestFactory struct{}
    23  type readerErrorTestFactory struct{}
    24  
    25  // The behavior of Reader is flexible so that callers can choose to read the same JSON value in
    26  // several different ways. Therefore, we generate variants for each value test as follows:
    27  // - A null JSON value could be read either as a null, or as a nullable value of another type.
    28  // - A JSON number could be read as an int (if the test value is an int), a float, or a nullable
    29  // int or float.
    30  // - Any other non-null value could be read as its own type, or as a nullable value of that type.
    31  // - Any value could be read with a nonspecific type using the Any() method.
    32  // - Any value could be skipped instead of read.
    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  	// Here our behavior is different from tokenReaderErrorTestFactory, because Reader has more possible
   310  	// kinds of errors due to the convenience features for reading values as "some type *or* null".
   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