1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package fmts
16
17 import (
18 "encoding/json"
19 "errors"
20 "net/http"
21 "net/http/httptest"
22 "testing"
23
24 yaml "gopkg.in/yaml.v3"
25
26 "github.com/go-openapi/swag"
27 "github.com/stretchr/testify/assert"
28 "github.com/stretchr/testify/require"
29 )
30
31 type failJSONMarshal struct {
32 }
33
34 func (f failJSONMarshal) MarshalJSON() ([]byte, error) {
35 return nil, errors.New("expected")
36 }
37
38 func TestLoadHTTPBytes(t *testing.T) {
39 _, err := swag.LoadFromFileOrHTTP("httx://12394:abd")
40 require.Error(t, err)
41
42 serv := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
43 rw.WriteHeader(http.StatusNotFound)
44 }))
45 defer serv.Close()
46
47 _, err = swag.LoadFromFileOrHTTP(serv.URL)
48 require.Error(t, err)
49
50 ts2 := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
51 rw.WriteHeader(http.StatusOK)
52 _, _ = rw.Write([]byte("the content"))
53 }))
54 defer ts2.Close()
55
56 d, err := swag.LoadFromFileOrHTTP(ts2.URL)
57 require.NoError(t, err)
58 assert.Equal(t, []byte("the content"), d)
59 }
60
61 func TestYAMLToJSON(t *testing.T) {
62 const sd = `---
63 1: the int key value
64 name: a string value
65 'y': some value
66 `
67 t.Run("YAML object as JSON", func(t *testing.T) {
68 var data interface{}
69 require.NoError(t, yaml.Unmarshal([]byte(sd), &data))
70
71 d, err := YAMLToJSON(data)
72 require.NoError(t, err)
73 assert.JSONEq(t,
74 `{"1":"the int key value","name":"a string value","y":"some value"}`,
75 string(d),
76 )
77 })
78
79 t.Run("YAML nodes as JSON", func(t *testing.T) {
80 var data yaml.Node
81 require.NoError(t, yaml.Unmarshal([]byte(sd), &data))
82
83 data.Content[0].Content = append(data.Content[0].Content,
84 &yaml.Node{Kind: yaml.ScalarNode, Value: "tag", Tag: "!!str"},
85 &yaml.Node{
86 Kind: yaml.MappingNode,
87 Content: []*yaml.Node{
88 {Kind: yaml.ScalarNode, Value: "name", Tag: "!!str"},
89 {Kind: yaml.ScalarNode, Value: "tag name", Tag: "!!str"},
90 },
91 },
92 )
93
94 d, err := YAMLToJSON(data)
95 require.NoError(t, err)
96 assert.JSONEq(t,
97 `{"1":"the int key value","name":"a string value","y":"some value","tag":{"name":"tag name"}}`,
98 string(d),
99 )
100 })
101
102 t.Run("YAML slice as JSON", func(t *testing.T) {
103 lst := []interface{}{"hello"}
104 d, err := YAMLToJSON(&lst)
105 require.NoError(t, err)
106 assert.JSONEq(t, `["hello"]`, string(d))
107 })
108
109 t.Run("fail to convert to JSON", func(t *testing.T) {
110 t.Run("with invalid receiver", func(t *testing.T) {
111 _, err := YAMLToJSON(failJSONMarshal{})
112 require.Error(t, err)
113 })
114
115 t.Run("with invalid document", func(t *testing.T) {
116 _, err := BytesToYAMLDoc([]byte("- name: hello\n"))
117 require.Error(t, err)
118 })
119 })
120
121 t.Run("with BytesToYamlDoc", func(t *testing.T) {
122 dd, err := BytesToYAMLDoc([]byte("description: 'object created'\n"))
123 require.NoError(t, err)
124
125 d, err := YAMLToJSON(dd)
126 require.NoError(t, err)
127 assert.Equal(t, json.RawMessage(`{"description":"object created"}`), d)
128 })
129 }
130
131 func TestLoadStrategy(t *testing.T) {
132 loader := func(_ string) ([]byte, error) {
133 return []byte(yamlPetStore), nil
134 }
135 remLoader := func(_ string) ([]byte, error) {
136 return []byte("not it"), nil
137 }
138
139 ld := swag.LoadStrategy("blah", loader, remLoader)
140 b, _ := ld("")
141 assert.Equal(t, []byte(yamlPetStore), b)
142
143 serv := httptest.NewServer(http.HandlerFunc(yamlPestoreServer))
144 defer serv.Close()
145
146 s, err := YAMLDoc(serv.URL)
147 require.NoError(t, err)
148 assert.NotNil(t, s)
149
150 ts2 := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
151 rw.WriteHeader(http.StatusNotFound)
152 _, _ = rw.Write([]byte("\n"))
153 }))
154 defer ts2.Close()
155 _, err = YAMLDoc(ts2.URL)
156 require.Error(t, err)
157 }
158
159 var yamlPestoreServer = func(rw http.ResponseWriter, _ *http.Request) {
160 rw.WriteHeader(http.StatusOK)
161 _, _ = rw.Write([]byte(yamlPetStore))
162 }
163
164 func TestWithYKey(t *testing.T) {
165 t.Run("with YAMLv3, unquoted y as key is parsed correctly", func(t *testing.T) {
166 doc, err := BytesToYAMLDoc([]byte(withYKey))
167 require.NoError(t, err)
168
169 _, err = YAMLToJSON(doc)
170 require.NoError(t, err)
171 })
172
173 t.Run("quoted y as key is parsed correctly", func(t *testing.T) {
174 doc, err := BytesToYAMLDoc([]byte(withQuotedYKey))
175 require.NoError(t, err)
176
177 jsond, err := YAMLToJSON(doc)
178 require.NoError(t, err)
179
180 var yt struct {
181 Definitions struct {
182 Viewbox struct {
183 Properties struct {
184 Y struct {
185 Type string `json:"type"`
186 } `json:"y"`
187 } `json:"properties"`
188 } `json:"viewbox"`
189 } `json:"definitions"`
190 }
191 require.NoError(t, json.Unmarshal(jsond, &yt))
192
193 assert.Equal(t, "integer", yt.Definitions.Viewbox.Properties.Y.Type)
194 })
195 }
196
197 const withQuotedYKey = `consumes:
198 - application/json
199 definitions:
200 viewBox:
201 type: object
202 properties:
203 x:
204 type: integer
205 format: int16
206 # y -> types don't match: expect map key string or int get: bool
207 "y":
208 type: integer
209 format: int16
210 width:
211 type: integer
212 format: int16
213 height:
214 type: integer
215 format: int16
216 info:
217 description: Test RESTful APIs
218 title: Test Server
219 version: 1.0.0
220 basePath: /api
221 paths:
222 /test:
223 get:
224 operationId: findAll
225 parameters:
226 - name: since
227 in: query
228 type: integer
229 format: int64
230 - name: limit
231 in: query
232 type: integer
233 format: int32
234 default: 20
235 responses:
236 200:
237 description: Array[Trigger]
238 schema:
239 type: array
240 items:
241 $ref: "#/definitions/viewBox"
242 produces:
243 - application/json
244 schemes:
245 - https
246 swagger: "2.0"
247 `
248
249 const withYKey = `consumes:
250 - application/json
251 definitions:
252 viewBox:
253 type: object
254 properties:
255 x:
256 type: integer
257 format: int16
258 # y -> types don't match: expect map key string or int get: bool
259 y:
260 type: integer
261 format: int16
262 width:
263 type: integer
264 format: int16
265 height:
266 type: integer
267 format: int16
268 info:
269 description: Test RESTful APIs
270 title: Test Server
271 version: 1.0.0
272 basePath: /api
273 paths:
274 /test:
275 get:
276 operationId: findAll
277 parameters:
278 - name: since
279 in: query
280 type: integer
281 format: int64
282 - name: limit
283 in: query
284 type: integer
285 format: int32
286 default: 20
287 responses:
288 200:
289 description: Array[Trigger]
290 schema:
291 type: array
292 items:
293 $ref: "#/definitions/viewBox"
294 produces:
295 - application/json
296 schemes:
297 - https
298 swagger: "2.0"
299 `
300
301 const yamlPetStore = `swagger: '2.0'
302 info:
303 version: '1.0.0'
304 title: Swagger Petstore
305 description: A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification
306 termsOfService: http://helloreverb.com/terms/
307 contact:
308 name: Swagger API team
309 email: foo@example.com
310 url: http://swagger.io
311 license:
312 name: MIT
313 url: http://opensource.org/licenses/MIT
314 host: petstore.swagger.wordnik.com
315 basePath: /api
316 schemes:
317 - http
318 consumes:
319 - application/json
320 produces:
321 - application/json
322 paths:
323 /pets:
324 get:
325 description: Returns all pets from the system that the user has access to
326 operationId: findPets
327 produces:
328 - application/json
329 - application/xml
330 - text/xml
331 - text/html
332 parameters:
333 - name: tags
334 in: query
335 description: tags to filter by
336 required: false
337 type: array
338 items:
339 type: string
340 collectionFormat: csv
341 - name: limit
342 in: query
343 description: maximum number of results to return
344 required: false
345 type: integer
346 format: int32
347 responses:
348 '200':
349 description: pet response
350 schema:
351 type: array
352 items:
353 $ref: '#/definitions/pet'
354 default:
355 description: unexpected error
356 schema:
357 $ref: '#/definitions/errorModel'
358 post:
359 description: Creates a new pet in the store. Duplicates are allowed
360 operationId: addPet
361 produces:
362 - application/json
363 parameters:
364 - name: pet
365 in: body
366 description: Pet to add to the store
367 required: true
368 schema:
369 $ref: '#/definitions/newPet'
370 responses:
371 '200':
372 description: pet response
373 schema:
374 $ref: '#/definitions/pet'
375 default:
376 description: unexpected error
377 schema:
378 $ref: '#/definitions/errorModel'
379 /pets/{id}:
380 get:
381 description: Returns a user based on a single ID, if the user does not have access to the pet
382 operationId: findPetById
383 produces:
384 - application/json
385 - application/xml
386 - text/xml
387 - text/html
388 parameters:
389 - name: id
390 in: path
391 description: ID of pet to fetch
392 required: true
393 type: integer
394 format: int64
395 responses:
396 '200':
397 description: pet response
398 schema:
399 $ref: '#/definitions/pet'
400 default:
401 description: unexpected error
402 schema:
403 $ref: '#/definitions/errorModel'
404 delete:
405 description: deletes a single pet based on the ID supplied
406 operationId: deletePet
407 parameters:
408 - name: id
409 in: path
410 description: ID of pet to delete
411 required: true
412 type: integer
413 format: int64
414 responses:
415 '204':
416 description: pet deleted
417 default:
418 description: unexpected error
419 schema:
420 $ref: '#/definitions/errorModel'
421 definitions:
422 pet:
423 required:
424 - id
425 - name
426 properties:
427 id:
428 type: integer
429 format: int64
430 name:
431 type: string
432 tag:
433 type: string
434 newPet:
435 allOf:
436 - $ref: '#/definitions/pet'
437 - required:
438 - name
439 properties:
440 id:
441 type: integer
442 format: int64
443 name:
444 type: string
445 errorModel:
446 required:
447 - code
448 - message
449 properties:
450 code:
451 type: integer
452 format: int32
453 message:
454 type: string
455 `
456
View as plain text