From 72a5560a2e293d0e6f712a486c2051a8ce9a0f16 Mon Sep 17 00:00:00 2001 From: Pranav Gore Date: Thu, 18 Aug 2016 14:29:29 +0530 Subject: [PATCH] Table driven tests for SimpleType, EnumType and ListType. (#115) * table drive tests for SimpleType. WIP * conflict fix added pkg govalidator to glide * added KindList and updated related tests. * Basic Enum tests added. * variable declaration factored into blocks. * Test cases added for available Types. #115 These test cases are testing ConvertToModel function of the Type. Also added fixes in existing ConvertToModel as needed. Few validations are added in Conversion logic. govalidator lib is added for the validations like IsURL. Fixes: #77 --- glide.lock | 8 ++- glide.yaml | 1 + models/fieldtype_test.go | 134 +++++++++++++++++++++++++++++++++++++++ models/list_type.go | 11 ++-- models/simple_type.go | 27 ++++++-- 5 files changed, 170 insertions(+), 11 deletions(-) create mode 100644 models/fieldtype_test.go diff --git a/glide.lock b/glide.lock index 2cb724cf3a..df4a941c3d 100644 --- a/glide.lock +++ b/glide.lock @@ -1,10 +1,12 @@ -hash: 36c2204f43f77bbff3766ab60737169c9239f2ea903c2fd7e85f4309323cc820 -updated: 2016-08-02T14:48:37.043081154+02:00 +hash: 62c37b7f2fdfa1d53eec7f5c805018cf237bf16bd721487664f8ee13de505cf7 +updated: 2016-08-12T15:27:52.439580409+05:30 imports: - name: bitbucket.org/pkg/inflect version: 8961c3750a47b8c0b3e118d52513b97adf85a7e8 - name: github.com/armon/go-metrics version: fbf75676ee9c0a3a23eb0a4d9220a3612cfbd1ed +- name: github.com/asaskevich/govalidator + version: 593d64559f7600f29581a3ee42177f5dbded27a9 - name: github.com/axw/gocov version: 54b98cfcac0c63fb3f9bd8e7ad241b724d4e985b subpackages: @@ -87,6 +89,8 @@ imports: version: 527b253f588776e5f72a0a0d1e93195cd3f82707 subpackages: - go/ast/astutil +- name: gopkg.in/asaskevich/govalidator.v4 + version: 7664702784775e51966f0885f5cd27435916517b - name: gopkg.in/yaml.v2 version: a83829b6f1293c91addabc89d0571c246397bbf4 testImports: [] diff --git a/glide.yaml b/glide.yaml index b39ef18aff..4873510ecd 100644 --- a/glide.yaml +++ b/glide.yaml @@ -51,3 +51,4 @@ import: subpackages: - gocov - package: github.com/wadey/gocovmerge +- package: gopkg.in/asaskevich/govalidator.v4 diff --git a/models/fieldtype_test.go b/models/fieldtype_test.go new file mode 100644 index 0000000000..a349fd8260 --- /dev/null +++ b/models/fieldtype_test.go @@ -0,0 +1,134 @@ +package models + +import "testing" + +var ( + stString = SimpleType{KindString} + stInt = SimpleType{KindInteger} + stFloat = SimpleType{KindFloat} + stDuration = SimpleType{KindDuration} + stURL = SimpleType{KindURL} + stList = SimpleType{KindList} +) + +type input struct { + t FieldType + value interface{} + expectedValue interface{} + errorExpected bool +} + +func TestSimpleTypeConversion(t *testing.T) { + test_data := []input{ + {stString, "hello world", "hello world", false}, + {stString, "", "", false}, + {stString, 100, nil, true}, + {stString, 1.90, nil, true}, + + {stInt, 100.0, nil, true}, + {stInt, 100, 100, false}, + {stInt, "100", nil, true}, + {stInt, true, nil, true}, + + {stFloat, 1.1, 1.1, false}, + {stFloat, 1, nil, true}, + {stFloat, "a", nil, true}, + + {stDuration, 0, 0, false}, + {stDuration, 1.1, nil, true}, + {stDuration, "duration", nil, true}, + + {stURL, "http://www.google.com", "http://www.google.com", false}, + {stURL, "", nil, true}, + {stURL, "google", nil, true}, + {stURL, "http://google.com", "http://google.com", false}, + + {stList, [4]int{1, 2, 3, 4}, [4]int{1, 2, 3, 4}, false}, + {stList, [2]string{"1", "2"}, [2]string{"1", "2"}, false}, + {stList, "", nil, true}, + // {stList, []int{}, []int{}, false}, need to find out the way for empty array. + // because slices do not have equality operator. + } + for _, inp := range test_data { + retVal, err := inp.t.ConvertToModel(inp.value) + if retVal == inp.expectedValue && (err != nil) == inp.errorExpected { + t.Log("test pass for input: ", inp) + } else { + t.Error(retVal, err) + t.Fail() + } + } +} + +var ( + stEnum = SimpleType{KindEnum} + enum = EnumType{ + BaseType: stEnum, + // ENUM with same type values + Values: []interface{}{"new", "triaged", "WIP", "QA", "done"}, + } + + multipleTypeEnum = EnumType{ + BaseType: stEnum, + // ENUM with different type values. + Values: []interface{}{100, 1.1, "hello"}, + } +) + +func TestEnumTypeConversion(t *testing.T) { + data := []input{ + {enum, "string", nil, true}, + {enum, "triaged", "triaged", false}, + {enum, "done", "done", false}, + {enum, "", nil, true}, + {enum, 100, nil, true}, + + {multipleTypeEnum, "abcd", nil, true}, + {multipleTypeEnum, 100, 100, false}, + {multipleTypeEnum, "hello", "hello", false}, + } + for _, inp := range data { + retVal, err := inp.t.ConvertToModel(inp.value) + if retVal == inp.expectedValue && (err != nil) == inp.errorExpected { + t.Log("test pass for input: ", inp) + } else { + t.Error(retVal, err) + t.Fail() + } + } +} + +var ( + intList = ListType{ + SimpleType: SimpleType{KindList}, + ComponentType: SimpleType{KindInteger}, + } + strList = ListType{ + SimpleType: SimpleType{KindList}, + ComponentType: SimpleType{KindString}, + } +) + +func TestListTypeConversion(t *testing.T) { + data := []input{ + {intList, [2]int{11, 2}, "array/slice", false}, + {intList, [2]string{"11", "2"}, nil, true}, + + {strList, [2]string{"11", "2"}, "array/slice", false}, + {strList, [2]int{11, 2}, nil, true}, + } + + for _, inp := range data { + // Ignore expectedValue for now. + // slices can be compared only with nil. + // Because we will need to iterate and match the output. + retVal, err := inp.t.ConvertToModel(inp.value) + if (err != nil) == inp.errorExpected { + t.Log("test pass for input: ", inp) + } else { + t.Error("failed for input=", inp) + t.Error(retVal, err) + t.Fail() + } + } +} diff --git a/models/list_type.go b/models/list_type.go index 5f8729ea2e..13ce404460 100644 --- a/models/list_type.go +++ b/models/list_type.go @@ -34,14 +34,15 @@ func convertList(converter converter, fieldType ListType, value interface{}) (in // the assumption is that work item types do not change over time...only new ones can be created valueType := reflect.TypeOf(value) - if valueType.Kind() != reflect.Array { - return nil, fmt.Errorf("value %v should be %s, but is %s", value, "array", valueType.Name()) + if (valueType.Kind() != reflect.Array) && (valueType.Kind() != reflect.Slice) { + return nil, fmt.Errorf("value %v should be %s, but is %s", value, "array/slice", valueType.Name()) } - valueArray := value.([]interface{}) - converted := make([]interface{}, len(valueArray)) + valueArray := reflect.ValueOf(value) + converted := make([]interface{}, valueArray.Len()) for i := range converted { var err error - converted[i], err = converter(fieldType, valueArray[i]) + // valueArray index value must be converted to Interface else it has TYPE=Value + converted[i], err = converter(fieldType.ComponentType, valueArray.Index(i).Interface()) if err != nil { return nil, fmt.Errorf("error converting list value: %s", err.Error()) } diff --git a/models/simple_type.go b/models/simple_type.go index a201fda903..39983bbdb6 100644 --- a/models/simple_type.go +++ b/models/simple_type.go @@ -5,6 +5,8 @@ import ( "reflect" "strconv" "time" + + "github.com/asaskevich/govalidator" ) // SimpleType is an unstructured FieldType @@ -23,18 +25,28 @@ var timeType = reflect.TypeOf((*time.Time)(nil)).Elem() func (fieldType SimpleType) ConvertToModel(value interface{}) (interface{}, error) { valueType := reflect.TypeOf(value) switch fieldType.GetKind() { - case KindString, KindURL, KindUser: + case KindString, KindUser: if valueType.Kind() != reflect.String { return nil, fmt.Errorf("value %v should be %s, but is %s", value, "string", valueType.Name()) } return value, nil - case KindInteger, KindFloat, KindDuration: - // instant == milliseconds + case KindURL: + if valueType.Kind() == reflect.String && govalidator.IsURL(value.(string)) { + return value, nil + } + return nil, fmt.Errorf("value %v should be %s, but is %s", value, "URL", valueType.Name()) + case KindFloat: if valueType.Kind() != reflect.Float64 { return nil, fmt.Errorf("value %v should be %s, but is %s", value, "float64", valueType.Name()) } return value, nil + case KindInteger, KindDuration: + if valueType.Kind() != reflect.Int { + return nil, fmt.Errorf("value %v should be %s, but is %s", value, "int", valueType.Name()) + } + return value, nil case KindInstant: + // instant == milliseconds if !valueType.Implements(timeType) { return nil, fmt.Errorf("value %v should be %s, but is %s", value, "time.Time", valueType.Name()) } @@ -45,7 +57,14 @@ func (fieldType SimpleType) ConvertToModel(value interface{}) (interface{}, erro } idValue, err := strconv.Atoi(value.(string)) return idValue, err - + case KindList: + if (valueType.Kind() != reflect.Array) && (valueType.Kind() != reflect.Slice) { + return nil, fmt.Errorf("value %v should be %s, but is %s,", value, "array/slice", valueType.Kind()) + } + return value, nil + case KindEnum: + // to be done yet | not sure what to write here as of now. + return value, nil default: return nil, fmt.Errorf("unexpected type constant: %d", fieldType.GetKind()) }