From 2edf712e9cac8f39651eb8ebbd75853bdb5a501a Mon Sep 17 00:00:00 2001 From: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com> Date: Wed, 28 May 2025 16:03:31 +0300 Subject: [PATCH] [release-11.4.6]: Add static tests (#106132) --- pkg/api/static/static_test.go | 179 ++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 pkg/api/static/static_test.go diff --git a/pkg/api/static/static_test.go b/pkg/api/static/static_test.go new file mode 100644 index 00000000000..d7d84658bd2 --- /dev/null +++ b/pkg/api/static/static_test.go @@ -0,0 +1,179 @@ +package httpstatic + +import ( + "io" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "testing" + + "github.com/grafana/authlib/claims" + "github.com/grafana/grafana/pkg/infra/tracing" + "github.com/grafana/grafana/pkg/models/usertoken" + "github.com/grafana/grafana/pkg/services/authn" + "github.com/grafana/grafana/pkg/services/authn/authntest" + "github.com/grafana/grafana/pkg/services/contexthandler" + "github.com/grafana/grafana/pkg/services/featuremgmt" + "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/web" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestStatic(t *testing.T) { + // Create a temporary directory for test files + tmpDir, err := os.MkdirTemp("", "static-test") + require.NoError(t, err) + defer func() { + err := os.RemoveAll(tmpDir) + require.NoError(t, err) + }() + + // Create test files + testFiles := map[string]string{ + "test.txt": "Test content", + "subdir/test.txt": "Subdir content", + } + + for path, content := range testFiles { + fullPath := filepath.Join(tmpDir, path) + err := os.MkdirAll(filepath.Dir(fullPath), 0o750) + require.NoError(t, err) + err = os.WriteFile(fullPath, []byte(content), 0o644) + require.NoError(t, err) + } + + tests := []struct { + dir string + name string + path string + options StaticOptions + expectedStatus int + expectedBody string + expectedLocation string + }{ + { + name: "should serve existing file", + path: "/test.txt", + expectedStatus: http.StatusOK, + expectedBody: "Test content", + dir: tmpDir, + }, + { + name: "should serve file from subdirectory", + path: "/subdir/test.txt", + expectedStatus: http.StatusOK, + expectedBody: "Subdir content", + dir: tmpDir, + }, + + { + name: "should redirect directory without trailing slash", + path: "/subdir", + expectedStatus: http.StatusFound, + expectedLocation: "/subdir/", + dir: tmpDir, + }, + { + name: "should handle prefix", + path: "/static/test.txt", + options: StaticOptions{Prefix: "/static"}, + expectedStatus: http.StatusOK, + expectedBody: "Test content", + dir: tmpDir, + }, + { + name: "should handle excluded path", + path: "/test.txt", + options: StaticOptions{Exclude: []string{"/test.txt"}}, + expectedStatus: http.StatusNotFound, + dir: tmpDir, + }, + { + name: "should add custom headers", + path: "/test.txt", + options: StaticOptions{AddHeaders: func(ctx *web.Context) { ctx.Resp.Header().Set("X-Test", "test") }}, + expectedStatus: http.StatusOK, + expectedBody: "Test content", + dir: tmpDir, + }, + { + name: "should clean up path before redirecting", + path: "/subdir/..%2F%5C127.0.0.1:80%2F%3F%2F..%2F..", + options: StaticOptions{Prefix: "subdir"}, + expectedStatus: http.StatusFound, + expectedLocation: "/", + dir: tmpDir, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sc := setupScenarioContext(t, "") + sc.m.Use(Static(tt.dir, tt.options)) + + // Create a test request + req := httptest.NewRequest("GET", tt.path, nil) + w := httptest.NewRecorder() + + // Execute the handler + sc.m.ServeHTTP(w, req) + + // Verify the response + resp := w.Result() + require.Equal(t, tt.expectedStatus, resp.StatusCode) + + if tt.expectedBody != "" { + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + assert.Equal(t, tt.expectedBody, string(body)) + } + + if tt.options.AddHeaders != nil { + assert.Equal(t, "test", resp.Header.Get("X-Test")) + } + + if tt.expectedLocation != "" { + assert.Equal(t, tt.expectedLocation, resp.Header.Get("Location")) + } + }) + } +} + +type scenarioContext struct { + t *testing.T + cfg *setting.Cfg + m *web.Mux + ctxHdlr *contexthandler.ContextHandler +} + +func getContextHandler(t *testing.T, cfg *setting.Cfg) *contexthandler.ContextHandler { + t.Helper() + + if cfg == nil { + cfg = setting.NewCfg() + } + + return contexthandler.ProvideService( + cfg, + tracing.InitializeTracerForTest(), + &authntest.FakeService{ExpectedIdentity: &authn.Identity{ID: "0", Type: claims.TypeAnonymous, SessionToken: &usertoken.UserToken{}}}, + featuremgmt.WithFeatures(), + ) +} + +func setupScenarioContext(t *testing.T, url string) *scenarioContext { + cfg := setting.NewCfg() + ctxHdlr := getContextHandler(t, cfg) + sc := &scenarioContext{ + t: t, + cfg: cfg, + ctxHdlr: ctxHdlr, + } + + sc.m = web.New() + sc.m.Use(ctxHdlr.Middleware) + + return sc +}