...

Source file src/github.com/go-openapi/runtime/middleware/router_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  	"net/http"
    20  	"net/http/httptest"
    21  	"sort"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/go-openapi/analysis"
    26  	"github.com/go-openapi/loads"
    27  	"github.com/go-openapi/runtime/internal/testing/petstore"
    28  	"github.com/go-openapi/runtime/middleware/untyped"
    29  	"github.com/stretchr/testify/assert"
    30  	"github.com/stretchr/testify/require"
    31  )
    32  
    33  func terminator(rw http.ResponseWriter, _ *http.Request) {
    34  	rw.WriteHeader(http.StatusOK)
    35  }
    36  
    37  func TestRouterMiddleware(t *testing.T) {
    38  	spec, api := petstore.NewAPI(t)
    39  	context := NewContext(spec, api, nil)
    40  	mw := NewRouter(context, http.HandlerFunc(terminator))
    41  
    42  	recorder := httptest.NewRecorder()
    43  	request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, "/api/pets", nil)
    44  	require.NoError(t, err)
    45  
    46  	mw.ServeHTTP(recorder, request)
    47  	assert.Equal(t, http.StatusOK, recorder.Code)
    48  
    49  	recorder = httptest.NewRecorder()
    50  	request, err = http.NewRequestWithContext(stdcontext.Background(), http.MethodDelete, "/api/pets", nil)
    51  	require.NoError(t, err)
    52  
    53  	mw.ServeHTTP(recorder, request)
    54  	assert.Equal(t, http.StatusMethodNotAllowed, recorder.Code)
    55  
    56  	methods := strings.Split(recorder.Header().Get("Allow"), ",")
    57  	sort.Strings(methods)
    58  	assert.Equal(t, "GET,POST", strings.Join(methods, ","))
    59  
    60  	recorder = httptest.NewRecorder()
    61  	request, err = http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, "/nopets", nil)
    62  	require.NoError(t, err)
    63  
    64  	mw.ServeHTTP(recorder, request)
    65  	assert.Equal(t, http.StatusNotFound, recorder.Code)
    66  
    67  	recorder = httptest.NewRecorder()
    68  	request, err = http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, "/pets", nil)
    69  	require.NoError(t, err)
    70  
    71  	mw.ServeHTTP(recorder, request)
    72  	assert.Equal(t, http.StatusNotFound, recorder.Code)
    73  
    74  	spec, api = petstore.NewRootAPI(t)
    75  	context = NewContext(spec, api, nil)
    76  	mw = NewRouter(context, http.HandlerFunc(terminator))
    77  
    78  	recorder = httptest.NewRecorder()
    79  	request, err = http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, "/pets", nil)
    80  	require.NoError(t, err)
    81  
    82  	mw.ServeHTTP(recorder, request)
    83  	assert.Equal(t, http.StatusOK, recorder.Code)
    84  
    85  	recorder = httptest.NewRecorder()
    86  	request, err = http.NewRequestWithContext(stdcontext.Background(), http.MethodDelete, "/pets", nil)
    87  	require.NoError(t, err)
    88  
    89  	mw.ServeHTTP(recorder, request)
    90  	assert.Equal(t, http.StatusMethodNotAllowed, recorder.Code)
    91  
    92  	methods = strings.Split(recorder.Header().Get("Allow"), ",")
    93  	sort.Strings(methods)
    94  	assert.Equal(t, "GET,POST", strings.Join(methods, ","))
    95  
    96  	recorder = httptest.NewRecorder()
    97  	request, err = http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, "/nopets", nil)
    98  	require.NoError(t, err)
    99  
   100  	mw.ServeHTTP(recorder, request)
   101  	assert.Equal(t, http.StatusNotFound, recorder.Code)
   102  }
   103  
   104  func TestRouterBuilder(t *testing.T) {
   105  	spec, api := petstore.NewAPI(t)
   106  	analyzed := analysis.New(spec.Spec())
   107  
   108  	assert.Len(t, analyzed.RequiredConsumes(), 3)
   109  	assert.Len(t, analyzed.RequiredProduces(), 5)
   110  	assert.Len(t, analyzed.OperationIDs(), 4)
   111  
   112  	// context := NewContext(spec, api)
   113  	builder := petAPIRouterBuilder(spec, api, analyzed)
   114  	getRecords := builder.records[http.MethodGet]
   115  	postRecords := builder.records[http.MethodPost]
   116  	deleteRecords := builder.records[http.MethodDelete]
   117  
   118  	assert.Len(t, getRecords, 2)
   119  	assert.Len(t, postRecords, 1)
   120  	assert.Len(t, deleteRecords, 1)
   121  
   122  	assert.Empty(t, builder.records[http.MethodPatch])
   123  	assert.Empty(t, builder.records[http.MethodOptions])
   124  	assert.Empty(t, builder.records[http.MethodHead])
   125  	assert.Empty(t, builder.records[http.MethodPut])
   126  
   127  	rec := postRecords[0]
   128  	assert.Equal(t, "/pets", rec.Key)
   129  	val := rec.Value.(*routeEntry)
   130  	assert.Len(t, val.Consumers, 2)
   131  	assert.Len(t, val.Producers, 2)
   132  	assert.Len(t, val.Consumes, 2)
   133  	assert.Len(t, val.Produces, 2)
   134  
   135  	assert.Contains(t, val.Consumers, "application/json")
   136  	assert.Contains(t, val.Producers, "application/x-yaml")
   137  	assert.Contains(t, val.Consumes, "application/json")
   138  	assert.Contains(t, val.Produces, "application/x-yaml")
   139  
   140  	assert.Len(t, val.Parameters, 1)
   141  
   142  	recG := getRecords[0]
   143  	assert.Equal(t, "/pets", recG.Key)
   144  	valG := recG.Value.(*routeEntry)
   145  	assert.Len(t, valG.Consumers, 2)
   146  	assert.Len(t, valG.Producers, 4)
   147  	assert.Len(t, valG.Consumes, 2)
   148  	assert.Len(t, valG.Produces, 4)
   149  
   150  	assert.Len(t, valG.Parameters, 2)
   151  }
   152  
   153  func TestRouterCanonicalBasePath(t *testing.T) {
   154  	spec, api := petstore.NewAPI(t)
   155  	spec.Spec().BasePath = "/api///"
   156  	context := NewContext(spec, api, nil)
   157  	mw := NewRouter(context, http.HandlerFunc(terminator))
   158  
   159  	recorder := httptest.NewRecorder()
   160  	request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, "/api/pets", nil)
   161  	require.NoError(t, err)
   162  
   163  	mw.ServeHTTP(recorder, request)
   164  	assert.Equal(t, http.StatusOK, recorder.Code)
   165  }
   166  
   167  func TestRouter_EscapedPath(t *testing.T) {
   168  	spec, api := petstore.NewAPI(t)
   169  	spec.Spec().BasePath = "/api/"
   170  	context := NewContext(spec, api, nil)
   171  	mw := NewRouter(context, http.HandlerFunc(terminator))
   172  
   173  	recorder := httptest.NewRecorder()
   174  	request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, "/api/pets/123", nil)
   175  	require.NoError(t, err)
   176  
   177  	mw.ServeHTTP(recorder, request)
   178  	assert.Equal(t, http.StatusOK, recorder.Code)
   179  
   180  	recorder = httptest.NewRecorder()
   181  	request, err = http.NewRequestWithContext(stdcontext.Background(), http.MethodGet, "/api/pets/abc%2Fdef", nil)
   182  	require.NoError(t, err)
   183  
   184  	mw.ServeHTTP(recorder, request)
   185  	assert.Equal(t, http.StatusOK, recorder.Code)
   186  	ri, _, _ := context.RouteInfo(request)
   187  	require.NotNil(t, ri)
   188  	require.NotNil(t, ri.Params)
   189  	assert.Equal(t, "abc/def", ri.Params.Get("id"))
   190  }
   191  
   192  func TestRouterStruct(t *testing.T) {
   193  	spec, api := petstore.NewAPI(t)
   194  	router := DefaultRouter(spec, newRoutableUntypedAPI(spec, api, new(Context)))
   195  
   196  	methods := router.OtherMethods("post", "/api/pets/{id}")
   197  	assert.Len(t, methods, 2)
   198  
   199  	entry, ok := router.Lookup("delete", "/api/pets/{id}")
   200  	assert.True(t, ok)
   201  	require.NotNil(t, entry)
   202  	assert.Len(t, entry.Params, 1)
   203  	assert.Equal(t, "id", entry.Params[0].Name)
   204  
   205  	_, ok = router.Lookup("delete", "/pets")
   206  	assert.False(t, ok)
   207  
   208  	_, ok = router.Lookup("post", "/no-pets")
   209  	assert.False(t, ok)
   210  }
   211  
   212  func petAPIRouterBuilder(spec *loads.Document, api *untyped.API, analyzed *analysis.Spec) *defaultRouteBuilder {
   213  	builder := newDefaultRouteBuilder(spec, newRoutableUntypedAPI(spec, api, new(Context)))
   214  	builder.AddRoute(http.MethodGet, "/pets", analyzed.AllPaths()["/pets"].Get)
   215  	builder.AddRoute(http.MethodPost, "/pets", analyzed.AllPaths()["/pets"].Post)
   216  	builder.AddRoute(http.MethodDelete, "/pets/{id}", analyzed.AllPaths()["/pets/{id}"].Delete)
   217  	builder.AddRoute(http.MethodGet, "/pets/{id}", analyzed.AllPaths()["/pets/{id}"].Get)
   218  
   219  	return builder
   220  }
   221  
   222  func TestPathConverter(t *testing.T) {
   223  	cases := []struct {
   224  		swagger string
   225  		denco   string
   226  	}{
   227  		{"/", "/"},
   228  		{"/something", "/something"},
   229  		{"/{id}", "/:id"},
   230  		{"/{id}/something/{anotherId}", "/:id/something/:anotherId"},
   231  		{"/{petid}", "/:petid"},
   232  		{"/{pet_id}", "/:pet_id"},
   233  		{"/{petId}", "/:petId"},
   234  		{"/{pet-id}", "/:pet-id"},
   235  		// compost parameters tests
   236  		{"/p_{pet_id}", "/p_:pet_id"},
   237  		{"/p_{petId}.{petSubId}", "/p_:petId"},
   238  	}
   239  
   240  	for _, tc := range cases {
   241  		actual := pathConverter.ReplaceAllString(tc.swagger, ":$1")
   242  		assert.Equal(t, tc.denco, actual, "expected swagger path %s to match %s but got %s", tc.swagger, tc.denco, actual)
   243  	}
   244  }
   245  
   246  func TestExtractCompositParameters(t *testing.T) {
   247  	// name is the composite parameter's name, value is the value of this compost parameter, pattern is the pattern to be matched
   248  	cases := []struct {
   249  		name    string
   250  		value   string
   251  		pattern string
   252  		names   []string
   253  		values  []string
   254  	}{
   255  		{name: "fragment", value: "gie", pattern: "e", names: []string{"fragment"}, values: []string{"gi"}},
   256  		{name: "fragment", value: "t.simpson", pattern: ".{subfragment}", names: []string{"fragment", "subfragment"}, values: []string{"t", "simpson"}},
   257  	}
   258  	for _, tc := range cases {
   259  		names, values := decodeCompositParams(tc.name, tc.value, tc.pattern, nil, nil)
   260  		assert.EqualValues(t, tc.names, names)
   261  		assert.EqualValues(t, tc.values, values)
   262  	}
   263  }
   264  

View as plain text