1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package validate
16
17 import (
18 "encoding/json"
19 "flag"
20 "os"
21 "path/filepath"
22 "strings"
23 "testing"
24
25 "github.com/davecgh/go-spew/spew"
26 "github.com/go-openapi/analysis"
27 "github.com/go-openapi/loads"
28 "github.com/go-openapi/loads/fmts"
29 "github.com/go-openapi/spec"
30 "github.com/go-openapi/strfmt"
31 "github.com/stretchr/testify/assert"
32 "github.com/stretchr/testify/require"
33 )
34
35 const testID = "id"
36
37
38
39
40
41
42
43
44
45
46
47 var enableLongTests bool
48 var enableGoSwaggerTests bool
49
50 func init() {
51 loads.AddLoader(fmts.YAMLMatcher, fmts.YAMLDoc)
52 flag.BoolVar(&enableLongTests, "enable-long", false, "enable long runnning tests")
53 flag.BoolVar(&enableGoSwaggerTests, "enable-go-swagger", false, "enable go-swagger non-regression test")
54 }
55
56 func skipNotify(t *testing.T) {
57 t.Log("To enable this long running test, use -args -enable-long in your go test command line")
58 }
59
60 func debugTest(t *testing.T, path string, res *Result) {
61 if DebugTest && t.Failed() {
62 verifiedErrors := verifiedTestErrors(res)
63 if len(verifiedErrors) > 0 {
64 t.Logf("DEVMODE:Returned error messages validating %s ", path)
65 for _, v := range verifiedErrors {
66 t.Logf("%s", v)
67 }
68 }
69 verifiedWarnings := verifiedTestWarnings(res)
70 if len(verifiedWarnings) > 0 {
71 t.Logf("DEVMODE: Returned warnings for %s:", path)
72 for _, e := range res.Warnings {
73 t.Logf("%v", e)
74 }
75 }
76 }
77 }
78
79 func verifiedTestErrors(res *Result) []string {
80 verifiedErrors := make([]string, 0, 50)
81 for _, e := range res.Errors {
82 verifiedErrors = append(verifiedErrors, e.Error())
83 }
84 return verifiedErrors
85 }
86
87 func verifiedTestWarnings(res *Result) []string {
88 verifiedWarnings := make([]string, 0, 50)
89 for _, e := range res.Warnings {
90 verifiedWarnings = append(verifiedWarnings, e.Error())
91 }
92 return verifiedWarnings
93 }
94
95 func TestSpec_ExpandResponseLocalFile(t *testing.T) {
96 res, _ := loadAndValidate(t, filepath.Join("fixtures", "local_expansion", "spec.yaml"))
97 assert.True(t, res.IsValid())
98 assert.Empty(t, res.Errors)
99 }
100
101 func TestSpec_ExpandResponseRecursive(t *testing.T) {
102 res, _ := loadAndValidate(t, filepath.Join("fixtures", "recursive_expansion", "spec.yaml"))
103 assert.True(t, res.IsValid())
104 assert.Empty(t, res.Errors)
105 }
106
107
108 func TestSpec_Issue52(t *testing.T) {
109 fp := filepath.Join("fixtures", "bugs", "52", "swagger.json")
110 jstext, _ := os.ReadFile(fp)
111
112
113 var sch spec.Schema
114 require.NoError(t, json.Unmarshal(jstext, &sch))
115
116 schemaValidator := NewSchemaValidator(spec.MustLoadSwagger20Schema(), nil, "", strfmt.Default)
117 res := schemaValidator.Validate(&sch)
118 assert.False(t, res.IsValid())
119 require.NotEmpty(t, res.Errors)
120 require.EqualError(t, res.Errors[0], ".paths in body is required")
121
122
123
124 res, _ = loadAndValidate(t, fp)
125 assert.False(t, res.IsValid())
126
127 verifiedErrors := verifiedTestErrors(res)
128 assert.Len(t, verifiedErrors, 2, "Unexpected number of error messages returned")
129 assert.Contains(t, verifiedErrors, ".paths in body is required")
130 assert.Contains(t, verifiedErrors, "spec has no valid path defined")
131 }
132
133 func TestSpec_Issue53(t *testing.T) {
134 fp := filepath.Join("fixtures", "bugs", "53", "noswagger.json")
135 jstext, _ := os.ReadFile(fp)
136
137
138 var sch spec.Schema
139 require.NoError(t, json.Unmarshal(jstext, &sch))
140
141 schemaValidator := NewSchemaValidator(spec.MustLoadSwagger20Schema(), nil, "", strfmt.Default)
142 res := schemaValidator.Validate(&sch)
143 assert.False(t, res.IsValid())
144 require.NotEmpty(t, res.Errors)
145 require.EqualError(t, res.Errors[0], ".swagger in body is required")
146
147
148 res, _ = loadAndValidate(t, fp, false)
149 require.False(t, res.IsValid())
150 require.NotEmpty(t, res.Errors)
151 require.EqualError(t, res.Errors[0], ".swagger in body is required")
152 }
153
154 func TestSpec_Issue62(t *testing.T) {
155 fp := filepath.Join("fixtures", "bugs", "62", "swagger.json")
156
157
158 doc, err := loads.Spec(fp)
159 require.NoError(t, err)
160
161 validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default)
162 res, _ := validator.Validate(doc)
163 assert.NotEmpty(t, res.Errors)
164 assert.True(t, res.HasErrors())
165 }
166
167 func TestSpec_Issue63(t *testing.T) {
168 res, _ := loadAndValidate(t, filepath.Join("fixtures", "bugs", "63", "swagger.json"))
169 assert.True(t, res.IsValid())
170 }
171
172 func TestSpec_Issue61_MultipleRefs(t *testing.T) {
173 res, _ := loadAndValidate(t, filepath.Join("fixtures", "bugs", "61", "multiple-refs.json"))
174 assert.Empty(t, res.Errors)
175 assert.True(t, res.IsValid())
176 }
177
178 func TestSpec_Issue61_ResolvedRef(t *testing.T) {
179 res, _ := loadAndValidate(t, filepath.Join("fixtures", "bugs", "61", "unresolved-ref-for-name.json"))
180 assert.Empty(t, res.Errors)
181 assert.True(t, res.IsValid())
182 }
183
184
185 func TestSpec_Issue123(t *testing.T) {
186 fp := filepath.Join("fixtures", "bugs", "123", "swagger.yml")
187 res, _ := loadAndValidate(t, fp)
188 assert.True(t, res.IsValid())
189 assert.Empty(t, res.Errors)
190
191 debugTest(t, fp, res)
192 }
193
194 func TestSpec_Issue6(t *testing.T) {
195 files, _ := filepath.Glob(filepath.Join("fixtures", "bugs", "6", "*.json"))
196 for _, path := range files {
197 t.Logf("Tested spec=%s", path)
198 res, _ := loadAndValidate(t, path)
199 assert.False(t, res.IsValid())
200
201 verifiedErrors := verifiedTestErrors(res)
202 switch {
203 case strings.Contains(path, "empty-responses.json"):
204 assert.Contains(t, verifiedErrors, "\"paths./foo.get.responses\" must not validate the schema (not)")
205 assert.Contains(t, verifiedErrors, "paths./foo.get.responses in body should have at least 1 properties")
206 case strings.Contains(path, "no-responses.json"):
207 assert.Contains(t, verifiedErrors, "paths./foo.get.responses in body is required")
208 default:
209 t.Logf("Returned error messages: %v", verifiedErrors)
210 t.Fatal("fixture not tested. Please add assertions for messages")
211 }
212
213 debugTest(t, path, res)
214 }
215 }
216
217
218 func TestSpec_Issue18(t *testing.T) {
219 files, _ := filepath.Glob(filepath.Join("fixtures", "bugs", "18", "*.json"))
220 for _, path := range files {
221 t.Logf("Tested spec=%s", path)
222 res, _ := loadAndValidate(t, path)
223 assert.False(t, res.IsValid())
224
225 verifiedErrors := verifiedTestErrors(res)
226 switch {
227 case strings.Contains(path, "headerItems.json"):
228 assert.Contains(t, verifiedErrors, "X-Foo in header has invalid pattern: \")<-- bad pattern\"")
229 case strings.Contains(path, "headers.json"):
230 assert.Contains(t, verifiedErrors, "in operation \"\", header X-Foo for default response has invalid pattern \")<-- bad pattern\": error parsing regexp: unexpected ): `)<-- bad pattern`")
231
232 assert.Contains(t, verifiedErrors, "in operation \"\", header X-Foo for response 402 has invalid pattern \")<-- bad pattern\": error parsing regexp: unexpected ): `)<-- bad pattern`")
233
234
235 case strings.Contains(path, "paramItems.json"):
236 assert.Contains(t, verifiedErrors, "body param \"user\" for \"\" has invalid items pattern: \")<-- bad pattern\"")
237
238 assert.Contains(t, verifiedErrors, "default value for user in body does not validate its schema")
239 assert.Contains(t, verifiedErrors, "user.items.default in body has invalid pattern: \")<-- bad pattern\"")
240 case strings.Contains(path, "parameters.json"):
241 assert.Contains(t, verifiedErrors, "operation \"\" has invalid pattern in param \"userId\": \")<-- bad pattern\"")
242 case strings.Contains(path, "schema.json"):
243
244 assert.Contains(t, verifiedErrors, "200 in response has invalid pattern: \")<-- bad pattern\"")
245 default:
246 t.Logf("Returned error messages: %v", verifiedErrors)
247 t.Fatal("fixture not tested. Please add assertions for messages")
248 }
249
250 debugTest(t, path, res)
251 }
252 }
253
254
255 func TestSpec_Issue39(t *testing.T) {
256 fp := filepath.Join("fixtures", "bugs", "39", "swagger.yml")
257 res, _ := loadAndValidate(t, fp)
258 assert.True(t, res.IsValid())
259 assert.Empty(t, res.Errors)
260 debugTest(t, fp, res)
261 }
262
263 func TestSpec_ValidateDuplicatePropertyNames(t *testing.T) {
264
265 doc, err := loads.Spec(filepath.Join("fixtures", "validation", "duplicateprops.json"))
266 require.NoError(t, err)
267
268 validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default)
269 validator.spec = doc
270 res := validator.validateDuplicatePropertyNames()
271 assert.NotEmpty(t, res.Errors)
272 assert.Len(t, res.Errors, 1)
273
274
275 doc, err = loads.Spec(filepath.Join("fixtures", "validation", "nestedduplicateprops.json"))
276 require.NoError(t, err)
277
278 validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default)
279 validator.spec = doc
280 res = validator.validateDuplicatePropertyNames()
281 assert.NotEmpty(t, res.Errors)
282 assert.Len(t, res.Errors, 1)
283 }
284
285 func TestSpec_ValidateNonEmptyPathParameterNames(t *testing.T) {
286 doc, err := loads.Spec(filepath.Join("fixtures", "validation", "empty-path-param-name.json"))
287 require.NoError(t, err)
288
289 validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default)
290 validator.spec = doc
291 res := validator.validateNonEmptyPathParamNames()
292 assert.NotEmpty(t, res.Errors)
293 assert.Len(t, res.Errors, 1)
294 }
295
296 func TestSpec_ValidateCircularAncestry(t *testing.T) {
297 doc, err := loads.Spec(filepath.Join("fixtures", "validation", "direct-circular-ancestor.json"))
298 require.NoError(t, err)
299
300 validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default)
301 validator.spec = doc
302 res := validator.validateDuplicatePropertyNames()
303 assert.NotEmpty(t, res.Errors)
304 assert.Len(t, res.Errors, 1)
305
306 doc, err = loads.Spec(filepath.Join("fixtures", "validation", "indirect-circular-ancestor.json"))
307 require.NoError(t, err)
308
309 validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default)
310 validator.spec = doc
311 res = validator.validateDuplicatePropertyNames()
312 assert.NotEmpty(t, res.Errors)
313 assert.Len(t, res.Errors, 1)
314
315 doc, err = loads.Spec(filepath.Join("fixtures", "validation", "recursive-circular-ancestor.json"))
316 require.NoError(t, err)
317
318 validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default)
319 validator.spec = doc
320 res = validator.validateDuplicatePropertyNames()
321 assert.NotEmpty(t, res.Errors)
322 assert.Len(t, res.Errors, 1)
323 }
324
325 func TestSpec_ValidateReferenced(t *testing.T) {
326 doc, err := loads.Spec(filepath.Join("fixtures", "validation", "valid-referenced.yml"))
327 require.NoError(t, err)
328
329 validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default)
330 validator.spec = doc
331 validator.analyzer = analysis.New(doc.Spec())
332 res := validator.validateReferenced()
333 assert.Empty(t, res.Errors)
334
335 doc, err = loads.Spec(filepath.Join("fixtures", "validation", "invalid-referenced.yml"))
336 require.NoError(t, err)
337
338 validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default)
339 validator.spec = doc
340 validator.analyzer = analysis.New(doc.Spec())
341 res = validator.validateReferenced()
342 assert.Empty(t, res.Errors)
343 assert.NotEmpty(t, res.Warnings)
344 assert.Len(t, res.Warnings, 3)
345 }
346
347 func TestSpec_ValidateReferencesValid(t *testing.T) {
348 doc, err := loads.Spec(filepath.Join("fixtures", "validation", "valid-ref.json"))
349 require.NoError(t, err)
350
351 validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default)
352 validator.spec = doc
353 validator.analyzer = analysis.New(doc.Spec())
354 res := validator.validateReferencesValid()
355 assert.Empty(t, res.Errors)
356
357 doc, err = loads.Spec(filepath.Join("fixtures", "validation", "invalid-ref.json"))
358 require.NoError(t, err)
359
360 validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default)
361 validator.spec = doc
362 validator.analyzer = analysis.New(doc.Spec())
363 res = validator.validateReferencesValid()
364 assert.NotEmpty(t, res.Errors)
365 }
366
367 func TestSpec_ValidateRequiredDefinitions(t *testing.T) {
368 doc, _ := loads.Analyzed(PetStoreJSONMessage, "")
369 validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default)
370 validator.spec = doc
371 validator.analyzer = analysis.New(doc.Spec())
372 res := validator.validateRequiredDefinitions()
373 assert.Empty(t, res.Errors)
374
375
376 sw := doc.Spec()
377 def := sw.Definitions["Tag"]
378 def.Required = append(def.Required, "type")
379 sw.Definitions["Tag"] = def
380 res = validator.validateRequiredDefinitions()
381 assert.NotEmpty(t, res.Errors)
382
383
384 def.PatternProperties = make(map[string]spec.Schema)
385 def.PatternProperties["ty.*"] = *spec.StringProperty()
386 sw.Definitions["Tag"] = def
387 res = validator.validateRequiredDefinitions()
388 assert.Empty(t, res.Errors)
389
390 def.PatternProperties = make(map[string]spec.Schema)
391 def.PatternProperties["^ty.$"] = *spec.StringProperty()
392 sw.Definitions["Tag"] = def
393 res = validator.validateRequiredDefinitions()
394 assert.NotEmpty(t, res.Errors)
395
396
397 def.PatternProperties = nil
398 def.AdditionalProperties = &spec.SchemaOrBool{Allows: true}
399 sw.Definitions["Tag"] = def
400 res = validator.validateRequiredDefinitions()
401 assert.Empty(t, res.Errors)
402
403 def.AdditionalProperties = &spec.SchemaOrBool{Allows: false}
404 sw.Definitions["Tag"] = def
405 res = validator.validateRequiredDefinitions()
406 assert.NotEmpty(t, res.Errors)
407 }
408
409 func TestSpec_ValidateParameters(t *testing.T) {
410 validatorForDoc := func(doc *loads.Document) *SpecValidator {
411
412 validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default)
413 validator.spec = doc
414 validator.analyzer = analysis.New(doc.Spec())
415
416 return validator
417 }
418
419 t.Run("should validate classic PetStore", func(t *testing.T) {
420 doc, err := loads.Analyzed(PetStoreJSONMessage, "")
421 require.NoError(t, err)
422 validator := validatorForDoc(doc)
423
424 res := validator.validateParameters()
425 require.Empty(t, res.Errors)
426 })
427
428 t.Run("should detect duplicate parameters", func(t *testing.T) {
429 doc, err := loads.Analyzed(PetStoreJSONMessage, "")
430 require.NoError(t, err)
431
432 sw := doc.Spec()
433 sw.Paths.Paths["/pets"].Get.Parameters = append(sw.Paths.Paths["/pets"].Get.Parameters, *spec.QueryParam("limit").Typed(stringType, ""))
434 validator := validatorForDoc(doc)
435
436 res := validator.validateParameters()
437 require.NotEmpty(t, res.Errors)
438 assert.Contains(t, res.Errors[0].Error(),
439 `duplicate parameter name "limit" for "query" in operation "getAllPets"`,
440 )
441 })
442
443 t.Run("should detect multiple parameters in body", func(t *testing.T) {
444 doc, err := loads.Analyzed(PetStoreJSONMessage, "")
445 require.NoError(t, err)
446
447 sw := doc.Spec()
448 sw.Paths.Paths["/pets"].Post.Parameters = append(sw.Paths.Paths["/pets"].Post.Parameters, *spec.BodyParam("fake", spec.RefProperty("#/definitions/Pet")))
449 validator := validatorForDoc(doc)
450
451 res := validator.validateParameters()
452 assert.NotEmpty(t, res.Errors)
453 require.Len(t, res.Errors, 1)
454 assert.Contains(t, res.Errors[0].Error(), "has more than 1 body param")
455 })
456
457 t.Run("should detect invalid parameter schema in (modified) classic PetStore", func(t *testing.T) {
458 fixture := filepath.Join("fixtures", "petstore", "swagger-invalid.json")
459
460 t.Run("with raw JSON", func(t *testing.T) {
461
462 jazon, err := os.ReadFile(fixture)
463 require.NoError(t, err)
464 doc, err := loads.Analyzed(jazon, "")
465 require.NoError(t, err)
466 validator := validatorForDoc(doc)
467
468 res := validator.validateParameters()
469 require.Len(t, res.Errors, 2)
470 assert.Contains(t, res.Errors[0].Error(),
471 `"/pets.POST.parameters.pet" must validate one and only one schema (oneOf). Found none valid`,
472 )
473 assert.Contains(t, res.Errors[1].Error(),
474 `/pets.POST.parameters.pet.schema.anyOf in body is a forbidden property`,
475 )
476 })
477 t.Run("with loads.Spec", func(t *testing.T) {
478
479 doc, err := loads.Spec(fixture)
480 require.NoError(t, err)
481
482 err = Spec(doc, strfmt.Default)
483 require.Error(t, err)
484 require.ErrorContains(t, err,
485 "definitions.newPet.anyOf in body is a forbidden property",
486 )
487 })
488
489 t.Run("with invalid Swagger schema", func(t *testing.T) {
490 doc, err := loads.Analyzed(PetStoreJSONMessage, "")
491 require.NoError(t, err)
492 validator := validatorForDoc(doc)
493 delete(validator.schema.Definitions, "parameter")
494
495 require.Panics(t, func() {
496 _ = validator.validateParameters()
497 })
498 })
499 })
500
501 t.Run("should detect duplicate parameters", func(t *testing.T) {
502 doc, err := loads.Analyzed(PetStoreJSONMessage, "")
503 require.NoError(t, err)
504
505 sw := doc.Spec()
506 pp := sw.Paths.Paths["/pets/{id}"]
507 pp.Delete = nil
508 var nameParams []spec.Parameter
509 for _, p := range pp.Parameters {
510 if p.Name == testID {
511 p.Name = "name"
512 nameParams = append(nameParams, p)
513 }
514 }
515 pp.Parameters = nameParams
516 sw.Paths.Paths["/pets/{name}"] = pp
517 validator := validatorForDoc(doc)
518
519 res := validator.validateParameters()
520 assert.NotEmpty(t, res.Errors)
521 require.Len(t, res.Errors, 1)
522 assert.Contains(t, res.Errors[0].Error(), "overlaps with")
523
524 t.Run("should tolerate duplicate parameters, on option", func(t *testing.T) {
525
526 validator.Options.StrictPathParamUniqueness = false
527 res := validator.validateParameters()
528 require.Empty(t, res.Errors)
529 })
530 })
531
532 t.Run("should detect mismatch with path parameter", func(t *testing.T) {
533 doc, err := loads.Analyzed(PetStoreJSONMessage, "")
534 require.NoError(t, err)
535
536 sw := doc.Spec()
537 pp := sw.Paths.Paths["/pets/{id}"]
538 pp.Delete = nil
539 var nameParams []spec.Parameter
540 for _, p := range pp.Parameters {
541 if p.Name == testID {
542 p.Name = "name"
543 nameParams = append(nameParams, p)
544 }
545 }
546 pp.Get.Parameters = nameParams
547 pp.Parameters = nil
548 sw.Paths.Paths["/pets/{id}"] = pp
549 validator := validatorForDoc(doc)
550
551 res := validator.validateParameters()
552 require.NotEmpty(t, res.Errors)
553 require.Len(t, res.Errors, 2)
554 assert.Contains(t, res.Errors[1].Error(),
555 `is not present in path "/pets/{id}"`,
556 )
557 assert.Contains(t, res.Errors[0].Error(),
558 "has no parameter definition",
559 )
560 })
561
562 t.Run("with issue go-swagger/go-swagger#2527", func(t *testing.T) {
563 basePath := filepath.Join("fixtures", "bugs", "2527")
564
565 t.Run("should detect mismatch between parameter and schema", func(t *testing.T) {
566 doc, err := loads.Spec(filepath.Join(basePath, "swagger.yml"))
567 require.NoError(t, err)
568
569 err = Spec(doc, strfmt.Default)
570 require.Error(t, err)
571 require.ErrorContains(t, err,
572 `/deposits.GET.parameters..enum in body is a forbidden property`,
573 )
574 require.ErrorContains(t, err,
575 `deposits.GET.parameters..type in body is a forbidden property`,
576 )
577 require.ErrorContains(t, err,
578 `/deposits.GET.parameters..name in body is required`,
579 )
580 require.ErrorContains(t, err,
581 `/deposits.GET.parameters..in in body is required`,
582 )
583 })
584
585 t.Run("should validate fixed spec", func(t *testing.T) {
586 doc, err := loads.Spec(filepath.Join(basePath, "swagger-fixed.yml"))
587 require.NoError(t, err)
588
589 require.NoError(t, Spec(doc, strfmt.Default))
590 })
591
592 t.Run("should detect missing name and in from refed parameter", func(t *testing.T) {
593 doc, err := loads.Spec(filepath.Join(basePath, "swagger-other.yml"))
594 require.NoError(t, err)
595
596 err = Spec(doc, strfmt.Default)
597 require.ErrorContains(t, err,
598 `"parameters.missingName" must validate one and only one schema (oneOf). Found none valid`,
599 )
600 require.ErrorContains(t, err,
601 `parameters.missingName.name in body is required`,
602 )
603 require.ErrorContains(t, err,
604 `"parameters.missingIn" must validate one and only one schema (oneOf). Found none valid`,
605 )
606 require.ErrorContains(t, err,
607 `parameters.missingIn.in in body is required`,
608 )
609 })
610
611 t.Run("extra parameter JSONSchema validation should not result in duplicate errors", func(t *testing.T) {
612 t.Run("with spec validator", func(t *testing.T) {
613 doc, err := loads.Spec(filepath.Join(basePath, "swagger-schema-error.yml"))
614 require.NoError(t, err)
615
616 errs, warns := NewSpecValidator(doc.Schema(), strfmt.Default).Validate(doc)
617 require.Len(t, errs.Errors, 3)
618 require.Empty(t, warns.Errors)
619
620 var found1, found2, found3 int
621 for _, err := range errs.Errors {
622 switch {
623 case strings.Contains(err.Error(), `definitions.WrongSchema.descriptions in body is a forbidden property`):
624 found1++
625 case strings.Contains(err.Error(), `"definitions.WrongSchema.type" must validate at least one schema (anyOf)`):
626 found2++
627 case strings.Contains(err.Error(), `definitions.WrongSchema.type in body should be one of [array boolean integer null number object string]`):
628 found3++
629 }
630 }
631
632 t.Run("each message should appear exactly once", func(t *testing.T) {
633 require.Equal(t, 1, found1)
634 require.Equal(t, 1, found2)
635 require.Equal(t, 1, found3)
636 })
637 })
638 })
639 })
640 }
641
642 func TestSpec_ValidateItems(t *testing.T) {
643 doc, _ := loads.Analyzed(PetStoreJSONMessage, "")
644 validator := NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default)
645 validator.spec = doc
646 validator.analyzer = analysis.New(doc.Spec())
647 res := validator.validateItems()
648 assert.Empty(t, res.Errors)
649
650
651 sw := doc.Spec()
652 sw.Paths.Paths["/pets"].Get.Parameters[0].Type = arrayType
653 res = validator.validateItems()
654 assert.NotEmpty(t, res.Errors)
655
656 sw.Paths.Paths["/pets"].Get.Parameters[0].Items = spec.NewItems().Typed(stringType, "")
657 res = validator.validateItems()
658 assert.Empty(t, res.Errors)
659
660 sw.Paths.Paths["/pets"].Get.Parameters[0].Items = spec.NewItems().Typed(arrayType, "")
661 res = validator.validateItems()
662 assert.NotEmpty(t, res.Errors)
663
664 sw.Paths.Paths["/pets"].Get.Parameters[0].Items.Items = spec.NewItems().Typed(stringType, "")
665 res = validator.validateItems()
666 assert.Empty(t, res.Errors)
667
668
669 sw.Parameters = make(map[string]spec.Parameter)
670 sw.Parameters["other"] = *spec.SimpleArrayParam("other", arrayType, "csv")
671 res = validator.validateItems()
672 assert.Empty(t, res.Errors)
673
674
675
676
677
678
679
680
681 doc, _ = loads.Analyzed(PetStoreJSONMessage, "")
682 validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default)
683 validator.spec = doc
684 validator.analyzer = analysis.New(doc.Spec())
685 sw = doc.Spec()
686
687 pa := sw.Paths.Paths["/pets"]
688 pa.Parameters = []spec.Parameter{*spec.SimpleArrayParam("another", arrayType, "csv")}
689 sw.Paths.Paths["/pets"] = pa
690 res = validator.validateItems()
691 assert.Empty(t, res.Errors)
692
693 pa = sw.Paths.Paths["/pets"]
694 pp := spec.SimpleArrayParam("other", arrayType, "")
695 pp.Items = nil
696 pa.Parameters = []spec.Parameter{*pp}
697 sw.Paths.Paths["/pets"] = pa
698 res = validator.validateItems()
699 assert.NotEmpty(t, res.Errors)
700
701
702 doc, _ = loads.Analyzed(PetStoreJSONMessage, "")
703 validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default)
704 validator.spec = doc
705 validator.analyzer = analysis.New(doc.Spec())
706 sw = doc.Spec()
707 pa = sw.Paths.Paths["/pets"]
708 pa.Post.Parameters[0].Schema = spec.ArrayProperty(nil)
709 res = validator.validateItems()
710 assert.NotEmpty(t, res.Errors)
711
712
713 doc, _ = loads.Analyzed(PetStoreJSONMessage, "")
714 validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default)
715 validator.spec = doc
716 validator.analyzer = analysis.New(doc.Spec())
717 sw = doc.Spec()
718 pa = sw.Paths.Paths["/pets"]
719 rp := pa.Post.Responses.StatusCodeResponses[200]
720 var hdr spec.Header
721 hdr.Type = arrayType
722 rp.Headers = make(map[string]spec.Header)
723 rp.Headers["X-YADA"] = hdr
724 pa.Post.Responses.StatusCodeResponses[200] = rp
725 res = validator.validateItems()
726 assert.NotEmpty(t, res.Errors)
727
728
729 doc, _ = loads.Analyzed(PetStoreJSONMessage, "")
730 validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default)
731 validator.spec = doc
732 validator.analyzer = analysis.New(doc.Spec())
733 sw = doc.Spec()
734 pa = sw.Paths.Paths["/pets"]
735 rp = pa.Post.Responses.StatusCodeResponses[200]
736 rp.Schema = spec.ArrayProperty(nil)
737 pa.Post.Responses.StatusCodeResponses[200] = rp
738 res = validator.validateItems()
739 assert.NotEmpty(t, res.Errors)
740 }
741
742
743 func TestSpec_ValidDoc(t *testing.T) {
744 fp := filepath.Join("fixtures", "local_expansion", "spec.yaml")
745 doc2, err := loads.Spec(fp)
746 require.NoError(t, err)
747 err = Spec(doc2, strfmt.Default)
748 require.NoError(t, err)
749 }
750
751
752 func TestSpec_InvalidDoc(t *testing.T) {
753 doc, err := loads.Spec(filepath.Join("fixtures", "validation", "default", "invalid-default-value-parameter.json"))
754 require.NoError(t, err)
755 err = Spec(doc, strfmt.Default)
756 require.Error(t, err)
757 }
758
759 func TestSpec_Validate_InvalidInterface(t *testing.T) {
760 fp := filepath.Join("fixtures", "local_expansion", "spec.yaml")
761 doc2, err := loads.Spec(fp)
762 require.NoError(t, err)
763 require.NotNil(t, doc2)
764
765 validator := NewSpecValidator(doc2.Schema(), strfmt.Default)
766 bug := "bzzz"
767 res, _ := validator.Validate(bug)
768 assert.NotEmpty(t, res.Errors)
769 assert.Contains(t, res.Errors[0].Error(), "can only validate spec.Document objects")
770 }
771
772 func TestSpec_ValidateBodyFormDataParams(t *testing.T) {
773 res, _ := loadAndValidate(t, filepath.Join("fixtures", "validation", "invalid-formdata-body-params.json"))
774 assert.NotEmpty(t, res.Errors)
775 assert.Len(t, res.Errors, 1)
776 }
777
778 func TestSpec_Issue73(t *testing.T) {
779 res, _ := loadAndValidate(t, filepath.Join("fixtures", "bugs", "73", "fixture-swagger.yaml"))
780 assert.Empty(t, res.Errors, " in fixture-swagger.yaml")
781
782 res, _ = loadAndValidate(t, filepath.Join("fixtures", "bugs", "73", "fixture-swagger-2.yaml"))
783 assert.Empty(t, res.Errors, "in fixture-swagger-2.yaml")
784
785 res, _ = loadAndValidate(t, filepath.Join("fixtures", "bugs", "73", "fixture-swagger-3.yaml"))
786 assert.Empty(t, res.Errors, "in fixture-swagger-3.yaml")
787
788 res, _ = loadAndValidate(t, filepath.Join("fixtures", "bugs", "73", "fixture-swagger-good.yaml"))
789 assert.Empty(t, res.Errors, " in fixture-swagger-good.yaml")
790 }
791
792 func TestSpec_Issue1341(t *testing.T) {
793
794 res, _ := loadAndValidate(t, filepath.Join("fixtures", "bugs", "1341", "fixture-1341-good.yaml"))
795 assert.Empty(t, res.Errors, " in fixture-1341-good.yaml")
796 assert.Len(t, res.Warnings, 1, " in fixture-1341-good.yaml")
797
798 res, _ = loadAndValidate(t, filepath.Join("fixtures", "bugs", "1341", "fixture-1341.yaml"))
799 assert.Empty(t, res.Errors, "in fixture-1341.yaml")
800 assert.Empty(t, res.Warnings, "in fixture-1341.yaml")
801
802 res, _ = loadAndValidate(t, filepath.Join("fixtures", "bugs", "1341", "fixture-1341-2.yaml"))
803 assert.Empty(t, res.Errors, "in fixture-1341-2.yaml")
804 assert.Empty(t, res.Warnings, "in fixture-1341-2.yaml")
805
806 res, _ = loadAndValidate(t, filepath.Join("fixtures", "bugs", "1341", "fixture-1341-3.yaml"))
807 assert.Empty(t, res.Errors, "in fixture-1341-3.yaml")
808 assert.Len(t, res.Warnings, 4, "in fixture-1341-3.yaml")
809
810 res, _ = loadAndValidate(t, filepath.Join("fixtures", "bugs", "1341", "fixture-1341-4.yaml"))
811 assert.Empty(t, res.Errors, "in fixture-1341-4.yaml")
812 assert.Empty(t, res.Warnings, "in fixture-1341-4.yaml")
813
814 res, _ = loadAndValidate(t, filepath.Join("fixtures", "bugs", "1341", "fixture-1341-5.yaml"))
815 assert.Len(t, res.Errors, 4, "in fixture-1341-5.yaml")
816 assert.Empty(t, res.Warnings, "in fixture-1341-5.yaml")
817 }
818
819
820 func Test_Issue1614(t *testing.T) {
821 path := filepath.Join("fixtures", "bugs", "1614", "gitea.json")
822 testIssue(t, path, 0, 3)
823 }
824
825
826 func Test_Issue1621(t *testing.T) {
827 path := filepath.Join("fixtures", "bugs", "1621", "fixture-1621.yaml")
828 testIssue(t, path, 0, 0)
829 }
830
831
832 func Test_Issue1429(t *testing.T) {
833 path := filepath.Join("fixtures", "bugs", "1429", "swagger.yaml")
834 testIssue(t, path, 0, 0)
835 }
836
837 func TestSpec_ValidationTypeMismatch(t *testing.T) {
838 doc, err := loads.Spec(filepath.Join("fixtures", "validation", "type-keyword-mismatch.yaml"))
839 require.NoError(t, err)
840 validator := NewSpecValidator(doc.Schema(), strfmt.Default)
841 validator.spec = doc
842 validator.analyzer = analysis.New(doc.Spec())
843 res := validator.validateParameters()
844 assert.NotEmpty(t, res.Warnings)
845 assert.Len(t, res.Warnings, 3)
846
847 warnings := verifiedTestWarnings(res)
848 assert.Contains(t, warnings, `validation keywords of parameter "id" in path "/test/{id}/string" don't match its type string`)
849 assert.Contains(t, warnings, `validation keywords of parameter "id" in path "/test/{id}/integer" don't match its type integer`)
850 assert.Contains(t, warnings, `validation keywords of parameter "id" in path "/test/{id}/array" don't match its type array`)
851 }
852
853 func loadAndValidate(t testing.TB, fp string, early ...bool) (*Result, *Result) {
854 doc, err := loads.Spec(fp)
855 require.NoError(t, err)
856 require.NotNil(t, doc)
857 validator := NewSpecValidator(doc.Schema(), strfmt.Default)
858
859 if len(early) == 0 {
860 validator.Options = Opts{ContinueOnErrors: true}
861 } else {
862 for _, flag := range early {
863 validator.Options = Opts{ContinueOnErrors: flag}
864 }
865 }
866 return validator.Validate(doc)
867 }
868
869 func TestItemsProperty_Issue43(t *testing.T) {
870 for _, fixture := range []string{
871 "fixture-43.yaml",
872 "fixture-43-variants.yaml",
873 "fixture-1456.yaml",
874 } {
875 fp := filepath.Join("fixtures", "bugs", "43", fixture)
876 res, warnings := loadAndValidate(t, fp)
877 assert.Truef(t, res.IsValid(), "expected spec from %s to be valid", fixture)
878 assert.Emptyf(t, res.Errors, "expected no error in %s", fixture)
879 assert.Emptyf(t, res.Warnings, "expected no warning in %s", fixture)
880 assert.Emptyf(t, warnings, "expected no warning in %s", fixture)
881 }
882
883 fp := filepath.Join("fixtures", "bugs", "43", "fixture-43-fail.yaml")
884 res, _ := loadAndValidate(t, fp)
885 assert.Falsef(t, res.IsValid(), "expected spec to be invalid")
886 assert.Greater(t, len(res.Errors), 3)
887
888 fp = filepath.Join("fixtures", "validation", "fixture-1171.yaml")
889 res, _ = loadAndValidate(t, fp)
890 assert.Falsef(t, res.IsValid(), "expected spec to be invalid")
891 assert.Greater(t, len(res.Errors), 3)
892 found := false
893 for _, e := range res.Errors {
894 found = strings.Contains(e.Error(), "array requires items definition")
895 if found {
896 break
897 }
898 }
899 assert.True(t, found)
900 }
901
902 func Test_Issue2137(t *testing.T) {
903 fp := filepath.Join("fixtures", "bugs", "2137", "fixture-2137.yaml")
904 res, _ := loadAndValidate(t, fp)
905 assert.Falsef(t, res.IsValid(), "expected spec to be invalid")
906 found := false
907 for _, e := range res.Errors {
908 found = strings.Contains(e.Error(), `"test" is defined 2 times`)
909 if found {
910 break
911 }
912 }
913 assert.True(t, found)
914 }
915
916 func Test_Examples(t *testing.T) {
917 fp := filepath.Join("fixtures", "bugs", "2649", "swagger.yaml")
918
919 doc, err := loads.Spec(fp)
920 require.NoError(t, err)
921 require.NotNil(t, doc)
922
923 validator := NewSpecValidator(doc.Schema(), strfmt.Default)
924 validator.Options.SkipSchemataResult = true
925
926 res, _ := validator.Validate(doc)
927 if !assert.Truef(t, res.IsValid(), "expected spec to be valid") {
928 spew.Dump(res.Errors)
929 }
930 }
931
932 func Test_2866(t *testing.T) {
933
934
935
936 fp := filepath.Join("fixtures", "bugs", "2866", "2866.yaml")
937
938 doc, err := loads.Spec(fp)
939 require.NoError(t, err)
940 require.NotNil(t, doc)
941
942 require.NoError(t, Spec(doc, strfmt.Default))
943 }
944
View as plain text