1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package validate
16
17 import (
18 "reflect"
19 "strings"
20 "testing"
21
22 "github.com/go-openapi/spec"
23 "github.com/stretchr/testify/assert"
24 "github.com/stretchr/testify/require"
25 )
26
27 func itemsFixture() map[string]interface{} {
28 return map[string]interface{}{
29 "type": "array",
30 "items": "dummy",
31 }
32 }
33
34 func expectAllValid(t *testing.T, ov EntityValidator, dataValid, dataInvalid map[string]interface{}) {
35 res := ov.Validate(dataValid)
36 assert.Empty(t, res.Errors)
37
38 res = ov.Validate(dataInvalid)
39 assert.Empty(t, res.Errors)
40 }
41
42 func expectOnlyInvalid(t *testing.T, ov EntityValidator, dataValid, dataInvalid map[string]interface{}) {
43 res := ov.Validate(dataValid)
44 assert.Empty(t, res.Errors)
45
46 res = ov.Validate(dataInvalid)
47 assert.NotEmpty(t, res.Errors)
48 }
49
50 func TestItemsMustBeTypeArray(t *testing.T) {
51 ov := newObjectValidator("", "", nil, nil, nil, nil, nil, nil, nil, nil, nil)
52 dataValid := itemsFixture()
53 dataInvalid := map[string]interface{}{
54 "type": "object",
55 "items": "dummy",
56 }
57 expectAllValid(t, ov, dataValid, dataInvalid)
58
59 ov.Options.EnableObjectArrayTypeCheck = true
60 expectOnlyInvalid(t, ov, dataValid, dataInvalid)
61 }
62
63 func TestItemsMustHaveType(t *testing.T) {
64 ov := newObjectValidator("", "", nil, nil, nil, nil, nil, nil, nil, nil, nil)
65 dataValid := itemsFixture()
66 dataInvalid := map[string]interface{}{
67 "items": "dummy",
68 }
69 expectAllValid(t, ov, dataValid, dataInvalid)
70
71 ov.Options.EnableObjectArrayTypeCheck = true
72 expectOnlyInvalid(t, ov, dataValid, dataInvalid)
73 }
74
75 func TestTypeArrayMustHaveItems(t *testing.T) {
76 ov := newObjectValidator("", "", nil, nil, nil, nil, nil, nil, nil, nil, nil)
77 dataValid := itemsFixture()
78 dataInvalid := map[string]interface{}{
79 "type": "array",
80 "key": "dummy",
81 }
82 expectAllValid(t, ov, dataValid, dataInvalid)
83
84 ov.Options.EnableArrayMustHaveItemsCheck = true
85 expectOnlyInvalid(t, ov, dataValid, dataInvalid)
86 }
87
88
89
90
91 func TestObjectValidator_EdgeCases(t *testing.T) {
92 s := newObjectValidator("", "", nil, nil, nil, nil, nil, nil, nil, nil, nil)
93 s.SetPath("path")
94 assert.Equal(t, "path", s.Path)
95 }
96
97 func TestObjectValidatorApply(t *testing.T) {
98 s := newObjectValidator("", "", nil, nil, nil, nil, nil, nil, nil, nil, nil)
99 require.True(t, s.Applies(&spec.Schema{}, reflect.Map))
100 require.False(t, s.Applies(&spec.Response{}, reflect.Map))
101 require.False(t, s.Applies(&struct{}{}, reflect.Map))
102 }
103
104 func TestObjectValidatorPatternProperties(t *testing.T) {
105 patternWithValid := spec.SchemaProperties{
106 "valid": spec.Schema{
107 SchemaProps: spec.SchemaProps{
108 Type: []string{"string"},
109 },
110 },
111 "#(.((garbled": spec.Schema{
112 SchemaProps: spec.SchemaProps{
113 Type: []string{"string"},
114 },
115 },
116 }
117
118 patternGarbled := spec.SchemaProperties{
119 "#(.((garbled": spec.Schema{
120 SchemaProps: spec.SchemaProps{
121 Type: []string{"string"},
122 },
123 },
124 }
125
126 t.Run("should ignore invalid regexp in pattern properties", func(t *testing.T) {
127 s := newObjectValidator("test", "body", nil, nil, nil, nil, nil, patternWithValid, nil, nil, nil)
128
129 res := s.Validate(map[string]interface{}{"valid": "test_string"})
130 require.NotNil(t, res)
131 require.Empty(t, res.Errors)
132 })
133
134 t.Run("should report forbidden property when invalid regexp in pattern properties", func(t *testing.T) {
135 s := newObjectValidator("test", "body", nil, nil, nil, nil, nil, patternGarbled, nil, nil, nil)
136
137 res := s.Validate(map[string]interface{}{"valid": "test_string"})
138 require.NotNil(t, res)
139 require.Empty(t, res.Errors)
140 })
141
142 t.Run("should ignore invalid regexp in pattern properties of additional properties", func(t *testing.T) {
143 s := newObjectValidator("test", "body", nil, nil, nil, nil, &spec.SchemaOrBool{
144 Schema: &spec.Schema{},
145 Allows: false,
146 }, patternWithValid, nil, nil, nil)
147
148 res := s.Validate(map[string]interface{}{"valid": "test_string"})
149 require.NotNil(t, res)
150 require.Empty(t, res.Errors)
151 })
152
153 t.Run("should report forbidden property when invalid regexp in pattern properties of additional properties", func(t *testing.T) {
154 s := newObjectValidator("test", "body", nil, nil, nil, nil, &spec.SchemaOrBool{
155 Schema: &spec.Schema{},
156 Allows: false,
157 }, patternGarbled, nil, nil, nil)
158
159 res := s.Validate(map[string]interface{}{"valid": "test_string"})
160 require.NotNil(t, res)
161 require.Len(t, res.Errors, 1)
162 require.ErrorContains(t, res.Errors[0], "forbidden property")
163 })
164 }
165
166 func TestObjectValidatorNilData(t *testing.T) {
167 t.Run("object Validate should NOT panic on nil data", func(t *testing.T) {
168 s := newObjectValidator("", "", nil, nil, nil, nil, nil, nil, nil, nil, nil)
169 require.NotPanics(t, func() {
170 _ = s.Validate(nil)
171 })
172
173 res := s.Validate(nil)
174 require.NotNil(t, res)
175 require.Empty(t, res.Errors)
176 })
177
178 t.Run("object Validate should validate required on nil data", func(t *testing.T) {
179 s := newObjectValidator("", "", nil, nil, []string{"wanted"}, nil, nil, nil, nil, nil, nil)
180 res := s.Validate(nil)
181 require.NotNil(t, res)
182 require.NotEmpty(t, res.Errors)
183 })
184
185 t.Run("object Validate should NOT panic on unexpected input", func(t *testing.T) {
186 s := newObjectValidator("", "", nil, nil, []string{"wanted"}, nil, nil, nil, nil, nil, nil)
187 res := s.Validate(map[string]string{"wanted": "not expected"})
188 require.NotNil(t, res)
189 require.Len(t, res.Errors, 1)
190 require.ErrorContains(t, res.Errors[0], "expected an object")
191 })
192
193 t.Run("object Validate should NOT panic on nil input (with array type check)", func(t *testing.T) {
194 s := newObjectValidator("", "", nil, nil, []string{"wanted"}, nil, nil, nil, nil, nil, &SchemaValidatorOptions{
195 EnableArrayMustHaveItemsCheck: true,
196 EnableObjectArrayTypeCheck: true,
197 })
198 res := s.Validate(nil)
199 require.NotNil(t, res)
200 require.Len(t, res.Errors, 1)
201 require.ErrorContains(t, res.Errors[0], "wanted is required")
202 })
203 }
204
205 func TestObjectValidatorWithHeaderProperty(t *testing.T) {
206 t.Run("should report extra information about forbidden $ref in this context", func(t *testing.T) {
207 s := newObjectValidator("test", "body", nil, nil, nil, nil, &spec.SchemaOrBool{
208 Schema: &spec.Schema{},
209 Allows: false,
210 }, nil, nil, nil, nil)
211
212 res := s.Validate(map[string]interface{}{
213 "headers": map[string]interface{}{
214 "X-Custom": map[string]interface{}{
215 "$ref": "#/definitions/myHeader",
216 },
217 },
218 })
219 require.NotNil(t, res)
220 require.Len(t, res.Errors, 2)
221 found := 0
222 for _, err := range res.Errors {
223 switch {
224 case strings.Contains(err.Error(), "forbidden property"):
225 found++
226 case strings.Contains(err.Error(), "$ref are not allowed in headers"):
227 found++
228 }
229 }
230 require.Equal(t, 2, found)
231 })
232
233 t.Run("should NOT report extra information when header is not detected", func(t *testing.T) {
234 s := newObjectValidator("test", "body", nil, nil, nil, nil, &spec.SchemaOrBool{
235 Schema: &spec.Schema{},
236 Allows: false,
237 }, nil, nil, nil, nil)
238
239 t.Run("when key is not headers", func(t *testing.T) {
240 res := s.Validate(map[string]interface{}{
241 "Headers": map[string]interface{}{
242 "X-Custom": map[string]interface{}{
243 "$ref": "#/definitions/myHeader",
244 },
245 },
246 })
247 require.NotNil(t, res)
248 require.Len(t, res.Errors, 1)
249 })
250
251 t.Run("when key is not the expected map", func(t *testing.T) {
252 res := s.Validate(map[string]interface{}{
253 "headers": map[string]string{
254 "X-Custom": "#/definitions/myHeader",
255 },
256 })
257 require.NotNil(t, res)
258 require.Len(t, res.Errors, 1)
259 })
260
261 t.Run("when key content not the expected map", func(t *testing.T) {
262 res := s.Validate(map[string]interface{}{
263 "headers": map[string]interface{}{
264 "X-Custom": 1,
265 },
266 })
267 require.NotNil(t, res)
268 require.Len(t, res.Errors, 1)
269 })
270
271 t.Run("when key content not the expected map", func(t *testing.T) {
272 res := s.Validate(map[string]interface{}{
273 "headers": map[string]interface{}{
274 "X-Custom": nil,
275 },
276 })
277 require.NotNil(t, res)
278 require.Len(t, res.Errors, 1)
279 })
280
281 t.Run("when header is not a valid $ref", func(t *testing.T) {
282 res := s.Validate(map[string]interface{}{
283 "headers": map[string]interface{}{
284 "X-Custom": map[string]interface{}{
285 "$ref": 1,
286 },
287 },
288 })
289 require.NotNil(t, res)
290 require.Len(t, res.Errors, 1)
291 })
292
293 t.Run("when header is not a $ref", func(t *testing.T) {
294 res := s.Validate(map[string]interface{}{
295 "headers": map[string]interface{}{
296 "X-Custom": map[string]interface{}{
297 "ref": "#/definitions/myHeader",
298 },
299 },
300 })
301 require.NotNil(t, res)
302 require.Len(t, res.Errors, 1)
303 })
304 })
305 }
306
307 func TestObjectValidatorWithDefault(t *testing.T) {
308
313 t.Run("should accept required populated with a default", func(t *testing.T) {
314 s := newObjectValidator("test", "body", nil, nil,
315 []string{"wanted"},
316 spec.SchemaProperties{
317 "wanted": spec.Schema{
318 SchemaProps: spec.SchemaProps{
319 Default: "default_value"},
320 },
321 },
322 nil, nil,
323 nil, nil, nil)
324 res := s.Validate(nil)
325 require.NotNil(t, res)
326 require.Empty(t, res.Errors)
327 })
328 }
329
View as plain text