diff --git a/cmd/server/tests/library_browse_test.go b/cmd/server/tests/library_browse_test.go new file mode 100644 index 0000000..c6e0a6f --- /dev/null +++ b/cmd/server/tests/library_browse_test.go @@ -0,0 +1,62 @@ +package main + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBrowseLibraryFoldersEndpoint(t *testing.T) { + setup := setupTestServer(t) + defer setup.Close() + + t.Run("GET /api/libraries/browse - no authentication returns 401", func(t *testing.T) { + req := httptest.NewRequest("GET", "/api/libraries/browse?path=/tmp", nil) + rec := httptest.NewRecorder() + setup.Server.Config.Handler.ServeHTTP(rec, req) + assert.Equal(t, http.StatusUnauthorized, rec.Code) + }) + + t.Run("GET /api/libraries/browse - regular user returns 403 forbidden", func(t *testing.T) { + req := httptest.NewRequest("GET", "/api/libraries/browse?path=/tmp", nil) + req.Header.Set("Authorization", "Bearer "+setup.RegularToken) + rec := httptest.NewRecorder() + setup.Server.Config.Handler.ServeHTTP(rec, req) + assert.Equal(t, http.StatusForbidden, rec.Code) + }) + + t.Run("GET /api/libraries/browse - admin can browse directories", func(t *testing.T) { + req := httptest.NewRequest("GET", "/api/libraries/browse?path=/tmp", nil) + req.Header.Set("Authorization", "Bearer "+setup.Token) + rec := httptest.NewRecorder() + setup.Server.Config.Handler.ServeHTTP(rec, req) + assert.Equal(t, http.StatusOK, rec.Code) + + var data struct { + CurrentPath string `json:"current_path"` + ParentPath string `json:"parent_path"` + Directories []string `json:"directories"` + } + err := json.Unmarshal(rec.Body.Bytes(), &data) + assert.NoError(t, err) + assert.Equal(t, "/tmp", data.CurrentPath) + assert.NotEmpty(t, data.Directories) + }) + + t.Run("GET /api/libraries/browse - blocks path traversal attempts", func(t *testing.T) { + req := httptest.NewRequest("GET", "/api/libraries/browse?path=/etc/../root", nil) + req.Header.Set("Authorization", "Bearer "+setup.Token) + rec := httptest.NewRecorder() + setup.Server.Config.Handler.ServeHTTP(rec, req) + assert.Equal(t, http.StatusBadRequest, rec.Code) + + var errResp struct { + Error string `json:"error"` + } + json.Unmarshal(rec.Body.Bytes(), &errResp) + assert.Contains(t, errResp.Error, "path traversal not allowed") + }) +} diff --git a/internal/services/library_service_test.go b/internal/services/library_service_test.go new file mode 100644 index 0000000..78b8528 --- /dev/null +++ b/internal/services/library_service_test.go @@ -0,0 +1,38 @@ +package services + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBrowseDirectories(t *testing.T) { + service := &LibraryService{} + + t.Run("blocks path traversal with ..", func(t *testing.T) { + _, _, _, err := service.BrowseDirectories(context.Background(), "/etc/../root") + assert.Error(t, err) + assert.Contains(t, err.Error(), "path traversal not allowed") + }) + + t.Run("returns error for non-existent path", func(t *testing.T) { + _, _, _, err := service.BrowseDirectories(context.Background(), "/nonexistent/path") + assert.Error(t, err) + assert.Contains(t, err.Error(), "path does not exist") + }) + + t.Run("returns error when path is a file", func(t *testing.T) { + _, _, _, err := service.BrowseDirectories(context.Background(), "/etc/passwd") + assert.Error(t, err) + assert.Contains(t, err.Error(), "not a directory") + }) + + t.Run("returns subdirectories for valid path", func(t *testing.T) { + dirs, currentPath, parentPath, err := service.BrowseDirectories(context.Background(), "/tmp") + assert.NoError(t, err) + assert.NotEmpty(t, dirs) + assert.Equal(t, "/tmp", currentPath) + assert.Equal(t, "/", parentPath) + }) +}