| // Package frontend contains the Go code that servers the Perf web UI. |
| package frontend |
| |
| import ( |
| "crypto/tls" |
| "encoding/json" |
| "net/http" |
| "net/http/httptest" |
| "net/url" |
| "testing" |
| "testing/fstest" |
| |
| "github.com/stretchr/testify/require" |
| "go.skia.org/infra/go/alogin" |
| "go.skia.org/infra/go/alogin/mocks" |
| "go.skia.org/infra/go/roles" |
| "go.skia.org/infra/go/testutils" |
| "go.skia.org/infra/perf/go/config" |
| ) |
| |
| func TestFrontend_ShouldInitAllHandlers(t *testing.T) { |
| f := &Frontend{ |
| loginProvider: mocks.NewLogin(t), |
| flags: &config.FrontendFlags{ |
| NumParamSetsForQueries: 2, |
| }, |
| } |
| configFileBytes := testutils.ReadFileBytes(t, "config.json") |
| err := json.Unmarshal(configFileBytes, &config.Config) |
| require.NoError(t, err) |
| |
| // Check if there is a conflict or misuse in the http/chi handler API |
| require.NotPanics(t, func() { |
| f.GetHandler([]string{}) |
| }) |
| } |
| |
| func TestFrontend_RoleEnforced_ReportsError(t *testing.T) { |
| r := httptest.NewRequest("GET", "/", nil) |
| login := mocks.NewLogin(t) |
| login.On("Status", r).Return(alogin.Status{ |
| EMail: "nobody@example.org", |
| }) |
| login.On("HasRole", r, roles.Admin).Return(false) |
| |
| f := &Frontend{ |
| loginProvider: login, |
| } |
| h := f.RoleEnforcedHandler(roles.Admin, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| _, _ = w.Write([]byte("hello")) |
| })) |
| |
| w := httptest.NewRecorder() |
| h.ServeHTTP(w, r) |
| require.Equal(t, http.StatusForbidden, w.Result().StatusCode) |
| require.Contains(t, w.Body.String(), "not authenticated") |
| } |
| |
| func TestFrontend_RoleEnforced_ReportsOK(t *testing.T) { |
| r := httptest.NewRequest("GET", "/", nil) |
| login := mocks.NewLogin(t) |
| login.On("Status", r).Return(alogin.Status{ |
| EMail: "nobody@example.org", |
| }) |
| login.On("HasRole", r, roles.Admin).Return(true) |
| const expected_body = "hello" |
| |
| f := &Frontend{ |
| loginProvider: login, |
| } |
| h := f.RoleEnforcedHandler(roles.Admin, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| _, _ = w.Write([]byte(expected_body)) |
| })) |
| |
| w := httptest.NewRecorder() |
| h.ServeHTTP(w, r) |
| require.Equal(t, http.StatusOK, w.Result().StatusCode) |
| require.Equal(t, expected_body, w.Body.String()) |
| } |
| |
| func TestFrontend_UnspecifiedRedirectUrl_Redirects(t *testing.T) { |
| configFileBytes := testutils.ReadFileBytes(t, "config.json") |
| err := json.Unmarshal(configFileBytes, &config.Config) |
| require.NoError(t, err) |
| |
| r := httptest.NewRequest("GET", "/", nil) |
| w := httptest.NewRecorder() |
| oldMainHandler(w, r) |
| |
| require.Equal(t, http.StatusMovedPermanently, w.Result().StatusCode) |
| require.Equal(t, "/e", w.Result().Header.Get("Location")) |
| } |
| |
| func TestFrontend_SpecifiedRedirectUrl_Redirects(t *testing.T) { |
| configFileBytes := testutils.ReadFileBytes(t, "config.json") |
| err := json.Unmarshal(configFileBytes, &config.Config) |
| config.Config.LandingPageRelPath = "/m" |
| require.NoError(t, err) |
| |
| r := httptest.NewRequest("GET", "/", nil) |
| w := httptest.NewRecorder() |
| oldMainHandler(w, r) |
| |
| require.Equal(t, http.StatusMovedPermanently, w.Result().StatusCode) |
| require.Equal(t, "/m", w.Result().Header.Get("Location")) |
| } |
| |
| func TestFrontend_StripSlashes(t *testing.T) { |
| host := "localhost:8001" |
| |
| f := &Frontend{ |
| loginProvider: mocks.NewLogin(t), |
| flags: &config.FrontendFlags{ |
| ConfigFilename: "./testdata/config.json", |
| NumParamSetsForQueries: 2, |
| }, |
| } |
| |
| mapFS := fstest.MapFS{} |
| for _, filename := range templateFilenames { |
| mapFS[filename] = &fstest.MapFile{ |
| Data: []byte(""), |
| } |
| } |
| f.distFileSystem = http.FS(mapFS) |
| |
| configFileBytes := testutils.ReadFileBytes(t, "config.json") |
| err := json.Unmarshal(configFileBytes, &config.Config) |
| require.NoError(t, err) |
| u, err := url.Parse(config.Config.URL) |
| require.NoError(t, err) |
| f.host = u.Host |
| // Setup redirect for the root path "/" |
| config.Config.LandingPageRelPath = "/e" |
| require.NoError(t, err) |
| |
| r := f.GetHandler([]string{host}) |
| |
| queryThatDoesNotChangeAnything := "?params_that_should_be_ignored=ignored" |
| |
| testCases := []struct { |
| path string |
| expectedStatusCode int |
| expectedRedirect string |
| compareAgainstPathWithoutParams string |
| }{ |
| { |
| path: "/c/", |
| expectedStatusCode: http.StatusOK, |
| }, |
| { |
| path: "/c", |
| expectedStatusCode: http.StatusOK, |
| }, |
| { |
| path: "/a/", |
| expectedStatusCode: http.StatusOK, |
| }, |
| { |
| path: "/a", |
| expectedStatusCode: http.StatusOK, |
| }, |
| { |
| path: "/m/", |
| expectedStatusCode: http.StatusOK, |
| }, |
| { |
| path: "/m", |
| expectedStatusCode: http.StatusOK, |
| }, |
| { |
| path: "/e", |
| expectedStatusCode: http.StatusOK, |
| }, |
| { |
| path: "/e/", |
| expectedStatusCode: http.StatusOK, |
| }, |
| { |
| path: "/", |
| expectedStatusCode: http.StatusMovedPermanently, |
| expectedRedirect: config.Config.LandingPageRelPath, |
| }, |
| { |
| path: "/m" + queryThatDoesNotChangeAnything, |
| expectedStatusCode: http.StatusOK, |
| compareAgainstPathWithoutParams: "/m", |
| }, |
| { |
| // Note that the path does not change in browser. |
| path: "/m/" + queryThatDoesNotChangeAnything, |
| expectedStatusCode: http.StatusOK, |
| compareAgainstPathWithoutParams: "/m/", |
| }, |
| } |
| |
| for _, tc := range testCases { |
| t.Run(tc.path, func(t *testing.T) { |
| w := httptest.NewRecorder() |
| req := httptest.NewRequest("GET", tc.path, nil) |
| req.Host = host |
| req.TLS = &tls.ConnectionState{} |
| r.ServeHTTP(w, req) |
| |
| require.Equal(t, tc.expectedStatusCode, w.Result().StatusCode) |
| if tc.expectedRedirect != "" { |
| require.Equal(t, tc.expectedRedirect, w.Header().Get("Location")) |
| } else { |
| expectedPath := tc.compareAgainstPathWithoutParams |
| if expectedPath == "" { |
| expectedPath = tc.path |
| } |
| require.Equal(t, expectedPath, req.URL.Path) |
| } |
| }) |
| } |
| } |