2023-10-24 11:12:36 +00:00
|
|
|
package convert
|
|
|
|
|
|
|
|
import (
|
|
|
|
"reflect"
|
|
|
|
"testing"
|
|
|
|
|
2023-12-22 13:20:45 +00:00
|
|
|
"github.com/databricks/cli/libs/dyn"
|
2023-10-24 11:12:36 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestStructInfoPlain(t *testing.T) {
|
|
|
|
type Tmp struct {
|
|
|
|
Foo string `json:"foo"`
|
|
|
|
Bar string `json:"bar,omitempty"`
|
|
|
|
|
|
|
|
// Baz must be skipped.
|
|
|
|
Baz string `json:""`
|
|
|
|
|
|
|
|
// Qux must be skipped.
|
|
|
|
Qux string `json:"-"`
|
|
|
|
}
|
|
|
|
|
|
|
|
si := getStructInfo(reflect.TypeOf(Tmp{}))
|
|
|
|
assert.Len(t, si.Fields, 2)
|
|
|
|
assert.Equal(t, []int{0}, si.Fields["foo"])
|
|
|
|
assert.Equal(t, []int{1}, si.Fields["bar"])
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStructInfoAnonymousByValue(t *testing.T) {
|
|
|
|
type Bar struct {
|
|
|
|
Bar string `json:"bar"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Foo struct {
|
|
|
|
Foo string `json:"foo"`
|
|
|
|
Bar
|
|
|
|
}
|
|
|
|
|
|
|
|
type Tmp struct {
|
|
|
|
Foo
|
|
|
|
}
|
|
|
|
|
|
|
|
si := getStructInfo(reflect.TypeOf(Tmp{}))
|
|
|
|
assert.Len(t, si.Fields, 2)
|
|
|
|
assert.Equal(t, []int{0, 0}, si.Fields["foo"])
|
|
|
|
assert.Equal(t, []int{0, 1, 0}, si.Fields["bar"])
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStructInfoAnonymousByValuePrecedence(t *testing.T) {
|
|
|
|
type Bar struct {
|
|
|
|
Bar string `json:"bar"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Foo struct {
|
|
|
|
Foo string `json:"foo"`
|
|
|
|
Bar
|
|
|
|
}
|
|
|
|
|
|
|
|
type Tmp struct {
|
|
|
|
// "foo" comes from [Foo].
|
|
|
|
Foo
|
|
|
|
// "bar" comes from [Bar] directly, not through [Foo].
|
|
|
|
Bar
|
|
|
|
}
|
|
|
|
|
|
|
|
si := getStructInfo(reflect.TypeOf(Tmp{}))
|
|
|
|
assert.Len(t, si.Fields, 2)
|
|
|
|
assert.Equal(t, []int{0, 0}, si.Fields["foo"])
|
|
|
|
assert.Equal(t, []int{1, 0}, si.Fields["bar"])
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStructInfoAnonymousByPointer(t *testing.T) {
|
|
|
|
type Bar struct {
|
|
|
|
Bar string `json:"bar"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Foo struct {
|
|
|
|
Foo string `json:"foo"`
|
|
|
|
*Bar
|
|
|
|
}
|
|
|
|
|
|
|
|
type Tmp struct {
|
|
|
|
*Foo
|
|
|
|
}
|
|
|
|
|
|
|
|
si := getStructInfo(reflect.TypeOf(Tmp{}))
|
|
|
|
assert.Len(t, si.Fields, 2)
|
|
|
|
assert.Equal(t, []int{0, 0}, si.Fields["foo"])
|
|
|
|
assert.Equal(t, []int{0, 1, 0}, si.Fields["bar"])
|
|
|
|
}
|
2023-11-15 09:19:51 +00:00
|
|
|
|
|
|
|
func TestStructInfoFieldValues(t *testing.T) {
|
|
|
|
type Tmp struct {
|
|
|
|
Foo string `json:"foo"`
|
|
|
|
Bar string `json:"bar"`
|
|
|
|
}
|
|
|
|
|
|
|
|
var src = Tmp{
|
|
|
|
Foo: "foo",
|
|
|
|
Bar: "bar",
|
|
|
|
}
|
|
|
|
|
|
|
|
si := getStructInfo(reflect.TypeOf(Tmp{}))
|
|
|
|
fv := si.FieldValues(reflect.ValueOf(src))
|
|
|
|
assert.Len(t, fv, 2)
|
|
|
|
assert.Equal(t, "foo", fv["foo"].String())
|
|
|
|
assert.Equal(t, "bar", fv["bar"].String())
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStructInfoFieldValuesAnonymousByValue(t *testing.T) {
|
|
|
|
type Bar struct {
|
|
|
|
Bar string `json:"bar"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Foo struct {
|
|
|
|
Foo string `json:"foo"`
|
|
|
|
Bar
|
|
|
|
}
|
|
|
|
|
|
|
|
type Tmp struct {
|
|
|
|
Foo
|
|
|
|
}
|
|
|
|
|
|
|
|
var src = Tmp{
|
|
|
|
Foo: Foo{
|
|
|
|
Foo: "foo",
|
|
|
|
Bar: Bar{
|
|
|
|
Bar: "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
si := getStructInfo(reflect.TypeOf(Tmp{}))
|
|
|
|
fv := si.FieldValues(reflect.ValueOf(src))
|
|
|
|
assert.Len(t, fv, 2)
|
|
|
|
assert.Equal(t, "foo", fv["foo"].String())
|
|
|
|
assert.Equal(t, "bar", fv["bar"].String())
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStructInfoFieldValuesAnonymousByPointer(t *testing.T) {
|
|
|
|
type Bar struct {
|
|
|
|
Bar string `json:"bar"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Foo struct {
|
|
|
|
Foo string `json:"foo"`
|
|
|
|
*Bar
|
|
|
|
}
|
|
|
|
|
|
|
|
type Tmp struct {
|
|
|
|
*Foo
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test that the embedded fields are dereferenced properly.
|
|
|
|
t.Run("all are set", func(t *testing.T) {
|
|
|
|
src := Tmp{
|
|
|
|
Foo: &Foo{
|
|
|
|
Foo: "foo",
|
|
|
|
Bar: &Bar{
|
|
|
|
Bar: "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
si := getStructInfo(reflect.TypeOf(Tmp{}))
|
|
|
|
fv := si.FieldValues(reflect.ValueOf(src))
|
|
|
|
assert.Len(t, fv, 2)
|
|
|
|
assert.Equal(t, "foo", fv["foo"].String())
|
|
|
|
assert.Equal(t, "bar", fv["bar"].String())
|
|
|
|
})
|
|
|
|
|
|
|
|
// Test that fields of embedded types are skipped if the embedded type is nil.
|
|
|
|
t.Run("top level is set", func(t *testing.T) {
|
|
|
|
src := Tmp{
|
|
|
|
Foo: &Foo{
|
|
|
|
Foo: "foo",
|
|
|
|
Bar: nil,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
si := getStructInfo(reflect.TypeOf(Tmp{}))
|
|
|
|
fv := si.FieldValues(reflect.ValueOf(src))
|
|
|
|
assert.Len(t, fv, 1)
|
|
|
|
assert.Equal(t, "foo", fv["foo"].String())
|
|
|
|
})
|
|
|
|
|
|
|
|
// Test that fields of embedded types are skipped if the embedded type is nil.
|
|
|
|
t.Run("none are set", func(t *testing.T) {
|
|
|
|
src := Tmp{
|
|
|
|
Foo: nil,
|
|
|
|
}
|
|
|
|
|
|
|
|
si := getStructInfo(reflect.TypeOf(Tmp{}))
|
|
|
|
fv := si.FieldValues(reflect.ValueOf(src))
|
|
|
|
assert.Empty(t, fv)
|
|
|
|
})
|
|
|
|
}
|
2023-11-27 10:06:29 +00:00
|
|
|
|
|
|
|
func TestStructInfoValueFieldAbsent(t *testing.T) {
|
|
|
|
type Tmp struct {
|
|
|
|
Foo string `json:"foo"`
|
|
|
|
}
|
|
|
|
|
|
|
|
si := getStructInfo(reflect.TypeOf(Tmp{}))
|
|
|
|
assert.Nil(t, si.ValueField)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStructInfoValueFieldPresent(t *testing.T) {
|
|
|
|
type Tmp struct {
|
2023-12-22 13:20:45 +00:00
|
|
|
Foo dyn.Value
|
2023-11-27 10:06:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
si := getStructInfo(reflect.TypeOf(Tmp{}))
|
|
|
|
assert.NotNil(t, si.ValueField)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStructInfoValueFieldMultiple(t *testing.T) {
|
|
|
|
type Tmp struct {
|
2023-12-22 13:20:45 +00:00
|
|
|
Foo dyn.Value
|
|
|
|
Bar dyn.Value
|
2023-11-27 10:06:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
assert.Panics(t, func() {
|
|
|
|
getStructInfo(reflect.TypeOf(Tmp{}))
|
|
|
|
})
|
|
|
|
}
|