fix: Fix type conversion for numbers (#204)

* Fix type conversion for numbers

* add tests

* add more tests
This commit is contained in:
Khosrow Moossavi
2020-02-24 10:18:02 -05:00
committed by GitHub
parent 27b95c4adf
commit f55fd6cc5b
13 changed files with 536 additions and 23 deletions

View File

@@ -0,0 +1,31 @@
package types
import (
"testing"
)
func TestBool(t *testing.T) {
values := List{true, false}
testPrimitive(t, []testprimitive{
{
name: "value not nil and type bool",
values: values,
types: "bool",
expected: expected{
typeName: "bool",
valueKind: "types.Bool",
hasDefault: true,
},
},
{
name: "value not nil and type empty",
values: values,
types: "",
expected: expected{
typeName: "bool",
valueKind: "types.Bool",
hasDefault: true,
},
},
})
}

View File

@@ -0,0 +1,31 @@
package types
import (
"testing"
)
func TestEmpty(t *testing.T) {
values := List{""}
testPrimitive(t, []testprimitive{
{
name: "value empty and type string",
values: values,
types: "string",
expected: expected{
typeName: "string",
valueKind: "types.Empty",
hasDefault: true,
},
},
{
name: "value empty and type empty",
values: values,
types: "",
expected: expected{
typeName: "string",
valueKind: "types.Empty",
hasDefault: true,
},
},
})
}

View File

@@ -0,0 +1,36 @@
package types
import (
"testing"
)
func TestList(t *testing.T) {
values := []List{
List{"foo", "bar", "baz"},
List{"1", "2", "3"},
List{true, false, true},
List{10, float64(1000), int8(42)},
}
testList(t, []testlist{
{
name: "value not nil and type list",
values: values,
types: "list",
expected: expected{
typeName: "list",
valueKind: "types.List",
hasDefault: true,
},
},
{
name: "value not nil and type empty",
values: values,
types: "",
expected: expected{
typeName: "list",
valueKind: "types.List",
hasDefault: true,
},
},
})
}

View File

@@ -0,0 +1,50 @@
package types
import (
"testing"
)
func TestMap(t *testing.T) {
values := []Map{
{
"a": 1,
"b": 2,
"c": 3,
},
{
"name": "hello",
"foo": map[string]string{
"foo": "foo",
"bar": "foo",
},
"bar": map[string]string{
"foo": "bar",
"bar": "bar",
},
"buzz": []string{"fizz", "buzz"},
},
{},
}
testMap(t, []testmap{
{
name: "value not nil and type map",
values: values,
types: "map",
expected: expected{
typeName: "map",
valueKind: "types.Map",
hasDefault: true,
},
},
{
name: "value not nil and type empty",
values: values,
types: "",
expected: expected{
typeName: "map",
valueKind: "types.Map",
hasDefault: true,
},
},
})
}

View File

@@ -0,0 +1,71 @@
package types
import (
"testing"
)
func TestNil(t *testing.T) {
nils := List{nil}
testPrimitive(t, []testprimitive{
{
name: "value nil and type bool",
values: nils,
types: "bool",
expected: expected{
typeName: "bool",
valueKind: "*types.Nil",
hasDefault: false,
},
},
{
name: "value nil and type number",
values: nils,
types: "number",
expected: expected{
typeName: "number",
valueKind: "*types.Nil",
hasDefault: false,
},
},
{
name: "value nil and type list",
values: nils,
types: "list",
expected: expected{
typeName: "list",
valueKind: "*types.Nil",
hasDefault: false,
},
},
{
name: "value nil and type map",
values: nil,
types: "map",
expected: expected{
typeName: "map",
valueKind: "*types.Nil",
hasDefault: false,
},
},
{
name: "value nil and type string",
values: nils,
types: "string",
expected: expected{
typeName: "string",
valueKind: "*types.Nil",
hasDefault: false,
},
},
{
name: "value nil and type empty",
values: nils,
types: "",
expected: expected{
typeName: "any",
valueKind: "*types.Nil",
hasDefault: false,
},
},
})
}

View File

@@ -0,0 +1,31 @@
package types
import (
"testing"
)
func TestNumber(t *testing.T) {
values := List{int(0), int8(42), int16(-1200), int32(1140085647), int64(8922336854775807), float32(13.75), float64(2317483.64)}
testPrimitive(t, []testprimitive{
{
name: "value not nil and type number",
values: values,
types: "number",
expected: expected{
typeName: "number",
valueKind: "types.Number",
hasDefault: true,
},
},
{
name: "value not nil and type empty",
values: values,
types: "",
expected: expected{
typeName: "number",
valueKind: "types.Number",
hasDefault: true,
},
},
})
}

View File

@@ -0,0 +1,31 @@
package types
import (
"testing"
)
func TestString(t *testing.T) {
values := List{"foo", "42", "false", "true"}
testPrimitive(t, []testprimitive{
{
name: "value not nil and type string",
values: values,
types: "string",
expected: expected{
typeName: "string",
valueKind: "types.String",
hasDefault: true,
},
},
{
name: "value not nil and type empty",
values: values,
types: "",
expected: expected{
typeName: "string",
valueKind: "types.String",
hasDefault: true,
},
},
})
}

View File

@@ -2,8 +2,8 @@ package types
import (
"bytes"
"fmt"
"go/types"
"reflect"
"strings"
)
@@ -28,20 +28,23 @@ func ValueOf(v interface{}) Default {
if v == nil {
return new(Nil)
}
switch xType := fmt.Sprintf("%T", v); xType {
case "string":
if v.(string) == "" {
value := reflect.ValueOf(v)
switch value.Kind() {
case reflect.String:
if value.IsZero() {
return Empty("")
}
return String(v.(string))
case "int", "int8", "int16", "int32", "int64", "float32", "float64":
return Number(v.(float64))
case "bool":
return Bool(v.(bool))
case "[]interface {}":
return List(v.([]interface{}))
case "map[string]interface {}":
return Map(v.(map[string]interface{}))
return String(value.String())
case reflect.Float32, reflect.Float64:
return Number(value.Float())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return Number(float64(value.Int()))
case reflect.Bool:
return Bool(value.Bool())
case reflect.Slice:
return List(value.Interface().([]interface{}))
case reflect.Map:
return Map(value.Interface().(map[string]interface{}))
}
return new(Nil)
}
@@ -54,16 +57,16 @@ func TypeOf(t string, v interface{}) String {
return String(t)
}
if v != nil {
switch xType := fmt.Sprintf("%T", v); xType {
case "string":
switch reflect.ValueOf(v).Kind() {
case reflect.String:
return String("string")
case "int", "int8", "int16", "int32", "int64", "float32", "float64":
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float32, reflect.Float64:
return String("number")
case "bool":
case reflect.Bool:
return String("bool")
case "[]interface {}":
case reflect.Slice:
return String("list")
case "map[string]interface {}":
case reflect.Map:
return String("map")
}
}
@@ -102,6 +105,11 @@ func (s String) String() string {
return string(s)
}
// nolint
func (s String) underlying() string {
return string(s)
}
// HasDefault indicates a Terraform variable has a default value set.
func (s String) HasDefault() bool {
return true
@@ -135,6 +143,11 @@ func (s String) MarshalYAML() (interface{}, error) {
// marshaled to `""` in JSON and YAML
type Empty string
// nolint
func (e Empty) underlying() string {
return string(e)
}
// HasDefault indicates a Terraform variable has a default value set.
func (e Empty) HasDefault() bool {
return true
@@ -150,6 +163,11 @@ func (e Empty) MarshalJSON() ([]byte, error) {
// marshaled to `null` when emty in JSON and YAML
type Number float64
// nolint
func (n Number) underlying() float64 {
return float64(n)
}
// HasDefault indicates a Terraform variable has a default value set.
func (n Number) HasDefault() bool {
return true
@@ -158,6 +176,11 @@ func (n Number) HasDefault() bool {
// Bool represents a 'bool' value
type Bool bool
// nolint
func (b Bool) underlying() bool {
return bool(b)
}
// HasDefault indicates a Terraform variable has a default value set.
func (b Bool) HasDefault() bool {
return true
@@ -166,6 +189,15 @@ func (b Bool) HasDefault() bool {
// List represents a 'list' of values
type List []interface{}
// nolint
func (l List) underlying() []interface{} {
r := make([]interface{}, 0)
for _, i := range l {
r = append(r, i)
}
return r
}
// HasDefault indicates a Terraform variable has a default value set.
func (l List) HasDefault() bool {
return true
@@ -174,6 +206,15 @@ func (l List) HasDefault() bool {
// Map represents a 'map' of values
type Map map[string]interface{}
// nolint
func (m Map) underlying() map[string]interface{} {
r := make(map[string]interface{}, 0)
for k, e := range m {
r[k] = e
}
return r
}
// HasDefault indicates a Terraform variable has a default value set.
func (m Map) HasDefault() bool {
return true

View File

@@ -0,0 +1,86 @@
package types
import (
"reflect"
"testing"
"github.com/stretchr/testify/assert"
)
type expected struct {
typeName string
valueKind string
hasDefault bool
}
type testprimitive struct {
name string
values List
types string
expected expected
}
type testlist struct {
name string
values []List
types string
expected expected
}
type testmap struct {
name string
values []Map
types string
expected expected
}
func testPrimitive(t *testing.T, tests []testprimitive) {
for _, tt := range tests {
for _, tv := range tt.values {
t.Run(tt.name, func(t *testing.T) {
assert := assert.New(t)
actualValue := ValueOf(tv)
actualType := TypeOf(tt.types, tv)
assert.Equal(tt.expected.typeName, string(actualType))
assert.Equal(tt.expected.valueKind, reflect.TypeOf(actualValue).String())
assert.Equal(tt.expected.hasDefault, actualValue.HasDefault())
})
}
}
}
func testList(t *testing.T, tests []testlist) {
for _, tt := range tests {
for _, tv := range tt.values {
t.Run(tt.name, func(t *testing.T) {
assert := assert.New(t)
actualValue := ValueOf(tv.underlying())
actualType := TypeOf(tt.types, tv.underlying())
assert.Equal(tt.expected.typeName, string(actualType))
assert.Equal(tt.expected.valueKind, reflect.TypeOf(actualValue).String())
assert.Equal(tt.expected.hasDefault, actualValue.HasDefault())
})
}
}
}
func testMap(t *testing.T, tests []testmap) {
for _, tt := range tests {
for _, tv := range tt.values {
t.Run(tt.name, func(t *testing.T) {
assert := assert.New(t)
actualValue := ValueOf(tv.underlying())
actualType := TypeOf(tt.types, tv.underlying())
assert.Equal(tt.expected.typeName, string(actualType))
assert.Equal(tt.expected.valueKind, reflect.TypeOf(actualValue).String())
assert.Equal(tt.expected.hasDefault, actualValue.HasDefault())
})
}
}
}