github.com/galdor/go-ejson@v0.0.0-20231201100034-d335379f26b0/validator_test.go (about)

     1  package ejson
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/assert"
     7  )
     8  
     9  type TestFoo struct {
    10  	String   string
    11  	Bar      *TestBar
    12  	Bars     []*TestBar
    13  	BarTable map[string]*TestBar
    14  	Tag      string
    15  }
    16  
    17  type TestBar struct {
    18  	Integers []int
    19  }
    20  
    21  func (foo *TestFoo) ValidateJSON(v *Validator) {
    22  	v.CheckStringLengthMin("String", foo.String, 3)
    23  	v.CheckOptionalObject("Bar", foo.Bar)
    24  	v.CheckObjectArray("Bars", foo.Bars)
    25  	v.CheckObjectMap("BarTable", foo.BarTable)
    26  	v.CheckStringValue("Tag", foo.Tag, []string{"", "a", "b", "c"})
    27  }
    28  
    29  func (bar *TestBar) ValidateJSON(v *Validator) {
    30  	v.WithChild("Integers", func() {
    31  		for i, integer := range bar.Integers {
    32  			v.CheckIntMax(i, integer, 10)
    33  		}
    34  	})
    35  }
    36  
    37  func TestValidate(t *testing.T) {
    38  	assert := assert.New(t)
    39  
    40  	var data TestFoo
    41  	var err error
    42  	var validationErrs ValidationErrors
    43  	var validationErr *ValidationError
    44  
    45  	// Valid data
    46  	data = TestFoo{
    47  		String: "abcdef",
    48  		Bar:    &TestBar{Integers: []int{1, 2, 3}},
    49  		Bars: []*TestBar{
    50  			{Integers: []int{4}},
    51  			{Integers: []int{5, 6}},
    52  		},
    53  		BarTable: map[string]*TestBar{
    54  			"foo": {Integers: []int{4}},
    55  			"bar": {Integers: []int{5, 6}},
    56  		},
    57  	}
    58  
    59  	assert.NoError(Validate(&data))
    60  
    61  	// Valid data with null optional object
    62  	data = TestFoo{
    63  		String: "abcdef",
    64  		Bar:    nil,
    65  	}
    66  
    67  	assert.NoError(Validate(&data))
    68  
    69  	// Simple top-level violation
    70  	data = TestFoo{
    71  		String: "ab",
    72  		Bar:    nil,
    73  	}
    74  
    75  	err = Validate(&data)
    76  
    77  	if assert.ErrorAs(err, &validationErrs) {
    78  		if assert.Equal(1, len(validationErrs)) {
    79  			validationErr = validationErrs[0]
    80  			assert.Equal("/String", validationErr.Pointer.String())
    81  			assert.Equal("string_too_short", validationErr.Code)
    82  		}
    83  	}
    84  
    85  	// String value violation
    86  	data = TestFoo{
    87  		String: "abcdef",
    88  		Tag:    "xyz",
    89  	}
    90  
    91  	err = Validate(&data)
    92  
    93  	if assert.ErrorAs(err, &validationErrs) {
    94  		if assert.Equal(1, len(validationErrs)) {
    95  			validationErr = validationErrs[0]
    96  			assert.Equal("/Tag", validationErr.Pointer.String())
    97  			assert.Equal("invalid_value", validationErr.Code)
    98  		}
    99  	}
   100  
   101  	// Null objects in an object array
   102  	data = TestFoo{
   103  		String: "abcdef",
   104  		Bars: []*TestBar{
   105  			{Integers: []int{4}},
   106  			nil,
   107  			{Integers: []int{5, 6}},
   108  			nil,
   109  		},
   110  	}
   111  
   112  	err = Validate(&data)
   113  
   114  	if assert.ErrorAs(err, &validationErrs) {
   115  		if assert.Equal(2, len(validationErrs)) {
   116  			validationErr = validationErrs[0]
   117  			assert.Equal("/Bars/1", validationErr.Pointer.String())
   118  			assert.Equal("missing_value", validationErr.Code)
   119  
   120  			validationErr = validationErrs[1]
   121  			assert.Equal("/Bars/3", validationErr.Pointer.String())
   122  			assert.Equal("missing_value", validationErr.Code)
   123  		}
   124  	}
   125  
   126  	// Nested violations
   127  	data = TestFoo{
   128  		String: "abcdef",
   129  		Bars: []*TestBar{
   130  			nil,
   131  			{Integers: []int{15}},
   132  			{Integers: []int{5, 20}},
   133  		},
   134  		BarTable: map[string]*TestBar{
   135  			"foo": {Integers: []int{15}},
   136  		},
   137  	}
   138  
   139  	err = Validate(&data)
   140  
   141  	if assert.ErrorAs(err, &validationErrs) {
   142  		if assert.Equal(4, len(validationErrs)) {
   143  			validationErr = validationErrs[0]
   144  			assert.Equal("/Bars/0", validationErr.Pointer.String())
   145  			assert.Equal("missing_value", validationErr.Code)
   146  
   147  			validationErr = validationErrs[1]
   148  			assert.Equal("/Bars/1/Integers/0", validationErr.Pointer.String())
   149  			assert.Equal("integer_too_large", validationErr.Code)
   150  
   151  			validationErr = validationErrs[2]
   152  			assert.Equal("/Bars/2/Integers/1", validationErr.Pointer.String())
   153  			assert.Equal("integer_too_large", validationErr.Code)
   154  
   155  			validationErr = validationErrs[3]
   156  			assert.Equal("/BarTable/foo/Integers/0",
   157  				validationErr.Pointer.String())
   158  			assert.Equal("integer_too_large", validationErr.Code)
   159  		}
   160  	}
   161  
   162  	// Invalid top-level type
   163  	err = Unmarshal([]byte(`42`), &data)
   164  
   165  	if assert.ErrorAs(err, &validationErrs) {
   166  		if assert.Equal(1, len(validationErrs)) {
   167  			validationErr = validationErrs[0]
   168  			assert.Equal("", validationErr.Pointer.String())
   169  			assert.Equal("invalid_value_type", validationErr.Code)
   170  		}
   171  	}
   172  
   173  	// Invalid member type
   174  	err = Unmarshal([]byte(`{"String": 42}`), &data)
   175  
   176  	if assert.ErrorAs(err, &validationErrs) {
   177  		if assert.Equal(1, len(validationErrs)) {
   178  			validationErr = validationErrs[0]
   179  			assert.Equal("/String", validationErr.Pointer.String())
   180  			assert.Equal("invalid_value_type", validationErr.Code)
   181  		}
   182  	}
   183  
   184  	// Invalid nested member type
   185  	//
   186  	// The standard JSON parser returns the error on the array, nothing we can
   187  	// do about it.
   188  	err = Unmarshal([]byte(`{"String": "abcd", "Bars": [{"Integers": true}]}`),
   189  		&data)
   190  
   191  	if assert.ErrorAs(err, &validationErrs) {
   192  		if assert.Equal(1, len(validationErrs)) {
   193  			validationErr = validationErrs[0]
   194  			assert.Equal("/Bars/Integers", validationErr.Pointer.String())
   195  			assert.Equal("invalid_value_type", validationErr.Code)
   196  		}
   197  	}
   198  }