From ac736bbf84d5376c3363b8bfe116ccd1293aa41d Mon Sep 17 00:00:00 2001 From: Maxim Goncharenko Date: Mon, 27 May 2019 14:45:54 +0300 Subject: [PATCH] Written base for patterns using TDD --- pkg/engine/pattern.go | 106 ++++++++++++++++++ pkg/engine/pattern_test.go | 213 +++++++++++++++++++++++++++++++++++++ 2 files changed, 319 insertions(+) create mode 100644 pkg/engine/pattern.go create mode 100644 pkg/engine/pattern_test.go diff --git a/pkg/engine/pattern.go b/pkg/engine/pattern.go new file mode 100644 index 0000000000..e387a02b04 --- /dev/null +++ b/pkg/engine/pattern.go @@ -0,0 +1,106 @@ +package engine + +import ( + "log" + "math" + "strings" +) + +// ValidateValueWithPattern validates value with operators and wildcards +func ValidateValueWithPattern(value, pattern interface{}) bool { + switch typedPattern := pattern.(type) { + case bool: + typedValue, ok := value.(bool) + if !ok { + log.Printf("Expected bool, found %T", value) + return false + } + return typedPattern == typedValue + case int: + return validateValueWithIntPattern(value, typedPattern) + case float64: + return validateValueWithFloatPattern(value, typedPattern) + case string: + return validateValueWithStringPattern(value, typedPattern) + case map[string]interface{}, []interface{}: + log.Println("Maps and arrays as patterns are not supported") + return false + case nil: + return validateValueWithNilPattern(value) + default: + log.Printf("Unknown type as pattern: %T\n", pattern) + return false + } +} + +func validateValueWithIntPattern(value interface{}, pattern int) bool { + switch typedValue := value.(type) { + case int: + return typedValue == pattern + case float64: + // check that float has no fraction + if typedValue == math.Trunc(typedValue) { + return int(typedValue) == pattern + } + + log.Printf("Expected int, found float: %f\n", typedValue) + return false + default: + log.Printf("Expected int, found: %T\n", value) + return false + } +} + +func validateValueWithFloatPattern(value interface{}, pattern float64) bool { + switch typedValue := value.(type) { + case int: + // check that float has no fraction + if pattern == math.Trunc(pattern) { + return int(pattern) == value + } + + log.Printf("Expected float, found int: %d\n", typedValue) + return false + case float64: + return typedValue == pattern + default: + log.Printf("Expected float, found: %T\n", value) + return false + } +} + +func validateValueWithNilPattern(value interface{}) bool { + switch typed := value.(type) { + case float64: + return typed == 0.0 + case int: + return typed == 0 + case string: + return typed == "" + case bool: + return typed == false + case nil: + return true + case map[string]interface{}, []interface{}: + log.Println("Maps and arrays could not be checked with nil pattern") + return false + default: + log.Printf("Unknown type as value when checking for nil pattern: %T\n", value) + return false + } +} + +func validateValueWithStringPattern(value interface{}, pattern string) bool { + statements := strings.Split(pattern, "|") + for statement := range statements { + if checkSingleStatement(value, statement) { + return true + } + } + + return false +} + +func checkSingleStatement(value, pattern interface{}) bool { + return true +} diff --git a/pkg/engine/pattern_test.go b/pkg/engine/pattern_test.go new file mode 100644 index 0000000000..5e1c8c2b82 --- /dev/null +++ b/pkg/engine/pattern_test.go @@ -0,0 +1,213 @@ +package engine + +import ( + "encoding/json" + "testing" + + "gotest.tools/assert" +) + +func TestValidateValueWithPattern_Bool(t *testing.T) { + assert.Assert(t, ValidateValueWithPattern(true, true)) + assert.Assert(t, !ValidateValueWithPattern(true, false)) + assert.Assert(t, !ValidateValueWithPattern(false, true)) + assert.Assert(t, ValidateValueWithPattern(false, false)) +} + +func TestValidateValueWithPattern_BoolInJson(t *testing.T) { + rawPattern := []byte(` + { + "key": true + } + `) + + rawValue := []byte(` + { + "key": true + } + `) + + var pattern, value map[string]interface{} + err := json.Unmarshal(rawPattern, &pattern) + assert.Assert(t, err) + err = json.Unmarshal(rawValue, &value) + assert.Assert(t, err) + + assert.Assert(t, ValidateValueWithPattern(value["key"], pattern["key"])) +} + +func TestValidateValueWithPattern_NullPatternStringValue(t *testing.T) { + rawPattern := []byte(` + { + "key": null + } + `) + + rawValue := []byte(` + { + "key": "value" + } + `) + + var pattern, value map[string]interface{} + err := json.Unmarshal(rawPattern, &pattern) + assert.Assert(t, err) + err = json.Unmarshal(rawValue, &value) + assert.Assert(t, err) + + assert.Assert(t, !ValidateValueWithPattern(value["key"], pattern["key"])) +} + +func TestValidateValueWithPattern_NullPatternDefaultString(t *testing.T) { + rawPattern := []byte(` + { + "key": null + } + `) + + rawValue := []byte(` + { + "key": "" + } + `) + + var pattern, value map[string]interface{} + err := json.Unmarshal(rawPattern, &pattern) + assert.Assert(t, err) + err = json.Unmarshal(rawValue, &value) + assert.Assert(t, err) + + assert.Assert(t, ValidateValueWithPattern(value["key"], pattern["key"])) +} + +func TestValidateValueWithPattern_NullPatternDefaultFloat(t *testing.T) { + rawPattern := []byte(` + { + "key": null + } + `) + + rawValue := []byte(` + { + "key": 0.0 + } + `) + + var pattern, value map[string]interface{} + err := json.Unmarshal(rawPattern, &pattern) + assert.Assert(t, err) + err = json.Unmarshal(rawValue, &value) + assert.Assert(t, err) + + assert.Assert(t, ValidateValueWithPattern(value["key"], pattern["key"])) +} + +func TestValidateValueWithPattern_NullPatternDefaultInt(t *testing.T) { + rawPattern := []byte(` + { + "key": null + } + `) + + rawValue := []byte(` + { + "key": 0 + } + `) + + var pattern, value map[string]interface{} + err := json.Unmarshal(rawPattern, &pattern) + assert.Assert(t, err) + err = json.Unmarshal(rawValue, &value) + assert.Assert(t, err) + + assert.Assert(t, ValidateValueWithPattern(value["key"], pattern["key"])) +} + +func TestValidateValueWithPattern_NullPatternDefaultBool(t *testing.T) { + rawPattern := []byte(` + { + "key": null + } + `) + + rawValue := []byte(` + { + "key": false + } + `) + + var pattern, value map[string]interface{} + err := json.Unmarshal(rawPattern, &pattern) + assert.Assert(t, err) + err = json.Unmarshal(rawValue, &value) + assert.Assert(t, err) + + assert.Assert(t, ValidateValueWithPattern(value["key"], pattern["key"])) +} + +func TestValidateValueWithPattern_StringsLogicalOr(t *testing.T) { + pattern := "192.168.88.1 | 10.100.11.*" + value := "10.100.11.54" + assert.Assert(t, ValidateValueWithPattern(value, pattern)) +} + +func TestValidateValueWithNilPattern_NullPatternStringValue(t *testing.T) { + assert.Assert(t, !validateValueWithNilPattern("value")) +} + +func TestValidateValueWithNilPattern_NullPatternDefaultString(t *testing.T) { + assert.Assert(t, validateValueWithNilPattern("")) +} + +func TestValidateValueWithNilPattern_NullPatternDefaultFloat(t *testing.T) { + assert.Assert(t, validateValueWithNilPattern(0.0)) +} + +func TestValidateValueWithNilPattern_NullPatternFloat(t *testing.T) { + assert.Assert(t, !validateValueWithNilPattern(0.1)) +} + +func TestValidateValueWithNilPattern_NullPatternDefaultInt(t *testing.T) { + assert.Assert(t, validateValueWithNilPattern(0)) +} + +func TestValidateValueWithNilPattern_NullPatternInt(t *testing.T) { + assert.Assert(t, !validateValueWithNilPattern(1)) +} + +func TestValidateValueWithNilPattern_NullPatternDefaultBool(t *testing.T) { + assert.Assert(t, validateValueWithNilPattern(false)) +} + +func TestValidateValueWithNilPattern_NullPatternTrueBool(t *testing.T) { + assert.Assert(t, !validateValueWithNilPattern(true)) +} + +func TestValidateValueWithFloatPattern_FloatValue(t *testing.T) { + assert.Assert(t, validateValueWithFloatPattern(7.9914, 7.9914)) +} + +func TestValidateValueWithFloatPattern_FloatValueNotPass(t *testing.T) { + assert.Assert(t, !validateValueWithFloatPattern(7.9914, 7.99141)) +} + +func TestValidateValueWithFloatPattern_FloatPatternWithoutFractionIntValue(t *testing.T) { + assert.Assert(t, validateValueWithFloatPattern(7, 7.000000)) +} + +func TestValidateValueWithFloatPattern_FloatPatternWithoutFraction(t *testing.T) { + assert.Assert(t, validateValueWithFloatPattern(7.000000, 7.000000)) +} + +func TestValidateValueWithIntPattern_FloatValueWithoutFraction(t *testing.T) { + assert.Assert(t, validateValueWithFloatPattern(7.000000, 7)) +} + +func TestValidateValueWithIntPattern_FloatValueWitFraction(t *testing.T) { + assert.Assert(t, !validateValueWithFloatPattern(7.000001, 7)) +} + +func TestValidateValueWithIntPattern_NotPass(t *testing.T) { + assert.Assert(t, !validateValueWithFloatPattern(8, 7)) +}