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 }