1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package analysis
16
17 import (
18 "bytes"
19 "encoding/json"
20 "fmt"
21 "io"
22 "log"
23 "os"
24 "path"
25 "path/filepath"
26 "regexp"
27 "strings"
28 "testing"
29
30 "github.com/go-openapi/analysis/internal/antest"
31 "github.com/go-openapi/analysis/internal/flatten/operations"
32 "github.com/go-openapi/jsonpointer"
33 "github.com/go-openapi/spec"
34 "github.com/stretchr/testify/assert"
35 "github.com/stretchr/testify/require"
36 )
37
38 var (
39 rex = regexp.MustCompile(`"\$ref":\s*"(.+)"`)
40 oairex = regexp.MustCompile(`oiagen`)
41 )
42
43 type refFixture struct {
44 Key string
45 Ref spec.Ref
46 Location string
47 Expected interface{}
48 }
49
50 func makeRefFixtures() []refFixture {
51 return []refFixture{
52 {Key: "#/parameters/someParam/schema", Ref: spec.MustCreateRef("#/definitions/record")},
53 {Key: "#/paths/~1some~1where~1{id}/parameters/1/schema", Ref: spec.MustCreateRef("#/definitions/record")},
54 {Key: "#/paths/~1some~1where~1{id}/get/parameters/2/schema", Ref: spec.MustCreateRef("#/definitions/record")},
55 {Key: "#/responses/someResponse/schema", Ref: spec.MustCreateRef("#/definitions/record")},
56 {Key: "#/paths/~1some~1where~1{id}/get/responses/default/schema", Ref: spec.MustCreateRef("#/definitions/record")},
57 {Key: "#/paths/~1some~1where~1{id}/get/responses/200/schema", Ref: spec.MustCreateRef("#/definitions/tag")},
58 {Key: "#/definitions/namedAgain", Ref: spec.MustCreateRef("#/definitions/named")},
59 {Key: "#/definitions/datedTag/allOf/1", Ref: spec.MustCreateRef("#/definitions/tag")},
60 {Key: "#/definitions/datedRecords/items/1", Ref: spec.MustCreateRef("#/definitions/record")},
61 {Key: "#/definitions/datedTaggedRecords/items/1", Ref: spec.MustCreateRef("#/definitions/record")},
62 {Key: "#/definitions/datedTaggedRecords/additionalItems", Ref: spec.MustCreateRef("#/definitions/tag")},
63 {Key: "#/definitions/otherRecords/items", Ref: spec.MustCreateRef("#/definitions/record")},
64 {Key: "#/definitions/tags/additionalProperties", Ref: spec.MustCreateRef("#/definitions/tag")},
65 {Key: "#/definitions/namedThing/properties/name", Ref: spec.MustCreateRef("#/definitions/named")},
66 }
67 }
68
69 func TestFlatten_ImportExternalReferences(t *testing.T) {
70 log.SetOutput(io.Discard)
71 defer log.SetOutput(os.Stdout)
72
73
74
75 bp := filepath.Join(".", "fixtures", "external_definitions_valid.yml")
76 sp := antest.LoadOrFail(t, bp)
77
78 opts := &FlattenOpts{
79 Spec: New(sp),
80 BasePath: bp,
81 }
82
83
84
85 _, erx := importExternalReferences(opts)
86 require.NoError(t, erx)
87
88 require.Len(t, sp.Definitions, 11)
89 require.Contains(t, sp.Definitions, "tag")
90 require.Contains(t, sp.Definitions, "named")
91 require.Contains(t, sp.Definitions, "record")
92
93 for idx, toPin := range makeRefFixtures() {
94 i := idx
95 v := toPin
96 sp := sp
97
98 t.Run(fmt.Sprintf("import check ref [%d]: %q", i, v.Key), func(t *testing.T) {
99 t.Parallel()
100
101 ptr, err := jsonpointer.New(v.Key[1:])
102 require.NoErrorf(t, err, "error on jsonpointer.New(%q)", v.Key[1:])
103
104 vv, _, err := ptr.Get(sp)
105 require.NoErrorf(t, err, "error on ptr.Get(p for key=%s)", v.Key[1:])
106
107 switch tv := vv.(type) {
108 case *spec.Schema:
109 require.Equal(t, v.Ref.String(), tv.Ref.String(), "for %s", v.Key)
110
111 case spec.Schema:
112 require.Equal(t, v.Ref.String(), tv.Ref.String(), "for %s", v.Key)
113
114 case *spec.SchemaOrBool:
115 require.Equal(t, v.Ref.String(), tv.Schema.Ref.String(), "for %s", v.Key)
116
117 case *spec.SchemaOrArray:
118 require.Equal(t, v.Ref.String(), tv.Schema.Ref.String(), "for %s", v.Key)
119
120 default:
121 require.Fail(t, "unknown type", "got %T", vv)
122 }
123 })
124 }
125
126
127 jazon := antest.AsJSON(t, sp)
128
129 expected, err := os.ReadFile(filepath.Join("fixtures", "expected", "external-references-1.json"))
130 require.NoError(t, err)
131
132 assert.JSONEq(t, string(expected), jazon)
133
134
135 opts.Spec.reload()
136
137 _, err = importExternalReferences(&FlattenOpts{
138 Spec: New(sp),
139 BasePath: bp,
140 })
141 require.NoError(t, err)
142
143 opts.Spec.reload()
144 for _, ref := range opts.Spec.references.schemas {
145 require.True(t, ref.HasFragmentOnly)
146 }
147
148
149 sp = antest.LoadOrFail(t, bp)
150 an := New(sp)
151
152 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: true}))
153
154 jazon = antest.AsJSON(t, an.spec)
155
156 expected, err = os.ReadFile(filepath.Join("fixtures", "expected", "external-references-2.json"))
157 require.NoError(t, err)
158
159 assert.JSONEq(t, string(expected), jazon)
160 }
161
162 func makeFlattenFixtures() []refFixture {
163 return []refFixture{
164 {
165 Key: "#/responses/notFound/schema",
166 Location: "#/responses/notFound/schema",
167 Ref: spec.MustCreateRef("#/definitions/error"),
168 Expected: nil,
169 },
170 {
171 Key: "#/paths/~1some~1where~1{id}/parameters/0",
172 Location: "#/paths/~1some~1where~1{id}/parameters/0/name",
173 Ref: spec.Ref{},
174 Expected: "id",
175 },
176 {
177 Key: "#/paths/~1other~1place",
178 Location: "#/paths/~1other~1place/get/operationId",
179 Ref: spec.Ref{},
180 Expected: "modelOp",
181 },
182 {
183 Key: "#/paths/~1some~1where~1{id}/get/parameters/0",
184 Location: "#/paths/~1some~1where~1{id}/get/parameters/0/name",
185 Ref: spec.Ref{},
186 Expected: "limit",
187 },
188 {
189 Key: "#/paths/~1some~1where~1{id}/get/parameters/1",
190 Location: "#/paths/~1some~1where~1{id}/get/parameters/1/name",
191 Ref: spec.Ref{},
192 Expected: "some",
193 },
194 {
195 Key: "#/paths/~1some~1where~1{id}/get/parameters/2",
196 Location: "#/paths/~1some~1where~1{id}/get/parameters/2/name",
197 Ref: spec.Ref{},
198 Expected: "other",
199 },
200 {
201 Key: "#/paths/~1some~1where~1{id}/get/parameters/3",
202 Location: "#/paths/~1some~1where~1{id}/get/parameters/3/schema",
203 Ref: spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBody"),
204 Expected: "",
205 },
206 {
207 Key: "#/paths/~1some~1where~1{id}/get/responses/200",
208 Location: "#/paths/~1some~1where~1{id}/get/responses/200/schema",
209 Ref: spec.MustCreateRef("#/definitions/getSomeWhereIdOKBody"),
210 Expected: "",
211 },
212 {
213 Key: "#/definitions/namedAgain",
214 Location: "",
215 Ref: spec.MustCreateRef("#/definitions/named"),
216 Expected: "",
217 },
218 {
219 Key: "#/definitions/namedThing/properties/name",
220 Location: "",
221 Ref: spec.MustCreateRef("#/definitions/named"),
222 Expected: "",
223 },
224 {
225 Key: "#/definitions/namedThing/properties/namedAgain",
226 Location: "",
227 Ref: spec.MustCreateRef("#/definitions/namedAgain"),
228 Expected: "",
229 },
230 {
231 Key: "#/definitions/datedRecords/items/1",
232 Location: "",
233 Ref: spec.MustCreateRef("#/definitions/record"),
234 Expected: "",
235 },
236 {
237 Key: "#/definitions/otherRecords/items",
238 Location: "",
239 Ref: spec.MustCreateRef("#/definitions/record"),
240 Expected: "",
241 },
242 {
243 Key: "#/definitions/tags/additionalProperties",
244 Location: "",
245 Ref: spec.MustCreateRef("#/definitions/tag"),
246 Expected: "",
247 },
248 {
249 Key: "#/definitions/datedTag/allOf/1",
250 Location: "",
251 Ref: spec.MustCreateRef("#/definitions/tag"),
252 Expected: "",
253 },
254 {
255 Key: "#/definitions/nestedThingRecord/items/1",
256 Location: "",
257 Ref: spec.MustCreateRef("#/definitions/nestedThingRecordItems1"),
258 Expected: "",
259 },
260 {
261 Key: "#/definitions/nestedThingRecord/items/2",
262 Location: "",
263 Ref: spec.MustCreateRef("#/definitions/nestedThingRecordItems2"),
264 Expected: "",
265 },
266 {
267 Key: "#/definitions/nestedThing/properties/record",
268 Location: "",
269 Ref: spec.MustCreateRef("#/definitions/nestedThingRecord"),
270 Expected: "",
271 },
272 {
273 Key: "#/definitions/named",
274 Location: "#/definitions/named/type",
275 Ref: spec.Ref{},
276 Expected: spec.StringOrArray{"string"},
277 },
278 {
279 Key: "#/definitions/error",
280 Location: "#/definitions/error/properties/id/type",
281 Ref: spec.Ref{},
282 Expected: spec.StringOrArray{"integer"},
283 },
284 {
285 Key: "#/definitions/record",
286 Location: "#/definitions/record/properties/createdAt/format",
287 Ref: spec.Ref{},
288 Expected: "date-time",
289 },
290 {
291 Key: "#/definitions/getSomeWhereIdOKBody",
292 Location: "#/definitions/getSomeWhereIdOKBody/properties/record",
293 Ref: spec.MustCreateRef("#/definitions/nestedThing"),
294 Expected: nil,
295 },
296 {
297 Key: "#/definitions/getSomeWhereIdParamsBody",
298 Location: "#/definitions/getSomeWhereIdParamsBody/properties/record",
299 Ref: spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecord"),
300 Expected: nil,
301 },
302 {
303 Key: "#/definitions/getSomeWhereIdParamsBodyRecord",
304 Location: "#/definitions/getSomeWhereIdParamsBodyRecord/items/1",
305 Ref: spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems1"),
306 Expected: nil,
307 },
308 {
309 Key: "#/definitions/getSomeWhereIdParamsBodyRecord",
310 Location: "#/definitions/getSomeWhereIdParamsBodyRecord/items/2",
311 Ref: spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems2"),
312 Expected: nil,
313 },
314 {
315 Key: "#/definitions/getSomeWhereIdParamsBodyRecordItems2",
316 Location: "#/definitions/getSomeWhereIdParamsBodyRecordItems2/allOf/0/format",
317 Ref: spec.Ref{},
318 Expected: "date",
319 },
320 {
321 Key: "#/definitions/getSomeWhereIdParamsBodyRecordItems2Name",
322 Location: "#/definitions/getSomeWhereIdParamsBodyRecordItems2Name/properties/createdAt/format",
323 Ref: spec.Ref{},
324 Expected: "date-time",
325 },
326 {
327 Key: "#/definitions/getSomeWhereIdParamsBodyRecordItems2",
328 Location: "#/definitions/getSomeWhereIdParamsBodyRecordItems2/properties/name",
329 Ref: spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems2Name"),
330 Expected: "date",
331 },
332 }
333 }
334
335 func TestFlatten_CheckRef(t *testing.T) {
336 bp := filepath.Join("fixtures", "flatten.yml")
337 sp := antest.LoadOrFail(t, bp)
338
339 require.NoError(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp}))
340
341 for idx, toPin := range makeFlattenFixtures() {
342 i := idx
343 v := toPin
344 sp := sp
345
346 t.Run(fmt.Sprintf("check ref after flatten %q", v.Key), func(t *testing.T) {
347 pk := v.Key[1:]
348 if v.Location != "" {
349 pk = v.Location[1:]
350 }
351
352 ptr, err := jsonpointer.New(pk)
353 require.NoError(t, err, "at %d for %s", i, v.Key)
354
355 d, _, err := ptr.Get(sp)
356 require.NoError(t, err)
357
358 if v.Ref.String() == "" {
359 assert.Equal(t, v.Expected, d)
360
361 return
362 }
363
364 switch s := d.(type) {
365 case *spec.Schema:
366 assert.Equal(t, v.Ref.String(), s.Ref.String(), "at %d for %s", i, v.Key)
367
368 case spec.Schema:
369 assert.Equal(t, v.Ref.String(), s.Ref.String(), "at %d for %s", i, v.Key)
370
371 case *spec.SchemaOrArray:
372 var sRef spec.Ref
373 if s != nil && s.Schema != nil {
374 sRef = s.Schema.Ref
375 }
376 assert.Equal(t, v.Ref.String(), sRef.String(), "at %d for %s", i, v.Key)
377
378 case *spec.SchemaOrBool:
379 var sRef spec.Ref
380 if s != nil && s.Schema != nil {
381 sRef = s.Schema.Ref
382 }
383 assert.Equal(t, v.Ref.String(), sRef.String(), "at %d for %s", i, v.Key)
384
385 default:
386 assert.Fail(t, "unknown type", "got %T at %d for %s", d, i, v.Key)
387 }
388 })
389 }
390 }
391
392 func TestFlatten_FullWithOAIGen(t *testing.T) {
393 bp := filepath.Join("fixtures", "oaigen", "fixture-oaigen.yaml")
394 sp := antest.LoadOrFail(t, bp)
395
396 require.NoError(t, Flatten(FlattenOpts{
397 Spec: New(sp), BasePath: bp, Verbose: true,
398 Minimal: false, RemoveUnused: false,
399 }))
400
401 res := getInPath(t, sp, "/some/where", "/get/responses/204/schema")
402 assert.JSONEqf(t, `{"$ref": "#/definitions/uniqueName1"}`, res, "Expected a simple schema for response")
403
404 res = getInPath(t, sp, "/some/where", "/post/responses/204/schema")
405 assert.JSONEqf(t, `{"$ref": "#/definitions/d"}`, res, "Expected a simple schema for response")
406
407 res = getInPath(t, sp, "/some/where", "/get/responses/206/schema")
408 assert.JSONEqf(t, `{"$ref": "#/definitions/a"}`, res, "Expected a simple schema for response")
409
410 res = getInPath(t, sp, "/some/where", "/get/responses/304/schema")
411 assert.JSONEqf(t, `{"$ref": "#/definitions/transitive11"}`, res, "Expected a simple schema for response")
412
413 res = getInPath(t, sp, "/some/where", "/get/responses/205/schema")
414 assert.JSONEqf(t, `{"$ref": "#/definitions/b"}`, res, "Expected a simple schema for response")
415
416 res = getInPath(t, sp, "/some/where", "/post/responses/200/schema")
417 assert.JSONEqf(t, `{"type": "integer"}`, res, "Expected a simple schema for response")
418
419 res = getInPath(t, sp, "/some/where", "/post/responses/default/schema")
420
421 assert.JSONEqf(t, `{"type": "integer"}`, res, "Expected a simple schema for response")
422
423 res = getDefinition(t, sp, "a")
424 assert.JSONEqf(t,
425 `{"type": "object", "properties": { "a": { "$ref": "#/definitions/aAOAIGen" }}}`,
426 res, "Expected a simple schema for response")
427
428 res = getDefinition(t, sp, "aA")
429 assert.JSONEqf(t, `{"type": "string", "format": "date"}`, res, "Expected a simple schema for response")
430
431 res = getDefinition(t, sp, "aAOAIGen")
432 assert.JSONEqf(t, `
433 {
434 "type": "object",
435 "properties": {
436 "b": {
437 "type": "integer"
438 }
439 },
440 "x-go-gen-location": "models"
441 }
442 `, res, "Expected a simple schema for response")
443
444 res = getDefinition(t, sp, "bB")
445 assert.JSONEqf(t, `{"type": "string", "format": "date-time"}`, res, "Expected a simple schema for response")
446
447 _, ok := sp.Definitions["bItems"]
448 assert.Falsef(t, ok, "Did not expect a definition for %s", "bItems")
449
450 res = getDefinition(t, sp, "d")
451 assert.JSONEqf(t, `
452 {
453 "type": "object",
454 "properties": {
455 "c": {
456 "type": "integer"
457 }
458 }
459 }
460 `, res, "Expected a simple schema for response")
461
462 res = getDefinition(t, sp, "b")
463 assert.JSONEqf(t, `
464 {
465 "type": "array",
466 "items": {
467 "$ref": "#/definitions/d"
468 }
469 }
470 `, res, "Expected a ref in response")
471
472 res = getDefinition(t, sp, "myBody")
473 assert.JSONEqf(t, `
474 {
475 "type": "object",
476 "properties": {
477 "aA": {
478 "$ref": "#/definitions/aA"
479 },
480 "prop1": {
481 "type": "integer"
482 }
483 }
484 }
485 `, res, "Expected a simple schema for response")
486
487 res = getDefinition(t, sp, "uniqueName2")
488 assert.JSONEqf(t, `{"$ref": "#/definitions/notUniqueName2"}`, res, "Expected a simple schema for response")
489
490 res = getDefinition(t, sp, "notUniqueName2")
491 assert.JSONEqf(t, `
492 {
493 "type": "object",
494 "properties": {
495 "prop6": {
496 "type": "integer"
497 }
498 }
499 }
500 `, res, "Expected a simple schema for response")
501
502 res = getDefinition(t, sp, "uniqueName1")
503 assert.JSONEqf(t, `{
504 "type": "object",
505 "properties": {
506 "prop5": {
507 "type": "integer"
508 }}}`, res, "Expected a simple schema for response")
509
510
511 res = getDefinition(t, sp, "getWithSliceContainerDefaultBody")
512 assert.JSONEqf(t, `{
513 "allOf": [
514 {
515 "$ref": "#/definitions/uniqueName3"
516 },
517 {
518 "$ref": "#/definitions/getWithSliceContainerDefaultBodyAllOf1"
519 }
520 ],
521 "x-go-gen-location": "operations"
522 }`, res, "Expected a simple schema for response")
523
524 res = getDefinition(t, sp, "getWithSliceContainerDefaultBodyAllOf1")
525 assert.JSONEqf(t, `{
526 "type": "object",
527 "properties": {
528 "prop8": {
529 "type": "string"
530 }
531 },
532 "x-go-gen-location": "models"
533 }`, res, "Expected a simple schema for response")
534
535 res = getDefinition(t, sp, "getWithTupleContainerDefaultBody")
536 assert.JSONEqf(t, `{
537 "type": "array",
538 "items": [
539 {
540 "$ref": "#/definitions/uniqueName3"
541 },
542 {
543 "$ref": "#/definitions/getWithSliceContainerDefaultBodyAllOf1"
544 }
545 ],
546 "x-go-gen-location": "operations"
547 }`, res, "Expected a simple schema for response")
548
549
550 res = getDefinition(t, sp, "getWithTupleConflictDefaultBody")
551 assert.JSONEqf(t, `{
552 "type": "array",
553 "items": [
554 {
555 "$ref": "#/definitions/uniqueName4"
556 },
557 {
558 "$ref": "#/definitions/getWithTupleConflictDefaultBodyItems1"
559 }
560 ],
561 "x-go-gen-location": "operations"
562 }`, res, "Expected a simple schema for response")
563
564 res = getDefinition(t, sp, "getWithTupleConflictDefaultBodyItems1")
565 assert.JSONEqf(t, `{
566 "type": "object",
567 "properties": {
568 "prop10": {
569 "type": "string"
570 }
571 },
572 "x-go-gen-location": "models"
573 }`, res, "Expected a simple schema for response")
574 }
575
576 func TestFlatten_MinimalWithOAIGen(t *testing.T) {
577 var sp *spec.Swagger
578 defer func() {
579 if t.Failed() && sp != nil {
580 t.Log(antest.AsJSON(t, sp))
581 }
582 }()
583
584 bp := filepath.Join("fixtures", "oaigen", "fixture-oaigen.yaml")
585 sp = antest.LoadOrFail(t, bp)
586
587 var logCapture bytes.Buffer
588 log.SetOutput(&logCapture)
589 defer log.SetOutput(os.Stdout)
590
591 require.NoError(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false}))
592
593 msg := logCapture.String()
594 if !assert.NotContainsf(t, msg,
595 "warning: duplicate flattened definition name resolved as aAOAIGen", "Expected log message") {
596 t.Logf("Captured log: %s", msg)
597 }
598 if !assert.NotContainsf(t, msg,
599 "warning: duplicate flattened definition name resolved as uniqueName2OAIGen", "Expected log message") {
600 t.Logf("Captured log: %s", msg)
601 }
602 res := getInPath(t, sp, "/some/where", "/get/responses/204/schema")
603 assert.JSONEqf(t, `{"$ref": "#/definitions/uniqueName1"}`, res, "Expected a simple schema for response")
604
605 res = getInPath(t, sp, "/some/where", "/post/responses/204/schema")
606 assert.JSONEqf(t, `{"$ref": "#/definitions/d"}`, res, "Expected a simple schema for response")
607
608 res = getInPath(t, sp, "/some/where", "/get/responses/206/schema")
609 assert.JSONEqf(t, `{"$ref": "#/definitions/a"}`, res, "Expected a simple schema for response")
610
611 res = getInPath(t, sp, "/some/where", "/get/responses/304/schema")
612 assert.JSONEqf(t, `{"$ref": "#/definitions/transitive11"}`, res, "Expected a simple schema for response")
613
614 res = getInPath(t, sp, "/some/where", "/get/responses/205/schema")
615 assert.JSONEqf(t, `{"$ref": "#/definitions/b"}`, res, "Expected a simple schema for response")
616
617 res = getInPath(t, sp, "/some/where", "/post/responses/200/schema")
618 assert.JSONEqf(t, `{"type": "integer"}`, res, "Expected a simple schema for response")
619
620 res = getInPath(t, sp, "/some/where", "/post/responses/default/schema")
621
622 assert.JSONEqf(t, `{"type": "integer"}`, res, "Expected a simple schema for response")
623
624 res = getDefinition(t, sp, "aA")
625 assert.JSONEqf(t, `{"type": "string", "format": "date"}`, res, "Expected a simple schema for response")
626
627 res = getDefinition(t, sp, "a")
628 assert.JSONEqf(t, `{
629 "type": "object",
630 "properties": {
631 "a": {
632 "type": "object",
633 "properties": {
634 "b": {
635 "type": "integer"
636 }
637 }
638 }
639 }
640 }`, res, "Expected a simple schema for response")
641
642 res = getDefinition(t, sp, "bB")
643 assert.JSONEqf(t, `{"type": "string", "format": "date-time"}`, res, "Expected a simple schema for response")
644
645 _, ok := sp.Definitions["bItems"]
646 assert.Falsef(t, ok, "Did not expect a definition for %s", "bItems")
647
648 res = getDefinition(t, sp, "d")
649 assert.JSONEqf(t, `{
650 "type": "object",
651 "properties": {
652 "c": {
653 "type": "integer"
654 }
655 }
656 }`, res, "Expected a simple schema for response")
657
658 res = getDefinition(t, sp, "b")
659 assert.JSONEqf(t, `{
660 "type": "array",
661 "items": {
662 "$ref": "#/definitions/d"
663 }
664 }`, res, "Expected a ref in response")
665
666 res = getDefinition(t, sp, "myBody")
667 assert.JSONEqf(t, `{
668 "type": "object",
669 "properties": {
670 "aA": {
671 "$ref": "#/definitions/aA"
672 },
673 "prop1": {
674 "type": "integer"
675 }
676 }
677 }`, res, "Expected a simple schema for response")
678
679 res = getDefinition(t, sp, "uniqueName2")
680 assert.JSONEqf(t, `{"$ref": "#/definitions/notUniqueName2"}`, res, "Expected a simple schema for response")
681
682
683 res = getInPath(t, sp, "/with/slice/container", "/get/responses/default/schema")
684 assert.JSONEqf(t, `{
685 "allOf": [
686 {
687 "$ref": "#/definitions/uniqueName3"
688 },
689 {
690 "$ref": "#/definitions/getWithSliceContainerDefaultBodyAllOf1"
691 }
692 ]
693 }`, res, "Expected a simple schema for response")
694
695
696 res = getInPath(t, sp, "/with/tuple/container", "/get/responses/default/schema")
697 assert.JSONEqf(t, `{
698 "type": "array",
699 "items": [
700 {
701 "$ref": "#/definitions/uniqueName3"
702 },
703 {
704 "$ref": "#/definitions/getWithSliceContainerDefaultBodyAllOf1"
705 }
706 ]
707 }`, res, "Expected a simple schema for response")
708
709
710 res = getInPath(t, sp, "/with/tuple/conflict", "/get/responses/default/schema")
711 assert.JSONEqf(t, `{
712 "type": "array",
713 "items": [
714 {
715 "$ref": "#/definitions/uniqueName4"
716 },
717 {
718 "type": "object",
719 "properties": {
720 "prop10": {
721 "type": "string"
722 }
723 }
724 }
725 ]
726 }`, res, "Expected a simple schema for response")
727 }
728
729 func assertNoOAIGen(t *testing.T, bp string, sp *spec.Swagger) (success bool) {
730 var logCapture bytes.Buffer
731 log.SetOutput(&logCapture)
732 defer log.SetOutput(os.Stdout)
733
734 defer func() {
735 success = !t.Failed()
736 }()
737
738 require.NoError(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Verbose: true, Minimal: false, RemoveUnused: false}))
739
740 msg := logCapture.String()
741 assert.NotContains(t, msg, "warning")
742
743 for k := range sp.Definitions {
744 require.NotContains(t, k, "OAIGen")
745 }
746
747 return
748 }
749
750 func TestFlatten_OAIGen(t *testing.T) {
751 for _, fixture := range []string{
752 filepath.Join("fixtures", "oaigen", "test3-swagger.yaml"),
753 filepath.Join("fixtures", "oaigen", "test3-bis-swagger.yaml"),
754 filepath.Join("fixtures", "oaigen", "test3-ter-swagger.yaml"),
755 } {
756 t.Run("flatten_oiagen_1260_"+fixture, func(t *testing.T) {
757 t.Parallel()
758
759 bp := filepath.Join("fixtures", "oaigen", "test3-swagger.yaml")
760 sp := antest.LoadOrFail(t, bp)
761
762 require.Truef(t, assertNoOAIGen(t, bp, sp), "did not expect an OAIGen definition here")
763 })
764 }
765 }
766
767 func TestMoreNameInlinedSchemas(t *testing.T) {
768 bp := filepath.Join("fixtures", "more_nested_inline_schemas.yml")
769 sp := antest.LoadOrFail(t, bp)
770
771 require.NoError(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Verbose: true, Minimal: false, RemoveUnused: false}))
772
773 res := getInPath(t, sp, "/some/where/{id}", "/post/responses/200/schema")
774 assert.JSONEqf(t, `
775 {
776 "type": "object",
777 "additionalProperties": {
778 "type": "object",
779 "additionalProperties": {
780 "type": "object", "additionalProperties": {
781 "$ref": "#/definitions/postSomeWhereIdOKBodyAdditionalPropertiesAdditionalPropertiesAdditionalProperties"
782 }
783 }
784 }
785 }`,
786 res, "Expected a simple schema for response")
787
788 res = getInPath(t, sp, "/some/where/{id}", "/post/responses/204/schema")
789 assert.JSONEqf(t, `
790 {
791 "type": "object",
792 "additionalProperties": {
793 "type": "array",
794 "items": {
795 "type": "object",
796 "additionalProperties": {
797 "type": "array",
798 "items": {
799 "type": "object",
800 "additionalProperties": {
801 "type": "array",
802 "items": {
803 "$ref": "#/definitions/postSomeWhereIdNoContentBodyAdditionalPropertiesItemsAdditionalPropertiesItemsAdditionalPropertiesItems"
804 }
805 }
806 }
807 }
808 }
809 }
810 }
811 `, res, "Expected a simple schema for response")
812 }
813
814 func TestRemoveUnused(t *testing.T) {
815 log.SetOutput(io.Discard)
816 defer log.SetOutput(os.Stdout)
817
818 bp := filepath.Join("fixtures", "oaigen", "fixture-oaigen.yaml")
819 sp := antest.LoadOrFail(t, bp)
820
821 require.NoError(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Verbose: false, Minimal: true, RemoveUnused: true}))
822
823 assert.Nil(t, sp.Parameters)
824 assert.Nil(t, sp.Responses)
825
826 bp = filepath.Join("fixtures", "parameters", "fixture-parameters.yaml")
827 sp = antest.LoadOrFail(t, bp)
828 an := New(sp)
829
830 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: false, Minimal: true, RemoveUnused: true}))
831
832 assert.Nil(t, sp.Parameters)
833 assert.Nil(t, sp.Responses)
834
835 op, ok := an.OperationFor("GET", "/some/where")
836 assert.True(t, ok)
837 assert.Lenf(t, op.Parameters, 4, "Expected 4 parameters expanded for this operation")
838 assert.Lenf(t, an.ParamsFor("GET", "/some/where"), 7, "Expected 7 parameters (with default) expanded for this operation")
839
840 op, ok = an.OperationFor("PATCH", "/some/remote")
841 assert.True(t, ok)
842 assert.Lenf(t, op.Parameters, 1, "Expected 1 parameter expanded for this operation")
843 assert.Lenf(t, an.ParamsFor("PATCH", "/some/remote"), 2, "Expected 2 parameters (with default) expanded for this operation")
844
845 _, ok = sp.Definitions["unused"]
846 assert.False(t, ok, "Did not expect to find #/definitions/unused")
847
848 bp = filepath.Join("fixtures", "parameters", "fixture-parameters.yaml")
849 sp = antest.LoadOrFail(t, bp)
850
851 require.NoError(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Verbose: true, Minimal: false, RemoveUnused: true}))
852
853 assert.Nil(t, sp.Parameters)
854 assert.Nil(t, sp.Responses)
855 _, ok = sp.Definitions["unused"]
856 assert.Falsef(t, ok, "Did not expect to find #/definitions/unused")
857 }
858
859 func TestOperationIDs(t *testing.T) {
860 bp := filepath.Join("fixtures", "operations", "fixture-operations.yaml")
861 sp := antest.LoadOrFail(t, bp)
862
863 an := New(sp)
864 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: false, Minimal: false, RemoveUnused: false}))
865
866 t.Run("should GatherOperations", func(t *testing.T) {
867 res := operations.GatherOperations(New(sp), []string{"getSomeWhere", "getSomeWhereElse"})
868
869 assert.Containsf(t, res, "getSomeWhere", "expected to find operation")
870 assert.Containsf(t, res, "getSomeWhereElse", "expected to find operation")
871 assert.NotContainsf(t, res, "postSomeWhere", "did not expect to find operation")
872 })
873
874 op, ok := an.OperationFor("GET", "/some/where/else")
875 assert.True(t, ok)
876 assert.NotNil(t, op)
877 assert.Len(t, an.ParametersFor("getSomeWhereElse"), 2)
878
879 op, ok = an.OperationFor("POST", "/some/where/else")
880 assert.True(t, ok)
881 assert.NotNil(t, op)
882 assert.Len(t, an.ParametersFor("postSomeWhereElse"), 1)
883
884 op, ok = an.OperationFor("PUT", "/some/where/else")
885 assert.True(t, ok)
886 assert.NotNil(t, op)
887 assert.Len(t, an.ParametersFor("putSomeWhereElse"), 1)
888
889 op, ok = an.OperationFor("PATCH", "/some/where/else")
890 assert.True(t, ok)
891 assert.NotNil(t, op)
892 assert.Len(t, an.ParametersFor("patchSomeWhereElse"), 1)
893
894 op, ok = an.OperationFor("DELETE", "/some/where/else")
895 assert.True(t, ok)
896 assert.NotNil(t, op)
897 assert.Len(t, an.ParametersFor("deleteSomeWhereElse"), 1)
898
899 op, ok = an.OperationFor("HEAD", "/some/where/else")
900 assert.True(t, ok)
901 assert.NotNil(t, op)
902 assert.Len(t, an.ParametersFor("headSomeWhereElse"), 1)
903
904 op, ok = an.OperationFor("OPTIONS", "/some/where/else")
905 assert.True(t, ok)
906 assert.NotNil(t, op)
907 assert.Len(t, an.ParametersFor("optionsSomeWhereElse"), 1)
908
909 assert.Empty(t, an.ParametersFor("outOfThisWorld"))
910 }
911
912 func TestFlatten_Pointers(t *testing.T) {
913 log.SetOutput(io.Discard)
914 defer log.SetOutput(os.Stdout)
915
916 bp := filepath.Join("fixtures", "pointers", "fixture-pointers.yaml")
917 sp := antest.LoadOrFail(t, bp)
918
919 an := New(sp)
920 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false}))
921
922
923 bn := New(sp)
924 for _, r := range bn.AllRefs() {
925 assert.Equal(t, definitionsPath, path.Dir(r.String()))
926 }
927 }
928
929
930 func TestFlatten_ErrorHandling(t *testing.T) {
931 log.SetOutput(io.Discard)
932 defer log.SetOutput(os.Stdout)
933
934 const wantedFailure = "Expected a failure"
935 bp := filepath.Join("fixtures", "errors", "fixture-unexpandable.yaml")
936
937
938 sp := antest.LoadOrFail(t, bp)
939 require.Errorf(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Expand: true}), wantedFailure)
940
941
942 sp = antest.LoadOrFail(t, bp)
943 require.Errorf(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Expand: false}), wantedFailure)
944
945 bp = filepath.Join("fixtures", "errors", "fixture-unexpandable-2.yaml")
946 sp = antest.LoadOrFail(t, bp)
947 require.Errorf(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Expand: false}), wantedFailure)
948
949
950 sp = antest.LoadOrFail(t, bp)
951 require.Errorf(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Minimal: true, Expand: false}), wantedFailure)
952 }
953
954 func TestFlatten_PointersLoop(t *testing.T) {
955 log.SetOutput(io.Discard)
956 defer log.SetOutput(os.Stdout)
957
958 bp := filepath.Join("fixtures", "pointers", "fixture-pointers-loop.yaml")
959 sp := antest.LoadOrFail(t, bp)
960
961 an := New(sp)
962 require.Error(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false}))
963 }
964
965 func TestFlatten_Bitbucket(t *testing.T) {
966 log.SetOutput(io.Discard)
967 defer log.SetOutput(os.Stdout)
968
969 bp := filepath.Join("fixtures", "bugs", "bitbucket.json")
970 sp := antest.LoadOrFail(t, bp)
971
972 an := New(sp)
973 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false}))
974
975
976 sp = antest.LoadOrFail(t, bp)
977 an = New(sp)
978 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: false, RemoveUnused: false}))
979
980
981 sp = antest.LoadOrFail(t, bp)
982 an = New(sp)
983 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Expand: true, RemoveUnused: false}))
984
985
986 sp = antest.LoadOrFail(t, bp)
987 an = New(sp)
988 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Expand: true, RemoveUnused: true}))
989
990 assert.Len(t, sp.Definitions, 2)
991 _, ok := sp.Definitions["base_commit"]
992 assert.True(t, ok)
993 _, ok = sp.Definitions["repository"]
994 assert.True(t, ok)
995 }
996
997 func TestFlatten_Issue_1602(t *testing.T) {
998 log.SetOutput(io.Discard)
999 defer log.SetOutput(os.Stdout)
1000
1001
1002
1003
1004 bp := filepath.Join("fixtures", "bugs", "1602", "fixture-1602-1.yaml")
1005 sp := antest.LoadOrFail(t, bp)
1006 an := New(sp)
1007
1008 require.NoError(t, Flatten(FlattenOpts{
1009 Spec: an, BasePath: bp, Verbose: true,
1010 Minimal: true,
1011 Expand: false,
1012 RemoveUnused: false,
1013 }))
1014
1015
1016 sp = antest.LoadOrFail(t, bp)
1017 an = New(sp)
1018 require.NoError(t, Flatten(FlattenOpts{
1019 Spec: an, BasePath: bp, Verbose: false,
1020 Minimal: false,
1021 Expand: false,
1022 RemoveUnused: false,
1023 }))
1024
1025
1026
1027 sp = antest.LoadOrFail(t, bp)
1028 an = New(sp)
1029 require.NoError(t, Flatten(FlattenOpts{
1030 Spec: an, BasePath: bp, Verbose: false,
1031 Minimal: false,
1032 Expand: true,
1033 RemoveUnused: false,
1034 }))
1035 }
1036
1037 func TestFlatten_Issue_1602_All(t *testing.T) {
1038 for _, toPin := range []string{
1039 filepath.Join("fixtures", "bugs", "1602", "fixture-1602-full.yaml"),
1040 filepath.Join("fixtures", "bugs", "1602", "fixture-1602-1.yaml"),
1041 filepath.Join("fixtures", "bugs", "1602", "fixture-1602-2.yaml"),
1042 filepath.Join("fixtures", "bugs", "1602", "fixture-1602-3.yaml"),
1043 filepath.Join("fixtures", "bugs", "1602", "fixture-1602-4.yaml"),
1044 filepath.Join("fixtures", "bugs", "1602", "fixture-1602-5.yaml"),
1045 filepath.Join("fixtures", "bugs", "1602", "fixture-1602-6.yaml"),
1046 } {
1047 fixture := toPin
1048 t.Run("issue_1602_all_"+fixture, func(t *testing.T) {
1049 t.Parallel()
1050 sp := antest.LoadOrFail(t, fixture)
1051
1052 an := New(sp)
1053 require.NoError(t, Flatten(FlattenOpts{
1054 Spec: an, BasePath: fixture, Verbose: false, Minimal: true, Expand: false,
1055 RemoveUnused: false,
1056 }))
1057 })
1058 }
1059 }
1060
1061 func TestFlatten_Issue_1614(t *testing.T) {
1062 log.SetOutput(io.Discard)
1063 defer log.SetOutput(os.Stdout)
1064
1065
1066
1067
1068 bp := filepath.Join("fixtures", "bugs", "1614", "gitea.yaml")
1069 sp := antest.LoadOrFail(t, bp)
1070 an := New(sp)
1071 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false,
1072 RemoveUnused: false}))
1073
1074
1075 jazon := antest.AsJSON(t, sp)
1076 assert.NotContains(t, jazon, `#/responses/forbidden`)
1077 assert.NotContains(t, jazon, `#/responses/empty`)
1078 }
1079
1080 func TestFlatten_Issue_1621(t *testing.T) {
1081
1082
1083
1084 bp := filepath.Join("fixtures", "bugs", "1621", "fixture-1621.yaml")
1085 sp := antest.LoadOrFail(t, bp)
1086 an := New(sp)
1087 require.NoError(t, Flatten(FlattenOpts{
1088 Spec: an, BasePath: bp, Verbose: true,
1089 Minimal: true,
1090 Expand: false,
1091 RemoveUnused: false,
1092 }))
1093
1094 sch1 := sp.Paths.Paths["/v4/users/"].Get.Responses.StatusCodeResponses[200].Schema
1095 jazon := antest.AsJSON(t, sch1)
1096 assert.JSONEq(t, `{"type": "array", "items": {"$ref": "#/definitions/v4UserListItem" }}`, jazon)
1097
1098 sch2 := sp.Paths.Paths["/v4/user/"].Get.Responses.StatusCodeResponses[200].Schema
1099 jazon = antest.AsJSON(t, sch2)
1100 assert.JSONEq(t, `{"$ref": "#/definitions/v4UserListItem"}`, jazon)
1101
1102 sch3 := sp.Paths.Paths["/v4/users/{email}/"].Get.Responses.StatusCodeResponses[200].Schema
1103 jazon = antest.AsJSON(t, sch3)
1104 assert.JSONEq(t, `{"$ref": "#/definitions/v4UserListItem"}`, jazon)
1105 }
1106
1107 func TestFlatten_Issue_1796(t *testing.T) {
1108
1109 bp := filepath.Join("fixtures", "bugs", "1796", "queryIssue.json")
1110 sp := antest.LoadOrFail(t, bp)
1111 an := New(sp)
1112
1113 require.NoError(t, Flatten(FlattenOpts{
1114 Spec: an, BasePath: bp, Verbose: true,
1115 Minimal: true, Expand: false,
1116 RemoveUnused: false,
1117 }))
1118
1119
1120 for _, ref := range an.AllReferences() {
1121 assert.True(t, strings.HasPrefix(ref, "#/definitions"))
1122 }
1123 }
1124
1125 func TestFlatten_Issue_1767(t *testing.T) {
1126
1127 bp := filepath.Join("fixtures", "bugs", "1767", "fixture-1767.yaml")
1128 sp := antest.LoadOrFail(t, bp)
1129 an := New(sp)
1130 require.NoError(t, Flatten(FlattenOpts{
1131 Spec: an, BasePath: bp, Verbose: true,
1132 Minimal: true, Expand: false,
1133 RemoveUnused: false,
1134 }))
1135
1136
1137 for _, ref := range an.AllReferences() {
1138 assert.True(t, strings.HasPrefix(ref, "#/definitions"))
1139 }
1140 }
1141
1142 func TestFlatten_Issue_1774(t *testing.T) {
1143
1144 bp := filepath.Join("fixtures", "bugs", "1774", "def_api.yaml")
1145 sp := antest.LoadOrFail(t, bp)
1146 an := New(sp)
1147
1148 require.NoError(t, Flatten(FlattenOpts{
1149 Spec: an, BasePath: bp, Verbose: true,
1150 Minimal: false,
1151 Expand: false,
1152 RemoveUnused: false,
1153 }))
1154
1155
1156 for _, ref := range an.AllReferences() {
1157 assert.True(t, strings.HasPrefix(ref, "#/definitions"))
1158 }
1159 }
1160
1161 func TestFlatten_1429(t *testing.T) {
1162
1163
1164 bp := filepath.Join("fixtures", "bugs", "1429", "swagger.yaml")
1165 sp := antest.LoadOrFail(t, bp)
1166
1167 an := New(sp)
1168 require.NoError(t, Flatten(FlattenOpts{
1169 Spec: an, BasePath: bp, Verbose: true,
1170 Minimal: true,
1171 RemoveUnused: false,
1172 }))
1173 }
1174
1175 func TestFlatten_1851(t *testing.T) {
1176
1177
1178 bp := filepath.Join("fixtures", "bugs", "1851", "fixture-1851.yaml")
1179 sp := antest.LoadOrFail(t, bp)
1180
1181 an := New(sp)
1182 require.NoError(t, Flatten(FlattenOpts{
1183 Spec: an, BasePath: bp, Verbose: true,
1184 Minimal: true,
1185 RemoveUnused: false,
1186 }))
1187
1188 serverDefinition, ok := an.spec.Definitions["server"]
1189 assert.True(t, ok)
1190
1191 serverStatusDefinition, ok := an.spec.Definitions["serverStatus"]
1192 assert.True(t, ok)
1193
1194 serverStatusProperty, ok := serverDefinition.Properties["Status"]
1195 assert.True(t, ok)
1196
1197 jazon := antest.AsJSON(t, serverStatusProperty)
1198 assert.JSONEq(t, `{"$ref": "#/definitions/serverStatus"}`, jazon)
1199
1200 jazon = antest.AsJSON(t, serverStatusDefinition)
1201 assert.JSONEq(t, `{"type": "string", "enum": [ "OK", "Not OK" ]}`, jazon)
1202
1203
1204 bp = filepath.Join("fixtures", "bugs", "1851", "fixture-1851-2.yaml")
1205 sp = antest.LoadOrFail(t, bp)
1206
1207 an = New(sp)
1208 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false}))
1209
1210 serverDefinition, ok = an.spec.Definitions["Server"]
1211 assert.True(t, ok)
1212
1213 serverStatusDefinition, ok = an.spec.Definitions["ServerStatus"]
1214 assert.True(t, ok)
1215
1216 serverStatusProperty, ok = serverDefinition.Properties["Status"]
1217 assert.True(t, ok)
1218
1219 jazon = antest.AsJSON(t, serverStatusProperty)
1220 assert.JSONEq(t, `{"$ref": "#/definitions/ServerStatus"}`, jazon)
1221
1222 jazon = antest.AsJSON(t, serverStatusDefinition)
1223 assert.JSONEq(t, `{"type": "string", "enum": [ "OK", "Not OK" ]}`, jazon)
1224 }
1225
1226 func TestFlatten_RemoteAbsolute(t *testing.T) {
1227 for _, toPin := range []string{
1228
1229 filepath.Join("fixtures", "bugs", "remote-absolute", "swagger-mini.json"),
1230
1231 filepath.Join("fixtures", "bugs", "remote-absolute", "swagger.json"),
1232
1233 filepath.Join("fixtures", "bugs", "remote-absolute", "swagger-with-local-ref.json"),
1234
1235 filepath.Join("fixtures", "bugs", "remote-absolute", "swagger-with-remote-only-ref.json"),
1236 } {
1237 fixture := toPin
1238 t.Run("remote_absolute_"+fixture, func(t *testing.T) {
1239 t.Parallel()
1240
1241 an := testFlattenWithDefaults(t, fixture)
1242 checkRefs(t, an.spec, true)
1243 })
1244 }
1245
1246
1247
1248
1249 an := testFlattenWithDefaults(t, filepath.Join("fixtures", "bugs", "remote-absolute", "swagger-with-ref.json"))
1250 checkRefs(t, an.spec, false)
1251 }
1252
1253 func TestFlatten_2092(t *testing.T) {
1254 log.SetOutput(io.Discard)
1255 defer log.SetOutput(os.Stdout)
1256
1257 bp := filepath.Join("fixtures", "bugs", "2092", "swagger.yaml")
1258 rexOAIGen := regexp.MustCompile(`(?i)("\$ref":\s*")(.?oaigen.?)"`)
1259
1260
1261 sp := antest.LoadOrFail(t, bp)
1262 an := New(sp)
1263 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false}))
1264 firstJSONMinimal := antest.AsJSON(t, an.spec)
1265
1266
1267 require.Falsef(t, rexOAIGen.MatchString(firstJSONMinimal), "unmatched regexp for: %s", firstJSONMinimal)
1268
1269 sp = antest.LoadOrFail(t, bp)
1270 an = New(sp)
1271 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: false, RemoveUnused: false}))
1272 firstJSONFull := antest.AsJSON(t, an.spec)
1273
1274
1275 require.Falsef(t, rexOAIGen.MatchString(firstJSONFull), "unmatched regexp for: %s", firstJSONFull)
1276
1277 for i := 0; i < 10; i++ {
1278 t.Run(fmt.Sprintf("issue_2092_%d", i), func(t *testing.T) {
1279 t.Parallel()
1280
1281
1282 sp := antest.LoadOrFail(t, bp)
1283 an := New(sp)
1284
1285 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false}))
1286
1287 jazon := antest.AsJSON(t, an.spec)
1288 assert.JSONEq(t, firstJSONMinimal, jazon)
1289
1290 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: true}))
1291
1292 sp = antest.LoadOrFail(t, bp)
1293 an = New(sp)
1294
1295 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: false, RemoveUnused: false}))
1296
1297 jazon = antest.AsJSON(t, an.spec)
1298 assert.JSONEq(t, firstJSONFull, jazon)
1299 })
1300 }
1301 }
1302
1303 func TestFlatten_2113(t *testing.T) {
1304
1305 log.SetOutput(io.Discard)
1306 defer log.SetOutput(os.Stdout)
1307
1308 bp := filepath.Join("fixtures", "bugs", "2113", "base.yaml")
1309 sp := antest.LoadOrFail(t, bp)
1310 an := New(sp)
1311
1312 require.NoError(t, Flatten(FlattenOpts{
1313 Spec: an, BasePath: bp, Verbose: true,
1314 Expand: true,
1315 RemoveUnused: false,
1316 }))
1317
1318 sp = antest.LoadOrFail(t, bp)
1319 an = New(sp)
1320
1321 require.NoError(t, Flatten(FlattenOpts{
1322 Spec: an, BasePath: bp, Verbose: true,
1323 Minimal: true,
1324 RemoveUnused: false,
1325 }))
1326
1327 jazon := antest.AsJSON(t, sp)
1328
1329 expected, err := os.ReadFile(filepath.Join("fixtures", "expected", "issue-2113.json"))
1330 require.NoError(t, err)
1331
1332 require.JSONEq(t, string(expected), jazon)
1333 }
1334
1335 func TestFlatten_2334(t *testing.T) {
1336
1337 log.SetOutput(io.Discard)
1338 defer log.SetOutput(os.Stdout)
1339
1340 bp := filepath.Join("fixtures", "bugs", "2334", "swagger.yaml")
1341 sp := antest.LoadOrFail(t, bp)
1342 an := New(sp)
1343
1344 require.NoError(t, Flatten(FlattenOpts{
1345 Spec: an, BasePath: bp, Verbose: false,
1346 Expand: false,
1347 Minimal: true,
1348 RemoveUnused: true,
1349 KeepNames: true,
1350 }))
1351
1352 jazon := antest.AsJSON(t, sp)
1353
1354 assert.Contains(t, jazon, `"$ref": "#/definitions/Bar"`)
1355 assert.Contains(t, jazon, `"Bar":`)
1356 assert.Contains(t, jazon, `"Baz":`)
1357 }
1358
1359 func TestFlatten_1898(t *testing.T) {
1360 log.SetOutput(io.Discard)
1361 defer log.SetOutput(os.Stdout)
1362
1363 bp := filepath.Join("fixtures", "bugs", "1898", "swagger.json")
1364 sp := antest.LoadOrFail(t, bp)
1365 an := New(sp)
1366
1367 require.NoError(t, Flatten(FlattenOpts{
1368 Spec: an, BasePath: bp, Verbose: true,
1369 Expand: false,
1370 RemoveUnused: false,
1371 }))
1372 op, ok := an.OperationFor("GET", "/example/v2/GetEvents")
1373 require.True(t, ok)
1374
1375 resp, _, ok := op.SuccessResponse()
1376 require.True(t, ok)
1377
1378 require.Equal(t, "#/definitions/xStreamDefinitionsV2EventMsg", resp.Schema.Ref.String())
1379 def, ok := sp.Definitions["xStreamDefinitionsV2EventMsg"]
1380 require.True(t, ok)
1381 require.Len(t, def.Properties, 2)
1382 require.Contains(t, def.Properties, "error")
1383 require.Contains(t, def.Properties, "result")
1384 }
1385
1386 func TestFlatten_RemoveUnused_2657(t *testing.T) {
1387 log.SetOutput(io.Discard)
1388 defer log.SetOutput(os.Stdout)
1389
1390 bp := filepath.Join("fixtures", "bugs", "2657", "schema.json")
1391 sp := antest.LoadOrFail(t, bp)
1392 an := New(sp)
1393
1394 require.NoError(t, Flatten(FlattenOpts{
1395 Spec: an, BasePath: bp, Verbose: true,
1396 Minimal: true,
1397 Expand: false,
1398 RemoveUnused: true,
1399 }))
1400 require.Empty(t, sp.Definitions)
1401 }
1402
1403 func TestFlatten_Relative_2743(t *testing.T) {
1404 log.SetOutput(io.Discard)
1405 defer log.SetOutput(os.Stdout)
1406
1407 t.Run("used to work, but should NOT", func(t *testing.T) {
1408 bp := filepath.Join("fixtures", "bugs", "2743", "working", "spec.yaml")
1409 sp := antest.LoadOrFail(t, bp)
1410 an := New(sp)
1411
1412 require.Error(t, Flatten(FlattenOpts{
1413 Spec: an, BasePath: bp, Verbose: true,
1414 Minimal: true,
1415 Expand: false,
1416 }))
1417 })
1418
1419 t.Run("used not to, but should work", func(t *testing.T) {
1420 bp := filepath.Join("fixtures", "bugs", "2743", "not-working", "spec.yaml")
1421 sp := antest.LoadOrFail(t, bp)
1422 an := New(sp)
1423
1424 require.NoError(t, Flatten(FlattenOpts{
1425 Spec: an, BasePath: bp, Verbose: true,
1426 Minimal: true,
1427 Expand: false,
1428 }))
1429 })
1430 }
1431
1432 func getDefinition(t testing.TB, sp *spec.Swagger, key string) string {
1433 d, ok := sp.Definitions[key]
1434 require.Truef(t, ok, "Expected definition for %s", key)
1435 res, err := json.Marshal(d)
1436 if err != nil {
1437 panic(err)
1438 }
1439
1440 return string(res)
1441 }
1442
1443 func getInPath(t testing.TB, sp *spec.Swagger, path, key string) string {
1444 ptr, erp := jsonpointer.New(key)
1445 require.NoError(t, erp, "at %s no key", key)
1446
1447 d, _, erg := ptr.Get(sp.Paths.Paths[path])
1448 require.NoError(t, erg, "at %s no value for %s", path, key)
1449
1450 res, err := json.Marshal(d)
1451 if err != nil {
1452 panic(err)
1453 }
1454
1455 return string(res)
1456 }
1457
1458 func checkRefs(t testing.TB, spec *spec.Swagger, expectNoConflict bool) {
1459
1460 jazon := antest.AsJSON(t, spec)
1461 m := rex.FindAllStringSubmatch(jazon, -1)
1462 require.NotNil(t, m)
1463
1464 for _, matched := range m {
1465 subMatch := matched[1]
1466 assert.True(t, strings.HasPrefix(subMatch, "#/definitions/"),
1467 "expected $ref to be inlined, got: %s", matched[0])
1468 }
1469
1470 if expectNoConflict {
1471
1472 m := oairex.FindAllStringSubmatch(jazon, -1)
1473 assert.Empty(t, m)
1474 }
1475 }
1476
1477 func testFlattenWithDefaults(t *testing.T, bp string) *Spec {
1478 sp := antest.LoadOrFail(t, bp)
1479 an := New(sp)
1480 require.NoError(t, Flatten(FlattenOpts{
1481 Spec: an,
1482 BasePath: bp,
1483 Verbose: true,
1484 Minimal: true,
1485 RemoveUnused: false,
1486 }))
1487
1488 return an
1489 }
1490
View as plain text