Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions internal/acc/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ package acc

import (
"context"
"fmt"
"os"
"testing"

"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/apierr"
"github.com/databricks/databricks-sdk-go/service/compute"
"github.com/databricks/databricks-sdk-go/service/workspace"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -94,3 +97,30 @@ func (t *WorkspaceT) RunPython(code string) (string, error) {
require.True(t, ok, "unexpected type %T", results.Data)
return output, nil
}

func (t *WorkspaceT) TemporaryWorkspaceDir(name ...string) string {
ctx := context.Background()
me, err := t.W.CurrentUser.Me(ctx)
require.NoError(t, err)

basePath := fmt.Sprintf("/Users/%s/%s", me.UserName, RandomName(name...))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be /Workspace/Users/?


t.Logf("Creating %s", basePath)
err = t.W.Workspace.MkdirsByPath(ctx, basePath)
require.NoError(t, err)

// Remove test directory on test completion.
t.Cleanup(func() {
t.Logf("Removing %s", basePath)
err := t.W.Workspace.Delete(ctx, workspace.Delete{
Path: basePath,
Recursive: true,
})
if err == nil || apierr.IsMissing(err) {
return
}
t.Logf("Unable to remove temporary workspace directory %s: %#v", basePath, err)
})

return basePath
}
110 changes: 110 additions & 0 deletions internal/dashboard_assumptions_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package internal

import (
"encoding/base64"
"testing"

"github.com/databricks/cli/internal/acc"
"github.com/databricks/cli/libs/dyn"
"github.com/databricks/cli/libs/dyn/convert"
"github.com/databricks/cli/libs/dyn/merge"
"github.com/databricks/databricks-sdk-go/apierr"
"github.com/databricks/databricks-sdk-go/service/dashboards"
"github.com/databricks/databricks-sdk-go/service/workspace"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// Verify that importing a dashboard through the Workspace API retains the identity of the underying resource,
// as well as properties exclusively accessible through the dashboards API.
func TestAccDashboardAssumptions_WorkspaceImport(t *testing.T) {
ctx, wt := acc.WorkspaceTest(t)

t.Parallel()

dashboardName := "New Dashboard"
dashboardPayload := []byte(`{"pages":[{"name":"2506f97a","displayName":"New Page"}]}`)
warehouseId := acc.GetEnvOrSkipTest(t, "TEST_DEFAULT_WAREHOUSE_ID")

dir := wt.TemporaryWorkspaceDir("dashboard-assumptions-")

dashboard, err := wt.W.Lakeview.Create(ctx, dashboards.CreateDashboardRequest{
DisplayName: dashboardName,
ParentPath: dir,
SerializedDashboard: string(dashboardPayload),
WarehouseId: warehouseId,
})
require.NoError(t, err)
t.Logf("Dashboard ID (per Lakeview API): %s", dashboard.DashboardId)

// Overwrite the dashboard via the workspace API.
{
err := wt.W.Workspace.Import(ctx, workspace.Import{
Format: workspace.ImportFormatAuto,
Path: dashboard.Path,
Content: base64.StdEncoding.EncodeToString(dashboardPayload),
Overwrite: true,
})
require.NoError(t, err)
}

// Cross-check consistency with the workspace object.
{
obj, err := wt.W.Workspace.GetStatusByPath(ctx, dashboard.Path)
require.NoError(t, err)

// Confirm that the resource ID included in the response is equal to the dashboard ID.
require.Equal(t, dashboard.DashboardId, obj.ResourceId)
t.Logf("Dashboard ID (per workspace object status): %s", obj.ResourceId)
}

// Try to overwrite the dashboard via the Lakeview API (and expect failure).
{
_, err := wt.W.Lakeview.Create(ctx, dashboards.CreateDashboardRequest{
DisplayName: dashboardName,
ParentPath: dir,
SerializedDashboard: string(dashboardPayload),
})
require.ErrorIs(t, err, apierr.ErrResourceAlreadyExists)
}

// Retrieve the dashboard object and confirm that only select fields were updated by the import.
{
previousDashboard := dashboard
currentDashboard, err := wt.W.Lakeview.Get(ctx, dashboards.GetDashboardRequest{
DashboardId: dashboard.DashboardId,
})
require.NoError(t, err)

// Convert the dashboard object to a [dyn.Value] to make comparison easier.
previous, err := convert.FromTyped(previousDashboard, dyn.NilValue)
require.NoError(t, err)
current, err := convert.FromTyped(currentDashboard, dyn.NilValue)
require.NoError(t, err)

// Collect updated paths.
var updatedFieldPaths []string
_, err = merge.Override(previous, current, merge.OverrideVisitor{
VisitDelete: func(basePath dyn.Path, left dyn.Value) error {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is smart :)

assert.Fail(t, "unexpected delete operation")
return nil
},
VisitInsert: func(basePath dyn.Path, right dyn.Value) (dyn.Value, error) {
assert.Fail(t, "unexpected insert operation")
return right, nil
},
VisitUpdate: func(basePath dyn.Path, left dyn.Value, right dyn.Value) (dyn.Value, error) {
updatedFieldPaths = append(updatedFieldPaths, basePath.String())
return right, nil
},
})
require.NoError(t, err)

// Confirm that only the expected fields have been updated.
assert.ElementsMatch(t, []string{
"etag",
"update_time",
}, updatedFieldPaths)
}
}