From d8b70de1e48da55e100881e11483688414d72834 Mon Sep 17 00:00:00 2001 From: Eamon Bauman Date: Wed, 3 Apr 2024 15:41:11 -0500 Subject: [PATCH] added unique tests, removed generics from property package --- v3/pkg/property/validation.go | 62 +++++++++++++++++------- v3/pkg/property/validation_test.go | 78 ++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 18 deletions(-) create mode 100644 v3/pkg/property/validation_test.go diff --git a/v3/pkg/property/validation.go b/v3/pkg/property/validation.go index 4bc8f278..eb10768c 100644 --- a/v3/pkg/property/validation.go +++ b/v3/pkg/property/validation.go @@ -1,6 +1,7 @@ package property import ( + "fmt" "github.com/peterhellberg/duration" "github.com/xeipuuv/gojsonschema" "math" @@ -362,48 +363,73 @@ func validateFloatEnum(value float64, enum []string) error { func (p Property) validateUniqueItems(value any) error { if p.UniqueItems { switch v := value.(type) { + case []bool: + return p.validateUniqueItemsSlice(v) case []string: - return validateUniqueItemsSlice(v) + return p.validateUniqueItemsSlice(v) case []int: - return validateUniqueItemsSlice(v) + return p.validateUniqueItemsSlice(v) case []float64: - return validateUniqueItemsSlice(v) + return p.validateUniqueItemsSlice(v) + case map[string]bool: + return p.validateUniqueItemsMap(v) case map[string]string: - return validateUniqueItemsMap(v) + return p.validateUniqueItemsMap(v) case map[string]int: - return validateUniqueItemsMap(v) + return p.validateUniqueItemsMap(v) case map[string]float64: - return validateUniqueItemsMap(v) + return p.validateUniqueItemsMap(v) } } return nil } -func validateUniqueItemsSlice[GenericType any](items []GenericType) error { +func (p Property) validateUniqueItemsSlice(value any) error { var unique = map[any]bool{} - for _, i := range items { - if unique[i] { - return NewValidationErrorf("value %v not unique", i) - } + if reflect.TypeOf(value).Kind() == reflect.Slice { + s := reflect.ValueOf(value) - unique[i] = true + for i := 0; i < s.Len(); i++ { + var mapVal = reflectValueOf(s.Index(i), p.DataType) + if unique[mapVal] { + return fmt.Errorf("duplicate value %v in unique slice", s.Index(i).String()) + } + unique[mapVal] = true + } } return nil } -func validateUniqueItemsMap[GenericType any](items map[string]GenericType) error { +func (p Property) validateUniqueItemsMap(value any) error { var unique = map[any]bool{} - for _, i := range items { - if unique[i] { - return NewValidationErrorf("value %v not unique", i) - } + if reflect.TypeOf(value).Kind() == reflect.Map { + s := reflect.ValueOf(value) - unique[i] = true + for _, k := range s.MapKeys() { + var mapVal = reflectValueOf(s.MapIndex(k), p.DataType) + if unique[mapVal] { + return fmt.Errorf("duplicate value %v in unique map", s.MapIndex(k).String()) + } + unique[mapVal] = true + } } return nil } + +func reflectValueOf(v reflect.Value, d DataType) any { + switch d { + case DataTypeInteger: + return v.Int() + case DataTypeBoolean: + return v.Bool() + case DataTypeFloat: + return v.Float() + default: + return v.String() + } +} diff --git a/v3/pkg/property/validation_test.go b/v3/pkg/property/validation_test.go new file mode 100644 index 00000000..fab477bb --- /dev/null +++ b/v3/pkg/property/validation_test.go @@ -0,0 +1,78 @@ +package property + +import ( + "fmt" + "testing" +) + +var ( + testValues = map[bool]map[ValueType]map[DataType]string{ + true: { + ValueTypeArray: { + DataTypeBoolean: "[true, false]", + DataTypeInteger: "[1,2,3,4]", + DataTypeFloat: "[1.234, 1.432]", + DataTypeString: "[\"one\", \"two\"]", + }, + ValueTypeMap: { + DataTypeBoolean: "{\"one\": true, \"two\": false}", + DataTypeInteger: "{\"one\": 1, \"two\": 2}", + DataTypeFloat: "{\"one\": 1.234, \"two\": 1.432}", + DataTypeString: "{\"one\": \"one\", \"two\": \"two\"}", + }, + }, + false: { + ValueTypeArray: { + DataTypeBoolean: "[true, true]", + DataTypeInteger: "[1, 1, 1]", + DataTypeFloat: "[1.234, 1.234]", + DataTypeString: "[\"one\", \"one\"]", + }, + ValueTypeMap: { + DataTypeBoolean: "{\"one\": true, \"two\": true}", + DataTypeInteger: "{\"one\": 1, \"two\": 1}", + DataTypeFloat: "{\"one\": 1.234, \"two\": 1.234}", + DataTypeString: "{\"one\": \"one\", \"two\": \"one\"}", + }, + }, + } +) + +func TestValidate_UniqueSlices(t *testing.T) { + for unique, valueTypes := range testValues { + for valueType, dataTypes := range valueTypes { + for dataType, testString := range dataTypes { + var name = fmt.Sprintf("%s of %s", valueType, dataType) + if unique { + name = "unique " + name + " should pass validation" + } else { + name = "non-unique " + name + " should not pass validation" + } + + t.Run(name, func(t *testing.T) { + var p = Property{ + DataType: dataType, + ValueType: valueType, + SettingValidation: SettingValidation{ + UniqueItems: true, + }, + } + + err := p.Validate(testString) + + // unique and no error is good + // unique and error is bad + if err != nil && unique { + t.Error(err) + } + + // not unique and error is good + // not unique and no error is bad + if err == nil && !unique { + t.Error(err) + } + }) + } + } + } +}