1 package analysis
2
3 import (
4 "encoding/json"
5 "fmt"
6 "net/http"
7 "net/http/httptest"
8 "path"
9 "path/filepath"
10 "testing"
11
12 "github.com/go-openapi/analysis/internal/antest"
13 "github.com/go-openapi/spec"
14 "github.com/stretchr/testify/assert"
15 "github.com/stretchr/testify/require"
16 )
17
18 var knownSchemas = []*spec.Schema{
19 spec.BoolProperty(),
20 spec.StringProperty(),
21 spec.Int8Property(),
22 spec.Int16Property(),
23 spec.Int32Property(),
24 spec.Int64Property(),
25 spec.Float32Property(),
26 spec.Float64Property(),
27 spec.DateProperty(),
28 spec.DateTimeProperty(),
29 (&spec.Schema{}),
30 (&spec.Schema{}).Typed("object", ""),
31 (&spec.Schema{}).Typed("", ""),
32 (&spec.Schema{}).Typed("", "uuid"),
33 }
34
35 func TestSchemaAnalysis_KnownTypes(t *testing.T) {
36 for i, v := range knownSchemas {
37 sch, err := Schema(SchemaOpts{Schema: v})
38 require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
39 assert.Truef(t, sch.IsKnownType, "item at %d should be a known type", i)
40 }
41
42 for i, v := range complexSchemas {
43 sch, err := Schema(SchemaOpts{Schema: v})
44 require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
45 assert.Falsef(t, sch.IsKnownType, "item at %d should not be a known type", i)
46 }
47
48 serv := refServer()
49 defer serv.Close()
50
51 for i, ref := range knownRefs(serv.URL) {
52 sch, err := Schema(SchemaOpts{Schema: refSchema(ref)})
53 require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
54 assert.Truef(t, sch.IsKnownType, "item at %d should be a known type", i)
55 }
56
57 for i, ref := range complexRefs(serv.URL) {
58 sch, err := Schema(SchemaOpts{Schema: refSchema(ref)})
59 require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
60 assert.Falsef(t, sch.IsKnownType, "item at %d should not be a known type", i)
61 }
62 }
63
64 func TestSchemaAnalysis_Array(t *testing.T) {
65 for i, v := range append(knownSchemas, (&spec.Schema{}).Typed("array", "")) {
66 sch, err := Schema(SchemaOpts{Schema: spec.ArrayProperty(v)})
67 require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
68 assert.Truef(t, sch.IsArray, "item at %d should be an array type", i)
69 assert.Truef(t, sch.IsSimpleArray, "item at %d should be a simple array type", i)
70 }
71
72 for i, v := range complexSchemas {
73 sch, err := Schema(SchemaOpts{Schema: spec.ArrayProperty(v)})
74 require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
75 assert.Truef(t, sch.IsArray, "item at %d should be an array type", i)
76 assert.Falsef(t, sch.IsSimpleArray, "item at %d should not be a simple array type", i)
77 }
78
79 serv := refServer()
80 defer serv.Close()
81
82 for i, ref := range knownRefs(serv.URL) {
83 sch, err := Schema(SchemaOpts{Schema: spec.ArrayProperty(refSchema(ref))})
84 require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
85 assert.Truef(t, sch.IsArray, "item at %d should be an array type", i)
86 assert.Truef(t, sch.IsSimpleArray, "item at %d should be a simple array type", i)
87 }
88
89 for i, ref := range complexRefs(serv.URL) {
90 sch, err := Schema(SchemaOpts{Schema: spec.ArrayProperty(refSchema(ref))})
91 require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
92 assert.Falsef(t, sch.IsKnownType, "item at %d should not be a known type", i)
93 assert.Truef(t, sch.IsArray, "item at %d should be an array type", i)
94 assert.Falsef(t, sch.IsSimpleArray, "item at %d should not be a simple array type", i)
95 }
96
97
98 at := spec.ArrayProperty(nil)
99 at.Items = nil
100 sch, err := Schema(SchemaOpts{Schema: at})
101 require.NoError(t, err)
102 assert.True(t, sch.IsArray)
103 assert.False(t, sch.IsTuple)
104 assert.False(t, sch.IsKnownType)
105 assert.True(t, sch.IsSimpleSchema)
106
107
108 at = spec.ArrayProperty(nil)
109 at.Items = &spec.SchemaOrArray{}
110 sch, err = Schema(SchemaOpts{Schema: at})
111 require.NoError(t, err)
112 assert.True(t, sch.IsArray)
113 assert.False(t, sch.IsTuple)
114 assert.False(t, sch.IsKnownType)
115 assert.True(t, sch.IsSimpleSchema)
116 }
117
118 func TestSchemaAnalysis_Map(t *testing.T) {
119 for i, v := range append(knownSchemas, spec.MapProperty(nil)) {
120 sch, err := Schema(SchemaOpts{Schema: spec.MapProperty(v)})
121 require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
122 assert.Truef(t, sch.IsMap, "item at %d should be a map type", i)
123 assert.Truef(t, sch.IsSimpleMap, "item at %d should be a simple map type", i)
124 }
125
126 for i, v := range complexSchemas {
127 sch, err := Schema(SchemaOpts{Schema: spec.MapProperty(v)})
128 require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
129 assert.Truef(t, sch.IsMap, "item at %d should be a map type", i)
130 assert.Falsef(t, sch.IsSimpleMap, "item at %d should not be a simple map type", i)
131 }
132 }
133
134 func TestSchemaAnalysis_ExtendedObject(t *testing.T) {
135 for i, v := range knownSchemas {
136 wex := spec.MapProperty(v).SetProperty("name", *spec.StringProperty())
137 sch, err := Schema(SchemaOpts{Schema: wex})
138 require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
139 assert.Truef(t, sch.IsExtendedObject, "item at %d should be an extended map object type", i)
140 assert.Falsef(t, sch.IsMap, "item at %d should not be a map type", i)
141 assert.Falsef(t, sch.IsSimpleMap, "item at %d should not be a simple map type", i)
142 }
143 }
144
145 func TestSchemaAnalysis_Tuple(t *testing.T) {
146 at := spec.ArrayProperty(nil)
147 at.Items = &spec.SchemaOrArray{}
148 at.Items.Schemas = append(at.Items.Schemas, *spec.StringProperty(), *spec.Int64Property())
149
150 sch, err := Schema(SchemaOpts{Schema: at})
151 require.NoError(t, err)
152 assert.True(t, sch.IsTuple)
153 assert.False(t, sch.IsTupleWithExtra)
154 assert.False(t, sch.IsKnownType)
155 assert.False(t, sch.IsSimpleSchema)
156
157
158 at.Items = &spec.SchemaOrArray{}
159 at.Items.Schemas = append(at.Items.Schemas, *spec.StringProperty())
160 sch, err = Schema(SchemaOpts{Schema: at})
161 require.NoError(t, err)
162 assert.True(t, sch.IsTuple)
163 assert.False(t, sch.IsTupleWithExtra)
164 assert.False(t, sch.IsKnownType)
165 assert.False(t, sch.IsSimpleSchema)
166 }
167
168 func TestSchemaAnalysis_TupleWithExtra(t *testing.T) {
169 at := spec.ArrayProperty(nil)
170 at.Items = &spec.SchemaOrArray{}
171 at.Items.Schemas = append(at.Items.Schemas, *spec.StringProperty(), *spec.Int64Property())
172 at.AdditionalItems = &spec.SchemaOrBool{Allows: true}
173 at.AdditionalItems.Schema = spec.Int32Property()
174
175 sch, err := Schema(SchemaOpts{Schema: at})
176 require.NoError(t, err)
177 assert.False(t, sch.IsTuple)
178 assert.True(t, sch.IsTupleWithExtra)
179 assert.False(t, sch.IsKnownType)
180 assert.False(t, sch.IsSimpleSchema)
181 }
182
183 func TestSchemaAnalysis_BaseType(t *testing.T) {
184 cl := (&spec.Schema{}).Typed("object", "").SetProperty("type", *spec.StringProperty()).WithDiscriminator("type")
185
186 sch, err := Schema(SchemaOpts{Schema: cl})
187 require.NoError(t, err)
188 assert.True(t, sch.IsBaseType)
189 assert.False(t, sch.IsKnownType)
190 assert.False(t, sch.IsSimpleSchema)
191 }
192
193 func TestSchemaAnalysis_SimpleSchema(t *testing.T) {
194 for i, v := range append(knownSchemas, spec.ArrayProperty(nil), spec.MapProperty(nil)) {
195 sch, err := Schema(SchemaOpts{Schema: v})
196 require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
197 assert.Truef(t, sch.IsSimpleSchema, "item at %d should be a simple schema", i)
198
199 asch, err := Schema(SchemaOpts{Schema: spec.ArrayProperty(v)})
200 require.NoErrorf(t, err, "failed to analyze array schema at %d: %v", i, err)
201 assert.Truef(t, asch.IsSimpleSchema, "array item at %d should be a simple schema", i)
202
203 msch, err := Schema(SchemaOpts{Schema: spec.MapProperty(v)})
204 require.NoErrorf(t, err, "failed to analyze map schema at %d: %v", i, err)
205 assert.Truef(t, msch.IsSimpleSchema, "map item at %d should be a simple schema", i)
206 }
207
208 for i, v := range complexSchemas {
209 sch, err := Schema(SchemaOpts{Schema: v})
210 require.NoErrorf(t, err, "failed to analyze schema at %d: %v", i, err)
211 assert.Falsef(t, sch.IsSimpleSchema, "item at %d should not be a simple schema", i)
212 }
213 }
214
215 func TestSchemaAnalys_InvalidSchema(t *testing.T) {
216
217
218 bp := filepath.Join("fixtures", "bugs", "1602", "other-invalid-pointers.yaml")
219 sp := antest.LoadOrFail(t, bp)
220
221
222 def := sp.Definitions["invalidRefInObject"]
223 _, err := Schema(SchemaOpts{Schema: &def, Root: sp, BasePath: bp})
224 require.NoError(t, err, "did not expect an error here, in spite of the underlying invalid $ref")
225
226 def = sp.Definitions["invalidRefInTuple"]
227 _, err = Schema(SchemaOpts{Schema: &def, Root: sp, BasePath: bp})
228 require.NoError(t, err, "did not expect an error here, in spite of the underlying invalid $ref")
229
230
231 schema := refSchema(spec.MustCreateRef("#/definitions/noWhere"))
232 _, err = Schema(SchemaOpts{Schema: schema, Root: sp, BasePath: bp})
233 require.Error(t, err, "expected an error here")
234
235 def = sp.Definitions["invalidRefInMap"]
236 _, err = Schema(SchemaOpts{Schema: &def, Root: sp, BasePath: bp})
237 require.Error(t, err, "expected an error here")
238
239 def = sp.Definitions["invalidRefInArray"]
240 _, err = Schema(SchemaOpts{Schema: &def, Root: sp, BasePath: bp})
241 require.Error(t, err, "expected an error here")
242
243 def = sp.Definitions["indirectToInvalidRef"]
244 _, err = Schema(SchemaOpts{Schema: &def, Root: sp, BasePath: bp})
245 require.Error(t, err, "expected an error here")
246 }
247
248 func TestSchemaAnalysis_EdgeCases(t *testing.T) {
249 t.Parallel()
250
251 _, err := Schema(SchemaOpts{Schema: nil})
252 require.Error(t, err)
253 }
254
255
256
257 func newCObj() *spec.Schema {
258 return (&spec.Schema{}).Typed("object", "").SetProperty("id", *spec.Int64Property())
259 }
260
261 var complexObject = newCObj()
262
263 var complexSchemas = []*spec.Schema{
264 complexObject,
265 spec.ArrayProperty(complexObject),
266 spec.MapProperty(complexObject),
267 }
268
269 func knownRefs(base string) []spec.Ref {
270 urls := []string{"bool", "string", "integer", "float", "date", "object", "format"}
271
272 result := make([]spec.Ref, 0, len(urls))
273 for _, u := range urls {
274 result = append(result, spec.MustCreateRef(fmt.Sprintf("%s/%s", base, path.Join("known", u))))
275 }
276
277 return result
278 }
279
280 func complexRefs(base string) []spec.Ref {
281 urls := []string{"object", "array", "map"}
282
283 result := make([]spec.Ref, 0, len(urls))
284 for _, u := range urls {
285 result = append(result, spec.MustCreateRef(fmt.Sprintf("%s/%s", base, path.Join("complex", u))))
286 }
287
288 return result
289 }
290
291 func refServer() *httptest.Server {
292 mux := http.NewServeMux()
293 mux.Handle("/known/bool", schemaHandler(knownSchemas[0]))
294 mux.Handle("/known/string", schemaHandler(knownSchemas[1]))
295 mux.Handle("/known/integer", schemaHandler(knownSchemas[5]))
296 mux.Handle("/known/float", schemaHandler(knownSchemas[6]))
297 mux.Handle("/known/date", schemaHandler(knownSchemas[8]))
298 mux.Handle("/known/object", schemaHandler(knownSchemas[11]))
299 mux.Handle("/known/format", schemaHandler(knownSchemas[13]))
300
301 mux.Handle("/complex/object", schemaHandler(complexSchemas[0]))
302 mux.Handle("/complex/array", schemaHandler(complexSchemas[1]))
303 mux.Handle("/complex/map", schemaHandler(complexSchemas[2]))
304
305 return httptest.NewServer(mux)
306 }
307
308 func refSchema(ref spec.Ref) *spec.Schema {
309 return &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
310 }
311
312 func schemaHandler(schema *spec.Schema) http.Handler {
313 return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
314 writeJSON(w, schema)
315 })
316 }
317
318 func writeJSON(w http.ResponseWriter, data interface{}) {
319 w.Header().Add("Content-Type", "application/json")
320 w.WriteHeader(http.StatusOK)
321 enc := json.NewEncoder(w)
322
323 if err := enc.Encode(data); err != nil {
324 panic(err)
325 }
326 }
327
View as plain text