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 middleware
16
17import (
18	"errors"
19	"net/http"
20	"net/http/httptest"
21	"testing"
22
23	"github.com/go-openapi/loads"
24	"github.com/go-openapi/loads/fmts"
25	"github.com/go-openapi/runtime"
26	"github.com/go-openapi/runtime/internal/testing/petstore"
27	"github.com/go-openapi/runtime/middleware/untyped"
28	"github.com/stretchr/testify/assert"
29)
30
31type stubOperationHandler struct {
32}
33
34func (s *stubOperationHandler) ParameterModel() interface{} {
35	return nil
36}
37
38func (s *stubOperationHandler) Handle(params interface{}) (interface{}, error) {
39	return nil, nil
40}
41
42func init() {
43	loads.AddLoader(fmts.YAMLMatcher, fmts.YAMLDoc)
44}
45
46func TestContentType_Issue264(t *testing.T) {
47	swspec, err := loads.Spec("../fixtures/bugs/264/swagger.yml")
48	if assert.NoError(t, err) {
49		api := untyped.NewAPI(swspec)
50		api.RegisterConsumer("application/json", runtime.JSONConsumer())
51		api.RegisterProducer("application/json", runtime.JSONProducer())
52		api.RegisterOperation("delete", "/key/{id}", new(stubOperationHandler))
53
54		handler := Serve(swspec, api)
55		request, _ := http.NewRequest("DELETE", "/key/1", nil)
56		recorder := httptest.NewRecorder()
57		handler.ServeHTTP(recorder, request)
58		assert.Equal(t, 200, recorder.Code)
59	}
60}
61
62func TestServe(t *testing.T) {
63	spec, api := petstore.NewAPI(t)
64	handler := Serve(spec, api)
65
66	// serve spec document
67	request, _ := http.NewRequest("GET", "http://localhost:8080/swagger.json", nil)
68	request.Header.Add("Content-Type", runtime.JSONMime)
69	request.Header.Add("Accept", runtime.JSONMime)
70	recorder := httptest.NewRecorder()
71
72	handler.ServeHTTP(recorder, request)
73	assert.Equal(t, 200, recorder.Code)
74
75	request, _ = http.NewRequest("GET", "http://localhost:8080/swagger-ui", nil)
76	recorder = httptest.NewRecorder()
77
78	handler.ServeHTTP(recorder, request)
79	assert.Equal(t, 404, recorder.Code)
80}
81
82func TestContextAuthorize(t *testing.T) {
83	spec, api := petstore.NewAPI(t)
84	ctx := NewContext(spec, api, nil)
85	ctx.router = DefaultRouter(spec, ctx.api)
86
87	request, _ := runtime.JSONRequest("GET", "/api/pets", nil)
88
89	ri, reqWithCtx, ok := ctx.RouteInfo(request)
90	assert.True(t, ok)
91	assert.NotNil(t, reqWithCtx)
92
93	request = reqWithCtx
94
95	p, reqWithCtx, err := ctx.Authorize(request, ri)
96	assert.Error(t, err)
97	assert.Nil(t, p)
98	assert.Nil(t, reqWithCtx)
99
100	v := request.Context().Value(ctxSecurityPrincipal)
101	assert.Nil(t, v)
102
103	request.SetBasicAuth("wrong", "wrong")
104	p, reqWithCtx, err = ctx.Authorize(request, ri)
105	assert.Error(t, err)
106	assert.Nil(t, p)
107	assert.Nil(t, reqWithCtx)
108
109	v = request.Context().Value(ctxSecurityPrincipal)
110	assert.Nil(t, v)
111
112	request.SetBasicAuth("admin", "admin")
113	p, reqWithCtx, err = ctx.Authorize(request, ri)
114	assert.NoError(t, err)
115	assert.Equal(t, "admin", p)
116	assert.NotNil(t, reqWithCtx)
117
118	// Assign the new returned request to follow with the test
119	request = reqWithCtx
120
121	v, ok = request.Context().Value(ctxSecurityPrincipal).(string)
122	assert.True(t, ok)
123	assert.Equal(t, "admin", v)
124
125	// Once the request context contains the principal the authentication
126	// isn't rechecked
127	request.SetBasicAuth("doesn't matter", "doesn't")
128	pp, reqCtx, rr := ctx.Authorize(request, ri)
129	assert.Equal(t, p, pp)
130	assert.Equal(t, err, rr)
131	assert.Equal(t, request, reqCtx)
132}
133
134func TestContextAuthorize_WithAuthorizer(t *testing.T) {
135	spec, api := petstore.NewAPI(t)
136	ctx := NewContext(spec, api, nil)
137	ctx.router = DefaultRouter(spec, ctx.api)
138
139	request, _ := runtime.JSONRequest("POST", "/api/pets", nil)
140
141	ri, reqWithCtx, ok := ctx.RouteInfo(request)
142	assert.True(t, ok)
143	assert.NotNil(t, reqWithCtx)
144
145	request = reqWithCtx
146
147	request.SetBasicAuth("topuser", "topuser")
148	p, reqWithCtx, err := ctx.Authorize(request, ri)
149	assert.Error(t, err)
150	assert.Nil(t, p)
151	assert.Nil(t, reqWithCtx)
152
153	request.SetBasicAuth("admin", "admin")
154	p, reqWithCtx, err = ctx.Authorize(request, ri)
155	assert.NoError(t, err)
156	assert.Equal(t, "admin", p)
157	assert.NotNil(t, reqWithCtx)
158}
159
160func TestContextNegotiateContentType(t *testing.T) {
161	spec, api := petstore.NewAPI(t)
162	ctx := NewContext(spec, api, nil)
163	ctx.router = DefaultRouter(spec, ctx.api)
164
165	request, _ := http.NewRequest("POST", "/api/pets", nil)
166	// request.Header.Add("Accept", "*/*")
167	request.Header.Add("content-type", "text/html")
168
169	v := request.Context().Value(ctxBoundParams)
170	assert.Nil(t, v)
171
172	ri, request, _ := ctx.RouteInfo(request)
173
174	res := NegotiateContentType(request, ri.Produces, "text/plain")
175	assert.Equal(t, ri.Produces[0], res)
176}
177
178func TestContextBindAndValidate(t *testing.T) {
179	spec, api := petstore.NewAPI(t)
180	ctx := NewContext(spec, api, nil)
181	ctx.router = DefaultRouter(spec, ctx.api)
182
183	request, _ := http.NewRequest("POST", "/api/pets", nil)
184	request.Header.Add("Accept", "*/*")
185	request.Header.Add("content-type", "text/html")
186	request.ContentLength = 1
187
188	v := request.Context().Value(ctxBoundParams)
189	assert.Nil(t, v)
190
191	ri, request, _ := ctx.RouteInfo(request)
192	data, request, result := ctx.BindAndValidate(request, ri) // this requires a much more thorough test
193	assert.NotNil(t, data)
194	assert.NotNil(t, result)
195
196	v, ok := request.Context().Value(ctxBoundParams).(*validation)
197	assert.True(t, ok)
198	assert.NotNil(t, v)
199
200	dd, rCtx, rr := ctx.BindAndValidate(request, ri)
201	assert.Equal(t, data, dd)
202	assert.Equal(t, result, rr)
203	assert.Equal(t, rCtx, request)
204}
205
206func TestContextRender(t *testing.T) {
207	ct := runtime.JSONMime
208	spec, api := petstore.NewAPI(t)
209
210	assert.NotNil(t, spec)
211	assert.NotNil(t, api)
212	ctx := NewContext(spec, api, nil)
213	ctx.router = DefaultRouter(spec, ctx.api)
214
215	request, _ := http.NewRequest("GET", "/api/pets", nil)
216	request.Header.Set(runtime.HeaderAccept, ct)
217	ri, request, _ := ctx.RouteInfo(request)
218
219	recorder := httptest.NewRecorder()
220	ctx.Respond(recorder, request, []string{ct}, ri, map[string]interface{}{"name": "hello"})
221	assert.Equal(t, 200, recorder.Code)
222	assert.Equal(t, "{\"name\":\"hello\"}\n", recorder.Body.String())
223
224	recorder = httptest.NewRecorder()
225	ctx.Respond(recorder, request, []string{ct}, ri, errors.New("this went wrong"))
226	assert.Equal(t, 500, recorder.Code)
227
228	// recorder = httptest.NewRecorder()
229	// assert.Panics(t, func() { ctx.Respond(recorder, request, []string{ct}, ri, map[int]interface{}{1: "hello"}) })
230
231	// Panic when route is nil and there is not a producer for the requested response format
232	recorder = httptest.NewRecorder()
233	request, _ = http.NewRequest("GET", "/api/pets", nil)
234	request.Header.Set(runtime.HeaderAccept, "text/xml")
235	assert.Panics(t, func() { ctx.Respond(recorder, request, []string{}, nil, map[string]interface{}{"name": "hello"}) })
236
237	request, _ = http.NewRequest("GET", "/api/pets", nil)
238	request.Header.Set(runtime.HeaderAccept, ct)
239	ri, request, _ = ctx.RouteInfo(request)
240
241	recorder = httptest.NewRecorder()
242	ctx.Respond(recorder, request, []string{ct}, ri, map[string]interface{}{"name": "hello"})
243	assert.Equal(t, 200, recorder.Code)
244	assert.Equal(t, "{\"name\":\"hello\"}\n", recorder.Body.String())
245
246	recorder = httptest.NewRecorder()
247	ctx.Respond(recorder, request, []string{ct}, ri, errors.New("this went wrong"))
248	assert.Equal(t, 500, recorder.Code)
249
250	// recorder = httptest.NewRecorder()
251	// assert.Panics(t, func() { ctx.Respond(recorder, request, []string{ct}, ri, map[int]interface{}{1: "hello"}) })
252
253	// recorder = httptest.NewRecorder()
254	// request, _ = http.NewRequest("GET", "/pets", nil)
255	// assert.Panics(t, func() { ctx.Respond(recorder, request, []string{}, ri, map[string]interface{}{"name": "hello"}) })
256
257	recorder = httptest.NewRecorder()
258	request, _ = http.NewRequest("DELETE", "/api/pets/1", nil)
259	ri, request, _ = ctx.RouteInfo(request)
260	ctx.Respond(recorder, request, ri.Produces, ri, nil)
261	assert.Equal(t, 204, recorder.Code)
262
263}
264
265func TestContextValidResponseFormat(t *testing.T) {
266	ct := "application/json"
267	spec, api := petstore.NewAPI(t)
268	ctx := NewContext(spec, api, nil)
269	ctx.router = DefaultRouter(spec, ctx.api)
270
271	request, _ := http.NewRequest("GET", "http://localhost:8080", nil)
272	request.Header.Set(runtime.HeaderAccept, ct)
273
274	// check there's nothing there
275	cached, ok := request.Context().Value(ctxResponseFormat).(string)
276	assert.False(t, ok)
277	assert.Empty(t, cached)
278
279	// trigger the parse
280	mt, request := ctx.ResponseFormat(request, []string{ct})
281	assert.Equal(t, ct, mt)
282
283	// check it was cached
284	cached, ok = request.Context().Value(ctxResponseFormat).(string)
285	assert.True(t, ok)
286	assert.Equal(t, ct, cached)
287
288	// check if the cast works and fetch from cache too
289	mt, _ = ctx.ResponseFormat(request, []string{ct})
290	assert.Equal(t, ct, mt)
291}
292
293func TestContextInvalidResponseFormat(t *testing.T) {
294	ct := "application/x-yaml"
295	other := "application/sgml"
296	spec, api := petstore.NewAPI(t)
297	ctx := NewContext(spec, api, nil)
298	ctx.router = DefaultRouter(spec, ctx.api)
299
300	request, _ := http.NewRequest("GET", "http://localhost:8080", nil)
301	request.Header.Set(runtime.HeaderAccept, ct)
302
303	// check there's nothing there
304	cached, ok := request.Context().Value(ctxResponseFormat).(string)
305	assert.False(t, ok)
306	assert.Empty(t, cached)
307
308	// trigger the parse
309	mt, request := ctx.ResponseFormat(request, []string{other})
310	assert.Empty(t, mt)
311
312	// check it was cached
313	cached, ok = request.Context().Value(ctxResponseFormat).(string)
314	assert.False(t, ok)
315	assert.Empty(t, cached)
316
317	// check if the cast works and fetch from cache too
318	mt, rCtx := ctx.ResponseFormat(request, []string{other})
319	assert.Empty(t, mt)
320	assert.Equal(t, request, rCtx)
321}
322
323func TestContextValidRoute(t *testing.T) {
324	spec, api := petstore.NewAPI(t)
325	ctx := NewContext(spec, api, nil)
326	ctx.router = DefaultRouter(spec, ctx.api)
327
328	request, _ := http.NewRequest("GET", "/api/pets", nil)
329
330	// check there's nothing there
331	cached := request.Context().Value(ctxMatchedRoute)
332	assert.Nil(t, cached)
333
334	matched, rCtx, ok := ctx.RouteInfo(request)
335	assert.True(t, ok)
336	assert.NotNil(t, matched)
337	assert.NotNil(t, rCtx)
338	assert.NotEqual(t, request, rCtx)
339
340	request = rCtx
341
342	// check it was cached
343	_, ok = request.Context().Value(ctxMatchedRoute).(*MatchedRoute)
344	assert.True(t, ok)
345
346	matched, rCtx, ok = ctx.RouteInfo(request)
347	assert.True(t, ok)
348	assert.NotNil(t, matched)
349	assert.Equal(t, request, rCtx)
350}
351
352func TestContextInvalidRoute(t *testing.T) {
353	spec, api := petstore.NewAPI(t)
354	ctx := NewContext(spec, api, nil)
355	ctx.router = DefaultRouter(spec, ctx.api)
356
357	request, _ := http.NewRequest("DELETE", "pets", nil)
358
359	// check there's nothing there
360	cached := request.Context().Value(ctxMatchedRoute)
361	assert.Nil(t, cached)
362
363	matched, rCtx, ok := ctx.RouteInfo(request)
364	assert.False(t, ok)
365	assert.Nil(t, matched)
366	assert.Nil(t, rCtx)
367
368	// check it was not cached
369	cached = request.Context().Value(ctxMatchedRoute)
370	assert.Nil(t, cached)
371
372	matched, rCtx, ok = ctx.RouteInfo(request)
373	assert.False(t, ok)
374	assert.Nil(t, matched)
375	assert.Nil(t, rCtx)
376}
377
378func TestContextValidContentType(t *testing.T) {
379	ct := "application/json"
380	ctx := NewContext(nil, nil, nil)
381
382	request, _ := http.NewRequest("GET", "http://localhost:8080", nil)
383	request.Header.Set(runtime.HeaderContentType, ct)
384
385	// check there's nothing there
386	cached := request.Context().Value(ctxContentType)
387	assert.Nil(t, cached)
388
389	// trigger the parse
390	mt, _, rCtx, err := ctx.ContentType(request)
391	assert.NoError(t, err)
392	assert.Equal(t, ct, mt)
393	assert.NotNil(t, rCtx)
394	assert.NotEqual(t, request, rCtx)
395
396	request = rCtx
397
398	// check it was cached
399	cached = request.Context().Value(ctxContentType)
400	assert.NotNil(t, cached)
401
402	// check if the cast works and fetch from cache too
403	mt, _, rCtx, err = ctx.ContentType(request)
404	assert.NoError(t, err)
405	assert.Equal(t, ct, mt)
406	assert.Equal(t, request, rCtx)
407}
408
409func TestContextInvalidContentType(t *testing.T) {
410	ct := "application("
411	ctx := NewContext(nil, nil, nil)
412
413	request, _ := http.NewRequest("GET", "http://localhost:8080", nil)
414	request.Header.Set(runtime.HeaderContentType, ct)
415
416	// check there's nothing there
417	cached := request.Context().Value(ctxContentType)
418	assert.Nil(t, cached)
419
420	// trigger the parse
421	mt, _, rCtx, err := ctx.ContentType(request)
422	assert.Error(t, err)
423	assert.Empty(t, mt)
424	assert.Nil(t, rCtx)
425
426	// check it was not cached
427	cached = request.Context().Value(ctxContentType)
428	assert.Nil(t, cached)
429
430	// check if the failure continues
431	_, _, rCtx, err = ctx.ContentType(request)
432	assert.Error(t, err)
433	assert.Nil(t, rCtx)
434}
435