package api import ( "net/http" "net/http/httptest" "os" "path/filepath" "testing" "github.com/gin-gonic/gin" ) func TestConfigureWebUIServesAssetsAndSPAFallback(t *testing.T) { gin.SetMode(gin.TestMode) root := t.TempDir() if err := os.WriteFile(filepath.Join(root, "index.html"), []byte("sense-index"), 0o600); err != nil { t.Fatal(err) } if err := os.Mkdir(filepath.Join(root, "js"), 0o700); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(root, "js", "app.js"), []byte("sense-app"), 0o600); err != nil { t.Fatal(err) } t.Setenv("SENSE_WEB_ROOT", root) r := gin.New() configureWebUI(r) for _, tc := range []struct { path string code int body string }{ {path: "/", code: http.StatusOK, body: "sense-index"}, {path: "/device/list", code: http.StatusOK, body: "sense-index"}, {path: "/js/app.js", code: http.StatusOK, body: "sense-app"}, {path: "/js/missing.js", code: http.StatusNotFound}, {path: "/api/v1/missing", code: http.StatusNotFound}, } { req := httptest.NewRequest(http.MethodGet, tc.path, nil) res := httptest.NewRecorder() r.ServeHTTP(res, req) if res.Code != tc.code || (tc.body != "" && res.Body.String() != tc.body) { t.Fatalf("%s: got %d %q", tc.path, res.Code, res.Body.String()) } } } func TestWithinRootRejectsTraversal(t *testing.T) { root := t.TempDir() if withinRoot(root, filepath.Join(root, "..", "secret.txt")) { t.Fatal("path traversal must be rejected") } }