1// Copyright 2015 go-swagger maintainers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package analysis
16
17import (
18	"bytes"
19	"encoding/json"
20	"io/ioutil"
21	"log"
22	"os"
23	"path"
24	"path/filepath"
25	"regexp"
26	"strings"
27	"testing"
28
29	"github.com/go-openapi/jsonpointer"
30	"github.com/go-openapi/spec"
31	"github.com/stretchr/testify/assert"
32	"github.com/stretchr/testify/require"
33)
34
35func TestSaveDefinition(t *testing.T) {
36	sp := &spec.Swagger{}
37	saveSchema(sp, "theName", spec.StringProperty())
38	assert.Contains(t, sp.Definitions, "theName")
39}
40
41func TestNameFromRef(t *testing.T) {
42	values := []struct{ Source, Expected string }{
43		{"#/definitions/errorModel", "errorModel"},
44		{"http://somewhere.com/definitions/errorModel", "errorModel"},
45		{"http://somewhere.com/definitions/errorModel.json", "errorModel"},
46		{"/definitions/errorModel", "errorModel"},
47		{"/definitions/errorModel.json", "errorModel"},
48		{"http://somewhere.com", "somewhereCom"},
49		{"#", ""},
50	}
51
52	for _, v := range values {
53		assert.Equal(t, v.Expected, nameFromRef(spec.MustCreateRef(v.Source)))
54	}
55}
56
57func TestDefinitionName(t *testing.T) {
58	values := []struct {
59		Source, Expected string
60		Definitions      spec.Definitions
61	}{
62		{"#/definitions/errorModel", "errorModel", map[string]spec.Schema(nil)},
63		{"http://somewhere.com/definitions/errorModel", "errorModel", map[string]spec.Schema(nil)},
64		{"#/definitions/errorModel", "errorModel", map[string]spec.Schema{"apples": *spec.StringProperty()}},
65		{"#/definitions/errorModel", "errorModelOAIGen", map[string]spec.Schema{"errorModel": *spec.StringProperty()}},
66		{"#/definitions/errorModel", "errorModelOAIGen1",
67			map[string]spec.Schema{"errorModel": *spec.StringProperty(), "errorModelOAIGen": *spec.StringProperty()}},
68		{"#", "oaiGen", nil},
69	}
70
71	for _, v := range values {
72		u, _ := uniqifyName(v.Definitions, nameFromRef(spec.MustCreateRef(v.Source)))
73		assert.Equal(t, v.Expected, u)
74	}
75}
76
77var refFixture = []struct {
78	Key string
79	Ref spec.Ref
80}{
81	{"#/parameters/someParam/schema", spec.MustCreateRef("#/definitions/record")},
82	{"#/paths/~1some~1where~1{id}/parameters/1/schema", spec.MustCreateRef("#/definitions/record")},
83	{"#/paths/~1some~1where~1{id}/get/parameters/2/schema", spec.MustCreateRef("#/definitions/record")},
84	{"#/responses/someResponse/schema", spec.MustCreateRef("#/definitions/record")},
85	{"#/paths/~1some~1where~1{id}/get/responses/default/schema", spec.MustCreateRef("#/definitions/record")},
86	{"#/paths/~1some~1where~1{id}/get/responses/200/schema", spec.MustCreateRef("#/definitions/record")},
87	{"#/definitions/namedAgain", spec.MustCreateRef("#/definitions/named")},
88	{"#/definitions/datedTag/allOf/1", spec.MustCreateRef("#/definitions/tag")},
89	{"#/definitions/datedRecords/items/1", spec.MustCreateRef("#/definitions/record")},
90	{"#/definitions/datedTaggedRecords/items/1", spec.MustCreateRef("#/definitions/record")},
91	{"#/definitions/datedTaggedRecords/additionalItems", spec.MustCreateRef("#/definitions/tag")},
92	{"#/definitions/otherRecords/items", spec.MustCreateRef("#/definitions/record")},
93	{"#/definitions/tags/additionalProperties", spec.MustCreateRef("#/definitions/tag")},
94	{"#/definitions/namedThing/properties/name", spec.MustCreateRef("#/definitions/named")},
95}
96
97func TestUpdateRef(t *testing.T) {
98	bp := filepath.Join("fixtures", "external_definitions.yml")
99	sp, erl := loadSpec(bp)
100	require.NoError(t, erl)
101
102	for _, v := range refFixture {
103		err := updateRef(sp, v.Key, v.Ref)
104		require.NoError(t, err)
105
106		ptr, err := jsonpointer.New(v.Key[1:])
107		require.NoError(t, err)
108
109		vv, _, err := ptr.Get(sp)
110		require.NoError(t, err)
111
112		switch tv := vv.(type) {
113		case *spec.Schema:
114			assert.Equal(t, v.Ref.String(), tv.Ref.String())
115		case spec.Schema:
116			assert.Equal(t, v.Ref.String(), tv.Ref.String())
117		case *spec.SchemaOrBool:
118			assert.Equal(t, v.Ref.String(), tv.Schema.Ref.String())
119		case *spec.SchemaOrArray:
120			assert.Equal(t, v.Ref.String(), tv.Schema.Ref.String())
121		default:
122			assert.Fail(t, "unknown type", "got %T", vv)
123		}
124	}
125}
126
127func TestImportExternalReferences(t *testing.T) {
128	log.SetOutput(ioutil.Discard)
129	defer log.SetOutput(os.Stdout)
130
131	// this fixture is the same as external_definitions.yml, but no more
132	// checks if invalid construct is supported (i.e. $ref in parameters items)
133	bp := filepath.Join(".", "fixtures", "external_definitions_valid.yml")
134	sp, err := loadSpec(bp)
135	require.NoError(t, err)
136
137	opts := &FlattenOpts{
138		Spec:     New(sp),
139		BasePath: bp,
140	}
141	// NOTE(fredbi): now we no more expand, but merely resolve and iterate until there is no more ext ref
142	// so calling importExternalReferences is no more idempotent
143	_, err = importExternalReferences(opts)
144	require.NoError(t, err)
145
146	for i, v := range refFixture {
147		// there is 1 notable difference with the updateRef test:
148		if i == 5 {
149			v.Ref = spec.MustCreateRef("#/definitions/tag")
150		}
151
152		ptr, erj := jsonpointer.New(v.Key[1:])
153		require.NoErrorf(t, erj, "error on jsonpointer.New(%q)", v.Key[1:])
154
155		vv, _, erg := ptr.Get(sp)
156		require.NoErrorf(t, erg, "error on ptr.Get(p for key=%s)", v.Key[1:])
157
158		switch tv := vv.(type) {
159		case *spec.Schema:
160			require.Equal(t, v.Ref.String(), tv.Ref.String(), "for %s", v.Key)
161		case spec.Schema:
162			require.Equal(t, v.Ref.String(), tv.Ref.String(), "for %s", v.Key)
163		case *spec.SchemaOrBool:
164			require.Equal(t, v.Ref.String(), tv.Schema.Ref.String(), "for %s", v.Key)
165		case *spec.SchemaOrArray:
166			require.Equal(t, v.Ref.String(), tv.Schema.Ref.String(), "for %s", v.Key)
167		default:
168			require.Fail(t, "unknown type", "got %T", vv)
169		}
170
171		require.Len(t, sp.Definitions, 11)
172		require.Contains(t, sp.Definitions, "tag")
173		require.Contains(t, sp.Definitions, "named")
174		require.Contains(t, sp.Definitions, "record")
175	}
176
177	// check the complete result for clarity
178	bb, _ := json.MarshalIndent(sp, "", " ")
179	assert.JSONEq(t, `{
180         "swagger": "2.0",
181         "info": {
182          "title": "reference analysis",
183          "version": "0.1.0"
184         },
185         "paths": {
186          "/other/place": {
187           "$ref": "external/pathItem.yml"
188          },
189          "/some/where/{id}": {
190           "get": {
191            "parameters": [
192             {
193              "$ref": "external/parameters.yml#/parameters/limitParam"
194             },
195             {
196              "type": "array",
197              "items": {
198               "type": "string"
199              },
200              "name": "other",
201              "in": "query"
202             },
203             {
204              "name": "body",
205              "in": "body",
206              "schema": {
207               "$ref": "#/definitions/record"
208              }
209             }
210            ],
211            "responses": {
212             "200": {
213			  "description": "",
214              "schema": {
215               "$ref": "#/definitions/tag"
216              }
217             },
218             "404": {
219              "$ref": "external/responses.yml#/responses/notFound"
220             },
221             "default": {
222			  "description": "",
223              "schema": {
224               "$ref": "#/definitions/record"
225              }
226             }
227            }
228           },
229           "parameters": [
230            {
231             "$ref": "external/parameters.yml#/parameters/idParam"
232            },
233            {
234             "name": "bodyId",
235             "in": "body",
236             "schema": {
237              "$ref": "#/definitions/record"
238             }
239            }
240           ]
241          }
242         },
243         "definitions": {
244          "datedRecords": {
245           "type": "array",
246           "items": [
247            {
248             "type": "string",
249             "format": "date-time"
250            },
251            {
252             "$ref": "#/definitions/record"
253            }
254           ]
255          },
256          "datedTag": {
257           "allOf": [
258            {
259             "type": "string",
260             "format": "date"
261            },
262            {
263             "$ref": "#/definitions/tag"
264            }
265           ]
266          },
267          "datedTaggedRecords": {
268           "type": "array",
269           "items": [
270            {
271             "type": "string",
272             "format": "date-time"
273            },
274            {
275             "$ref": "#/definitions/record"
276            }
277           ],
278           "additionalItems": {
279            "$ref": "#/definitions/tag"
280           }
281          },
282          "named": {
283           "type": "string"
284          },
285          "namedAgain": {
286           "$ref": "#/definitions/named"
287          },
288          "namedThing": {
289           "type": "object",
290           "properties": {
291            "name": {
292             "$ref": "#/definitions/named"
293            }
294           }
295          },
296          "otherRecords": {
297           "type": "array",
298           "items": {
299            "$ref": "#/definitions/record"
300           }
301          },
302          "record": {
303           "type": "object",
304           "properties": {
305            "createdAt": {
306             "type": "string",
307             "format": "date-time"
308            }
309           }
310          },
311          "records": {
312           "type": "array",
313           "items": [
314            {
315             "$ref": "#/definitions/record"
316            }
317           ]
318          },
319          "tag": {
320           "type": "object",
321           "properties": {
322            "audit": {
323             "$ref": "external/definitions.yml#/definitions/record"
324            },
325            "id": {
326             "type": "integer",
327             "format": "int64"
328            },
329            "value": {
330             "type": "string"
331            }
332           }
333          },
334          "tags": {
335           "type": "object",
336           "additionalProperties": {
337            "$ref": "#/definitions/tag"
338           }
339          }
340         },
341         "parameters": {
342          "someParam": {
343           "name": "someParam",
344           "in": "body",
345           "schema": {
346            "$ref": "#/definitions/record"
347           }
348          }
349         },
350         "responses": {
351          "someResponse": {
352		    "description": "",
353            "schema": {
354              "$ref": "#/definitions/record"
355            }
356          }
357         }
358        }`, string(bb))
359
360	// iterate again: this time all external schema $ref's should be reinlined
361	opts.Spec.reload()
362	_, err = importExternalReferences(&FlattenOpts{
363		Spec:     New(sp),
364		BasePath: bp,
365	})
366	require.NoError(t, err)
367
368	opts.Spec.reload()
369	for _, ref := range opts.Spec.references.schemas {
370		require.True(t, ref.HasFragmentOnly)
371	}
372
373	// now try complete flatten
374	sp = loadOrFail(t, bp)
375	an := New(sp)
376	err = Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: true})
377	require.NoError(t, err)
378
379	bbb, _ := json.MarshalIndent(an.spec, "", " ")
380	assert.JSONEq(t, `{
381		"swagger": "2.0",
382         "info": {
383          "title": "reference analysis",
384          "version": "0.1.0"
385         },
386         "paths": {
387          "/other/place": {
388           "get": {
389            "description": "Used to see if a codegen can render all the possible parameter variations for a header param",
390            "tags": [
391             "testcgen"
392            ],
393            "summary": "many model variations",
394            "operationId": "modelOp",
395            "responses": {
396             "default": {
397              "description": "Generic Out"
398             }
399            }
400           }
401          },
402          "/some/where/{id}": {
403           "get": {
404            "parameters": [
405             {
406              "type": "integer",
407              "format": "int32",
408              "name": "limit",
409              "in": "query"
410             },
411             {
412              "type": "array",
413              "items": {
414               "type": "string"
415              },
416              "name": "other",
417              "in": "query"
418             },
419             {
420              "name": "body",
421              "in": "body",
422              "schema": {
423               "$ref": "#/definitions/record"
424              }
425             }
426            ],
427            "responses": {
428             "200": {
429			  "description": "",
430              "schema": {
431               "$ref": "#/definitions/tag"
432              }
433             },
434             "404": {
435			  "description": "",
436              "schema": {
437               "$ref": "#/definitions/error"
438              }
439             },
440             "default": {
441			  "description": "",
442              "schema": {
443               "$ref": "#/definitions/record"
444              }
445             }
446            }
447           },
448           "parameters": [
449            {
450             "type": "integer",
451             "format": "int32",
452             "name": "id",
453             "in": "path"
454            },
455            {
456             "name": "bodyId",
457             "in": "body",
458             "schema": {
459              "$ref": "#/definitions/record"
460             }
461            }
462           ]
463          }
464         },
465         "definitions": {
466          "error": {
467           "type": "object",
468           "required": [
469            "id",
470            "message"
471           ],
472           "properties": {
473            "id": {
474             "type": "integer",
475             "format": "int64",
476             "readOnly": true
477            },
478            "message": {
479             "type": "string",
480             "readOnly": true
481            }
482           }
483          },
484          "named": {
485           "type": "string"
486          },
487          "record": {
488           "type": "object",
489           "properties": {
490            "createdAt": {
491             "type": "string",
492             "format": "date-time"
493            }
494           }
495          },
496          "tag": {
497           "type": "object",
498           "properties": {
499            "audit": {
500             "$ref": "#/definitions/record"
501            },
502            "id": {
503             "type": "integer",
504             "format": "int64"
505            },
506            "value": {
507             "type": "string"
508            }
509           }
510          }
511         }
512        }`, string(bbb))
513}
514
515func TestRewriteSchemaRef(t *testing.T) {
516	bp := filepath.Join("fixtures", "inline_schemas.yml")
517	sp, err := loadSpec(bp)
518	require.NoError(t, err)
519
520	for i, v := range refFixture {
521		err := rewriteSchemaToRef(sp, v.Key, v.Ref)
522		require.NoError(t, err)
523
524		ptr, err := jsonpointer.New(v.Key[1:])
525		require.NoError(t, err)
526
527		vv, _, err := ptr.Get(sp)
528		require.NoError(t, err)
529
530		switch tv := vv.(type) {
531		case *spec.Schema:
532			assert.Equal(t, v.Ref.String(), tv.Ref.String(), "at %d for %s", i, v.Key)
533		case spec.Schema:
534			assert.Equal(t, v.Ref.String(), tv.Ref.String(), "at %d for %s", i, v.Key)
535		case *spec.SchemaOrBool:
536			assert.Equal(t, v.Ref.String(), tv.Schema.Ref.String(), "at %d for %s", i, v.Key)
537		case *spec.SchemaOrArray:
538			assert.Equal(t, v.Ref.String(), tv.Schema.Ref.String(), "at %d for %s", i, v.Key)
539		default:
540			assert.Fail(t, "unknown type", "got %T", vv)
541		}
542	}
543}
544
545func TestSplitKey(t *testing.T) {
546	type KeyFlag uint64
547
548	const (
549		isOperation KeyFlag = 1 << iota
550		isDefinition
551		isSharedOperationParam
552		isOperationParam
553		isOperationResponse
554		isDefaultResponse
555		isStatusCodeResponse
556	)
557
558	values := []struct {
559		Key         string
560		Flags       KeyFlag
561		PathItemRef spec.Ref
562		PathRef     spec.Ref
563		Name        string
564	}{
565		{
566			"#/paths/~1some~1where~1{id}/parameters/1/schema",
567			isOperation | isSharedOperationParam,
568			spec.Ref{},
569			spec.MustCreateRef("#/paths/~1some~1where~1{id}"),
570			"",
571		},
572		{
573			"#/paths/~1some~1where~1{id}/get/parameters/2/schema",
574			isOperation | isOperationParam,
575			spec.MustCreateRef("#/paths/~1some~1where~1{id}/GET"),
576			spec.MustCreateRef("#/paths/~1some~1where~1{id}"),
577			"",
578		},
579		{
580			"#/paths/~1some~1where~1{id}/get/responses/default/schema",
581			isOperation | isOperationResponse | isDefaultResponse,
582			spec.MustCreateRef("#/paths/~1some~1where~1{id}/GET"),
583			spec.MustCreateRef("#/paths/~1some~1where~1{id}"),
584			"Default",
585		},
586		{
587			"#/paths/~1some~1where~1{id}/get/responses/200/schema",
588			isOperation | isOperationResponse | isStatusCodeResponse,
589			spec.MustCreateRef("#/paths/~1some~1where~1{id}/GET"),
590			spec.MustCreateRef("#/paths/~1some~1where~1{id}"),
591			"OK",
592		},
593		{
594			"#/definitions/namedAgain",
595			isDefinition,
596			spec.Ref{},
597			spec.Ref{},
598			"namedAgain",
599		},
600		{
601			"#/definitions/datedRecords/items/1",
602			isDefinition,
603			spec.Ref{},
604			spec.Ref{},
605			"datedRecords",
606		},
607		{
608			"#/definitions/datedRecords/items/1",
609			isDefinition,
610			spec.Ref{},
611			spec.Ref{},
612			"datedRecords",
613		},
614		{
615			"#/definitions/datedTaggedRecords/items/1",
616			isDefinition,
617			spec.Ref{},
618			spec.Ref{},
619			"datedTaggedRecords",
620		},
621		{
622			"#/definitions/datedTaggedRecords/additionalItems",
623			isDefinition,
624			spec.Ref{},
625			spec.Ref{},
626			"datedTaggedRecords",
627		},
628		{
629			"#/definitions/otherRecords/items",
630			isDefinition,
631			spec.Ref{},
632			spec.Ref{},
633			"otherRecords",
634		},
635		{
636			"#/definitions/tags/additionalProperties",
637			isDefinition,
638			spec.Ref{},
639			spec.Ref{},
640			"tags",
641		},
642		{
643			"#/definitions/namedThing/properties/name",
644			isDefinition,
645			spec.Ref{},
646			spec.Ref{},
647			"namedThing",
648		},
649	}
650
651	for i, v := range values {
652		parts := keyParts(v.Key)
653		pref := parts.PathRef()
654		piref := parts.PathItemRef()
655		assert.Equal(t, v.PathRef.String(), pref.String(), "pathRef: %s at %d", v.Key, i)
656		assert.Equal(t, v.PathItemRef.String(), piref.String(), "pathItemRef: %s at %d", v.Key, i)
657
658		if v.Flags&isOperation != 0 {
659			assert.True(t, parts.IsOperation(), "isOperation: %s at %d", v.Key, i)
660		} else {
661			assert.False(t, parts.IsOperation(), "isOperation: %s at %d", v.Key, i)
662		}
663
664		if v.Flags&isDefinition != 0 {
665			assert.True(t, parts.IsDefinition(), "isDefinition: %s at %d", v.Key, i)
666			assert.Equal(t, v.Name, parts.DefinitionName(), "definition name: %s at %d", v.Key, i)
667		} else {
668			assert.False(t, parts.IsDefinition(), "isDefinition: %s at %d", v.Key, i)
669			if v.Name != "" {
670				assert.Equal(t, v.Name, parts.ResponseName(), "response name: %s at %d", v.Key, i)
671			}
672		}
673
674		if v.Flags&isOperationParam != 0 {
675			assert.True(t, parts.IsOperationParam(), "isOperationParam: %s at %d", v.Key, i)
676		} else {
677			assert.False(t, parts.IsOperationParam(), "isOperationParam: %s at %d", v.Key, i)
678		}
679
680		if v.Flags&isSharedOperationParam != 0 {
681			assert.True(t, parts.IsSharedOperationParam(), "isSharedOperationParam: %s at %d", v.Key, i)
682		} else {
683			assert.False(t, parts.IsSharedOperationParam(), "isSharedOperationParam: %s at %d", v.Key, i)
684		}
685
686		if v.Flags&isOperationResponse != 0 {
687			assert.True(t, parts.IsOperationResponse(), "isOperationResponse: %s at %d", v.Key, i)
688		} else {
689			assert.False(t, parts.IsOperationResponse(), "isOperationResponse: %s at %d", v.Key, i)
690		}
691
692		if v.Flags&isDefaultResponse != 0 {
693			assert.True(t, parts.IsDefaultResponse(), "isDefaultResponse: %s at %d", v.Key, i)
694		} else {
695			assert.False(t, parts.IsDefaultResponse(), "isDefaultResponse: %s at %d", v.Key, i)
696		}
697
698		if v.Flags&isStatusCodeResponse != 0 {
699			assert.True(t, parts.IsStatusCodeResponse(), "isStatusCodeResponse: %s at %d", v.Key, i)
700		} else {
701			assert.False(t, parts.IsStatusCodeResponse(), "isStatusCodeResponse: %s at %d", v.Key, i)
702		}
703	}
704}
705
706func TestNamesFromKey(t *testing.T) {
707	bp := filepath.Join("fixtures", "inline_schemas.yml")
708	sp, erl := loadSpec(bp)
709	require.NoError(t, erl)
710
711	values := []struct {
712		Key   string
713		Names []string
714	}{
715		{"#/paths/~1some~1where~1{id}/parameters/1/schema",
716			[]string{"GetSomeWhereID params body", "PostSomeWhereID params body"}},
717		{"#/paths/~1some~1where~1{id}/get/parameters/2/schema", []string{"GetSomeWhereID params body"}},
718		{"#/paths/~1some~1where~1{id}/get/responses/default/schema", []string{"GetSomeWhereID Default body"}},
719		{"#/paths/~1some~1where~1{id}/get/responses/200/schema", []string{"GetSomeWhereID OK body"}},
720		{"#/definitions/namedAgain", []string{"namedAgain"}},
721		{"#/definitions/datedTag/allOf/1", []string{"datedTag allOf 1"}},
722		{"#/definitions/datedRecords/items/1", []string{"datedRecords tuple 1"}},
723		{"#/definitions/datedTaggedRecords/items/1", []string{"datedTaggedRecords tuple 1"}},
724		{"#/definitions/datedTaggedRecords/additionalItems", []string{"datedTaggedRecords tuple additionalItems"}},
725		{"#/definitions/otherRecords/items", []string{"otherRecords items"}},
726		{"#/definitions/tags/additionalProperties", []string{"tags additionalProperties"}},
727		{"#/definitions/namedThing/properties/name", []string{"namedThing name"}},
728	}
729
730	for i, v := range values {
731		ptr, err := jsonpointer.New(definitionPtr(v.Key)[1:])
732		require.NoError(t, err)
733
734		vv, _, err := ptr.Get(sp)
735		require.NoError(t, err)
736
737		switch tv := vv.(type) {
738		case *spec.Schema:
739			aschema, err := Schema(SchemaOpts{Schema: tv, Root: sp, BasePath: bp})
740			if assert.NoError(t, err) {
741				names := namesFromKey(keyParts(v.Key), aschema, opRefsByRef(gatherOperations(New(sp), nil)))
742				assert.Equal(t, v.Names, names, "for %s at %d", v.Key, i)
743			}
744		case spec.Schema:
745			aschema, err := Schema(SchemaOpts{Schema: &tv, Root: sp, BasePath: bp})
746			if assert.NoError(t, err) {
747				names := namesFromKey(keyParts(v.Key), aschema, opRefsByRef(gatherOperations(New(sp), nil)))
748				assert.Equal(t, v.Names, names, "for %s at %d", v.Key, i)
749			}
750		default:
751			assert.Fail(t, "unknown type", "got %T", vv)
752		}
753	}
754}
755
756func TestDepthFirstSort(t *testing.T) {
757	bp := filepath.Join("fixtures", "inline_schemas.yml")
758	sp, erl := loadSpec(bp)
759	require.NoError(t, erl)
760
761	values := []string{
762		// Added shared parameters and responses
763		"#/parameters/someParam/schema/properties/createdAt",
764		"#/parameters/someParam/schema",
765		"#/responses/someResponse/schema/properties/createdAt",
766		"#/responses/someResponse/schema",
767		"#/paths/~1some~1where~1{id}/parameters/1/schema/properties/createdAt",
768		"#/paths/~1some~1where~1{id}/parameters/1/schema",
769		"#/paths/~1some~1where~1{id}/get/parameters/2/schema/properties/createdAt",
770		"#/paths/~1some~1where~1{id}/get/parameters/2/schema",
771		"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/id",
772		"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/value",
773		"#/paths/~1some~1where~1{id}/get/responses/200/schema",
774		"#/paths/~1some~1where~1{id}/get/responses/404/schema",
775		"#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/createdAt",
776		"#/paths/~1some~1where~1{id}/get/responses/default/schema",
777		"#/definitions/datedRecords/items/1/properties/createdAt",
778		"#/definitions/datedTaggedRecords/items/1/properties/createdAt",
779		"#/definitions/namedThing/properties/name/properties/id",
780		"#/definitions/records/items/0/properties/createdAt",
781		"#/definitions/datedTaggedRecords/additionalItems/properties/id",
782		"#/definitions/datedTaggedRecords/additionalItems/properties/value",
783		"#/definitions/otherRecords/items/properties/createdAt",
784		"#/definitions/tags/additionalProperties/properties/id",
785		"#/definitions/tags/additionalProperties/properties/value",
786		"#/definitions/datedRecords/items/0",
787		"#/definitions/datedRecords/items/1",
788		"#/definitions/datedTag/allOf/0",
789		"#/definitions/datedTag/allOf/1",
790		"#/definitions/datedTag/properties/id",
791		"#/definitions/datedTag/properties/value",
792		"#/definitions/datedTaggedRecords/items/0",
793		"#/definitions/datedTaggedRecords/items/1",
794		"#/definitions/namedAgain/properties/id",
795		"#/definitions/namedThing/properties/name",
796		"#/definitions/pneumonoultramicroscopicsilicovolcanoconiosisAntidisestablishmentarianism/properties/" +
797			"floccinaucinihilipilificationCreatedAt",
798		"#/definitions/records/items/0",
799		"#/definitions/datedTaggedRecords/additionalItems",
800		"#/definitions/otherRecords/items",
801		"#/definitions/tags/additionalProperties",
802		"#/definitions/datedRecords",
803		"#/definitions/datedTag",
804		"#/definitions/datedTaggedRecords",
805		"#/definitions/namedAgain",
806		"#/definitions/namedThing",
807		"#/definitions/otherRecords",
808		"#/definitions/pneumonoultramicroscopicsilicovolcanoconiosisAntidisestablishmentarianism",
809		"#/definitions/records",
810		"#/definitions/tags",
811	}
812
813	a := New(sp)
814	result := sortDepthFirst(a.allSchemas)
815	assert.Equal(t, values, result)
816}
817
818func TestBuildNameWithReservedKeyWord(t *testing.T) {
819	s := splitKey([]string{"definitions", "fullview", "properties", "properties"})
820	startIdx := 2
821	segments := []string{"fullview"}
822	newName := s.BuildName(segments, startIdx, nil)
823	assert.Equal(t, "fullview properties", newName)
824
825	s = splitKey([]string{"definitions", "fullview",
826		"properties", "properties", "properties", "properties", "properties", "properties"})
827	newName = s.BuildName(segments, startIdx, nil)
828	assert.Equal(t, "fullview properties properties properties", newName)
829}
830
831func TestNameInlinedSchemas(t *testing.T) {
832	values := []struct {
833		Key      string
834		Location string
835		Ref      spec.Ref
836	}{
837		{"#/paths/~1some~1where~1{id}/get/parameters/2/schema/properties/record/items/2/properties/name",
838			"#/definitions/getSomeWhereIdParamsBodyRecordItems2/properties/name",
839			spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems2Name"),
840		},
841		{"#/paths/~1some~1where~1{id}/get/parameters/2/schema/properties/record/items/1",
842			"#/definitions/getSomeWhereIdParamsBodyRecord/items/1",
843			spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems1"),
844		},
845
846		{"#/paths/~1some~1where~1{id}/get/parameters/2/schema/properties/record/items/2",
847			"#/definitions/getSomeWhereIdParamsBodyRecord/items/2",
848			spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems2"),
849		},
850
851		{"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/record/items/2/properties/name",
852			"#/definitions/getSomeWhereIdOKBodyRecordItems2/properties/name",
853			spec.MustCreateRef("#/definitions/getSomeWhereIdOKBodyRecordItems2Name"),
854		},
855
856		{"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/record/items/1",
857			"#/definitions/getSomeWhereIdOKBodyRecord/items/1",
858			spec.MustCreateRef("#/definitions/getSomeWhereIdOKBodyRecordItems1"),
859		},
860
861		{"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/record/items/2",
862			"#/definitions/getSomeWhereIdOKBodyRecord/items/2",
863			spec.MustCreateRef("#/definitions/getSomeWhereIdOKBodyRecordItems2"),
864		},
865
866		{"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/record",
867			"#/definitions/getSomeWhereIdOKBody/properties/record",
868			spec.MustCreateRef("#/definitions/getSomeWhereIdOKBodyRecord"),
869		},
870
871		{"#/paths/~1some~1where~1{id}/get/responses/200/schema",
872			"#/paths/~1some~1where~1{id}/get/responses/200/schema",
873			spec.MustCreateRef("#/definitions/getSomeWhereIdOKBody"),
874		},
875
876		{"#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/record/items/2/properties/name",
877			"#/definitions/getSomeWhereIdDefaultBodyRecordItems2/properties/name",
878			spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBodyRecordItems2Name"),
879		},
880
881		{"#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/record/items/1",
882			"#/definitions/getSomeWhereIdDefaultBodyRecord/items/1",
883			spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBodyRecordItems1"),
884		},
885
886		{"#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/record/items/2",
887			"#/definitions/getSomeWhereIdDefaultBodyRecord/items/2",
888			spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBodyRecordItems2"),
889		},
890
891		{"#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/record",
892			"#/definitions/getSomeWhereIdDefaultBody/properties/record",
893			spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBodyRecord"),
894		},
895
896		{"#/paths/~1some~1where~1{id}/get/responses/default/schema",
897			"#/paths/~1some~1where~1{id}/get/responses/default/schema",
898			spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBody"),
899		},
900		// maps:
901		// {"#/definitions/nestedThing/properties/record/items/2/allOf/1/additionalProperties",
902		// "#/definitions/nestedThingRecordItems2AllOf1/additionalProperties",
903		// spec.MustCreateRef("#/definitions/nestedThingRecordItems2AllOf1AdditionalProperties"),
904		// },
905
906		// {"#/definitions/nestedThing/properties/record/items/2/allOf/1",
907		// "#/definitions/nestedThingRecordItems2/allOf/1",
908		// spec.MustCreateRef("#/definitions/nestedThingRecordItems2AllOf1"),
909		// },
910		{"#/definitions/nestedThing/properties/record/items/2/properties/name",
911			"#/definitions/nestedThingRecordItems2/properties/name",
912			spec.MustCreateRef("#/definitions/nestedThingRecordItems2Name"),
913		},
914
915		{"#/definitions/nestedThing/properties/record/items/1",
916			"#/definitions/nestedThingRecord/items/1",
917			spec.MustCreateRef("#/definitions/nestedThingRecordItems1"),
918		},
919
920		{"#/definitions/nestedThing/properties/record/items/2",
921			"#/definitions/nestedThingRecord/items/2",
922			spec.MustCreateRef("#/definitions/nestedThingRecordItems2"),
923		},
924
925		{"#/definitions/datedRecords/items/1",
926			"#/definitions/datedRecords/items/1",
927			spec.MustCreateRef("#/definitions/datedRecordsItems1"),
928		},
929
930		{"#/definitions/datedTaggedRecords/items/1",
931			"#/definitions/datedTaggedRecords/items/1",
932			spec.MustCreateRef("#/definitions/datedTaggedRecordsItems1"),
933		},
934
935		{"#/definitions/namedThing/properties/name",
936			"#/definitions/namedThing/properties/name",
937			spec.MustCreateRef("#/definitions/namedThingName"),
938		},
939
940		{"#/definitions/nestedThing/properties/record",
941			"#/definitions/nestedThing/properties/record",
942			spec.MustCreateRef("#/definitions/nestedThingRecord"),
943		},
944
945		{"#/definitions/records/items/0",
946			"#/definitions/records/items/0",
947			spec.MustCreateRef("#/definitions/recordsItems0"),
948		},
949
950		{"#/definitions/datedTaggedRecords/additionalItems",
951			"#/definitions/datedTaggedRecords/additionalItems",
952			spec.MustCreateRef("#/definitions/datedTaggedRecordsItemsAdditionalItems"),
953		},
954
955		{"#/definitions/otherRecords/items",
956			"#/definitions/otherRecords/items",
957			spec.MustCreateRef("#/definitions/otherRecordsItems"),
958		},
959
960		{"#/definitions/tags/additionalProperties",
961			"#/definitions/tags/additionalProperties",
962			spec.MustCreateRef("#/definitions/tagsAdditionalProperties"),
963		},
964	}
965
966	bp := filepath.Join("fixtures", "nested_inline_schemas.yml")
967	sp := loadOrFail(t, bp)
968
969	require.NoError(t, spec.ExpandSpec(sp, &spec.ExpandOptions{
970		RelativeBase: bp,
971		SkipSchemas:  true,
972	}))
973
974	require.NoError(t, nameInlinedSchemas(&FlattenOpts{
975		Spec:     New(sp),
976		BasePath: bp,
977	}))
978
979	for i, v := range values {
980		ptr, err := jsonpointer.New(v.Location[1:])
981		require.NoErrorf(t, err, "at %d for %s", i, v.Key)
982
983		vv, _, err := ptr.Get(sp)
984		require.NoErrorf(t, err, "at %d for %s", i, v.Key)
985
986		switch tv := vv.(type) {
987		case *spec.Schema:
988			assert.Equal(t, v.Ref.String(), tv.Ref.String(), "at %d for %s", i, v.Key)
989		case spec.Schema:
990			assert.Equal(t, v.Ref.String(), tv.Ref.String(), "at %d for %s", i, v.Key)
991		case *spec.SchemaOrBool:
992			var sRef spec.Ref
993			if tv != nil && tv.Schema != nil {
994				sRef = tv.Schema.Ref
995			}
996			assert.Equal(t, v.Ref.String(), sRef.String(), "at %d for %s", i, v.Key)
997		case *spec.SchemaOrArray:
998			var sRef spec.Ref
999			if tv != nil && tv.Schema != nil {
1000				sRef = tv.Schema.Ref
1001			}
1002			assert.Equal(t, v.Ref.String(), sRef.String(), "at %d for %s", i, v.Key)
1003		default:
1004			assert.Fail(t, "unknown type", "got %T", vv)
1005		}
1006	}
1007
1008	for k, rr := range New(sp).allSchemas {
1009		if strings.HasPrefix(k, "#/responses") || strings.HasPrefix(k, "#/parameters") {
1010			continue
1011		}
1012		if rr.Schema == nil || rr.Schema.Ref.String() != "" || rr.TopLevel {
1013			continue
1014		}
1015		asch, err := Schema(SchemaOpts{Schema: rr.Schema, Root: sp, BasePath: bp})
1016		require.NoErrorf(t, err, "for key: %s", k)
1017
1018		if !asch.IsSimpleSchema && !asch.IsArray && !asch.IsMap {
1019			assert.Fail(t, "not a top level schema", "for key: %s", k)
1020		}
1021	}
1022}
1023
1024func TestFlatten(t *testing.T) {
1025	cwd, _ := os.Getwd()
1026	bp := filepath.Join(cwd, "fixtures", "flatten.yml")
1027	sp, erl := loadSpec(bp)
1028	require.NoError(t, erl)
1029
1030	values := []struct {
1031		Key      string
1032		Location string
1033		Ref      spec.Ref
1034		Expected interface{}
1035	}{
1036		{
1037			"#/responses/notFound/schema",
1038			"#/responses/notFound/schema",
1039			spec.MustCreateRef("#/definitions/error"),
1040			nil,
1041		},
1042		{
1043			"#/paths/~1some~1where~1{id}/parameters/0",
1044			"#/paths/~1some~1where~1{id}/parameters/0/name",
1045			spec.Ref{},
1046			"id",
1047		},
1048		{
1049			"#/paths/~1other~1place",
1050			"#/paths/~1other~1place/get/operationId",
1051			spec.Ref{},
1052			"modelOp",
1053		},
1054		{
1055			"#/paths/~1some~1where~1{id}/get/parameters/0",
1056			"#/paths/~1some~1where~1{id}/get/parameters/0/name",
1057			spec.Ref{},
1058			"limit",
1059		},
1060		{
1061			"#/paths/~1some~1where~1{id}/get/parameters/1",
1062			"#/paths/~1some~1where~1{id}/get/parameters/1/name",
1063			spec.Ref{},
1064			"some",
1065		},
1066		{
1067			"#/paths/~1some~1where~1{id}/get/parameters/2",
1068			"#/paths/~1some~1where~1{id}/get/parameters/2/name",
1069			spec.Ref{},
1070			"other",
1071		},
1072		{
1073			"#/paths/~1some~1where~1{id}/get/parameters/3",
1074			"#/paths/~1some~1where~1{id}/get/parameters/3/schema",
1075			spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBody"),
1076			"",
1077		},
1078		{
1079			"#/paths/~1some~1where~1{id}/get/responses/200",
1080			"#/paths/~1some~1where~1{id}/get/responses/200/schema",
1081			spec.MustCreateRef("#/definitions/getSomeWhereIdOKBody"),
1082			"",
1083		},
1084		{
1085			"#/definitions/namedAgain",
1086			"",
1087			spec.MustCreateRef("#/definitions/named"),
1088			"",
1089		},
1090		{
1091			"#/definitions/namedThing/properties/name",
1092			"",
1093			spec.MustCreateRef("#/definitions/named"),
1094			"",
1095		},
1096		{
1097			"#/definitions/namedThing/properties/namedAgain",
1098			"",
1099			spec.MustCreateRef("#/definitions/namedAgain"),
1100			"",
1101		},
1102		{
1103			"#/definitions/datedRecords/items/1",
1104			"",
1105			spec.MustCreateRef("#/definitions/record"),
1106			"",
1107		},
1108		{
1109			"#/definitions/otherRecords/items",
1110			"",
1111			spec.MustCreateRef("#/definitions/record"),
1112			"",
1113		},
1114		{
1115			"#/definitions/tags/additionalProperties",
1116			"",
1117			spec.MustCreateRef("#/definitions/tag"),
1118			"",
1119		},
1120		{
1121			"#/definitions/datedTag/allOf/1",
1122			"",
1123			spec.MustCreateRef("#/definitions/tag"),
1124			"",
1125		},
1126		/* Maps are now considered simple schemas
1127		{
1128			"#/definitions/nestedThingRecordItems2/allOf/1",
1129			"",
1130			spec.MustCreateRef("#/definitions/nestedThingRecordItems2AllOf1"),
1131			"",
1132		},
1133		*/
1134		{
1135			"#/definitions/nestedThingRecord/items/1",
1136			"",
1137			spec.MustCreateRef("#/definitions/nestedThingRecordItems1"),
1138			"",
1139		},
1140		{
1141			"#/definitions/nestedThingRecord/items/2",
1142			"",
1143			spec.MustCreateRef("#/definitions/nestedThingRecordItems2"),
1144			"",
1145		},
1146		{
1147			"#/definitions/nestedThing/properties/record",
1148			"",
1149			spec.MustCreateRef("#/definitions/nestedThingRecord"),
1150			"",
1151		},
1152		{
1153			"#/definitions/named",
1154			"#/definitions/named/type",
1155			spec.Ref{},
1156			spec.StringOrArray{"string"},
1157		},
1158		{
1159			"#/definitions/error",
1160			"#/definitions/error/properties/id/type",
1161			spec.Ref{},
1162			spec.StringOrArray{"integer"},
1163		},
1164		{
1165			"#/definitions/record",
1166			"#/definitions/record/properties/createdAt/format",
1167			spec.Ref{},
1168			"date-time",
1169		},
1170		{
1171			"#/definitions/getSomeWhereIdOKBody",
1172			"#/definitions/getSomeWhereIdOKBody/properties/record",
1173			spec.MustCreateRef("#/definitions/nestedThing"),
1174			nil,
1175		},
1176		{
1177			"#/definitions/getSomeWhereIdParamsBody",
1178			"#/definitions/getSomeWhereIdParamsBody/properties/record",
1179			spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecord"),
1180			nil,
1181		},
1182		{
1183			"#/definitions/getSomeWhereIdParamsBodyRecord",
1184			"#/definitions/getSomeWhereIdParamsBodyRecord/items/1",
1185			spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems1"),
1186			nil,
1187		},
1188		{
1189			"#/definitions/getSomeWhereIdParamsBodyRecord",
1190			"#/definitions/getSomeWhereIdParamsBodyRecord/items/2",
1191			spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems2"),
1192			nil,
1193		},
1194		{
1195			"#/definitions/getSomeWhereIdParamsBodyRecordItems2",
1196			"#/definitions/getSomeWhereIdParamsBodyRecordItems2/allOf/0/format",
1197			spec.Ref{},
1198			"date",
1199		},
1200		{
1201			"#/definitions/getSomeWhereIdParamsBodyRecordItems2Name",
1202			"#/definitions/getSomeWhereIdParamsBodyRecordItems2Name/properties/createdAt/format",
1203			spec.Ref{},
1204			"date-time",
1205		},
1206		{
1207			"#/definitions/getSomeWhereIdParamsBodyRecordItems2",
1208			"#/definitions/getSomeWhereIdParamsBodyRecordItems2/properties/name",
1209			spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems2Name"),
1210			"date",
1211		},
1212	}
1213
1214	require.NoError(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp}))
1215
1216	for i, v := range values {
1217		pk := v.Key[1:]
1218		if v.Location != "" {
1219			pk = v.Location[1:]
1220		}
1221
1222		ptr, err := jsonpointer.New(pk)
1223		require.NoError(t, err, "at %d for %s", i, v.Key)
1224
1225		d, _, err := ptr.Get(sp)
1226		require.NoError(t, err)
1227
1228		if v.Ref.String() == "" {
1229			assert.Equal(t, v.Expected, d)
1230			continue
1231		}
1232
1233		switch s := d.(type) {
1234		case *spec.Schema:
1235			assert.Equal(t, v.Ref.String(), s.Ref.String(), "at %d for %s", i, v.Key)
1236		case spec.Schema:
1237			assert.Equal(t, v.Ref.String(), s.Ref.String(), "at %d for %s", i, v.Key)
1238		case *spec.SchemaOrArray:
1239			var sRef spec.Ref
1240			if s != nil && s.Schema != nil {
1241				sRef = s.Schema.Ref
1242			}
1243			assert.Equal(t, v.Ref.String(), sRef.String(), "at %d for %s", i, v.Key)
1244		case *spec.SchemaOrBool:
1245			var sRef spec.Ref
1246			if s != nil && s.Schema != nil {
1247				sRef = s.Schema.Ref
1248			}
1249			assert.Equal(t, v.Ref.String(), sRef.String(), "at %d for %s", i, v.Key)
1250		default:
1251			assert.Fail(t, "unknown type", "got %T at %d for %s", d, i, v.Key)
1252		}
1253	}
1254}
1255
1256func TestFlatten_oaigenFull(t *testing.T) {
1257	var sp *spec.Swagger
1258	defer func() {
1259		if t.Failed() && sp != nil {
1260			bbb, _ := json.MarshalIndent(sp, "", " ")
1261			t.Logf("%s", string(bbb))
1262		}
1263	}()
1264
1265	cwd, _ := os.Getwd()
1266	bp := filepath.Join(cwd, "fixtures", "oaigen", "fixture-oaigen.yaml")
1267	sp, erl := loadSpec(bp)
1268	require.NoError(t, erl)
1269
1270	var logCapture bytes.Buffer
1271	log.SetOutput(&logCapture)
1272	defer log.SetOutput(os.Stdout)
1273
1274	require.NoError(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Verbose: true, Minimal: false, RemoveUnused: false}))
1275
1276	msg := logCapture.String()
1277
1278	if !assert.Containsf(t, msg, "warning: duplicate flattened definition name resolved as aAOAIGen",
1279		"Expected log message") {
1280		t.Logf("Captured log: %s", msg)
1281	}
1282	if !assert.NotContainsf(t, msg, "warning: duplicate flattened definition name resolved as uniqueName2OAIGen",
1283		"Expected log message") {
1284		t.Logf("Captured log: %s", msg)
1285	}
1286
1287	res := getInPath(t, sp, "/some/where", "/get/responses/204/schema")
1288	assert.JSONEqf(t, `{"$ref": "#/definitions/uniqueName1"}`, res, "Expected a simple schema for response")
1289
1290	res = getInPath(t, sp, "/some/where", "/post/responses/204/schema")
1291	assert.JSONEqf(t, `{"$ref": "#/definitions/d"}`, res, "Expected a simple schema for response")
1292
1293	res = getInPath(t, sp, "/some/where", "/get/responses/206/schema")
1294	assert.JSONEqf(t, `{"$ref": "#/definitions/a"}`, res, "Expected a simple schema for response")
1295
1296	res = getInPath(t, sp, "/some/where", "/get/responses/304/schema")
1297	assert.JSONEqf(t, `{"$ref": "#/definitions/transitive11"}`, res, "Expected a simple schema for response")
1298
1299	res = getInPath(t, sp, "/some/where", "/get/responses/205/schema")
1300	assert.JSONEqf(t, `{"$ref": "#/definitions/b"}`, res, "Expected a simple schema for response")
1301
1302	res = getInPath(t, sp, "/some/where", "/post/responses/200/schema")
1303	assert.JSONEqf(t, `{"type": "integer"}`, res, "Expected a simple schema for response")
1304
1305	res = getInPath(t, sp, "/some/where", "/post/responses/default/schema")
1306	// pointer expanded
1307	assert.JSONEqf(t, `{"type": "integer"}`, res, "Expected a simple schema for response")
1308
1309	res = getDefinition(t, sp, "a")
1310	assert.JSONEqf(t,
1311		`{"type": "object", "properties": { "a": { "$ref": "#/definitions/aAOAIGen" }}}`,
1312		res, "Expected a simple schema for response")
1313
1314	res = getDefinition(t, sp, "aA")
1315	assert.JSONEqf(t, `{"type": "string", "format": "date"}`, res, "Expected a simple schema for response")
1316
1317	res = getDefinition(t, sp, "aAOAIGen")
1318	assert.JSONEqf(t, `{
1319		"type": "object",
1320		   "properties": {
1321		    "b": {
1322		     "type": "integer"
1323		 }},
1324		 "x-go-gen-location": "models"}`, res, "Expected a simple schema for response")
1325
1326	res = getDefinition(t, sp, "bB")
1327	assert.JSONEqf(t, `{"type": "string", "format": "date-time"}`, res, "Expected a simple schema for response")
1328
1329	_, ok := sp.Definitions["bItems"]
1330	assert.Falsef(t, ok, "Did not expect a definition for %s", "bItems")
1331
1332	res = getDefinition(t, sp, "d")
1333	assert.JSONEqf(t, `{
1334		   "type": "object",
1335		   "properties": {
1336		    "c": {
1337		     "type": "integer"
1338		    }
1339		   }
1340	}`, res, "Expected a simple schema for response")
1341
1342	res = getDefinition(t, sp, "b")
1343	assert.JSONEqf(t, `{
1344		   "type": "array",
1345		   "items": {
1346			   "$ref": "#/definitions/d"
1347		   }
1348	}`, res, "Expected a ref in response")
1349
1350	res = getDefinition(t, sp, "myBody")
1351	assert.JSONEqf(t, `{
1352		   "type": "object",
1353		   "properties": {
1354		    "aA": {
1355		     "$ref": "#/definitions/aA"
1356		    },
1357		    "prop1": {
1358		     "type": "integer"
1359		    }
1360		   }
1361	}`, res, "Expected a simple schema for response")
1362
1363	res = getDefinition(t, sp, "uniqueName2")
1364	assert.JSONEqf(t, `{"$ref": "#/definitions/notUniqueName2"}`, res, "Expected a simple schema for response")
1365
1366	res = getDefinition(t, sp, "notUniqueName2")
1367	assert.JSONEqf(t, `{
1368		  "type": "object",
1369		   "properties": {
1370		    "prop6": {
1371		     "type": "integer"
1372		    }
1373		   }
1374	   }`, res, "Expected a simple schema for response")
1375
1376	res = getDefinition(t, sp, "uniqueName1")
1377	assert.JSONEqf(t, `{
1378		   "type": "object",
1379		   "properties": {
1380		    "prop5": {
1381		     "type": "integer"
1382		    }}}`, res, "Expected a simple schema for response")
1383
1384	// allOf container: []spec.Schema
1385	res = getDefinition(t, sp, "getWithSliceContainerDefaultBody")
1386	assert.JSONEqf(t, `{
1387		"allOf": [
1388		    {
1389		     "$ref": "#/definitions/uniqueName3"
1390		    },
1391		    {
1392		     "$ref": "#/definitions/getWithSliceContainerDefaultBodyAllOf1"
1393		    }
1394		   ],
1395		   "x-go-gen-location": "operations"
1396		    }`, res, "Expected a simple schema for response")
1397
1398	res = getDefinition(t, sp, "getWithSliceContainerDefaultBodyAllOf1")
1399	assert.JSONEqf(t, `{
1400		"type": "object",
1401		   "properties": {
1402		    "prop8": {
1403		     "type": "string"
1404		    }
1405		   },
1406		   "x-go-gen-location": "models"
1407		    }`, res, "Expected a simple schema for response")
1408
1409	res = getDefinition(t, sp, "getWithTupleContainerDefaultBody")
1410	assert.JSONEqf(t, `{
1411		   "type": "array",
1412		   "items": [
1413		    {
1414		     "$ref": "#/definitions/uniqueName3"
1415		    },
1416		    {
1417		     "$ref": "#/definitions/getWithSliceContainerDefaultBodyAllOf1"
1418		    }
1419		   ],
1420		   "x-go-gen-location": "operations"
1421		    }`, res, "Expected a simple schema for response")
1422
1423	// with container SchemaOrArray
1424	res = getDefinition(t, sp, "getWithTupleConflictDefaultBody")
1425	assert.JSONEqf(t, `{
1426		   "type": "array",
1427		   "items": [
1428		    {
1429		     "$ref": "#/definitions/uniqueName4"
1430		    },
1431		    {
1432		     "$ref": "#/definitions/getWithTupleConflictDefaultBodyItems1"
1433		    }
1434		   ],
1435		   "x-go-gen-location": "operations"
1436	}`, res, "Expected a simple schema for response")
1437
1438	res = getDefinition(t, sp, "getWithTupleConflictDefaultBodyItems1")
1439	assert.JSONEqf(t, `{
1440		   "type": "object",
1441		   "properties": {
1442		    "prop10": {
1443		     "type": "string"
1444		    }
1445		   },
1446		   "x-go-gen-location": "models"
1447	}`, res, "Expected a simple schema for response")
1448}
1449
1450func TestFlatten_oaigenMinimal(t *testing.T) {
1451	var sp *spec.Swagger
1452	defer func() {
1453		if t.Failed() && sp != nil {
1454			bbb, _ := json.MarshalIndent(sp, "", " ")
1455			t.Logf("%s", string(bbb))
1456		}
1457	}()
1458
1459	cwd, _ := os.Getwd()
1460	bp := filepath.Join(cwd, "fixtures", "oaigen", "fixture-oaigen.yaml")
1461	sp, erl := loadSpec(bp)
1462	require.NoError(t, erl)
1463
1464	var logCapture bytes.Buffer
1465	log.SetOutput(&logCapture)
1466	defer log.SetOutput(os.Stdout)
1467
1468	require.NoError(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false}))
1469
1470	msg := logCapture.String()
1471	if !assert.NotContainsf(t, msg,
1472		"warning: duplicate flattened definition name resolved as aAOAIGen", "Expected log message") {
1473		t.Logf("Captured log: %s", msg)
1474	}
1475	if !assert.NotContainsf(t, msg,
1476		"warning: duplicate flattened definition name resolved as uniqueName2OAIGen", "Expected log message") {
1477		t.Logf("Captured log: %s", msg)
1478	}
1479	res := getInPath(t, sp, "/some/where", "/get/responses/204/schema")
1480	assert.JSONEqf(t, `{"$ref": "#/definitions/uniqueName1"}`, res, "Expected a simple schema for response")
1481
1482	res = getInPath(t, sp, "/some/where", "/post/responses/204/schema")
1483	assert.JSONEqf(t, `{"$ref": "#/definitions/d"}`, res, "Expected a simple schema for response")
1484
1485	res = getInPath(t, sp, "/some/where", "/get/responses/206/schema")
1486	assert.JSONEqf(t, `{"$ref": "#/definitions/a"}`, res, "Expected a simple schema for response")
1487
1488	res = getInPath(t, sp, "/some/where", "/get/responses/304/schema")
1489	assert.JSONEqf(t, `{"$ref": "#/definitions/transitive11"}`, res, "Expected a simple schema for response")
1490
1491	res = getInPath(t, sp, "/some/where", "/get/responses/205/schema")
1492	assert.JSONEqf(t, `{"$ref": "#/definitions/b"}`, res, "Expected a simple schema for response")
1493
1494	res = getInPath(t, sp, "/some/where", "/post/responses/200/schema")
1495	assert.JSONEqf(t, `{"type": "integer"}`, res, "Expected a simple schema for response")
1496
1497	res = getInPath(t, sp, "/some/where", "/post/responses/default/schema")
1498	// This JSON pointer is expanded
1499	assert.JSONEqf(t, `{"type": "integer"}`, res, "Expected a simple schema for response")
1500
1501	res = getDefinition(t, sp, "aA")
1502	assert.JSONEqf(t, `{"type": "string", "format": "date"}`, res, "Expected a simple schema for response")
1503
1504	res = getDefinition(t, sp, "a")
1505	assert.JSONEqf(t, `{
1506		   "type": "object",
1507		   "properties": {
1508		    "a": {
1509		     "type": "object",
1510		     "properties": {
1511		      "b": {
1512		       "type": "integer"
1513		      }
1514		     }
1515		    }
1516		   }
1517		  }`, res, "Expected a simple schema for response")
1518
1519	res = getDefinition(t, sp, "bB")
1520	assert.JSONEqf(t, `{"type": "string", "format": "date-time"}`, res, "Expected a simple schema for response")
1521
1522	_, ok := sp.Definitions["bItems"]
1523	assert.Falsef(t, ok, "Did not expect a definition for %s", "bItems")
1524
1525	res = getDefinition(t, sp, "d")
1526	assert.JSONEqf(t, `{
1527		   "type": "object",
1528		   "properties": {
1529		    "c": {
1530		     "type": "integer"
1531		    }
1532		   }
1533	}`, res, "Expected a simple schema for response")
1534
1535	res = getDefinition(t, sp, "b")
1536	assert.JSONEqf(t, `{
1537		   "type": "array",
1538		   "items": {
1539			   "$ref": "#/definitions/d"
1540		   }
1541	}`, res, "Expected a ref in response")
1542
1543	res = getDefinition(t, sp, "myBody")
1544	assert.JSONEqf(t, `{
1545		   "type": "object",
1546		   "properties": {
1547		    "aA": {
1548		     "$ref": "#/definitions/aA"
1549		    },
1550		    "prop1": {
1551		     "type": "integer"
1552		    }
1553		   }
1554	}`, res, "Expected a simple schema for response")
1555
1556	res = getDefinition(t, sp, "uniqueName2")
1557	assert.JSONEqf(t, `{"$ref": "#/definitions/notUniqueName2"}`, res, "Expected a simple schema for response")
1558
1559	// with allOf container: []spec.Schema
1560	res = getInPath(t, sp, "/with/slice/container", "/get/responses/default/schema")
1561	assert.JSONEqf(t, `{
1562 			"allOf": [
1563		        {
1564		         "$ref": "#/definitions/uniqueName3"
1565		        },
1566				{
1567			     "$ref": "#/definitions/getWithSliceContainerDefaultBodyAllOf1"
1568				}
1569		       ]
1570	}`, res, "Expected a simple schema for response")
1571
1572	// with tuple container
1573	res = getInPath(t, sp, "/with/tuple/container", "/get/responses/default/schema")
1574	assert.JSONEqf(t, `{
1575		       "type": "array",
1576		       "items": [
1577		        {
1578		         "$ref": "#/definitions/uniqueName3"
1579		        },
1580		        {
1581		         "$ref": "#/definitions/getWithSliceContainerDefaultBodyAllOf1"
1582		        }
1583		       ]
1584	}`, res, "Expected a simple schema for response")
1585
1586	// with SchemaOrArray container
1587	res = getInPath(t, sp, "/with/tuple/conflict", "/get/responses/default/schema")
1588	assert.JSONEqf(t, `{
1589		       "type": "array",
1590		       "items": [
1591		        {
1592		         "$ref": "#/definitions/uniqueName4"
1593		        },
1594		        {
1595		         "type": "object",
1596		         "properties": {
1597		          "prop10": {
1598		           "type": "string"
1599		          }
1600		         }
1601		        }
1602		       ]
1603	}`, res, "Expected a simple schema for response")
1604}
1605
1606func assertNoOAIGen(t *testing.T, bp string, sp *spec.Swagger) (success bool) {
1607	var logCapture bytes.Buffer
1608	log.SetOutput(&logCapture)
1609	defer log.SetOutput(os.Stdout)
1610
1611	defer func() {
1612		success = !t.Failed()
1613	}()
1614
1615	require.NoError(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Verbose: true, Minimal: false, RemoveUnused: false}))
1616
1617	msg := logCapture.String()
1618	assert.NotContains(t, msg, "warning")
1619
1620	for k := range sp.Definitions {
1621		require.NotContains(t, k, "OAIGen")
1622	}
1623
1624	return
1625}
1626
1627func TestFlatten_oaigen_1260(t *testing.T) {
1628	// test fixture from issue go-swagger/go-swagger#1260
1629	bp := filepath.Join("fixtures", "oaigen", "test3-swagger.yaml")
1630	sp := loadOrFail(t, bp)
1631	assert.Truef(t, assertNoOAIGen(t, bp, sp), "did not expect an OAIGen definition here")
1632}
1633
1634func TestFlatten_oaigen_1260bis(t *testing.T) {
1635	// test fixture from issue go-swagger/go-swagger#1260
1636	bp := filepath.Join("fixtures", "oaigen", "test3-bis-swagger.yaml")
1637	sp := loadOrFail(t, bp)
1638	assert.Truef(t, assertNoOAIGen(t, bp, sp), "did not expect an OAIGen definition here")
1639}
1640
1641func TestFlatten_oaigen_1260ter(t *testing.T) {
1642	// test fixture from issue go-swagger/go-swagger#1260
1643	bp := filepath.Join("fixtures", "oaigen", "test3-ter-swagger.yaml")
1644	sp := loadOrFail(t, bp)
1645	assert.Truef(t, assertNoOAIGen(t, bp, sp), "did not expect an OAIGen definition here")
1646}
1647
1648func getDefinition(t *testing.T, sp *spec.Swagger, key string) string {
1649	d, ok := sp.Definitions[key]
1650	require.Truef(t, ok, "Expected definition for %s", key)
1651	res, _ := json.Marshal(d)
1652	return string(res)
1653}
1654
1655func getInPath(t *testing.T, sp *spec.Swagger, path, key string) string {
1656	ptr, erp := jsonpointer.New(key)
1657	require.NoError(t, erp, "at %s no key", key)
1658
1659	d, _, erg := ptr.Get(sp.Paths.Paths[path])
1660	require.NoError(t, erg, "at %s no value for %s", path, key)
1661
1662	res, _ := json.Marshal(d)
1663	return string(res)
1664}
1665
1666func TestMoreNameInlinedSchemas(t *testing.T) {
1667	bp := filepath.Join("fixtures", "more_nested_inline_schemas.yml")
1668	sp := loadOrFail(t, bp)
1669
1670	err := Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Verbose: true, Minimal: false, RemoveUnused: false})
1671	require.NoError(t, err)
1672
1673	res := getInPath(t, sp, "/some/where/{id}", "/post/responses/200/schema")
1674	assert.JSONEqf(t,
1675		`{"type": "object", "additionalProperties":`+
1676			`{ "type": "object", "additionalProperties": { "type": "object", "additionalProperties":`+
1677			` { "$ref":`+
1678			` "#/definitions/postSomeWhereIdOKBodyAdditionalPropertiesAdditionalPropertiesAdditionalProperties"}}}}`,
1679		res, "Expected a simple schema for response")
1680
1681	res = getInPath(t, sp, "/some/where/{id}", "/post/responses/204/schema")
1682	assert.JSONEqf(t, `{
1683		       "type": "object",
1684		       "additionalProperties": {
1685		        "type": "array",
1686		        "items": {
1687		         "type": "object",
1688		         "additionalProperties": {
1689		          "type": "array",
1690		          "items": {
1691		           "type": "object",
1692		           "additionalProperties": {
1693		            "type": "array",
1694		            "items": {
1695						"$ref":`+
1696		`"#/definitions/`+
1697		`postSomeWhereIdNoContentBodyAdditionalPropertiesItemsAdditionalPropertiesItemsAdditionalPropertiesItems"
1698		            }
1699		           }
1700		          }
1701		         }
1702		        }
1703		       }
1704		   }`, res, "Expected a simple schema for response")
1705
1706}
1707
1708func TestRemoveUnused(t *testing.T) {
1709	bp := filepath.Join("fixtures", "oaigen", "fixture-oaigen.yaml")
1710	sp := loadOrFail(t, bp)
1711
1712	err := Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Verbose: false, Minimal: true, RemoveUnused: true})
1713	require.NoError(t, err)
1714
1715	assert.Nil(t, sp.Parameters)
1716	assert.Nil(t, sp.Responses)
1717
1718	bp = filepath.Join("fixtures", "parameters", "fixture-parameters.yaml")
1719	sp = loadOrFail(t, bp)
1720	an := New(sp)
1721	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: false, Minimal: true, RemoveUnused: true}))
1722
1723	assert.Nil(t, sp.Parameters)
1724	assert.Nil(t, sp.Responses)
1725
1726	op, ok := an.OperationFor("GET", "/some/where")
1727	assert.True(t, ok)
1728	assert.Lenf(t, op.Parameters, 4, "Expected 4 parameters expanded for this operation")
1729	assert.Lenf(t, an.ParamsFor("GET", "/some/where"), 7,
1730		"Expected 7 parameters (with default) expanded for this operation")
1731
1732	op, ok = an.OperationFor("PATCH", "/some/remote")
1733	assert.True(t, ok)
1734	assert.Lenf(t, op.Parameters, 1, "Expected 1 parameter expanded for this operation")
1735	assert.Lenf(t, an.ParamsFor("PATCH", "/some/remote"), 2,
1736		"Expected 2 parameters (with default) expanded for this operation")
1737
1738	_, ok = sp.Definitions["unused"]
1739	assert.False(t, ok, "Did not expect to find #/definitions/unused")
1740
1741	bp = filepath.Join("fixtures", "parameters", "fixture-parameters.yaml")
1742	sp, erl := loadSpec(bp)
1743	require.NoError(t, erl)
1744
1745	var logCapture bytes.Buffer
1746	log.SetOutput(&logCapture)
1747	defer log.SetOutput(os.Stdout)
1748
1749	require.NoError(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Verbose: true, Minimal: false, RemoveUnused: true}))
1750
1751	msg := logCapture.String()
1752	if !assert.Containsf(t, msg, "info: removing unused definition: unused", "Expected log message") {
1753		t.Logf("Captured log: %s", msg)
1754	}
1755
1756	assert.Nil(t, sp.Parameters)
1757	assert.Nil(t, sp.Responses)
1758	_, ok = sp.Definitions["unused"]
1759	assert.Falsef(t, ok, "Did not expect to find #/definitions/unused")
1760}
1761
1762func TestOperationIDs(t *testing.T) {
1763	bp := filepath.Join("fixtures", "operations", "fixture-operations.yaml")
1764	sp := loadOrFail(t, bp)
1765
1766	an := New(sp)
1767	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: false, Minimal: false, RemoveUnused: false}))
1768
1769	res := gatherOperations(New(sp), []string{"getSomeWhere", "getSomeWhereElse"})
1770	_, ok := res["getSomeWhere"]
1771	assert.Truef(t, ok, "Expected to find operation")
1772	_, ok = res["getSomeWhereElse"]
1773	assert.Truef(t, ok, "Expected to find operation")
1774	_, ok = res["postSomeWhere"]
1775	assert.Falsef(t, ok, "Did not expect to find operation")
1776
1777	op, ok := an.OperationFor("GET", "/some/where/else")
1778	assert.True(t, ok)
1779	assert.NotNil(t, op)
1780	assert.Len(t, an.ParametersFor("getSomeWhereElse"), 2)
1781
1782	op, ok = an.OperationFor("POST", "/some/where/else")
1783	assert.True(t, ok)
1784	assert.NotNil(t, op)
1785	assert.Len(t, an.ParametersFor("postSomeWhereElse"), 1)
1786
1787	op, ok = an.OperationFor("PUT", "/some/where/else")
1788	assert.True(t, ok)
1789	assert.NotNil(t, op)
1790	assert.Len(t, an.ParametersFor("putSomeWhereElse"), 1)
1791
1792	op, ok = an.OperationFor("PATCH", "/some/where/else")
1793	assert.True(t, ok)
1794	assert.NotNil(t, op)
1795	assert.Len(t, an.ParametersFor("patchSomeWhereElse"), 1)
1796
1797	op, ok = an.OperationFor("DELETE", "/some/where/else")
1798	assert.True(t, ok)
1799	assert.NotNil(t, op)
1800	assert.Len(t, an.ParametersFor("deleteSomeWhereElse"), 1)
1801
1802	op, ok = an.OperationFor("HEAD", "/some/where/else")
1803	assert.True(t, ok)
1804	assert.NotNil(t, op)
1805	assert.Len(t, an.ParametersFor("headSomeWhereElse"), 1)
1806
1807	op, ok = an.OperationFor("OPTIONS", "/some/where/else")
1808	assert.True(t, ok)
1809	assert.NotNil(t, op)
1810	assert.Len(t, an.ParametersFor("optionsSomeWhereElse"), 1)
1811
1812	assert.Len(t, an.ParametersFor("outOfThisWorld"), 0)
1813}
1814
1815func TestFlatten_Pointers(t *testing.T) {
1816	var sp *spec.Swagger
1817
1818	defer func() {
1819		if t.Failed() && sp != nil {
1820			bbb, _ := json.MarshalIndent(sp, "", " ")
1821			t.Logf("%s", string(bbb))
1822		}
1823	}()
1824
1825	bp := filepath.Join("fixtures", "pointers", "fixture-pointers.yaml")
1826	sp = loadOrFail(t, bp)
1827
1828	var logCapture bytes.Buffer
1829	log.SetOutput(&logCapture)
1830	defer log.SetOutput(os.Stdout)
1831
1832	an := New(sp)
1833	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false}))
1834
1835	msg := logCapture.String()
1836	require.NotContainsf(t, msg, "warning", msg)
1837
1838	// re-analyse and check all $ref's point to #/definitions
1839	bn := New(sp)
1840	for _, r := range bn.AllRefs() {
1841		assert.True(t, path.Dir(r.String()) == definitionsPath)
1842	}
1843}
1844
1845// unit test guards in flatten not easily testable with actual specs
1846func TestFlatten_ErrorHandling(t *testing.T) {
1847	const wantedFailure = "Expected a failure"
1848	bp := filepath.Join("fixtures", "errors", "fixture-unexpandable.yaml")
1849
1850	// invalid spec expansion
1851	sp := loadOrFail(t, bp)
1852
1853	require.Errorf(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Expand: true}), wantedFailure)
1854
1855	// reload original spec
1856	sp = loadOrFail(t, bp)
1857	require.Errorf(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Expand: false}), wantedFailure)
1858
1859	bp = filepath.Join("fixtures", "errors", "fixture-unexpandable-2.yaml")
1860	sp = loadOrFail(t, bp)
1861	require.Errorf(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Expand: false}), wantedFailure)
1862
1863	// reload original spec
1864	sp = loadOrFail(t, bp)
1865	require.Errorf(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Minimal: true, Expand: false}), wantedFailure)
1866
1867	// reload original spec
1868	sp = loadOrFail(t, bp)
1869	require.Errorf(t, rewriteSchemaToRef(sp, "#/invalidPointer/key", spec.Ref{}), wantedFailure)
1870
1871	require.Errorf(t, rewriteParentRef(sp, "#/invalidPointer/key", spec.Ref{}), wantedFailure)
1872
1873	require.Errorf(t, updateRef(sp, "#/invalidPointer/key", spec.Ref{}), wantedFailure)
1874
1875	require.Errorf(t, updateRefWithSchema(sp, "#/invalidPointer/key", &spec.Schema{}), wantedFailure)
1876
1877	_, _, err := getPointerFromKey(sp, "#/invalidPointer/key")
1878	require.Errorf(t, err, wantedFailure)
1879
1880	_, _, err = getPointerFromKey(sp, "--->#/invalidJsonPointer")
1881	require.Errorf(t, err, wantedFailure)
1882
1883	_, _, _, err = getParentFromKey(sp, "#/invalidPointer/key")
1884	require.Errorf(t, err, wantedFailure)
1885
1886	_, _, _, err = getParentFromKey(sp, "--->#/invalidJsonPointer")
1887	require.Errorf(t, err, wantedFailure)
1888
1889	assert.NotPanics(t, saveNilSchema)
1890}
1891
1892func saveNilSchema() {
1893	cwd, _ := os.Getwd()
1894	bp := filepath.Join(cwd, "fixtures", "errors", "fixture-unexpandable-2.yaml")
1895	sp, _ := loadSpec(bp)
1896	saveSchema(sp, "ThisNilSchema", nil)
1897}
1898
1899func TestFlatten_UnitGuards(t *testing.T) {
1900	parts := keyParts("#/nowhere/arbitrary/pointer")
1901	res := genLocation(parts)
1902	assert.Equal(t, "", res)
1903
1904	res = parts.DefinitionName()
1905	assert.Equal(t, "", res)
1906
1907	res = parts.ResponseName()
1908	assert.Equal(t, "", res)
1909
1910	b := parts.isKeyName(-1)
1911	assert.False(t, b)
1912
1913}
1914
1915func TestFlatten_PointersLoop(t *testing.T) {
1916	log.SetOutput(ioutil.Discard)
1917	defer log.SetOutput(os.Stdout)
1918
1919	bp := filepath.Join("fixtures", "pointers", "fixture-pointers-loop.yaml")
1920	sp := loadOrFail(t, bp)
1921
1922	an := New(sp)
1923	require.Error(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false}))
1924}
1925
1926func TestFlatten_Bitbucket(t *testing.T) {
1927	log.SetOutput(ioutil.Discard)
1928	defer log.SetOutput(os.Stdout)
1929
1930	bp := filepath.Join("fixtures", "bugs", "bitbucket.json")
1931	sp := loadOrFail(t, bp)
1932
1933	an := New(sp)
1934	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false}))
1935
1936	// reload original spec
1937	sp = loadOrFail(t, bp)
1938	an = New(sp)
1939	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: false, RemoveUnused: false}))
1940
1941	// reload original spec
1942	sp = loadOrFail(t, bp)
1943	an = New(sp)
1944	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Expand: true, RemoveUnused: false}))
1945
1946	// reload original spec
1947	sp = loadOrFail(t, bp)
1948	an = New(sp)
1949	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Expand: true, RemoveUnused: true}))
1950
1951	assert.Len(t, sp.Definitions, 2) // only 2 remaining refs after expansion: circular $ref
1952	_, ok := sp.Definitions["base_commit"]
1953	assert.True(t, ok)
1954	_, ok = sp.Definitions["repository"]
1955	assert.True(t, ok)
1956}
1957
1958func TestFlatten_Issue_1602(t *testing.T) {
1959	log.SetOutput(ioutil.Discard)
1960	defer log.SetOutput(os.Stdout)
1961
1962	// $ref as schema to #/responses or #/parameters
1963
1964	// minimal repro test case
1965	bp := filepath.Join("fixtures", "bugs", "1602", "fixture-1602-1.yaml")
1966	sp := loadOrFail(t, bp)
1967	an := New(sp)
1968	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false,
1969		RemoveUnused: false}))
1970
1971	// reload spec
1972	sp = loadOrFail(t, bp)
1973	an = New(sp)
1974	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: false, Minimal: false, Expand: false,
1975		RemoveUnused: false}))
1976
1977	// reload spec
1978	// with  prior expansion, a pseudo schema is produced
1979	sp = loadOrFail(t, bp)
1980	an = New(sp)
1981	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: false, Minimal: false, Expand: true,
1982		RemoveUnused: false}))
1983
1984	// full testcase
1985	bp = filepath.Join("fixtures", "bugs", "1602", "fixture-1602-full.yaml")
1986	sp = loadOrFail(t, bp)
1987	an = New(sp)
1988	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: false, Minimal: true, Expand: false,
1989		RemoveUnused: false}))
1990
1991	bp = filepath.Join("fixtures", "bugs", "1602", "fixture-1602-1.yaml")
1992	sp = loadOrFail(t, bp)
1993	an = New(sp)
1994	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false,
1995		RemoveUnused: false}))
1996
1997	bp = filepath.Join("fixtures", "bugs", "1602", "fixture-1602-2.yaml")
1998	sp = loadOrFail(t, bp)
1999	an = New(sp)
2000	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false,
2001		RemoveUnused: false}))
2002
2003	bp = filepath.Join("fixtures", "bugs", "1602", "fixture-1602-3.yaml")
2004	sp = loadOrFail(t, bp)
2005	an = New(sp)
2006	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false,
2007		RemoveUnused: false}))
2008
2009	bp = filepath.Join("fixtures", "bugs", "1602", "fixture-1602-4.yaml")
2010	sp = loadOrFail(t, bp)
2011	an = New(sp)
2012	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false,
2013		RemoveUnused: false}))
2014
2015	bp = filepath.Join("fixtures", "bugs", "1602", "fixture-1602-5.yaml")
2016	sp = loadOrFail(t, bp)
2017	an = New(sp)
2018	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false,
2019		RemoveUnused: false}))
2020
2021	bp = filepath.Join("fixtures", "bugs", "1602", "fixture-1602-6.yaml")
2022	sp = loadOrFail(t, bp)
2023	an = New(sp)
2024	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false,
2025		RemoveUnused: false}))
2026}
2027
2028func TestFlatten_Issue_1614(t *testing.T) {
2029	var logCapture bytes.Buffer
2030	log.SetOutput(&logCapture)
2031	defer log.SetOutput(os.Stdout)
2032
2033	// $ref as schema to #/responses or #/parameters
2034	// test warnings
2035
2036	bp := filepath.Join("fixtures", "bugs", "1614", "gitea.yaml")
2037	sp := loadOrFail(t, bp)
2038	an := New(sp)
2039	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false,
2040		RemoveUnused: false}))
2041
2042	msg := logCapture.String()
2043	require.Containsf(t, msg, `warning: found $ref "#/responses/empty" (response) interpreted as schema`,
2044		"Expected log message. Captured log: %s", msg)
2045
2046	require.Containsf(t, msg, `warning: found $ref "#/responses/forbidden" (response) interpreted as schema`,
2047		"Expected log message. Captured log: %s", msg)
2048
2049	// check responses subject to warning have been expanded
2050	bbb, _ := json.Marshal(sp)
2051	assert.NotContains(t, string(bbb), `#/responses/forbidden`)
2052	assert.NotContains(t, string(bbb), `#/responses/empty`)
2053}
2054
2055func TestFlatten_Issue_1621(t *testing.T) {
2056	// repeated remote refs
2057
2058	// minimal repro test case
2059	bp := filepath.Join("fixtures", "bugs", "1621", "fixture-1621.yaml")
2060	sp := loadOrFail(t, bp)
2061	an := New(sp)
2062	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false,
2063		RemoveUnused: false}))
2064
2065	sch1 := sp.Paths.Paths["/v4/users/"].Get.Responses.StatusCodeResponses[200].Schema
2066	bbb, _ := json.Marshal(sch1)
2067	assert.JSONEq(t, `{
2068			 "type": "array",
2069			 "items": {
2070			  "$ref": "#/definitions/v4UserListItem"
2071			 }
2072		 }`, string(bbb))
2073
2074	sch2 := sp.Paths.Paths["/v4/user/"].Get.Responses.StatusCodeResponses[200].Schema
2075	bbb, _ = json.Marshal(sch2)
2076	assert.JSONEq(t, `{
2077			 "$ref": "#/definitions/v4UserListItem"
2078			 }`, string(bbb))
2079
2080	sch3 := sp.Paths.Paths["/v4/users/{email}/"].Get.Responses.StatusCodeResponses[200].Schema
2081	bbb, _ = json.Marshal(sch3)
2082	assert.JSONEq(t, `{
2083			 "$ref": "#/definitions/v4UserListItem"
2084			 }`, string(bbb))
2085}
2086
2087func Test_NormalizePath(t *testing.T) {
2088	values := []struct{ Source, Expected string }{
2089		{"#/definitions/A", "#/definitions/A"},
2090		{"http://somewhere.com/definitions/A", "http://somewhere.com/definitions/A"},
2091		{wrapWindowsPath("/definitions/A"), wrapWindowsPath("/definitions/A")}, // considered absolute on unix but not on windows
2092		{wrapWindowsPath("/definitions/errorModel.json") + "#/definitions/A", wrapWindowsPath("/definitions/errorModel.json") + "#/definitions/A"},
2093		{"http://somewhere.com", "http://somewhere.com"},
2094		{wrapWindowsPath("./definitions/definitions.yaml") + "#/definitions/A", wrapWindowsPath("/abs/to/spec/definitions/definitions.yaml") + "#/definitions/A"},
2095		{"#", wrapWindowsPath("/abs/to/spec")},
2096	}
2097
2098	for _, v := range values {
2099		assert.Equal(t, v.Expected, normalizePath(spec.MustCreateRef(v.Source),
2100			&FlattenOpts{BasePath: wrapWindowsPath("/abs/to/spec/spec.json")}))
2101	}
2102}
2103
2104func TestFlatten_Issue_1796(t *testing.T) {
2105	var sp *spec.Swagger
2106	defer func() {
2107		if t.Failed() && sp != nil {
2108			bbb, _ := json.MarshalIndent(sp, "", " ")
2109			t.Logf("%s", string(bbb))
2110		}
2111	}()
2112
2113	// remote cyclic ref
2114	bp := filepath.Join("fixtures", "bugs", "1796", "queryIssue.json")
2115	sp = loadOrFail(t, bp)
2116	an := New(sp)
2117	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false,
2118		RemoveUnused: false}))
2119
2120	// assert all $ref match  "$ref": "#/definitions/something"
2121	for _, ref := range an.AllReferences() {
2122		assert.True(t, strings.HasPrefix(ref, "#/definitions"))
2123	}
2124}
2125
2126func TestFlatten_Issue_1767(t *testing.T) {
2127	// remote cyclic ref again
2128	bp := filepath.Join("fixtures", "bugs", "1767", "fixture-1767.yaml")
2129	sp := loadOrFail(t, bp)
2130	an := New(sp)
2131	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false,
2132		RemoveUnused: false}))
2133
2134	// assert all $ref match  "$ref": "#/definitions/something"
2135	for _, ref := range an.AllReferences() {
2136		assert.True(t, strings.HasPrefix(ref, "#/definitions"))
2137	}
2138}
2139
2140func TestFlatten_Issue_1774(t *testing.T) {
2141	var sp *spec.Swagger
2142	defer func() {
2143		if t.Failed() && sp != nil {
2144			bbb, _ := json.MarshalIndent(sp, "", " ")
2145			t.Logf("%s", string(bbb))
2146		}
2147	}()
2148
2149	// remote cyclic ref again
2150	bp := filepath.Join("fixtures", "bugs", "1774", "def_api.yaml")
2151	sp = loadOrFail(t, bp)
2152	an := New(sp)
2153	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: false, Expand: false,
2154		RemoveUnused: false}))
2155
2156	// assert all $ref match  "$ref": "#/definitions/something"
2157	for _, ref := range an.AllReferences() {
2158		assert.True(t, strings.HasPrefix(ref, "#/definitions"))
2159	}
2160}
2161
2162func TestFlatten_1429(t *testing.T) {
2163	// nested / remote $ref in response / param schemas
2164	// issue go-swagger/go-swagger#1429
2165	bp := filepath.Join("fixtures", "bugs", "1429", "swagger.yaml")
2166	sp := loadOrFail(t, bp)
2167
2168	an := New(sp)
2169	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false}))
2170}
2171
2172func TestRebaseRef(t *testing.T) {
2173	assert.Equal(t, "#/definitions/abc", rebaseRef("#/definitions/base", "#/definitions/abc"))
2174	assert.Equal(t, "#/definitions/abc", rebaseRef("", "#/definitions/abc"))
2175	assert.Equal(t, "#/definitions/abc", rebaseRef(".", "#/definitions/abc"))
2176	assert.Equal(t, "otherfile#/definitions/abc", rebaseRef("file#/definitions/base", "otherfile#/definitions/abc"))
2177	assert.Equal(t, wrapWindowsPath("../otherfile")+"#/definitions/abc", rebaseRef(wrapWindowsPath("../file")+"#/definitions/base", wrapWindowsPath("./otherfile")+"#/definitions/abc"))
2178	assert.Equal(t, wrapWindowsPath("../otherfile")+"#/definitions/abc", rebaseRef(wrapWindowsPath("../file")+"#/definitions/base", wrapWindowsPath("otherfile")+"#/definitions/abc"))
2179	assert.Equal(t, wrapWindowsPath("local/remote/otherfile")+"#/definitions/abc", rebaseRef(wrapWindowsPath("local/file")+"#/definitions/base", wrapWindowsPath("remote/otherfile")+"#/definitions/abc"))
2180	assert.Equal(t, wrapWindowsPath("local/remote/otherfile.yaml"), rebaseRef(wrapWindowsPath("local/file.yaml"), wrapWindowsPath("remote/otherfile.yaml")))
2181
2182	assert.Equal(t, "file#/definitions/abc", rebaseRef("file#/definitions/base", "#/definitions/abc"))
2183
2184	// with remote
2185	assert.Equal(t, "https://example.com/base#/definitions/abc", rebaseRef("https://example.com/base", "https://example.com/base#/definitions/abc"))
2186	assert.Equal(t, "https://example.com/base#/definitions/abc", rebaseRef("https://example.com/base", "#/definitions/abc"))
2187	assert.Equal(t, "https://example.com/base#/dir/definitions/abc", rebaseRef("https://example.com/base", "#/dir/definitions/abc"))
2188	assert.Equal(t, "https://example.com/base/dir/definitions/abc", rebaseRef("https://example.com/base/spec.yaml", "dir/definitions/abc"))
2189	assert.Equal(t, "https://example.com/base/dir/definitions/abc", rebaseRef("https://example.com/base/", "dir/definitions/abc"))
2190	assert.Equal(t, "https://example.com/dir/definitions/abc", rebaseRef("https://example.com/base", "dir/definitions/abc"))
2191}
2192
2193func TestFlatten_1851(t *testing.T) {
2194	// nested / remote $ref in response / param schemas
2195	// issue go-swagger/go-swagger#1851
2196	bp := filepath.Join("fixtures", "bugs", "1851", "fixture-1851.yaml")
2197	sp := loadOrFail(t, bp)
2198
2199	an := New(sp)
2200	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false}))
2201
2202	var jazon []byte
2203
2204	serverDefinition, ok := an.spec.Definitions["server"]
2205	assert.True(t, ok)
2206	serverStatusDefinition, ok := an.spec.Definitions["serverStatus"]
2207	assert.True(t, ok)
2208	serverStatusProperty, ok := serverDefinition.Properties["Status"]
2209	assert.True(t, ok)
2210	jazon, _ = json.Marshal(serverStatusProperty)
2211	assert.JSONEq(t, `{"$ref": "#/definitions/serverStatus"}`, string(jazon))
2212	jazon, _ = json.Marshal(serverStatusDefinition)
2213	assert.JSONEq(t, `{
2214         "type": "string",
2215         "enum": [
2216          "OK",
2217          "Not OK"
2218         ]
2219	 }`, string(jazon))
2220
2221	// additional test case: this one used to work
2222	bp = filepath.Join("fixtures", "bugs", "1851", "fixture-1851-2.yaml")
2223	sp = loadOrFail(t, bp)
2224
2225	an = New(sp)
2226	require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false}))
2227
2228	serverDefinition, ok = an.spec.Definitions["Server"]
2229	assert.True(t, ok)
2230	serverStatusDefinition, ok = an.spec.Definitions["ServerStatus"]
2231	assert.True(t, ok)
2232	serverStatusProperty, ok = serverDefinition.Properties["Status"]
2233	assert.True(t, ok)
2234	jazon, _ = json.Marshal(serverStatusProperty)
2235	assert.JSONEq(t, `{"$ref": "#/definitions/ServerStatus"}`, string(jazon))
2236	jazon, _ = json.Marshal(serverStatusDefinition)
2237	assert.JSONEq(t, `{
2238         "type": "string",
2239         "enum": [
2240          "OK",
2241          "Not OK"
2242         ]
2243	 }`, string(jazon))
2244}
2245
2246var (
2247	rex    = regexp.MustCompile(`"\$ref":\s*"(.+)"`)
2248	oairex = regexp.MustCompile(`oiagen`)
2249)
2250
2251func checkRefs(t *testing.T, spec *spec.Swagger, expectNoConflict bool) {
2252	// all $ref resolve locally
2253	jazon, _ := json.MarshalIndent(spec, "", " ")
2254	m := rex.FindAllStringSubmatch(string(jazon), -1)
2255	require.NotNil(t, m)
2256	for _, matched := range m {
2257		subMatch := matched[1]
2258		assert.True(t, strings.HasPrefix(subMatch, "#/definitions/"),
2259			"expected $ref to be inlined, got: %s", matched[0])
2260	}
2261
2262	if expectNoConflict {
2263		// no naming conflict
2264		m := oairex.FindAllStringSubmatch(string(jazon), -1)
2265		assert.Empty(t, m)
2266	}
2267}
2268
2269func testFlattenWithDefaults(t *testing.T, bp string) *Spec {
2270	sp := loadOrFail(t, bp)
2271	an := New(sp)
2272	err := Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false})
2273	require.NoError(t, err)
2274	return an
2275}
2276
2277func TestFlatten_RemoteAbsolute(t *testing.T) {
2278	// this one has simple remote ref pattern
2279	an := testFlattenWithDefaults(t, filepath.Join("fixtures", "bugs", "remote-absolute", "swagger-mini.json"))
2280	checkRefs(t, an.spec, true)
2281
2282	// this has no remote ref
2283	an = testFlattenWithDefaults(t, filepath.Join("fixtures", "bugs", "remote-absolute", "swagger.json"))
2284	checkRefs(t, an.spec, true)
2285
2286	// this one has local ref, no naming conflict (same as previous but with external ref imported)
2287	an = testFlattenWithDefaults(t, filepath.Join("fixtures", "bugs", "remote-absolute", "swagger-with-local-ref.json"))
2288	checkRefs(t, an.spec, true)
2289
2290	// this one has remote ref, no naming conflict (same as previous but with external ref imported)
2291	an = testFlattenWithDefaults(t, filepath.Join("fixtures", "bugs", "remote-absolute", "swagger-with-remote-only-ref.json"))
2292	checkRefs(t, an.spec, true)
2293
2294	// this one has both remote and local ref with naming conflict.
2295	// This creates some "oiagen" definitions to address naming conflict, which are removed by the oaigen pruning process (reinlined / merged with parents).
2296	an = testFlattenWithDefaults(t, filepath.Join("fixtures", "bugs", "remote-absolute", "swagger-with-ref.json"))
2297	checkRefs(t, an.spec, false)
2298}
2299
2300func TestTopmostFirs(t *testing.T) {
2301	assert.Equal(t, []string{"/a/b", "/a/b/c"}, topmostFirst([]string{"/a/b/c", "/a/b"}))
2302	assert.Equal(t, []string{"/a/b", "/a/c"}, topmostFirst([]string{"/a/c", "/a/b"}))
2303	assert.Equal(t, []string{"/a/b", "/a/c", "/a/b/c", "/a/b/d", "/a/a/b/d"}, topmostFirst([]string{"/a/a/b/d", "/a/b", "/a/b/c", "/a/b/d", "/a/c"}))
2304}
2305
2306func TestFlatten_2092(t *testing.T) {
2307	log.SetOutput(ioutil.Discard)
2308	defer log.SetOutput(os.Stdout)
2309
2310	bp := filepath.Join("fixtures", "bugs", "2092", "swagger.yaml")
2311	rexOAIGen := regexp.MustCompile(`(?i)("\$ref":\s*")(.?oaigen.?)"`)
2312
2313	// #2092 exhibits a stability issue: repeat 100 times the process to make sure it is stable
2314	var bb, bb2 string
2315	for i := 0; i < 100; i++ {
2316		sp := loadOrFail(t, bp)
2317		an := New(sp)
2318		err := Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false})
2319		require.NoError(t, err)
2320
2321		bbb, _ := json.MarshalIndent(an.spec, "", " ")
2322
2323		if i == 0 {
2324			// verify we don't have dangling oaigen refs
2325			bb = string(bbb)
2326			require.Falsef(t, rexOAIGen.Match(bbb), "unmatched regexp for: %s", bb)
2327		} else {
2328			// verify that we produce a stable result
2329			assert.JSONEq(t, bb, string(bbb))
2330		}
2331
2332		err = Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: true})
2333		require.NoError(t, err)
2334
2335		err = Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: false, RemoveUnused: false})
2336		require.NoError(t, err)
2337
2338		bbb, _ = json.MarshalIndent(an.spec, "", " ")
2339		if i == 0 {
2340			bb2 = string(bbb)
2341			require.Falsef(t, rexOAIGen.Match(bbb), "unmatched regexp for: %s")
2342		} else {
2343			// verify that we produce a stable result
2344			assert.JSONEq(t, bb2, string(bbb))
2345		}
2346	}
2347}
2348
2349func TestFlatten_2113(t *testing.T) {
2350	// flatten $ref under path
2351
2352	log.SetOutput(ioutil.Discard)
2353	defer log.SetOutput(os.Stdout)
2354
2355	bp := filepath.Join("fixtures", "bugs", "2113", "base.yaml")
2356
2357	sp := loadOrFail(t, bp)
2358	an := New(sp)
2359	err := Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Expand: true, RemoveUnused: false})
2360	require.NoError(t, err)
2361
2362	sp = loadOrFail(t, bp)
2363	an = New(sp)
2364	err = Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false})
2365	require.NoError(t, err)
2366
2367	jazon, err := json.MarshalIndent(sp, "", " ")
2368	require.NoError(t, err)
2369
2370	expected := `
2371	{
2372     "swagger": "2.0",
2373     "info": {
2374      "title": "nested $ref fixture",
2375      "version": "1"
2376     },
2377     "paths": {
2378      "/dummy": {
2379       "get": {
2380        "responses": {
2381         "200": {
2382          "description": "OK",
2383          "schema": {
2384           "$ref": "#/definitions/dummy"
2385          }
2386         }
2387        }
2388       }
2389      },
2390      "/example": {
2391       "get": {
2392        "responses": {
2393         "200": {
2394          "description": "OK",
2395          "schema": {
2396           "$ref": "#/definitions/example"
2397          }
2398         }
2399        }
2400       }
2401      }
2402     },
2403     "definitions": {
2404      "dummy": {
2405       "required": [
2406        "dummyPayload"
2407       ],
2408       "properties": {
2409        "dummyPayload": {
2410         "type": "string"
2411        }
2412       }
2413      },
2414      "example": {
2415       "required": [
2416        "payload"
2417       ],
2418       "properties": {
2419        "payload": {
2420         "type": "string"
2421        }
2422       },
2423       "$schema": "http://json-schema.org/draft-07/schema"
2424      }
2425     }
2426	}`
2427	require.JSONEq(t, expected, string(jazon))
2428}
2429