Skip to content

Commit c91ec1b

Browse files
bundle/statemgmt: unit-test LoadStateFromDMS with a mock workspace client
Adds three table-style tests: * NoDeploymentID — empty b.DeploymentID short-circuits without any RPC and leaves the in-memory state DB safe to read (ExportState returns empty). * PopulatesFromList — resources land under "resources.<key>" with their ID and State preserved verbatim; the nil-State path is exercised too. * ListError — DMS-side failures are wrapped with the package-level prefix so callers can tell them apart from local-filesystem errors. Together these cover the three branches a code reviewer would otherwise have to read the source to verify.
1 parent d5b2307 commit c91ec1b

1 file changed

Lines changed: 100 additions & 0 deletions

File tree

bundle/statemgmt/state_dms_test.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package statemgmt
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
"testing"
7+
8+
"github.com/databricks/cli/bundle"
9+
"github.com/databricks/cli/bundle/config"
10+
"github.com/databricks/databricks-sdk-go/experimental/mocks"
11+
sdkbundle "github.com/databricks/databricks-sdk-go/service/bundle"
12+
"github.com/stretchr/testify/assert"
13+
"github.com/stretchr/testify/mock"
14+
"github.com/stretchr/testify/require"
15+
)
16+
17+
// newTestBundle returns a Bundle wired with a mock workspace client and the
18+
// workspace fields that StateFilenameDirect needs (CurrentUser, RootPath).
19+
func newTestBundle(t *testing.T) (*bundle.Bundle, *mocks.MockWorkspaceClient) {
20+
b := &bundle.Bundle{
21+
BundleRootPath: t.TempDir(),
22+
Config: config.Root{
23+
Bundle: config.Bundle{Target: "default"},
24+
Workspace: config.Workspace{
25+
StatePath: "/Workspace/Users/me@example.com/.bundle/test/default/state",
26+
},
27+
},
28+
}
29+
m := mocks.NewMockWorkspaceClient(t)
30+
b.SetWorkpaceClient(m.WorkspaceClient)
31+
return b, m
32+
}
33+
34+
// TestLoadStateFromDMS_NoDeploymentID covers the "DMS enabled but the
35+
// workspace has no managed_service.json yet" case: the state DB should be
36+
// initialised empty (no panic on later ExportState) and no API call should be
37+
// made.
38+
func TestLoadStateFromDMS_NoDeploymentID(t *testing.T) {
39+
b, _ := newTestBundle(t)
40+
// b.DeploymentID stays empty.
41+
42+
err := LoadStateFromDMS(t.Context(), b)
43+
require.NoError(t, err)
44+
45+
// State is initialised but empty — Export should succeed and return no entries.
46+
got := b.DeploymentBundle.ExportState(t.Context())
47+
assert.Empty(t, got)
48+
}
49+
50+
// TestLoadStateFromDMS_PopulatesFromList confirms that resources reported by
51+
// ListResources land in the in-memory state DB under the fully-qualified
52+
// "resources.<key>" form, and that per-resource State payloads are preserved
53+
// verbatim.
54+
func TestLoadStateFromDMS_PopulatesFromList(t *testing.T) {
55+
b, m := newTestBundle(t)
56+
b.DeploymentID = "dep-123"
57+
58+
jobState := json.RawMessage(`{"name":"my-job","max_concurrent_runs":1}`)
59+
mockBundle := m.GetMockBundleAPI()
60+
mockBundle.EXPECT().
61+
ListResourcesAll(mock.Anything, sdkbundle.ListResourcesRequest{
62+
Parent: "deployments/dep-123",
63+
}).
64+
Return([]sdkbundle.Resource{
65+
{ResourceKey: "jobs.foo", ResourceId: "1001", State: &jobState},
66+
// State omitted — exercises the nil-state path.
67+
{ResourceKey: "pipelines.bar", ResourceId: "p-1"},
68+
}, nil)
69+
70+
err := LoadStateFromDMS(t.Context(), b)
71+
require.NoError(t, err)
72+
73+
job, ok := b.DeploymentBundle.StateDB.GetResourceEntry("resources.jobs.foo")
74+
require.True(t, ok)
75+
assert.Equal(t, "1001", job.ID)
76+
assert.JSONEq(t, string(jobState), string(job.State))
77+
78+
pipeline, ok := b.DeploymentBundle.StateDB.GetResourceEntry("resources.pipelines.bar")
79+
require.True(t, ok)
80+
assert.Equal(t, "p-1", pipeline.ID)
81+
assert.Empty(t, pipeline.State)
82+
}
83+
84+
// TestLoadStateFromDMS_ListError checks that an underlying API failure is
85+
// wrapped and surfaced rather than swallowed (otherwise the deploy would
86+
// proceed against an empty in-memory view and treat everything as a create).
87+
func TestLoadStateFromDMS_ListError(t *testing.T) {
88+
b, m := newTestBundle(t)
89+
b.DeploymentID = "dep-456"
90+
91+
mockBundle := m.GetMockBundleAPI()
92+
mockBundle.EXPECT().
93+
ListResourcesAll(mock.Anything, mock.Anything).
94+
Return(nil, errors.New("boom"))
95+
96+
err := LoadStateFromDMS(t.Context(), b)
97+
require.Error(t, err)
98+
assert.ErrorContains(t, err, "boom")
99+
assert.ErrorContains(t, err, "failed to list resources from deployment metadata service")
100+
}

0 commit comments

Comments
 (0)