databricks-cli/libs/template/reader_test.go

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

102 lines
2.7 KiB
Go
Raw Normal View History

Refactor `bundle init` (#2074) ## Summary of changes This PR introduces three new abstractions: 1. `Resolver`: Resolves which reader and writer to use for a template. 2. `Writer`: Writes a template project to disk. Prompts the user if necessary. 3. `Reader`: Reads a template specification from disk, built into the CLI or from GitHub. Introducing these abstractions helps decouple reading a template from writing it. When I tried adding telemetry for the `bundle init` command, I noticed that the code in `cmd/init.go` was getting convoluted and hard to test. A future change could have accidentally logged PII when a user initialised a custom template. Hedging against that risk is important here because we use a generic untyped `map<string, string>` representation in the backend to log telemetry for the `databricks bundle init`. Otherwise, we risk accidentally breaking our compliance with our centralization requirements. ### Details After this PR there are two classes of templates that can be initialized: 1. A `databricks` template: This could be a builtin template or a template outside the CLI like mlops-stacks, which is still owned and managed by Databricks. These templates log their telemetry arguments and template name. 2. A `custom` template: These are templates created by and managed by the end user. In these templates we do not log the template name and args. Instead a generic placeholder string of "custom" is logged in our telemetry system. NOTE: The functionality of the `databricks bundle init` command remains the same after this PR. Only the internal abstractions used are changed. ## Tests New unit tests. Existing golden and unit tests. Also a fair bit of manual testing.
2025-01-20 12:09:28 +00:00
package template
import (
"context"
"io/fs"
"path/filepath"
"testing"
"github.com/databricks/cli/internal/testutil"
"github.com/databricks/cli/libs/cmdio"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestBuiltInReader(t *testing.T) {
exists := []string{
"default-python",
"default-sql",
"dbt-sql",
"experimental-jobs-as-code",
}
for _, name := range exists {
t.Run(name, func(t *testing.T) {
r := &builtinReader{name: name}
fsys, err := r.FS(context.Background())
assert.NoError(t, err)
assert.NotNil(t, fsys)
// Assert file content returned is accurate and every template has a welcome
// message defined.
b, err := fs.ReadFile(fsys, "databricks_template_schema.json")
require.NoError(t, err)
assert.Contains(t, string(b), "welcome_message")
})
}
t.Run("doesnotexist", func(t *testing.T) {
r := &builtinReader{name: "doesnotexist"}
_, err := r.FS(context.Background())
assert.EqualError(t, err, "builtin template doesnotexist not found")
})
}
func TestGitUrlReader(t *testing.T) {
ctx := cmdio.MockDiscard(context.Background())
var args []string
numCalls := 0
cloneFunc := func(ctx context.Context, url, reference, targetPath string) error {
numCalls++
args = []string{url, reference, targetPath}
testutil.WriteFile(t, filepath.Join(targetPath, "a", "b", "c", "somefile"), "somecontent")
return nil
}
r := &gitReader{
gitUrl: "someurl",
cloneFunc: cloneFunc,
ref: "sometag",
templateDir: "a/b/c",
}
// Assert cloneFunc is called with the correct args.
fsys, err := r.FS(ctx)
require.NoError(t, err)
require.NotEmpty(t, r.tmpRepoDir)
assert.Equal(t, 1, numCalls)
assert.DirExists(t, r.tmpRepoDir)
assert.Equal(t, []string{"someurl", "sometag", r.tmpRepoDir}, args)
// Assert the fs returned is rooted at the templateDir.
b, err := fs.ReadFile(fsys, "somefile")
require.NoError(t, err)
assert.Equal(t, "somecontent", string(b))
// Assert second call to FS returns an error.
_, err = r.FS(ctx)
assert.ErrorContains(t, err, "FS called twice on git reader")
// Assert the downloaded repository is cleaned up.
_, err = fs.Stat(fsys, ".")
require.NoError(t, err)
r.Cleanup(ctx)
_, err = fs.Stat(fsys, ".")
assert.ErrorIs(t, err, fs.ErrNotExist)
}
func TestLocalReader(t *testing.T) {
tmpDir := t.TempDir()
testutil.WriteFile(t, filepath.Join(tmpDir, "somefile"), "somecontent")
ctx := context.Background()
r := &localReader{path: tmpDir}
fsys, err := r.FS(ctx)
require.NoError(t, err)
// Assert the fs returned is rooted at correct location.
b, err := fs.ReadFile(fsys, "somefile")
require.NoError(t, err)
assert.Equal(t, "somecontent", string(b))
}