diff --git a/internal/fs/ls_test.go b/internal/fs/ls_test.go new file mode 100644 index 00000000..757fa723 --- /dev/null +++ b/internal/fs/ls_test.go @@ -0,0 +1,85 @@ +package fs + +import ( + "encoding/json" + "fmt" + "path" + "testing" + + _ "github.com/databricks/cli/cmd/fs" + "github.com/databricks/cli/internal" + "github.com/databricks/cli/internal/helpers" + "github.com/databricks/cli/libs/cmdio" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func assertObjectListed(t *testing.T, parsedLogs []map[string]string, name string, objectType string) { + foundFile := false + for _, v := range parsedLogs { + if v["Name"] != name { + continue + } + foundFile = true + assert.Equal(t, objectType, v["Type"]) + } + assert.True(t, foundFile, fmt.Sprintf("failed to find file %s in output logs", name)) +} + +func TestAccFsLs(t *testing.T) { + t.Log(internal.GetEnvOrSkipTest(t, "CLOUD_ENV")) + + // setup some testdata in the workspace + w := helpers.NewWorkspaceTestdata(t) + w.AddFile("foo.txt", `hello, world`) + w.AddFile("python_notebook.py", cmdio.Heredoc(` + #Databricks notebook source + print(2)`)) + w.AddFile("python_file.py", `print(1)`) + w.Mkdir("my_directory") + w.AddFile("my_directory/.gitkeep", "") + + // run list command + stdout, stderr := internal.RequireSuccessfulRun(t, "fs", "ls", w.RootPath(), "--output=json") + + // read and parse the output logs + parsedLogs := make([]map[string]string, 0) + err := json.Unmarshal(stdout.Bytes(), &parsedLogs) + require.NoError(t, err) + + // make assertions on the output logs + assert.Equal(t, stderr.String(), "") + assertObjectListed(t, parsedLogs, "python_file.py", "FILE") + assertObjectListed(t, parsedLogs, "foo.txt", "FILE") + assertObjectListed(t, parsedLogs, "python_notebook", "NOTEBOOK") + assertObjectListed(t, parsedLogs, "my_directory", "DIRECTORY") +} + +func TestAccFsLsWithAbsoluteFlag(t *testing.T) { + t.Log(internal.GetEnvOrSkipTest(t, "CLOUD_ENV")) + + // setup some testdata in the workspace + w := helpers.NewWorkspaceTestdata(t) + w.AddFile("foo.txt", `hello, world`) + w.AddFile("python_notebook.py", cmdio.Heredoc(` + #Databricks notebook source + print(2)`)) + w.AddFile("python_file.py", `print(1)`) + w.Mkdir("my_directory") + w.AddFile("my_directory/.gitkeep", "") + + // run list command + stdout, stderr := internal.RequireSuccessfulRun(t, "fs", "ls", w.RootPath(), "--output=json", "--absolute") + + // read and parse the output logs + parsedLogs := make([]map[string]string, 0) + err := json.Unmarshal(stdout.Bytes(), &parsedLogs) + require.NoError(t, err) + + // make assertions on the output logs + assert.Equal(t, stderr.String(), "") + assertObjectListed(t, parsedLogs, path.Join(w.RootPath(), "python_file.py"), "FILE") + assertObjectListed(t, parsedLogs, path.Join(w.RootPath(), "foo.txt"), "FILE") + assertObjectListed(t, parsedLogs, path.Join(w.RootPath(), "python_notebook"), "NOTEBOOK") + assertObjectListed(t, parsedLogs, path.Join(w.RootPath(), "my_directory"), "DIRECTORY") +} diff --git a/internal/helpers/workspace_testdata.go b/internal/helpers/workspace_testdata.go new file mode 100644 index 00000000..85480378 --- /dev/null +++ b/internal/helpers/workspace_testdata.go @@ -0,0 +1,88 @@ +package helpers + +import ( + "context" + "fmt" + "net/http" + "net/url" + "path" + "strings" + "testing" + + "github.com/databricks/cli/internal" + "github.com/databricks/databricks-sdk-go" + "github.com/databricks/databricks-sdk-go/apierr" + "github.com/databricks/databricks-sdk-go/client" + "github.com/databricks/databricks-sdk-go/service/workspace" + "github.com/stretchr/testify/require" +) + +type workspaceTestdata struct { + root string + t *testing.T + client *databricks.WorkspaceClient +} + +func NewWorkspaceTestdata(t *testing.T) *workspaceTestdata { + ctx := context.Background() + w := databricks.Must(databricks.NewWorkspaceClient()) + + me, err := w.CurrentUser.Me(ctx) + require.NoError(t, err) + path := fmt.Sprintf("/Users/%s/%s", me.UserName, internal.RandomName("wsfs-files-")) + + // Ensure directory exists, but doesn't exist YET! + // Otherwise we could inadvertently remove a directory that already exists on cleanup. + t.Logf("mkdir %s", path) + err = w.Workspace.MkdirsByPath(ctx, path) + require.NoError(t, err) + + // Remove test directory on test completion. + t.Cleanup(func() { + t.Logf("rm -rf %s", path) + err := w.Workspace.Delete(ctx, workspace.Delete{ + Path: path, + Recursive: true, + }) + if err == nil || apierr.IsMissing(err) { + return + } + t.Logf("unable to remove temporary workspace path %s: %#v", path, err) + }) + + return &workspaceTestdata{ + root: path, + t: t, + client: w, + } +} + +func (w *workspaceTestdata) RootPath() string { + return w.root +} + +func (w *workspaceTestdata) AddFile(name string, content string) { + path := path.Join(w.root, name) + ctx := context.Background() + + // url path for uploading file API + urlPath := fmt.Sprintf( + "/api/2.0/workspace-files/import-file/%s?overwrite=true", + url.PathEscape(strings.TrimLeft(path, "/")), + ) + + // initialize API client + apiClient, err := client.New(w.client.Config) + require.NoError(w.t, err) + + // Make API request + err = apiClient.Do(ctx, http.MethodPost, urlPath, content, nil) + require.NoError(w.t, err) +} + +func (w *workspaceTestdata) Mkdir(name string) { + ctx := context.Background() + path := path.Join(w.root, name) + err := w.client.Workspace.MkdirsByPath(ctx, path) + require.NoError(w.t, err) +} diff --git a/libs/filer/filer.go b/libs/filer/filer.go index e8420b81..11b92296 100644 --- a/libs/filer/filer.go +++ b/libs/filer/filer.go @@ -59,4 +59,7 @@ type Filer interface { // Return contents of directory at `path` ReadDir(ctx context.Context, path string) ([]FileInfo, error) + + // Creates directory at `path`, creating any intermediate directories as required + Mkdir(ctx context.Context, path string) error } diff --git a/libs/filer/workspace_files_client.go b/libs/filer/workspace_files_client.go index 2203ff9b..a6f0c56e 100644 --- a/libs/filer/workspace_files_client.go +++ b/libs/filer/workspace_files_client.go @@ -154,3 +154,13 @@ func (w *WorkspaceFilesClient) ReadDir(ctx context.Context, name string) ([]File } return info, nil } + +func (w *WorkspaceFilesClient) Mkdir(ctx context.Context, name string) error { + dirPath, err := w.root.Join(name) + if err != nil { + return err + } + return w.workspaceClient.Workspace.Mkdirs(ctx, workspace.Mkdirs{ + Path: dirPath, + }) +}