github.com/mendersoftware/go-lib-micro@v0.0.0-20240304135804-e8e39c59b148/routing/routing_test.go (about)

     1  // Copyright 2023 Northern.tech AS
     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  package routing
    15  
    16  import (
    17  	"net/http"
    18  	"reflect"
    19  	"runtime"
    20  	"testing"
    21  
    22  	"github.com/ant0ine/go-json-rest/rest"
    23  	rtest "github.com/ant0ine/go-json-rest/rest/test"
    24  )
    25  
    26  func TestSupportsMethod(t *testing.T) {
    27  
    28  	var sets = []struct {
    29  		exp       bool
    30  		method    string
    31  		supported []string
    32  	}{
    33  		{
    34  			true,
    35  			http.MethodOptions,
    36  			[]string{
    37  				http.MethodGet,
    38  				http.MethodPut,
    39  				http.MethodOptions,
    40  			},
    41  		},
    42  		{
    43  			false,
    44  			http.MethodOptions,
    45  			[]string{
    46  				http.MethodGet,
    47  				http.MethodPut,
    48  			},
    49  		},
    50  	}
    51  
    52  	for _, tv := range sets {
    53  		if supportsMethod(tv.method, tv.supported) != tv.exp {
    54  			t.Errorf("failed case: %+v", tv)
    55  		}
    56  	}
    57  }
    58  
    59  // We can't compare functions, so let's take the hard way and extract
    60  // func name from runtime
    61  func funcName(f interface{}) string {
    62  	p := reflect.ValueOf(f).Pointer()
    63  	rfunc := runtime.FuncForPC(p)
    64  	return rfunc.Name()
    65  }
    66  
    67  func TestAutogenOptionRoutes(t *testing.T) {
    68  	// make sure that dummy and options are different to prevent
    69  	// the compiler making this a single symbol
    70  	dummy := func(w rest.ResponseWriter, r *rest.Request) {
    71  		// dummy
    72  		w.WriteJson(struct {
    73  			x int
    74  		}{
    75  			2,
    76  		})
    77  	}
    78  	options := func(w rest.ResponseWriter, r *rest.Request) {
    79  		// dummy
    80  		w.WriteJson(struct {
    81  			x int
    82  		}{
    83  			1,
    84  		})
    85  	}
    86  	gen := func(methods []string) rest.HandlerFunc {
    87  		return options
    88  	}
    89  
    90  	routes := []*rest.Route{
    91  		// expecting rest.Options(..) to be added for /foo
    92  		rest.Get("/foo", dummy),
    93  		rest.Post("/foo", dummy),
    94  
    95  		// no extra OPTIONS handler for /bar
    96  		rest.Get("/bar", dummy),
    97  		rest.Options("/bar", dummy),
    98  	}
    99  
   100  	augmented := AutogenOptionsRoutes(routes, gen)
   101  
   102  	type expHandler map[string]rest.HandlerFunc
   103  	exp := map[string]expHandler{
   104  		"/foo": {
   105  			http.MethodGet:     dummy,
   106  			http.MethodPost:    dummy,
   107  			http.MethodOptions: options,
   108  		},
   109  		"/bar": {
   110  			http.MethodGet:     dummy,
   111  			http.MethodOptions: dummy,
   112  		},
   113  	}
   114  
   115  	// we're expecting 5 handlers in total
   116  	expCount := 5
   117  	if len(augmented) != expCount {
   118  		t.Errorf("got %d handlers instead of %d", len(augmented), expCount)
   119  	}
   120  
   121  	for _, r := range augmented {
   122  		v, ok := exp[r.PathExp]
   123  		if ok != true {
   124  			t.Errorf("failed with route %+v, route not present", r)
   125  		}
   126  
   127  		h, ok := v[r.HttpMethod]
   128  		if ok != true {
   129  			t.Errorf("failed with route %+v, method not present", r)
   130  		}
   131  
   132  		if funcName(r.Func) != funcName(h) {
   133  			t.Errorf("failed with route %+v, different handler", r)
   134  		}
   135  	}
   136  }
   137  
   138  func TestAutogenOptionHeaders(t *testing.T) {
   139  
   140  	suppmeth := []string{
   141  		http.MethodGet,
   142  		http.MethodPut,
   143  	}
   144  
   145  	api := rest.NewApi()
   146  	api.Use(rest.DefaultDevStack...)
   147  	router, _ := rest.MakeRouter(
   148  		rest.Options("/test", AllowHeaderOptionsGenerator(suppmeth)),
   149  	)
   150  
   151  	api.SetApp(router)
   152  
   153  	rec := rtest.RunRequest(t, api.MakeHandler(),
   154  		rtest.MakeSimpleRequest(http.MethodOptions,
   155  			"http://1.2.3.4/test", nil))
   156  
   157  	allowmeth := rec.Recorder.HeaderMap[http.CanonicalHeaderKey("Allow")]
   158  
   159  	// expecting only 2 allowed methods (should OPTIONS be
   160  	// included in Allow too?)
   161  	if len(allowmeth) != 2 {
   162  		t.Errorf("too many allowed methods: %+v", allowmeth)
   163  	}
   164  
   165  	for _, sh := range suppmeth {
   166  		var found bool
   167  		for _, s := range allowmeth {
   168  			if sh == s {
   169  				found = true
   170  				break
   171  			}
   172  		}
   173  		if !found {
   174  			t.Errorf("supported method %s not in allowed: %+v",
   175  				sh, allowmeth)
   176  		}
   177  	}
   178  }