...

Source file src/github.com/go-openapi/runtime/middleware/context_test.go

Documentation: github.com/go-openapi/runtime/middleware

     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  
    15  package middleware
    16  
    17  import (
    18  	stdcontext "context"
    19  	"errors"
    20  	"fmt"
    21  	"net/http"
    22  	"net/http/httptest"
    23  	"strings"
    24  	"testing"
    25  
    26  	apierrors "github.com/go-openapi/errors"
    27  	"github.com/go-openapi/loads"
    28  	"github.com/go-openapi/loads/fmts"
    29  	"github.com/go-openapi/runtime"
    30  	"github.com/go-openapi/runtime/internal/testing/petstore"
    31  	"github.com/go-openapi/runtime/middleware/untyped"
    32  	"github.com/stretchr/testify/assert"
    33  	"github.com/stretchr/testify/require"
    34  )
    35  
    36  type stubBindRequester struct {
    37  }
    38  
    39  func (s *stubBindRequester) BindRequest(*http.Request, *MatchedRoute) error {
    40  	return nil
    41  }
    42  
    43  type stubOperationHandler struct {
    44  }
    45  
    46  func (s *stubOperationHandler) ParameterModel() interface{} {
    47  	return nil
    48  }
    49  
    50  func (s *stubOperationHandler) Handle(_ interface{}) (interface{}, error) {
    51  	return map[string]interface{}{}, nil
    52  }
    53  
    54  func init() {
    55  	loads.AddLoader(fmts.YAMLMatcher, fmts.YAMLDoc)
    56  }
    57  
    58  func assertAPIError(t *testing.T, wantCode int, err error) {
    59  	t.Helper()
    60  
    61  	require.Error(t, err)
    62  
    63  	ce, ok := err.(*apierrors.CompositeError)
    64  	assert.True(t, ok)
    65  	assert.NotEmpty(t, ce.Errors)
    66  
    67  	ae, ok := ce.Errors[0].(apierrors.Error)
    68  	assert.True(t, ok)
    69  	assert.Equal(t, wantCode, int(ae.Code()))
    70  }
    71  
    72  func TestContentType_Issue264(t *testing.T) {
    73  	swspec, err := loads.Spec("../fixtures/bugs/264/swagger.yml")
    74  	require.NoError(t, err)
    75  
    76  	api := untyped.NewAPI(swspec)
    77  	api.RegisterConsumer(applicationJSON, runtime.JSONConsumer())
    78  	api.RegisterProducer(applicationJSON, runtime.JSONProducer())
    79  	api.RegisterOperation("delete", "/key/{id}", new(stubOperationHandler))
    80  
    81  	handler := Serve(swspec, api)
    82  	request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodDelete, "/key/1", nil)
    83  	require.NoError(t, err)
    84  
    85  	recorder := httptest.NewRecorder()
    86  	handler.ServeHTTP(recorder, request)
    87  	assert.Equal(t, http.StatusOK, recorder.Code)
    88  }
    89  
    90  func TestContentType_Issue172(t *testing.T) {
    91  	swspec, err := loads.Spec("../fixtures/bugs/172/swagger.yml")
    92  	require.NoError(t, err)
    93  
    94  	api := untyped.NewAPI(swspec)
    95  	api.RegisterConsumer("application/vnd.cia.v1+json", runtime.JSONConsumer())
    96  	api.RegisterProducer("application/vnd.cia.v1+json", runtime.JSONProducer())
    97  	api.RegisterOperation("get", "/pets", new(stubOperationHandler))
    98  
    99  	handler := Serve(swspec, api)
   100  	request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, "/pets", nil)
   101  	require.NoError(t, err)
   102  
   103  	request.Header.Add("Accept", "application/json+special")
   104  	recorder := httptest.NewRecorder()
   105  	handler.ServeHTTP(recorder, request)
   106  	assert.Equal(t, http.StatusNotAcceptable, recorder.Code)
   107  
   108  	// acceptable as defined as default by the API (not explicit in the spec)
   109  	request.Header.Add("Accept", applicationJSON)
   110  	recorder = httptest.NewRecorder()
   111  	handler.ServeHTTP(recorder, request)
   112  	assert.Equal(t, http.StatusOK, recorder.Code)
   113  }
   114  
   115  func TestContentType_Issue174(t *testing.T) {
   116  	swspec, err := loads.Spec("../fixtures/bugs/174/swagger.yml")
   117  	require.NoError(t, err)
   118  
   119  	api := untyped.NewAPI(swspec)
   120  	api.RegisterConsumer(applicationJSON, runtime.JSONConsumer())
   121  	api.RegisterProducer(applicationJSON, runtime.JSONProducer())
   122  	api.RegisterOperation("get", "/pets", new(stubOperationHandler))
   123  
   124  	handler := Serve(swspec, api)
   125  	request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, "/pets", nil)
   126  	require.NoError(t, err)
   127  
   128  	recorder := httptest.NewRecorder()
   129  	handler.ServeHTTP(recorder, request)
   130  	assert.Equal(t, http.StatusOK, recorder.Code)
   131  }
   132  
   133  const (
   134  	testHost = "https://localhost:8080"
   135  
   136  	// how to get the spec document?
   137  	defaultSpecPath = "/swagger.json"
   138  	defaultSpecURL  = testHost + defaultSpecPath
   139  	// how to get the UI asset?
   140  	defaultUIURL = testHost + "/api/docs"
   141  )
   142  
   143  func TestServe(t *testing.T) {
   144  	spec, api := petstore.NewAPI(t)
   145  	handler := Serve(spec, api)
   146  
   147  	t.Run("serve spec document", func(t *testing.T) {
   148  		request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, defaultSpecURL, nil)
   149  		require.NoError(t, err)
   150  
   151  		request.Header.Add("Content-Type", runtime.JSONMime)
   152  		request.Header.Add("Accept", runtime.JSONMime)
   153  		recorder := httptest.NewRecorder()
   154  
   155  		handler.ServeHTTP(recorder, request)
   156  		assert.Equal(t, http.StatusOK, recorder.Code)
   157  	})
   158  
   159  	t.Run("should not find UI there", func(t *testing.T) {
   160  		request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, testHost+"/swagger-ui", nil)
   161  		require.NoError(t, err)
   162  		recorder := httptest.NewRecorder()
   163  
   164  		handler.ServeHTTP(recorder, request)
   165  		assert.Equal(t, http.StatusNotFound, recorder.Code)
   166  	})
   167  
   168  	t.Run("should find UI here", func(t *testing.T) {
   169  		request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, defaultUIURL, nil)
   170  		require.NoError(t, err)
   171  		recorder := httptest.NewRecorder()
   172  
   173  		handler.ServeHTTP(recorder, request)
   174  		assert.Equal(t, http.StatusOK, recorder.Code)
   175  
   176  		htmlResponse := recorder.Body.String()
   177  		assert.Containsf(t, htmlResponse, "<title>Swagger Petstore</title>", "should default to the API's title")
   178  		assert.Containsf(t, htmlResponse, "<redoc", "should default to Redoc UI")
   179  		assert.Containsf(t, htmlResponse, "spec-url='/swagger.json'>", "should default to /swagger.json spec document")
   180  	})
   181  }
   182  
   183  func TestServeWithUIs(t *testing.T) {
   184  	spec, api := petstore.NewAPI(t)
   185  	ctx := NewContext(spec, api, nil)
   186  
   187  	const (
   188  		alternateSpecURL  = testHost + "/specs/petstore.json"
   189  		alternateSpecPath = "/specs/petstore.json"
   190  		alternateUIURL    = testHost + "/ui/docs"
   191  	)
   192  
   193  	uiOpts := []UIOption{
   194  		WithUIBasePath("ui"), // override the base path from the spec, implies /ui
   195  		WithUIPath("docs"),
   196  		WithUISpecURL("/specs/petstore.json"),
   197  	}
   198  
   199  	t.Run("with APIHandler", func(t *testing.T) {
   200  		t.Run("with defaults", func(t *testing.T) {
   201  			handler := ctx.APIHandler(nil)
   202  
   203  			t.Run("should find UI", func(t *testing.T) {
   204  				request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, defaultUIURL, nil)
   205  				require.NoError(t, err)
   206  				recorder := httptest.NewRecorder()
   207  
   208  				handler.ServeHTTP(recorder, request)
   209  				assert.Equal(t, http.StatusOK, recorder.Code)
   210  
   211  				htmlResponse := recorder.Body.String()
   212  				assert.Containsf(t, htmlResponse, "<redoc", "should default to Redoc UI")
   213  			})
   214  
   215  			t.Run("should find spec", func(t *testing.T) {
   216  				request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, defaultSpecURL, nil)
   217  				require.NoError(t, err)
   218  				recorder := httptest.NewRecorder()
   219  
   220  				handler.ServeHTTP(recorder, request)
   221  				assert.Equal(t, http.StatusOK, recorder.Code)
   222  			})
   223  		})
   224  
   225  		t.Run("with options", func(t *testing.T) {
   226  			handler := ctx.APIHandler(nil, uiOpts...)
   227  
   228  			t.Run("should find UI", func(t *testing.T) {
   229  				request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, alternateUIURL, nil)
   230  				require.NoError(t, err)
   231  				recorder := httptest.NewRecorder()
   232  
   233  				handler.ServeHTTP(recorder, request)
   234  				assert.Equal(t, http.StatusOK, recorder.Code)
   235  
   236  				htmlResponse := recorder.Body.String()
   237  				assert.Contains(t, htmlResponse, fmt.Sprintf("<redoc spec-url='%s'></redoc>", alternateSpecPath))
   238  			})
   239  
   240  			t.Run("should find spec", func(t *testing.T) {
   241  				request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, alternateSpecURL, nil)
   242  				require.NoError(t, err)
   243  				recorder := httptest.NewRecorder()
   244  
   245  				handler.ServeHTTP(recorder, request)
   246  				assert.Equal(t, http.StatusOK, recorder.Code)
   247  			})
   248  		})
   249  	})
   250  
   251  	t.Run("with APIHandlerSwaggerUI", func(t *testing.T) {
   252  		t.Run("with defaults", func(t *testing.T) {
   253  			handler := ctx.APIHandlerSwaggerUI(nil)
   254  
   255  			t.Run("should find UI", func(t *testing.T) {
   256  				request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, defaultUIURL, nil)
   257  				require.NoError(t, err)
   258  				recorder := httptest.NewRecorder()
   259  
   260  				handler.ServeHTTP(recorder, request)
   261  				assert.Equal(t, http.StatusOK, recorder.Code)
   262  
   263  				htmlResponse := recorder.Body.String()
   264  				assert.Contains(t, htmlResponse, fmt.Sprintf(`url: '%s',`, strings.ReplaceAll(defaultSpecPath, `/`, `\/`)))
   265  			})
   266  
   267  			t.Run("should find spec", func(t *testing.T) {
   268  				request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, defaultSpecURL, nil)
   269  				require.NoError(t, err)
   270  				recorder := httptest.NewRecorder()
   271  
   272  				handler.ServeHTTP(recorder, request)
   273  				assert.Equal(t, http.StatusOK, recorder.Code)
   274  			})
   275  		})
   276  
   277  		t.Run("with options", func(t *testing.T) {
   278  			handler := ctx.APIHandlerSwaggerUI(nil, uiOpts...)
   279  
   280  			t.Run("should find UI", func(t *testing.T) {
   281  				request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, alternateUIURL, nil)
   282  				require.NoError(t, err)
   283  				recorder := httptest.NewRecorder()
   284  
   285  				handler.ServeHTTP(recorder, request)
   286  				assert.Equal(t, http.StatusOK, recorder.Code)
   287  
   288  				htmlResponse := recorder.Body.String()
   289  				assert.Contains(t, htmlResponse, fmt.Sprintf(`url: '%s',`, strings.ReplaceAll(alternateSpecPath, `/`, `\/`)))
   290  			})
   291  
   292  			t.Run("should find spec", func(t *testing.T) {
   293  				request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, alternateSpecURL, nil)
   294  				require.NoError(t, err)
   295  				recorder := httptest.NewRecorder()
   296  
   297  				handler.ServeHTTP(recorder, request)
   298  				assert.Equal(t, http.StatusOK, recorder.Code)
   299  			})
   300  		})
   301  	})
   302  
   303  	t.Run("with APIHandlerRapiDoc", func(t *testing.T) {
   304  		t.Run("with defaults", func(t *testing.T) {
   305  			handler := ctx.APIHandlerRapiDoc(nil)
   306  
   307  			t.Run("should find UI", func(t *testing.T) {
   308  				request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, defaultUIURL, nil)
   309  				require.NoError(t, err)
   310  				recorder := httptest.NewRecorder()
   311  
   312  				handler.ServeHTTP(recorder, request)
   313  				assert.Equal(t, http.StatusOK, recorder.Code)
   314  
   315  				htmlResponse := recorder.Body.String()
   316  				assert.Contains(t, htmlResponse, fmt.Sprintf("<rapi-doc spec-url=%q></rapi-doc>", defaultSpecPath))
   317  			})
   318  
   319  			t.Run("should find spec", func(t *testing.T) {
   320  				request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, defaultSpecURL, nil)
   321  				require.NoError(t, err)
   322  				recorder := httptest.NewRecorder()
   323  
   324  				handler.ServeHTTP(recorder, request)
   325  				assert.Equal(t, http.StatusOK, recorder.Code)
   326  			})
   327  		})
   328  
   329  		t.Run("with options", func(t *testing.T) {
   330  			handler := ctx.APIHandlerRapiDoc(nil, uiOpts...)
   331  
   332  			t.Run("should find UI", func(t *testing.T) {
   333  				request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, alternateUIURL, nil)
   334  				require.NoError(t, err)
   335  				recorder := httptest.NewRecorder()
   336  
   337  				handler.ServeHTTP(recorder, request)
   338  				assert.Equal(t, http.StatusOK, recorder.Code)
   339  
   340  				htmlResponse := recorder.Body.String()
   341  				assert.Contains(t, htmlResponse, fmt.Sprintf("<rapi-doc spec-url=%q></rapi-doc>", alternateSpecPath))
   342  			})
   343  			t.Run("should find spec", func(t *testing.T) {
   344  				request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, alternateSpecURL, nil)
   345  				require.NoError(t, err)
   346  				recorder := httptest.NewRecorder()
   347  
   348  				handler.ServeHTTP(recorder, request)
   349  				assert.Equal(t, http.StatusOK, recorder.Code)
   350  			})
   351  		})
   352  	})
   353  }
   354  
   355  func TestContextAuthorize(t *testing.T) {
   356  	spec, api := petstore.NewAPI(t)
   357  	ctx := NewContext(spec, api, nil)
   358  	ctx.router = DefaultRouter(spec, ctx.api)
   359  
   360  	request, err := runtime.JSONRequest(http.MethodGet, "/api/pets", nil)
   361  	require.NoError(t, err)
   362  	request = request.WithContext(stdcontext.Background())
   363  
   364  	ri, reqWithCtx, ok := ctx.RouteInfo(request)
   365  	assert.True(t, ok)
   366  	require.NotNil(t, reqWithCtx)
   367  
   368  	request = reqWithCtx
   369  
   370  	p, reqWithCtx, err := ctx.Authorize(request, ri)
   371  	require.Error(t, err)
   372  	assert.Nil(t, p)
   373  	assert.Nil(t, reqWithCtx)
   374  
   375  	v := request.Context().Value(ctxSecurityPrincipal)
   376  	assert.Nil(t, v)
   377  
   378  	request.SetBasicAuth("wrong", "wrong")
   379  	p, reqWithCtx, err = ctx.Authorize(request, ri)
   380  	require.Error(t, err)
   381  	assert.Nil(t, p)
   382  	assert.Nil(t, reqWithCtx)
   383  
   384  	v = request.Context().Value(ctxSecurityPrincipal)
   385  	assert.Nil(t, v)
   386  
   387  	request.SetBasicAuth("admin", "admin")
   388  	p, reqWithCtx, err = ctx.Authorize(request, ri)
   389  	require.NoError(t, err)
   390  	assert.Equal(t, "admin", p)
   391  	require.NotNil(t, reqWithCtx)
   392  
   393  	// Assign the new returned request to follow with the test
   394  	request = reqWithCtx
   395  
   396  	v, ok = request.Context().Value(ctxSecurityPrincipal).(string)
   397  	assert.True(t, ok)
   398  	assert.Equal(t, "admin", v)
   399  
   400  	// Once the request context contains the principal the authentication
   401  	// isn't rechecked
   402  	request.SetBasicAuth("doesn't matter", "doesn't")
   403  	pp, reqCtx, rr := ctx.Authorize(request, ri)
   404  	assert.Equal(t, p, pp)
   405  	assert.Equal(t, err, rr)
   406  	assert.Equal(t, request, reqCtx)
   407  }
   408  
   409  func TestContextAuthorize_WithAuthorizer(t *testing.T) {
   410  	spec, api := petstore.NewAPI(t)
   411  	ctx := NewContext(spec, api, nil)
   412  	ctx.router = DefaultRouter(spec, ctx.api)
   413  
   414  	request, err := runtime.JSONRequest(http.MethodPost, "/api/pets", nil)
   415  	require.NoError(t, err)
   416  	request = request.WithContext(stdcontext.Background())
   417  
   418  	ri, reqWithCtx, ok := ctx.RouteInfo(request)
   419  	assert.True(t, ok)
   420  	require.NotNil(t, reqWithCtx)
   421  
   422  	request = reqWithCtx
   423  
   424  	request.SetBasicAuth("topuser", "topuser")
   425  	p, reqWithCtx, err := ctx.Authorize(request, ri)
   426  	assertAPIError(t, apierrors.InvalidTypeCode, err)
   427  	assert.Nil(t, p)
   428  	assert.Nil(t, reqWithCtx)
   429  
   430  	request.SetBasicAuth("admin", "admin")
   431  	p, reqWithCtx, err = ctx.Authorize(request, ri)
   432  	require.NoError(t, err)
   433  	assert.Equal(t, "admin", p)
   434  	require.NotNil(t, reqWithCtx)
   435  
   436  	request.SetBasicAuth("anyother", "anyother")
   437  	p, reqWithCtx, err = ctx.Authorize(request, ri)
   438  	require.Error(t, err)
   439  	ae, ok := err.(apierrors.Error)
   440  	assert.True(t, ok)
   441  	assert.Equal(t, http.StatusForbidden, int(ae.Code()))
   442  	assert.Nil(t, p)
   443  	assert.Nil(t, reqWithCtx)
   444  }
   445  
   446  func TestContextNegotiateContentType(t *testing.T) {
   447  	spec, api := petstore.NewAPI(t)
   448  	ctx := NewContext(spec, api, nil)
   449  	ctx.router = DefaultRouter(spec, ctx.api)
   450  
   451  	request, _ := http.NewRequestWithContext(stdcontext.Background(), http.MethodPost, "/api/pets", nil)
   452  	// request.Header.Add("Accept", "*/*")
   453  	request.Header.Add("content-type", "text/html")
   454  
   455  	v := request.Context().Value(ctxBoundParams)
   456  	assert.Nil(t, v)
   457  
   458  	ri, request, _ := ctx.RouteInfo(request)
   459  
   460  	res := NegotiateContentType(request, ri.Produces, "text/plain")
   461  	assert.Equal(t, ri.Produces[0], res)
   462  }
   463  
   464  func TestContextBindValidRequest(t *testing.T) {
   465  	spec, api := petstore.NewAPI(t)
   466  	ctx := NewContext(spec, api, nil)
   467  	ctx.router = DefaultRouter(spec, ctx.api)
   468  
   469  	// invalid content-type value
   470  	request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodPost, "/api/pets", strings.NewReader(`{"name":"dog"}`))
   471  	require.NoError(t, err)
   472  	request.Header.Add("content-type", "/json")
   473  
   474  	ri, request, _ := ctx.RouteInfo(request)
   475  	assertAPIError(t, 400, ctx.BindValidRequest(request, ri, new(stubBindRequester)))
   476  
   477  	// unsupported content-type value
   478  	request, err = http.NewRequestWithContext(stdcontext.Background(), http.MethodPost, "/api/pets", strings.NewReader(`{"name":"dog"}`))
   479  	require.NoError(t, err)
   480  	request.Header.Add("content-type", "text/html")
   481  
   482  	ri, request, _ = ctx.RouteInfo(request)
   483  	assertAPIError(t, http.StatusUnsupportedMediaType, ctx.BindValidRequest(request, ri, new(stubBindRequester)))
   484  
   485  	// unacceptable accept value
   486  	request, err = http.NewRequestWithContext(stdcontext.Background(), http.MethodPost, "/api/pets", nil)
   487  	require.NoError(t, err)
   488  	request.Header.Add("Accept", "application/vnd.cia.v1+json")
   489  	request.Header.Add("content-type", applicationJSON)
   490  
   491  	ri, request, _ = ctx.RouteInfo(request)
   492  	assertAPIError(t, http.StatusNotAcceptable, ctx.BindValidRequest(request, ri, new(stubBindRequester)))
   493  }
   494  
   495  func TestContextBindValidRequest_Issue174(t *testing.T) {
   496  	spec, err := loads.Spec("../fixtures/bugs/174/swagger.yml")
   497  	require.NoError(t, err)
   498  
   499  	api := untyped.NewAPI(spec)
   500  	api.RegisterConsumer(applicationJSON, runtime.JSONConsumer())
   501  	api.RegisterProducer(applicationJSON, runtime.JSONProducer())
   502  	api.RegisterOperation("get", "/pets", new(stubOperationHandler))
   503  
   504  	ctx := NewContext(spec, api, nil)
   505  	ctx.router = DefaultRouter(spec, ctx.api)
   506  
   507  	request, _ := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, "/pets", nil)
   508  	ri, request, _ := ctx.RouteInfo(request)
   509  	require.NoError(t, ctx.BindValidRequest(request, ri, new(stubBindRequester)))
   510  }
   511  
   512  func TestContextBindAndValidate(t *testing.T) {
   513  	spec, api := petstore.NewAPI(t)
   514  	ctx := NewContext(spec, api, nil)
   515  	ctx.router = DefaultRouter(spec, ctx.api)
   516  
   517  	request, _ := http.NewRequestWithContext(stdcontext.Background(), http.MethodPost, "/api/pets", nil)
   518  	request.Header.Add("Accept", "*/*")
   519  	request.Header.Add("content-type", "text/html")
   520  	request.ContentLength = 1
   521  
   522  	v := request.Context().Value(ctxBoundParams)
   523  	assert.Nil(t, v)
   524  
   525  	ri, request, _ := ctx.RouteInfo(request)
   526  	data, request, result := ctx.BindAndValidate(request, ri) // this requires a much more thorough test
   527  	assert.NotNil(t, data)
   528  	require.Error(t, result)
   529  
   530  	v, ok := request.Context().Value(ctxBoundParams).(*validation)
   531  	assert.True(t, ok)
   532  	assert.NotNil(t, v)
   533  
   534  	dd, rCtx, rr := ctx.BindAndValidate(request, ri)
   535  	assert.Equal(t, data, dd)
   536  	assert.Equal(t, result, rr)
   537  	assert.Equal(t, rCtx, request)
   538  }
   539  
   540  func TestContextRender(t *testing.T) {
   541  	ct := runtime.JSONMime
   542  	spec, api := petstore.NewAPI(t)
   543  	assert.NotNil(t, spec)
   544  	assert.NotNil(t, api)
   545  
   546  	ctx := NewContext(spec, api, nil)
   547  	ctx.router = DefaultRouter(spec, ctx.api)
   548  
   549  	request, _ := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, "/api/pets", nil)
   550  	request.Header.Set(runtime.HeaderAccept, ct)
   551  	ri, request, _ := ctx.RouteInfo(request)
   552  
   553  	recorder := httptest.NewRecorder()
   554  	ctx.Respond(recorder, request, []string{ct}, ri, map[string]interface{}{"name": "hello"})
   555  	assert.Equal(t, http.StatusOK, recorder.Code)
   556  	assert.Equal(t, "{\"name\":\"hello\"}\n", recorder.Body.String())
   557  
   558  	recorder = httptest.NewRecorder()
   559  	ctx.Respond(recorder, request, []string{ct}, ri, errors.New("this went wrong"))
   560  	assert.Equal(t, 500, recorder.Code)
   561  
   562  	// recorder = httptest.NewRecorder()
   563  	// assert.Panics(t, func() { ctx.Respond(recorder, request, []string{ct}, ri, map[int]interface{}{1: "hello"}) })
   564  
   565  	// Panic when route is nil and there is not a producer for the requested response format
   566  	recorder = httptest.NewRecorder()
   567  	request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, "/api/pets", nil)
   568  	require.NoError(t, err)
   569  	request.Header.Set(runtime.HeaderAccept, "text/xml")
   570  	assert.Panics(t, func() { ctx.Respond(recorder, request, []string{}, nil, map[string]interface{}{"name": "hello"}) })
   571  
   572  	request, err = http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, "/api/pets", nil)
   573  	require.NoError(t, err)
   574  	request.Header.Set(runtime.HeaderAccept, ct)
   575  	ri, request, _ = ctx.RouteInfo(request)
   576  
   577  	recorder = httptest.NewRecorder()
   578  	ctx.Respond(recorder, request, []string{ct}, ri, map[string]interface{}{"name": "hello"})
   579  	assert.Equal(t, http.StatusOK, recorder.Code)
   580  	assert.Equal(t, "{\"name\":\"hello\"}\n", recorder.Body.String())
   581  
   582  	recorder = httptest.NewRecorder()
   583  	ctx.Respond(recorder, request, []string{ct}, ri, errors.New("this went wrong"))
   584  	assert.Equal(t, 500, recorder.Code)
   585  
   586  	// recorder = httptest.NewRecorder()
   587  	// assert.Panics(t, func() { ctx.Respond(recorder, request, []string{ct}, ri, map[int]interface{}{1: "hello"}) })
   588  
   589  	// recorder = httptest.NewRecorder()
   590  	// request, _ = http.NewRequestWithContext(stdcontext.Background(),http.MethodGet, "/pets", nil)
   591  	// assert.Panics(t, func() { ctx.Respond(recorder, request, []string{}, ri, map[string]interface{}{"name": "hello"}) })
   592  
   593  	recorder = httptest.NewRecorder()
   594  	request, err = http.NewRequestWithContext(stdcontext.Background(), http.MethodDelete, "/api/pets/1", nil)
   595  	require.NoError(t, err)
   596  	ri, request, _ = ctx.RouteInfo(request)
   597  	ctx.Respond(recorder, request, ri.Produces, ri, nil)
   598  	assert.Equal(t, 204, recorder.Code)
   599  }
   600  
   601  func TestContextValidResponseFormat(t *testing.T) {
   602  	const ct = applicationJSON
   603  	spec, api := petstore.NewAPI(t)
   604  	ctx := NewContext(spec, api, nil)
   605  	ctx.router = DefaultRouter(spec, ctx.api)
   606  
   607  	request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, "http://localhost:8080", nil)
   608  	require.NoError(t, err)
   609  	request.Header.Set(runtime.HeaderAccept, ct)
   610  
   611  	// check there's nothing there
   612  	cached, ok := request.Context().Value(ctxResponseFormat).(string)
   613  	assert.False(t, ok)
   614  	assert.Empty(t, cached)
   615  
   616  	// trigger the parse
   617  	mt, request := ctx.ResponseFormat(request, []string{ct})
   618  	assert.Equal(t, ct, mt)
   619  
   620  	// check it was cached
   621  	cached, ok = request.Context().Value(ctxResponseFormat).(string)
   622  	assert.True(t, ok)
   623  	assert.Equal(t, ct, cached)
   624  
   625  	// check if the cast works and fetch from cache too
   626  	mt, _ = ctx.ResponseFormat(request, []string{ct})
   627  	assert.Equal(t, ct, mt)
   628  }
   629  
   630  func TestContextInvalidResponseFormat(t *testing.T) {
   631  	ct := "application/x-yaml"
   632  	other := "application/sgml"
   633  	spec, api := petstore.NewAPI(t)
   634  	ctx := NewContext(spec, api, nil)
   635  	ctx.router = DefaultRouter(spec, ctx.api)
   636  
   637  	request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, "http://localhost:8080", nil)
   638  	require.NoError(t, err)
   639  	request.Header.Set(runtime.HeaderAccept, ct)
   640  
   641  	// check there's nothing there
   642  	cached, ok := request.Context().Value(ctxResponseFormat).(string)
   643  	assert.False(t, ok)
   644  	assert.Empty(t, cached)
   645  
   646  	// trigger the parse
   647  	mt, request := ctx.ResponseFormat(request, []string{other})
   648  	assert.Empty(t, mt)
   649  
   650  	// check it was cached
   651  	cached, ok = request.Context().Value(ctxResponseFormat).(string)
   652  	assert.False(t, ok)
   653  	assert.Empty(t, cached)
   654  
   655  	// check if the cast works and fetch from cache too
   656  	mt, rCtx := ctx.ResponseFormat(request, []string{other})
   657  	assert.Empty(t, mt)
   658  	assert.Equal(t, request, rCtx)
   659  }
   660  
   661  func TestContextValidRoute(t *testing.T) {
   662  	spec, api := petstore.NewAPI(t)
   663  	ctx := NewContext(spec, api, nil)
   664  	ctx.router = DefaultRouter(spec, ctx.api)
   665  
   666  	request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, "/api/pets", nil)
   667  	require.NoError(t, err)
   668  
   669  	// check there's nothing there
   670  	cached := request.Context().Value(ctxMatchedRoute)
   671  	assert.Nil(t, cached)
   672  
   673  	matched, rCtx, ok := ctx.RouteInfo(request)
   674  	assert.True(t, ok)
   675  	assert.NotNil(t, matched)
   676  	assert.NotNil(t, rCtx)
   677  	assert.NotEqual(t, request, rCtx)
   678  
   679  	request = rCtx
   680  
   681  	// check it was cached
   682  	_, ok = request.Context().Value(ctxMatchedRoute).(*MatchedRoute)
   683  	assert.True(t, ok)
   684  
   685  	matched, rCtx, ok = ctx.RouteInfo(request)
   686  	assert.True(t, ok)
   687  	assert.NotNil(t, matched)
   688  	assert.Equal(t, request, rCtx)
   689  }
   690  
   691  func TestContextInvalidRoute(t *testing.T) {
   692  	spec, api := petstore.NewAPI(t)
   693  	ctx := NewContext(spec, api, nil)
   694  	ctx.router = DefaultRouter(spec, ctx.api)
   695  
   696  	request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodDelete, "pets", nil)
   697  	require.NoError(t, err)
   698  
   699  	// check there's nothing there
   700  	cached := request.Context().Value(ctxMatchedRoute)
   701  	assert.Nil(t, cached)
   702  
   703  	matched, rCtx, ok := ctx.RouteInfo(request)
   704  	assert.False(t, ok)
   705  	assert.Nil(t, matched)
   706  	assert.Nil(t, rCtx)
   707  
   708  	// check it was not cached
   709  	cached = request.Context().Value(ctxMatchedRoute)
   710  	assert.Nil(t, cached)
   711  
   712  	matched, rCtx, ok = ctx.RouteInfo(request)
   713  	assert.False(t, ok)
   714  	assert.Nil(t, matched)
   715  	assert.Nil(t, rCtx)
   716  }
   717  
   718  func TestContextValidContentType(t *testing.T) {
   719  	ct := applicationJSON
   720  	ctx := NewContext(nil, nil, nil)
   721  
   722  	request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, "http://localhost:8080", nil)
   723  	require.NoError(t, err)
   724  	request.Header.Set(runtime.HeaderContentType, ct)
   725  
   726  	// check there's nothing there
   727  	cached := request.Context().Value(ctxContentType)
   728  	assert.Nil(t, cached)
   729  
   730  	// trigger the parse
   731  	mt, _, rCtx, err := ctx.ContentType(request)
   732  	require.NoError(t, err)
   733  	assert.Equal(t, ct, mt)
   734  	assert.NotNil(t, rCtx)
   735  	assert.NotEqual(t, request, rCtx)
   736  
   737  	request = rCtx
   738  
   739  	// check it was cached
   740  	cached = request.Context().Value(ctxContentType)
   741  	assert.NotNil(t, cached)
   742  
   743  	// check if the cast works and fetch from cache too
   744  	mt, _, rCtx, err = ctx.ContentType(request)
   745  	require.NoError(t, err)
   746  	assert.Equal(t, ct, mt)
   747  	assert.Equal(t, request, rCtx)
   748  }
   749  
   750  func TestContextInvalidContentType(t *testing.T) {
   751  	ct := "application("
   752  	ctx := NewContext(nil, nil, nil)
   753  
   754  	request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, "http://localhost:8080", nil)
   755  	require.NoError(t, err)
   756  	request.Header.Set(runtime.HeaderContentType, ct)
   757  
   758  	// check there's nothing there
   759  	cached := request.Context().Value(ctxContentType)
   760  	assert.Nil(t, cached)
   761  
   762  	// trigger the parse
   763  	mt, _, rCtx, err := ctx.ContentType(request)
   764  	require.Error(t, err)
   765  	assert.Empty(t, mt)
   766  	assert.Nil(t, rCtx)
   767  
   768  	// check it was not cached
   769  	cached = request.Context().Value(ctxContentType)
   770  	assert.Nil(t, cached)
   771  
   772  	// check if the failure continues
   773  	_, _, rCtx, err = ctx.ContentType(request)
   774  	require.Error(t, err)
   775  	assert.Nil(t, rCtx)
   776  }
   777  

View as plain text