diff --git a/NOTICE b/NOTICE
index bce870f16b8..7c7eb7db461 100644
--- a/NOTICE
+++ b/NOTICE
@@ -16,6 +16,10 @@ go-ini/ini - https://github.com/go-ini/ini
 Copyright ini authors
 License - https://github.com/go-ini/ini/blob/main/LICENSE
 
+uber-go/mock - https://go.uber.org/mock
+Copyright Google Inc.
+License - https://github.com/uber-go/mock/blob/main/LICENSE
+
 —--
 
 This software contains code from the following open source projects, licensed under the MPL 2.0 license:
diff --git a/bundle/deploy/terraform/filer.go b/bundle/deploy/terraform/filer.go
new file mode 100644
index 00000000000..b1fa5a1bd09
--- /dev/null
+++ b/bundle/deploy/terraform/filer.go
@@ -0,0 +1,14 @@
+package terraform
+
+import (
+	"github.com/databricks/cli/bundle"
+	"github.com/databricks/cli/libs/filer"
+)
+
+// filerFunc is a function that returns a filer.Filer.
+type filerFunc func(b *bundle.Bundle) (filer.Filer, error)
+
+// stateFiler returns a filer.Filer that can be used to read/write state files.
+func stateFiler(b *bundle.Bundle) (filer.Filer, error) {
+	return filer.NewWorkspaceFilesClient(b.WorkspaceClient(), b.Config.Workspace.StatePath)
+}
diff --git a/bundle/deploy/terraform/state_pull.go b/bundle/deploy/terraform/state_pull.go
index 6dd12ccfc69..14e8ecf1212 100644
--- a/bundle/deploy/terraform/state_pull.go
+++ b/bundle/deploy/terraform/state_pull.go
@@ -1,6 +1,7 @@
 package terraform
 
 import (
+	"bytes"
 	"context"
 	"errors"
 	"io"
@@ -13,14 +14,38 @@ import (
 	"github.com/databricks/cli/libs/log"
 )
 
-type statePull struct{}
+type statePull struct {
+	filerFunc
+}
 
 func (l *statePull) Name() string {
 	return "terraform:state-pull"
 }
 
+func (l *statePull) remoteState(ctx context.Context, f filer.Filer) (*bytes.Buffer, error) {
+	// Download state file from filer to local cache directory.
+	remote, err := f.Read(ctx, TerraformStateFileName)
+	if err != nil {
+		// On first deploy this state file doesn't yet exist.
+		if errors.Is(err, fs.ErrNotExist) {
+			return nil, nil
+		}
+		return nil, err
+	}
+
+	defer remote.Close()
+
+	var buf bytes.Buffer
+	_, err = io.Copy(&buf, remote)
+	if err != nil {
+		return nil, err
+	}
+
+	return &buf, nil
+}
+
 func (l *statePull) Apply(ctx context.Context, b *bundle.Bundle) error {
-	f, err := filer.NewWorkspaceFilesClient(b.WorkspaceClient(), b.Config.Workspace.StatePath)
+	f, err := l.filerFunc(b)
 	if err != nil {
 		return err
 	}
@@ -32,15 +57,15 @@ func (l *statePull) Apply(ctx context.Context, b *bundle.Bundle) error {
 
 	// Download state file from filer to local cache directory.
 	log.Infof(ctx, "Opening remote state file")
-	remote, err := f.Read(ctx, TerraformStateFileName)
+	remote, err := l.remoteState(ctx, f)
 	if err != nil {
-		// On first deploy this state file doesn't yet exist.
-		if errors.Is(err, fs.ErrNotExist) {
-			log.Infof(ctx, "Remote state file does not exist")
-			return nil
-		}
+		log.Infof(ctx, "Unable to open remote state file: %s", err)
 		return err
 	}
+	if remote == nil {
+		log.Infof(ctx, "Remote state file does not exist")
+		return nil
+	}
 
 	// Expect the state file to live under dir.
 	local, err := os.OpenFile(filepath.Join(dir, TerraformStateFileName), os.O_CREATE|os.O_RDWR, 0600)
@@ -49,7 +74,7 @@ func (l *statePull) Apply(ctx context.Context, b *bundle.Bundle) error {
 	}
 	defer local.Close()
 
-	if !IsLocalStateStale(local, remote) {
+	if !IsLocalStateStale(local, bytes.NewReader(remote.Bytes())) {
 		log.Infof(ctx, "Local state is the same or newer, ignoring remote state")
 		return nil
 	}
@@ -60,7 +85,7 @@ func (l *statePull) Apply(ctx context.Context, b *bundle.Bundle) error {
 
 	// Write file to disk.
 	log.Infof(ctx, "Writing remote state file to local cache directory")
-	_, err = io.Copy(local, remote)
+	_, err = io.Copy(local, bytes.NewReader(remote.Bytes()))
 	if err != nil {
 		return err
 	}
@@ -69,5 +94,5 @@ func (l *statePull) Apply(ctx context.Context, b *bundle.Bundle) error {
 }
 
 func StatePull() bundle.Mutator {
-	return &statePull{}
+	return &statePull{stateFiler}
 }
diff --git a/bundle/deploy/terraform/state_pull_test.go b/bundle/deploy/terraform/state_pull_test.go
new file mode 100644
index 00000000000..60eb5d90c57
--- /dev/null
+++ b/bundle/deploy/terraform/state_pull_test.go
@@ -0,0 +1,128 @@
+package terraform
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"io"
+	"io/fs"
+	"os"
+	"testing"
+
+	"github.com/databricks/cli/bundle"
+	"github.com/databricks/cli/bundle/config"
+	mock "github.com/databricks/cli/internal/mocks/libs/filer"
+	"github.com/databricks/cli/libs/filer"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"go.uber.org/mock/gomock"
+)
+
+func mockStateFilerForPull(t *testing.T, contents map[string]int, merr error) filer.Filer {
+	buf, err := json.Marshal(contents)
+	require.NoError(t, err)
+
+	ctrl := gomock.NewController(t)
+	mock := mock.NewMockFiler(ctrl)
+	mock.
+		EXPECT().
+		Read(gomock.Any(), gomock.Eq(TerraformStateFileName)).
+		Return(io.NopCloser(bytes.NewReader(buf)), merr).
+		Times(1)
+	return mock
+}
+
+func statePullTestBundle(t *testing.T) *bundle.Bundle {
+	return &bundle.Bundle{
+		Config: config.Root{
+			Bundle: config.Bundle{
+				Target: "default",
+			},
+			Path: t.TempDir(),
+		},
+	}
+}
+
+func TestStatePullLocalMissingRemoteMissing(t *testing.T) {
+	m := &statePull{
+		identityFiler(mockStateFilerForPull(t, nil, os.ErrNotExist)),
+	}
+
+	ctx := context.Background()
+	b := statePullTestBundle(t)
+	err := bundle.Apply(ctx, b, m)
+	assert.NoError(t, err)
+
+	// Confirm that no local state file has been written.
+	_, err = os.Stat(localStateFile(t, ctx, b))
+	assert.ErrorIs(t, err, fs.ErrNotExist)
+}
+
+func TestStatePullLocalMissingRemotePresent(t *testing.T) {
+	m := &statePull{
+		identityFiler(mockStateFilerForPull(t, map[string]int{"serial": 5}, nil)),
+	}
+
+	ctx := context.Background()
+	b := statePullTestBundle(t)
+	err := bundle.Apply(ctx, b, m)
+	assert.NoError(t, err)
+
+	// Confirm that the local state file has been updated.
+	localState := readLocalState(t, ctx, b)
+	assert.Equal(t, map[string]int{"serial": 5}, localState)
+}
+
+func TestStatePullLocalStale(t *testing.T) {
+	m := &statePull{
+		identityFiler(mockStateFilerForPull(t, map[string]int{"serial": 5}, nil)),
+	}
+
+	ctx := context.Background()
+	b := statePullTestBundle(t)
+
+	// Write a stale local state file.
+	writeLocalState(t, ctx, b, map[string]int{"serial": 4})
+	err := bundle.Apply(ctx, b, m)
+	assert.NoError(t, err)
+
+	// Confirm that the local state file has been updated.
+	localState := readLocalState(t, ctx, b)
+	assert.Equal(t, map[string]int{"serial": 5}, localState)
+}
+
+func TestStatePullLocalEqual(t *testing.T) {
+	m := &statePull{
+		identityFiler(mockStateFilerForPull(t, map[string]int{"serial": 5, "some_other_key": 123}, nil)),
+	}
+
+	ctx := context.Background()
+	b := statePullTestBundle(t)
+
+	// Write a local state file with the same serial as the remote.
+	writeLocalState(t, ctx, b, map[string]int{"serial": 5})
+	err := bundle.Apply(ctx, b, m)
+	assert.NoError(t, err)
+
+	// Confirm that the local state file has not been updated.
+	localState := readLocalState(t, ctx, b)
+	assert.Equal(t, map[string]int{"serial": 5}, localState)
+}
+
+func TestStatePullLocalNewer(t *testing.T) {
+	m := &statePull{
+		identityFiler(mockStateFilerForPull(t, map[string]int{"serial": 5, "some_other_key": 123}, nil)),
+	}
+
+	ctx := context.Background()
+	b := statePullTestBundle(t)
+
+	// Write a local state file with a newer serial as the remote.
+	writeLocalState(t, ctx, b, map[string]int{"serial": 6})
+	err := bundle.Apply(ctx, b, m)
+	assert.NoError(t, err)
+
+	// Confirm that the local state file has not been updated.
+	localState := readLocalState(t, ctx, b)
+	assert.Equal(t, map[string]int{"serial": 6}, localState)
+}
diff --git a/bundle/deploy/terraform/state_push.go b/bundle/deploy/terraform/state_push.go
index ae1d8b8b3e1..30a43596206 100644
--- a/bundle/deploy/terraform/state_push.go
+++ b/bundle/deploy/terraform/state_push.go
@@ -10,14 +10,16 @@ import (
 	"github.com/databricks/cli/libs/log"
 )
 
-type statePush struct{}
+type statePush struct {
+	filerFunc
+}
 
 func (l *statePush) Name() string {
 	return "terraform:state-push"
 }
 
 func (l *statePush) Apply(ctx context.Context, b *bundle.Bundle) error {
-	f, err := filer.NewWorkspaceFilesClient(b.WorkspaceClient(), b.Config.Workspace.StatePath)
+	f, err := l.filerFunc(b)
 	if err != nil {
 		return err
 	}
@@ -45,5 +47,5 @@ func (l *statePush) Apply(ctx context.Context, b *bundle.Bundle) error {
 }
 
 func StatePush() bundle.Mutator {
-	return &statePush{}
+	return &statePush{stateFiler}
 }
diff --git a/bundle/deploy/terraform/state_push_test.go b/bundle/deploy/terraform/state_push_test.go
new file mode 100644
index 00000000000..4167b3cb985
--- /dev/null
+++ b/bundle/deploy/terraform/state_push_test.go
@@ -0,0 +1,63 @@
+package terraform
+
+import (
+	"context"
+	"encoding/json"
+	"io"
+	"testing"
+
+	"github.com/databricks/cli/bundle"
+	"github.com/databricks/cli/bundle/config"
+	mock "github.com/databricks/cli/internal/mocks/libs/filer"
+	"github.com/databricks/cli/libs/filer"
+	"github.com/stretchr/testify/assert"
+	"go.uber.org/mock/gomock"
+)
+
+func mockStateFilerForPush(t *testing.T, fn func(body io.Reader)) filer.Filer {
+	ctrl := gomock.NewController(t)
+	mock := mock.NewMockFiler(ctrl)
+	mock.
+		EXPECT().
+		Write(gomock.Any(), gomock.Any(), gomock.Any(), filer.CreateParentDirectories, filer.OverwriteIfExists).
+		Do(func(ctx context.Context, path string, reader io.Reader, mode ...filer.WriteMode) error {
+			fn(reader)
+			return nil
+		}).
+		Return(nil).
+		Times(1)
+	return mock
+}
+
+func statePushTestBundle(t *testing.T) *bundle.Bundle {
+	return &bundle.Bundle{
+		Config: config.Root{
+			Bundle: config.Bundle{
+				Target: "default",
+			},
+			Path: t.TempDir(),
+		},
+	}
+}
+
+func TestStatePush(t *testing.T) {
+	mock := mockStateFilerForPush(t, func(body io.Reader) {
+		dec := json.NewDecoder(body)
+		var contents map[string]int
+		err := dec.Decode(&contents)
+		assert.NoError(t, err)
+		assert.Equal(t, map[string]int{"serial": 4}, contents)
+	})
+
+	m := &statePush{
+		identityFiler(mock),
+	}
+
+	ctx := context.Background()
+	b := statePushTestBundle(t)
+
+	// Write a stale local state file.
+	writeLocalState(t, ctx, b, map[string]int{"serial": 4})
+	err := bundle.Apply(ctx, b, m)
+	assert.NoError(t, err)
+}
diff --git a/bundle/deploy/terraform/state_test.go b/bundle/deploy/terraform/state_test.go
new file mode 100644
index 00000000000..ee15b953bf6
--- /dev/null
+++ b/bundle/deploy/terraform/state_test.go
@@ -0,0 +1,48 @@
+package terraform
+
+import (
+	"context"
+	"encoding/json"
+	"os"
+	"path/filepath"
+	"testing"
+
+	"github.com/databricks/cli/bundle"
+	"github.com/databricks/cli/libs/filer"
+	"github.com/stretchr/testify/require"
+)
+
+// identityFiler returns a filerFunc that returns the specified filer.
+func identityFiler(f filer.Filer) filerFunc {
+	return func(_ *bundle.Bundle) (filer.Filer, error) {
+		return f, nil
+	}
+}
+
+func localStateFile(t *testing.T, ctx context.Context, b *bundle.Bundle) string {
+	dir, err := Dir(ctx, b)
+	require.NoError(t, err)
+	return filepath.Join(dir, TerraformStateFileName)
+}
+
+func readLocalState(t *testing.T, ctx context.Context, b *bundle.Bundle) map[string]int {
+	f, err := os.Open(localStateFile(t, ctx, b))
+	require.NoError(t, err)
+	defer f.Close()
+
+	var contents map[string]int
+	dec := json.NewDecoder(f)
+	err = dec.Decode(&contents)
+	require.NoError(t, err)
+	return contents
+}
+
+func writeLocalState(t *testing.T, ctx context.Context, b *bundle.Bundle, contents map[string]int) {
+	f, err := os.Create(localStateFile(t, ctx, b))
+	require.NoError(t, err)
+	defer f.Close()
+
+	enc := json.NewEncoder(f)
+	err = enc.Encode(contents)
+	require.NoError(t, err)
+}
diff --git a/bundle/deploy/terraform/util_test.go b/bundle/deploy/terraform/util_test.go
index 1ddfbab3515..4f2cf291879 100644
--- a/bundle/deploy/terraform/util_test.go
+++ b/bundle/deploy/terraform/util_test.go
@@ -2,92 +2,39 @@ package terraform
 
 import (
 	"fmt"
-	"io"
+	"strings"
 	"testing"
 	"testing/iotest"
 
 	"github.com/stretchr/testify/assert"
 )
 
-type mockedReader struct {
-	content string
-}
-
-func (r *mockedReader) Read(p []byte) (n int, err error) {
-	content := []byte(r.content)
-	n = copy(p, content)
-	return n, io.EOF
-}
-
 func TestLocalStateIsNewer(t *testing.T) {
-	local := &mockedReader{content: `
-{
-	"serial": 5
-}
-`}
-	remote := &mockedReader{content: `
-{
-	"serial": 4
-}
-`}
-
-	stale := IsLocalStateStale(local, remote)
-
-	assert.False(t, stale)
+	local := strings.NewReader(`{"serial": 5}`)
+	remote := strings.NewReader(`{"serial": 4}`)
+	assert.False(t, IsLocalStateStale(local, remote))
 }
 
 func TestLocalStateIsOlder(t *testing.T) {
-	local := &mockedReader{content: `
-{
-	"serial": 5
-}
-`}
-	remote := &mockedReader{content: `
-{
-	"serial": 6
-}
-`}
-
-	stale := IsLocalStateStale(local, remote)
-	assert.True(t, stale)
+	local := strings.NewReader(`{"serial": 5}`)
+	remote := strings.NewReader(`{"serial": 6}`)
+	assert.True(t, IsLocalStateStale(local, remote))
 }
 
 func TestLocalStateIsTheSame(t *testing.T) {
-	local := &mockedReader{content: `
-{
-	"serial": 5
-}
-`}
-	remote := &mockedReader{content: `
-{
-	"serial": 5
-}
-`}
-
-	stale := IsLocalStateStale(local, remote)
-	assert.False(t, stale)
+	local := strings.NewReader(`{"serial": 5}`)
+	remote := strings.NewReader(`{"serial": 5}`)
+	assert.False(t, IsLocalStateStale(local, remote))
 }
 
 func TestLocalStateMarkStaleWhenFailsToLoad(t *testing.T) {
 	local := iotest.ErrReader(fmt.Errorf("Random error"))
-	remote := &mockedReader{content: `
-{
-	"serial": 5
-}
-`}
-
-	stale := IsLocalStateStale(local, remote)
-	assert.True(t, stale)
+	remote := strings.NewReader(`{"serial": 5}`)
+	assert.True(t, IsLocalStateStale(local, remote))
 }
 
 func TestLocalStateMarkNonStaleWhenRemoteFailsToLoad(t *testing.T) {
-	local := &mockedReader{content: `
-{
-	"serial": 5
-}
-`}
+	local := strings.NewReader(`{"serial": 5}`)
 	remote := iotest.ErrReader(fmt.Errorf("Random error"))
-
-	stale := IsLocalStateStale(local, remote)
-	assert.False(t, stale)
+	assert.False(t, IsLocalStateStale(local, remote))
 }
diff --git a/go.mod b/go.mod
index 7cef4cd4e06..f703d8b06d4 100644
--- a/go.mod
+++ b/go.mod
@@ -31,7 +31,10 @@ require (
 	gopkg.in/ini.v1 v1.67.0 // Apache 2.0
 )
 
-require gopkg.in/yaml.v3 v3.0.1
+require (
+	go.uber.org/mock v0.3.0
+	gopkg.in/yaml.v3 v3.0.1
+)
 
 require (
 	cloud.google.com/go/compute v1.23.1 // indirect
diff --git a/go.sum b/go.sum
index 25409bd6f97..cff48c8fc30 100644
--- a/go.sum
+++ b/go.sum
@@ -158,6 +158,8 @@ github.com/zclconf/go-cty v1.14.1 h1:t9fyA35fwjjUMcmL5hLER+e/rEPqrbCK1/OSE4SI9KA
 github.com/zclconf/go-cty v1.14.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
 go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
 go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
+go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
+go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
diff --git a/internal/bundle/bundles/basic/databricks_template_schema.json b/internal/bundle/bundles/basic/databricks_template_schema.json
new file mode 100644
index 00000000000..c1c5cf12ebe
--- /dev/null
+++ b/internal/bundle/bundles/basic/databricks_template_schema.json
@@ -0,0 +1,16 @@
+{
+    "properties": {
+        "unique_id": {
+            "type": "string",
+            "description": "Unique ID for job name"
+        },
+        "spark_version": {
+            "type": "string",
+            "description": "Spark version used for job cluster"
+        },
+        "node_type_id": {
+            "type": "string",
+            "description": "Node type id for job cluster"
+        }
+    }
+}
diff --git a/internal/bundle/bundles/basic/template/databricks.yml.tmpl b/internal/bundle/bundles/basic/template/databricks.yml.tmpl
new file mode 100644
index 00000000000..a88cbd30eed
--- /dev/null
+++ b/internal/bundle/bundles/basic/template/databricks.yml.tmpl
@@ -0,0 +1,18 @@
+bundle:
+  name: basic
+
+workspace:
+  root_path: "~/.bundle/{{.unique_id}}"
+
+resources:
+  jobs:
+    foo:
+      name: test-job-basic-{{.unique_id}}
+      tasks:
+        - task_key: my_notebook_task
+          new_cluster:
+            num_workers: 1
+            spark_version: "{{.spark_version}}"
+            node_type_id: "{{.node_type_id}}"
+          spark_python_task:
+            python_file: ./hello_world.py
diff --git a/internal/bundle/bundles/basic/template/hello_world.py b/internal/bundle/bundles/basic/template/hello_world.py
new file mode 100644
index 00000000000..f301245e242
--- /dev/null
+++ b/internal/bundle/bundles/basic/template/hello_world.py
@@ -0,0 +1 @@
+print("Hello World!")
diff --git a/internal/bundle/local_state_staleness_test.go b/internal/bundle/local_state_staleness_test.go
new file mode 100644
index 00000000000..06cfe0e0d9b
--- /dev/null
+++ b/internal/bundle/local_state_staleness_test.go
@@ -0,0 +1,70 @@
+package bundle
+
+import (
+	"context"
+	"testing"
+
+	"github.com/databricks/cli/internal"
+	"github.com/databricks/databricks-sdk-go"
+	"github.com/databricks/databricks-sdk-go/listing"
+	"github.com/databricks/databricks-sdk-go/service/jobs"
+	"github.com/google/uuid"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestAccLocalStateStaleness(t *testing.T) {
+	env := internal.GetEnvOrSkipTest(t, "CLOUD_ENV")
+	t.Log(env)
+
+	w, err := databricks.NewWorkspaceClient()
+	require.NoError(t, err)
+
+	// The approach for this test is as follows:
+	// 1) First deploy of bundle instance A
+	// 2) First deploy of bundle instance B
+	// 3) Second deploy of bundle instance A
+	// Because of deploy (2), the locally cached state of bundle instance A should be stale.
+	// Then for deploy (3), it must use the remote state over the stale local state.
+
+	nodeTypeId := internal.GetNodeTypeId(env)
+	uniqueId := uuid.New().String()
+	initialize := func() string {
+		root, err := initTestTemplate(t, "basic", map[string]any{
+			"unique_id":     uniqueId,
+			"node_type_id":  nodeTypeId,
+			"spark_version": "13.2.x-snapshot-scala2.12",
+		})
+		require.NoError(t, err)
+
+		t.Cleanup(func() {
+			err = destroyBundle(t, root)
+			require.NoError(t, err)
+		})
+
+		return root
+	}
+
+	bundleA := initialize()
+	bundleB := initialize()
+
+	// 1) Deploy bundle A
+	err = deployBundle(t, bundleA)
+	require.NoError(t, err)
+
+	// 2) Deploy bundle B
+	err = deployBundle(t, bundleB)
+	require.NoError(t, err)
+
+	// 3) Deploy bundle A again
+	err = deployBundle(t, bundleA)
+	require.NoError(t, err)
+
+	// Assert that there is only a single job in the workspace corresponding to this bundle.
+	iter := w.Jobs.List(context.Background(), jobs.ListJobsRequest{
+		Name: "test-job-basic-" + uniqueId,
+	})
+	jobs, err := listing.ToSlice(context.Background(), iter)
+	require.NoError(t, err)
+	assert.Len(t, jobs, 1)
+}
diff --git a/internal/mocks/README.md b/internal/mocks/README.md
new file mode 100644
index 00000000000..231bbfaa46d
--- /dev/null
+++ b/internal/mocks/README.md
@@ -0,0 +1,7 @@
+# Interface mocking
+
+Use this directory to store mocks for interfaces in this repository.
+
+Please use the same package structure for the mocks as the interface it is mocking.
+
+See https://github.com/uber-go/mock for more information on how to generate mocks.
diff --git a/internal/mocks/libs/filer/filer_mock.go b/internal/mocks/libs/filer/filer_mock.go
new file mode 100644
index 00000000000..ef00976a2ac
--- /dev/null
+++ b/internal/mocks/libs/filer/filer_mock.go
@@ -0,0 +1,139 @@
+// Code generated by MockGen. DO NOT EDIT.
+// Source: github.com/databricks/cli/libs/filer (interfaces: Filer)
+//
+// Generated by this command:
+//
+//	mockgen -destination filer_mock.go github.com/databricks/cli/libs/filer Filer
+//
+// Package mock_filer is a generated GoMock package.
+package mock_filer
+
+import (
+	context "context"
+	io "io"
+	fs "io/fs"
+	reflect "reflect"
+
+	filer "github.com/databricks/cli/libs/filer"
+	gomock "go.uber.org/mock/gomock"
+)
+
+// MockFiler is a mock of Filer interface.
+type MockFiler struct {
+	ctrl     *gomock.Controller
+	recorder *MockFilerMockRecorder
+}
+
+// MockFilerMockRecorder is the mock recorder for MockFiler.
+type MockFilerMockRecorder struct {
+	mock *MockFiler
+}
+
+// NewMockFiler creates a new mock instance.
+func NewMockFiler(ctrl *gomock.Controller) *MockFiler {
+	mock := &MockFiler{ctrl: ctrl}
+	mock.recorder = &MockFilerMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockFiler) EXPECT() *MockFilerMockRecorder {
+	return m.recorder
+}
+
+// Delete mocks base method.
+func (m *MockFiler) Delete(arg0 context.Context, arg1 string, arg2 ...filer.DeleteMode) error {
+	m.ctrl.T.Helper()
+	varargs := []any{arg0, arg1}
+	for _, a := range arg2 {
+		varargs = append(varargs, a)
+	}
+	ret := m.ctrl.Call(m, "Delete", varargs...)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// Delete indicates an expected call of Delete.
+func (mr *MockFilerMockRecorder) Delete(arg0, arg1 any, arg2 ...any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	varargs := append([]any{arg0, arg1}, arg2...)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockFiler)(nil).Delete), varargs...)
+}
+
+// Mkdir mocks base method.
+func (m *MockFiler) Mkdir(arg0 context.Context, arg1 string) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Mkdir", arg0, arg1)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// Mkdir indicates an expected call of Mkdir.
+func (mr *MockFilerMockRecorder) Mkdir(arg0, arg1 any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Mkdir", reflect.TypeOf((*MockFiler)(nil).Mkdir), arg0, arg1)
+}
+
+// Read mocks base method.
+func (m *MockFiler) Read(arg0 context.Context, arg1 string) (io.ReadCloser, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Read", arg0, arg1)
+	ret0, _ := ret[0].(io.ReadCloser)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// Read indicates an expected call of Read.
+func (mr *MockFilerMockRecorder) Read(arg0, arg1 any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockFiler)(nil).Read), arg0, arg1)
+}
+
+// ReadDir mocks base method.
+func (m *MockFiler) ReadDir(arg0 context.Context, arg1 string) ([]fs.DirEntry, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "ReadDir", arg0, arg1)
+	ret0, _ := ret[0].([]fs.DirEntry)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// ReadDir indicates an expected call of ReadDir.
+func (mr *MockFilerMockRecorder) ReadDir(arg0, arg1 any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadDir", reflect.TypeOf((*MockFiler)(nil).ReadDir), arg0, arg1)
+}
+
+// Stat mocks base method.
+func (m *MockFiler) Stat(arg0 context.Context, arg1 string) (fs.FileInfo, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Stat", arg0, arg1)
+	ret0, _ := ret[0].(fs.FileInfo)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// Stat indicates an expected call of Stat.
+func (mr *MockFilerMockRecorder) Stat(arg0, arg1 any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stat", reflect.TypeOf((*MockFiler)(nil).Stat), arg0, arg1)
+}
+
+// Write mocks base method.
+func (m *MockFiler) Write(arg0 context.Context, arg1 string, arg2 io.Reader, arg3 ...filer.WriteMode) error {
+	m.ctrl.T.Helper()
+	varargs := []any{arg0, arg1, arg2}
+	for _, a := range arg3 {
+		varargs = append(varargs, a)
+	}
+	ret := m.ctrl.Call(m, "Write", varargs...)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// Write indicates an expected call of Write.
+func (mr *MockFilerMockRecorder) Write(arg0, arg1, arg2 any, arg3 ...any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	varargs := append([]any{arg0, arg1, arg2}, arg3...)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockFiler)(nil).Write), varargs...)
+}
diff --git a/libs/filer/filer.go b/libs/filer/filer.go
index 8267dc34315..c1c747c5462 100644
--- a/libs/filer/filer.go
+++ b/libs/filer/filer.go
@@ -10,14 +10,14 @@ import (
 type WriteMode int
 
 const (
-	OverwriteIfExists       WriteMode = iota
-	CreateParentDirectories           = iota << 1
+	OverwriteIfExists WriteMode = 1 << iota
+	CreateParentDirectories
 )
 
 type DeleteMode int
 
 const (
-	DeleteRecursively DeleteMode = iota
+	DeleteRecursively DeleteMode = 1 << iota
 )
 
 type FileAlreadyExistsError struct {
