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 spec
16
17import (
18	"bytes"
19	"encoding/gob"
20	"encoding/json"
21	"testing"
22
23	"github.com/stretchr/testify/assert"
24)
25
26var operation = Operation{
27	VendorExtensible: VendorExtensible{
28		Extensions: map[string]interface{}{
29			"x-framework": "go-swagger",
30		},
31	},
32	OperationProps: OperationProps{
33		Description: "operation description",
34		Consumes:    []string{"application/json", "application/x-yaml"},
35		Produces:    []string{"application/json", "application/x-yaml"},
36		Schemes:     []string{"http", "https"},
37		Tags:        []string{"dogs"},
38		Summary:     "the summary of the operation",
39		ID:          "sendCat",
40		Deprecated:  true,
41		Security: []map[string][]string{
42			{
43				"apiKey": {},
44			},
45		},
46		Parameters: []Parameter{
47			{Refable: Refable{Ref: MustCreateRef("Cat")}},
48		},
49		Responses: &Responses{
50			ResponsesProps: ResponsesProps{
51				Default: &Response{
52					ResponseProps: ResponseProps{
53						Description: "void response",
54					},
55				},
56			},
57		},
58	},
59}
60
61const operationJSON = `{
62	"description": "operation description",
63	"x-framework": "go-swagger",
64	"consumes": [ "application/json", "application/x-yaml" ],
65	"produces": [ "application/json", "application/x-yaml" ],
66	"schemes": ["http", "https"],
67	"tags": ["dogs"],
68	"summary": "the summary of the operation",
69	"operationId": "sendCat",
70	"deprecated": true,
71	"security": [ { "apiKey": [] } ],
72	"parameters": [{"$ref":"Cat"}],
73	"responses": {
74		"default": {
75			"description": "void response"
76		}
77	}
78}`
79
80func TestSuccessResponse(t *testing.T) {
81	ope := &Operation{}
82	resp, n, f := ope.SuccessResponse()
83	assert.Nil(t, resp)
84	assert.Equal(t, 0, n)
85	assert.Equal(t, false, f)
86
87	resp, n, f = operation.SuccessResponse()
88	if assert.NotNil(t, resp) {
89		assert.Equal(t, "void response", resp.Description)
90	}
91	assert.Equal(t, 0, n)
92	assert.Equal(t, false, f)
93
94	err := json.Unmarshal([]byte(operationJSON), ope)
95	if !assert.Nil(t, err) {
96		t.FailNow()
97	}
98	ope = ope.RespondsWith(301, &Response{
99		ResponseProps: ResponseProps{
100			Description: "failure",
101		},
102	})
103	resp, n, f = ope.SuccessResponse()
104	if assert.NotNil(t, resp) {
105		assert.Equal(t, "void response", resp.Description)
106	}
107	assert.Equal(t, 0, n)
108	assert.Equal(t, false, f)
109
110	ope = ope.RespondsWith(200, &Response{
111		ResponseProps: ResponseProps{
112			Description: "success",
113		},
114	})
115
116	resp, n, f = ope.SuccessResponse()
117	if assert.NotNil(t, resp) {
118		assert.Equal(t, "success", resp.Description)
119	}
120	assert.Equal(t, 200, n)
121	assert.Equal(t, true, f)
122}
123
124func TestOperationBuilder(t *testing.T) {
125	ope := NewOperation("").WithID("operationID")
126	ope = ope.RespondsWith(200, &Response{
127		ResponseProps: ResponseProps{
128			Description: "success",
129		},
130	}).
131		WithDefaultResponse(&Response{
132			ResponseProps: ResponseProps{
133				Description: "default",
134			},
135		}).
136		SecuredWith("scheme-name", "scope1", "scope2").
137		WithConsumes("application/json").
138		WithProduces("application/json").
139		Deprecate().
140		WithTags("this", "that").
141		AddParam(nil).
142		AddParam(QueryParam("myQueryParam").Typed("integer", "int32")).
143		AddParam(QueryParam("myQueryParam").Typed("string", "hostname")).
144		AddParam(PathParam("myPathParam").Typed("string", "uuid")).
145		WithDescription("test operation").
146		WithSummary("my summary").
147		WithExternalDocs("some doc", "https://www.example.com")
148
149	jazon, _ := json.MarshalIndent(ope, "", " ")
150	assert.JSONEq(t, `{
151		     "operationId": "operationID",
152				 "description": "test operation",
153				 "summary": "my summary",
154				 "externalDocs": {
155					 "description": "some doc",
156					 "url": "https://www.example.com"
157				 },
158	       "security": [
159          {
160           "scheme-name": [
161            "scope1",
162            "scope2"
163           ]
164          }
165         ],
166         "consumes": [
167          "application/json"
168         ],
169         "produces": [
170          "application/json"
171         ],
172         "tags": [
173          "this",
174          "that"
175         ],
176         "deprecated": true,
177         "parameters": [
178          {
179           "type": "string",
180           "format": "hostname",
181           "name": "myQueryParam",
182           "in": "query"
183          },
184          {
185           "type": "string",
186           "format": "uuid",
187           "name": "myPathParam",
188           "in": "path",
189           "required": true
190          }
191         ],
192				 "responses": {
193          "200": {
194           "description": "success"
195          },
196          "default": {
197           "description": "default"
198          }
199         }
200		 }`, string(jazon))
201
202	// check token lookup
203	token, err := ope.JSONLookup("responses")
204	assert.NoError(t, err)
205	jazon, _ = json.MarshalIndent(token, "", " ")
206	assert.JSONEq(t, `{
207         "200": {
208          "description": "success"
209         },
210         "default": {
211          "description": "default"
212         }
213			 }`, string(jazon))
214
215	// check delete methods
216	ope = ope.RespondsWith(200, nil).
217		RemoveParam("myQueryParam", "query").
218		RemoveParam("myPathParam", "path").
219		RemoveParam("fakeParam", "query").
220		Undeprecate().
221		WithExternalDocs("", "")
222	jazon, _ = json.MarshalIndent(ope, "", " ")
223	assert.JSONEq(t, `{
224         "security": [
225          {
226           "scheme-name": [
227            "scope1",
228            "scope2"
229           ]
230          }
231         ],
232         "description": "test operation",
233         "consumes": [
234          "application/json"
235         ],
236         "produces": [
237          "application/json"
238         ],
239         "tags": [
240          "this",
241          "that"
242         ],
243         "summary": "my summary",
244         "operationId": "operationID",
245         "responses": {
246          "default": {
247           "description": "default"
248          }
249         }
250			 }`, string(jazon))
251}
252
253func TestIntegrationOperation(t *testing.T) {
254	var actual Operation
255	if assert.NoError(t, json.Unmarshal([]byte(operationJSON), &actual)) {
256		assert.EqualValues(t, actual, operation)
257	}
258
259	assertParsesJSON(t, operationJSON, operation)
260}
261
262func TestSecurityProperty(t *testing.T) {
263	// Ensure we omit security key when unset
264	securityNotSet := OperationProps{}
265	jsonResult, err := json.Marshal(securityNotSet)
266	if assert.NoError(t, err) {
267		assert.NotContains(t, string(jsonResult), "security", "security key should be omitted when unset")
268	}
269
270	// Ensure we preserve the security key when it contains an empty (zero length) slice
271	securityContainsEmptyArray := OperationProps{
272		Security: []map[string][]string{},
273	}
274	jsonResult, err = json.Marshal(securityContainsEmptyArray)
275	if assert.NoError(t, err) {
276		var props OperationProps
277		if assert.NoError(t, json.Unmarshal(jsonResult, &props)) {
278			assert.Equal(t, securityContainsEmptyArray, props)
279		}
280	}
281}
282
283func TestOperationGobEncoding(t *testing.T) {
284	// 1. empty scope in security requirements:  "security": [ { "apiKey": [] } ],
285	doTestOperationGobEncoding(t, operationJSON)
286
287	// 2. nil security requirements
288	doTestOperationGobEncoding(t, `{
289			"description": "operation description",
290			"x-framework": "go-swagger",
291			"consumes": [ "application/json", "application/x-yaml" ],
292			"produces": [ "application/json", "application/x-yaml" ],
293			"schemes": ["http", "https"],
294			"tags": ["dogs"],
295			"summary": "the summary of the operation",
296			"operationId": "sendCat",
297			"deprecated": true,
298			"parameters": [{"$ref":"Cat"}],
299			"responses": {
300				"default": {
301					"description": "void response"
302				}
303			}
304		}`)
305
306	// 3. empty security requirement
307	doTestOperationGobEncoding(t, `{
308			"description": "operation description",
309			"x-framework": "go-swagger",
310			"consumes": [ "application/json", "application/x-yaml" ],
311			"produces": [ "application/json", "application/x-yaml" ],
312			"schemes": ["http", "https"],
313			"tags": ["dogs"],
314			"security": [],
315			"summary": "the summary of the operation",
316			"operationId": "sendCat",
317			"deprecated": true,
318			"parameters": [{"$ref":"Cat"}],
319			"responses": {
320				"default": {
321					"description": "void response"
322				}
323			}
324		}`)
325
326	// 4. non-empty security requirements
327	doTestOperationGobEncoding(t, `{
328			"description": "operation description",
329			"x-framework": "go-swagger",
330			"consumes": [ "application/json", "application/x-yaml" ],
331			"produces": [ "application/json", "application/x-yaml" ],
332			"schemes": ["http", "https"],
333			"tags": ["dogs"],
334			"summary": "the summary of the operation",
335			"security": [ { "scoped-auth": [ "phone", "email" ] , "api-key": []} ],
336			"operationId": "sendCat",
337			"deprecated": true,
338			"parameters": [{"$ref":"Cat"}],
339			"responses": {
340				"default": {
341					"description": "void response"
342				}
343			}
344		}`)
345}
346
347func doTestOperationGobEncoding(t *testing.T, fixture string) {
348	var src, dst Operation
349
350	if !assert.NoError(t, json.Unmarshal([]byte(fixture), &src)) {
351		t.FailNow()
352	}
353
354	doTestAnyGobEncoding(t, &src, &dst)
355}
356
357func doTestAnyGobEncoding(t *testing.T, src, dst interface{}) {
358	expectedJSON, _ := json.MarshalIndent(src, "", " ")
359
360	var b bytes.Buffer
361	err := gob.NewEncoder(&b).Encode(src)
362	if !assert.NoError(t, err) {
363		t.FailNow()
364	}
365
366	err = gob.NewDecoder(&b).Decode(dst)
367	if !assert.NoError(t, err) {
368		t.FailNow()
369	}
370
371	jazon, err := json.MarshalIndent(dst, "", " ")
372	if !assert.NoError(t, err) {
373		t.FailNow()
374	}
375	assert.JSONEq(t, string(expectedJSON), string(jazon))
376}
377