mirror of https://github.com/databricks/cli.git
180 lines
5.5 KiB
Go
180 lines
5.5 KiB
Go
|
package python
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"path/filepath"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/databricks/cli/libs/diag"
|
||
|
"github.com/stretchr/testify/require"
|
||
|
|
||
|
"github.com/databricks/cli/libs/dyn"
|
||
|
assert "github.com/databricks/cli/libs/dyn/dynassert"
|
||
|
)
|
||
|
|
||
|
func TestMergeLocations(t *testing.T) {
|
||
|
pythonLocation := dyn.Location{File: "foo.py", Line: 1, Column: 1}
|
||
|
generatedLocation := dyn.Location{File: generatedFileName, Line: 1, Column: 1}
|
||
|
yamlLocation := dyn.Location{File: "foo.yml", Line: 1, Column: 1}
|
||
|
|
||
|
locations := newPythonLocations()
|
||
|
putPythonLocation(locations, dyn.MustPathFromString("foo"), pythonLocation)
|
||
|
|
||
|
input := dyn.NewValue(
|
||
|
map[string]dyn.Value{
|
||
|
"foo": dyn.NewValue(
|
||
|
map[string]dyn.Value{
|
||
|
"baz": dyn.NewValue("baz", []dyn.Location{yamlLocation}),
|
||
|
"qux": dyn.NewValue("baz", []dyn.Location{generatedLocation, yamlLocation}),
|
||
|
},
|
||
|
[]dyn.Location{},
|
||
|
),
|
||
|
"bar": dyn.NewValue("baz", []dyn.Location{generatedLocation}),
|
||
|
},
|
||
|
[]dyn.Location{yamlLocation},
|
||
|
)
|
||
|
|
||
|
expected := dyn.NewValue(
|
||
|
map[string]dyn.Value{
|
||
|
"foo": dyn.NewValue(
|
||
|
map[string]dyn.Value{
|
||
|
// pythonLocation is appended to the beginning of the list if absent
|
||
|
"baz": dyn.NewValue("baz", []dyn.Location{pythonLocation, yamlLocation}),
|
||
|
// generatedLocation is replaced by pythonLocation
|
||
|
"qux": dyn.NewValue("baz", []dyn.Location{pythonLocation, yamlLocation}),
|
||
|
},
|
||
|
[]dyn.Location{pythonLocation},
|
||
|
),
|
||
|
// if location is unknown, we keep it as-is
|
||
|
"bar": dyn.NewValue("baz", []dyn.Location{generatedLocation}),
|
||
|
},
|
||
|
[]dyn.Location{yamlLocation},
|
||
|
)
|
||
|
|
||
|
actual, err := mergePythonLocations(input, locations)
|
||
|
|
||
|
assert.NoError(t, err)
|
||
|
assert.Equal(t, expected, actual)
|
||
|
}
|
||
|
|
||
|
func TestFindLocation(t *testing.T) {
|
||
|
location0 := dyn.Location{File: "foo.py", Line: 1, Column: 1}
|
||
|
location1 := dyn.Location{File: "foo.py", Line: 2, Column: 1}
|
||
|
|
||
|
locations := newPythonLocations()
|
||
|
putPythonLocation(locations, dyn.MustPathFromString("foo"), location0)
|
||
|
putPythonLocation(locations, dyn.MustPathFromString("foo.bar"), location1)
|
||
|
|
||
|
actual, exists := findPythonLocation(locations, dyn.MustPathFromString("foo.bar"))
|
||
|
|
||
|
assert.True(t, exists)
|
||
|
assert.Equal(t, location1, actual)
|
||
|
}
|
||
|
|
||
|
func TestFindLocation_indexPathComponent(t *testing.T) {
|
||
|
location0 := dyn.Location{File: "foo.py", Line: 1, Column: 1}
|
||
|
location1 := dyn.Location{File: "foo.py", Line: 2, Column: 1}
|
||
|
location2 := dyn.Location{File: "foo.py", Line: 3, Column: 1}
|
||
|
|
||
|
locations := newPythonLocations()
|
||
|
putPythonLocation(locations, dyn.MustPathFromString("foo"), location0)
|
||
|
putPythonLocation(locations, dyn.MustPathFromString("foo.bar"), location1)
|
||
|
putPythonLocation(locations, dyn.MustPathFromString("foo.bar[0]"), location2)
|
||
|
|
||
|
actual, exists := findPythonLocation(locations, dyn.MustPathFromString("foo.bar[0]"))
|
||
|
|
||
|
assert.True(t, exists)
|
||
|
assert.Equal(t, location2, actual)
|
||
|
}
|
||
|
|
||
|
func TestFindLocation_closestAncestorLocation(t *testing.T) {
|
||
|
location0 := dyn.Location{File: "foo.py", Line: 1, Column: 1}
|
||
|
location1 := dyn.Location{File: "foo.py", Line: 2, Column: 1}
|
||
|
|
||
|
locations := newPythonLocations()
|
||
|
putPythonLocation(locations, dyn.MustPathFromString("foo"), location0)
|
||
|
putPythonLocation(locations, dyn.MustPathFromString("foo.bar"), location1)
|
||
|
|
||
|
actual, exists := findPythonLocation(locations, dyn.MustPathFromString("foo.bar.baz"))
|
||
|
|
||
|
assert.True(t, exists)
|
||
|
assert.Equal(t, location1, actual)
|
||
|
}
|
||
|
|
||
|
func TestFindLocation_unknownLocation(t *testing.T) {
|
||
|
location0 := dyn.Location{File: "foo.py", Line: 1, Column: 1}
|
||
|
location1 := dyn.Location{File: "foo.py", Line: 2, Column: 1}
|
||
|
|
||
|
locations := newPythonLocations()
|
||
|
putPythonLocation(locations, dyn.MustPathFromString("foo"), location0)
|
||
|
putPythonLocation(locations, dyn.MustPathFromString("foo.bar"), location1)
|
||
|
|
||
|
_, exists := findPythonLocation(locations, dyn.MustPathFromString("bar"))
|
||
|
|
||
|
assert.False(t, exists)
|
||
|
}
|
||
|
|
||
|
func TestLoadOutput(t *testing.T) {
|
||
|
location := dyn.Location{File: "my_job.py", Line: 1, Column: 1}
|
||
|
bundleRoot := t.TempDir()
|
||
|
output := `{
|
||
|
"resources": {
|
||
|
"jobs": {
|
||
|
"my_job": {
|
||
|
"name": "my_job",
|
||
|
"tasks": [
|
||
|
{
|
||
|
"task_key": "my_task",
|
||
|
"notebook_task": {
|
||
|
"notebook_path": "my_notebook"
|
||
|
}
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}`
|
||
|
|
||
|
locations := newPythonLocations()
|
||
|
putPythonLocation(
|
||
|
locations,
|
||
|
dyn.MustPathFromString("resources.jobs.my_job"),
|
||
|
location,
|
||
|
)
|
||
|
|
||
|
value, diags := loadOutput(
|
||
|
bundleRoot,
|
||
|
bytes.NewReader([]byte(output)),
|
||
|
locations,
|
||
|
)
|
||
|
|
||
|
assert.Equal(t, diag.Diagnostics{}, diags)
|
||
|
|
||
|
name, err := dyn.Get(value, "resources.jobs.my_job.name")
|
||
|
require.NoError(t, err)
|
||
|
require.Equal(t, []dyn.Location{location}, name.Locations())
|
||
|
|
||
|
// until we implement path normalization, we have to keep locations of values
|
||
|
// that change semantic depending on their location
|
||
|
//
|
||
|
// note: it's important to have absolute path including 'bundleRoot'
|
||
|
// because mutator pipeline already has expanded locations into absolute path
|
||
|
notebookPath, err := dyn.Get(value, "resources.jobs.my_job.tasks[0].notebook_task.notebook_path")
|
||
|
require.NoError(t, err)
|
||
|
require.Len(t, notebookPath.Locations(), 1)
|
||
|
require.Equal(t, filepath.Join(bundleRoot, generatedFileName), notebookPath.Locations()[0].File)
|
||
|
}
|
||
|
|
||
|
func TestParsePythonLocations(t *testing.T) {
|
||
|
expected := dyn.Location{File: "foo.py", Line: 1, Column: 2}
|
||
|
|
||
|
input := `{"path": "foo", "file": "foo.py", "line": 1, "column": 2}`
|
||
|
reader := bytes.NewReader([]byte(input))
|
||
|
locations, err := parsePythonLocations(reader)
|
||
|
|
||
|
assert.NoError(t, err)
|
||
|
|
||
|
assert.True(t, locations.keys["foo"].exists)
|
||
|
assert.Equal(t, expected, locations.keys["foo"].location)
|
||
|
}
|