github.com/kiali/kiali@v1.84.0/routing/router_test.go (about) 1 package routing 2 3 import ( 4 "io" 5 "net/http" 6 "net/http/httptest" 7 "os" 8 rpprof "runtime/pprof" 9 "testing" 10 11 "github.com/gorilla/mux" 12 "github.com/prometheus/client_golang/prometheus" 13 "github.com/stretchr/testify/assert" 14 15 "github.com/kiali/kiali/config" 16 "github.com/kiali/kiali/kubernetes/kubetest" 17 "github.com/kiali/kiali/prometheus/internalmetrics" 18 ) 19 20 func TestDrawPathProperly(t *testing.T) { 21 conf := new(config.Config) 22 mockClientFactory := kubetest.NewK8SClientFactoryMock(kubetest.NewFakeK8sClient()) 23 router, _ := NewRouter(conf, nil, mockClientFactory, nil, nil, nil) 24 testRoute(router, "Root", "GET", t) 25 } 26 27 func testRoute(router *mux.Router, name string, method string, t *testing.T) { 28 path := router.Get(name) 29 30 if path == nil { 31 t.Error("path is not registered into router") 32 } 33 34 methods, err := path.GetMethods() 35 if err != nil { 36 t.Error(err) 37 } 38 39 if len(methods) != 1 && methods[0] != method { 40 t.Error("Root path is not registered with method") 41 } 42 } 43 44 func TestWebRootRedirect(t *testing.T) { 45 conf := new(config.Config) 46 conf.Server.WebRoot = "/test" 47 48 mockClientFactory := kubetest.NewK8SClientFactoryMock(kubetest.NewFakeK8sClient()) 49 router, _ := NewRouter(conf, nil, mockClientFactory, nil, nil, nil) 50 ts := httptest.NewServer(router) 51 defer ts.Close() 52 53 client := &http.Client{ 54 CheckRedirect: func(req *http.Request, via []*http.Request) error { 55 return http.ErrUseLastResponse 56 }, 57 } 58 59 resp, err := client.Get(ts.URL + "/") 60 if err != nil { 61 t.Fatal(err) 62 } 63 // body, _ := ioutil.ReadAll(resp.Body) 64 assert.Equal(t, 302, resp.StatusCode, "Response should redirect to the webroot") 65 assert.Equal(t, "/test/", resp.Header.Get("Location"), "Response should redirect to the webroot") 66 } 67 68 func TestSimpleRoute(t *testing.T) { 69 conf := new(config.Config) 70 71 mockClientFactory := kubetest.NewK8SClientFactoryMock(kubetest.NewFakeK8sClient()) 72 router, _ := NewRouter(conf, nil, mockClientFactory, nil, nil, nil) 73 ts := httptest.NewServer(router) 74 defer ts.Close() 75 76 resp, err := http.Get(ts.URL + "/healthz") 77 if err != nil { 78 t.Fatal(err) 79 } 80 assert.Equal(t, 200, resp.StatusCode, "Response should be ok") 81 82 body, _ := io.ReadAll(resp.Body) 83 assert.Equal(t, "", string(body), "Response should be empty") 84 } 85 86 func TestProfilerRoute(t *testing.T) { 87 conf := new(config.Config) 88 conf.Server.Profiler.Enabled = true 89 90 mockClientFactory := kubetest.NewK8SClientFactoryMock(kubetest.NewFakeK8sClient()) 91 router, _ := NewRouter(conf, nil, mockClientFactory, nil, nil, nil) 92 ts := httptest.NewServer(router) 93 defer ts.Close() 94 95 resp, err := http.Get(ts.URL + "/debug/pprof/") 96 if err != nil { 97 t.Fatal(err) 98 } 99 assert.Equal(t, 400, resp.StatusCode, "pprof index should exist but needed credentials") 100 101 for _, p := range rpprof.Profiles() { 102 resp, err = http.Get(ts.URL + "/debug/pprof/" + p.Name()) 103 if err != nil { 104 t.Fatalf("Failed to get profile [%v]: %v", p, err) 105 } 106 assert.Equal(t, 400, resp.StatusCode, "pprof profile [%v] should exist but needed credentials", p.Name()) 107 } 108 // note we do not test "profile" endpoint - it takes too long and besides that the test framework eventually times out 109 for _, p := range []string{"symbol", "trace"} { 110 resp, err = http.Get(ts.URL + "/debug/pprof/" + p) 111 if err != nil { 112 t.Fatalf("Failed to get profile [%v]: %v", p, err) 113 } 114 assert.Equal(t, 400, resp.StatusCode, "pprof endpoint [%v] should exist but needed credentials", p) 115 } 116 } 117 118 func TestDisabledProfilerRoute(t *testing.T) { 119 conf := new(config.Config) 120 conf.Server.Profiler.Enabled = false 121 122 mockClientFactory := kubetest.NewK8SClientFactoryMock(kubetest.NewFakeK8sClient()) 123 router, _ := NewRouter(conf, nil, mockClientFactory, nil, nil, nil) 124 ts := httptest.NewServer(router) 125 defer ts.Close() 126 127 resp, err := http.Get(ts.URL + "/debug/pprof/") 128 if err != nil { 129 t.Fatal(err) 130 } 131 assert.Equal(t, 404, resp.StatusCode, "pprof should have been disabled") 132 133 for _, p := range rpprof.Profiles() { 134 resp, err = http.Get(ts.URL + "/debug/pprof/" + p.Name()) 135 if err != nil { 136 t.Fatal(err) 137 } 138 assert.Equal(t, 404, resp.StatusCode, "pprof should have been disabled [%v]", p.Name()) 139 } 140 for _, p := range []string{"symbol", "trace", "profile"} { 141 resp, err = http.Get(ts.URL + "/debug/pprof/" + p) 142 if err != nil { 143 t.Fatal(err) 144 } 145 assert.Equal(t, 404, resp.StatusCode, "pprof should have been disabled [%v]", p) 146 } 147 } 148 149 func TestRedirectWithSetWebRootKeepsParams(t *testing.T) { 150 oldWd, _ := os.Getwd() 151 defer func() { _ = os.Chdir(oldWd) }() 152 _ = os.Chdir(os.TempDir()) 153 _ = os.MkdirAll("./console", 0o777) 154 _, _ = os.Create("./console/index.html") 155 156 conf := new(config.Config) 157 conf.Server.WebRoot = "/test" 158 159 mockClientFactory := kubetest.NewK8SClientFactoryMock(kubetest.NewFakeK8sClient()) 160 router, _ := NewRouter(conf, nil, mockClientFactory, nil, nil, nil) 161 ts := httptest.NewServer(router) 162 defer ts.Close() 163 164 client := &http.Client{ 165 CheckRedirect: func(req *http.Request, via []*http.Request) error { 166 return http.ErrUseLastResponse 167 }, 168 } 169 170 resp, err := client.Get(ts.URL + "/test") 171 if err != nil { 172 t.Fatal(err) 173 } 174 body, _ := io.ReadAll(resp.Body) 175 assert.Equal(t, 200, resp.StatusCode, "Response should not redirect") 176 177 resp, err = client.Get(ts.URL + "/test/") 178 if err != nil { 179 t.Fatal(err) 180 } 181 body2, _ := io.ReadAll(resp.Body) 182 assert.Equal(t, 200, resp.StatusCode, string(body2)) 183 184 assert.Equal(t, string(body), string(body2), "Response with and without the trailing slash on the webroot are not the same") 185 } 186 187 func TestMetricHandlerAPIFailures(t *testing.T) { 188 errcodes := []struct { 189 Name string 190 Code int 191 }{ 192 {Name: "InternalServerError", Code: http.StatusInternalServerError}, 193 {Name: "StatusServiceUnavailable", Code: http.StatusServiceUnavailable}, 194 } 195 196 for _, errcode := range errcodes { 197 t.Run(errcode.Name, func(t *testing.T) { 198 req, err := http.NewRequest("GET", "/error", nil) 199 if err != nil { 200 t.Fatal(err) 201 } 202 203 rr := httptest.NewRecorder() 204 handler := metricHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 205 w.WriteHeader(errcode.Code) 206 }), Route{Name: "error"}) 207 208 handler.ServeHTTP(rr, req) 209 210 if status := rr.Code; status != http.StatusInternalServerError && status != http.StatusServiceUnavailable { 211 t.Errorf("handler returned wrong status code: got %v want %v", 212 status, errcode.Code) 213 } 214 }) 215 } 216 217 registry := prometheus.NewRegistry() 218 prometheus.DefaultRegisterer = registry 219 internalmetrics.RegisterInternalMetrics() 220 221 metrics, err := registry.Gather() 222 assert.Nil(t, err) 223 224 for _, m := range metrics { 225 if m.GetName() == "kiali_api_failures_total" { 226 if m.GetMetric()[0].Counter.GetValue() != 2 { 227 t.Errorf("Failure counter metric should have a value of 2: %+v", m) 228 } 229 } 230 } 231 }