mirror of https://github.com/grafana/grafana
parent
626f72f5f4
commit
5e7a8b1652
@ -0,0 +1,174 @@ |
|||||||
|
package httpstatic |
||||||
|
|
||||||
|
import ( |
||||||
|
"io" |
||||||
|
"net/http" |
||||||
|
"net/http/httptest" |
||||||
|
"os" |
||||||
|
"path/filepath" |
||||||
|
"testing" |
||||||
|
|
||||||
|
claims "github.com/grafana/authlib/types" |
||||||
|
"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 os.RemoveAll(tmpDir) |
||||||
|
|
||||||
|
// 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), 0o755) |
||||||
|
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, |
||||||
|
&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 |
||||||
|
} |
Loading…
Reference in new issue