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