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 }