Skip to content

Commit

Permalink
Tet coverage and fix contactql error messages
Browse files Browse the repository at this point in the history
  • Loading branch information
rowanseymour committed Dec 20, 2018
1 parent ebf7f28 commit 7e09702
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 30 deletions.
1 change: 1 addition & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
ignore:
- "test"
- "contactql/gen"
- "excellent/gen"
13 changes: 9 additions & 4 deletions contactql/evaluator.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package contactql

import (
"fmt"
"strings"
"time"

Expand All @@ -14,10 +15,12 @@ const (
ImplicitKey string = "*"
)

// Queryable is the interface objects must implement queried
type Queryable interface {
ResolveQueryKey(utils.Environment, string) []interface{}
}

// EvaluateQuery evaluates the given parsed query against a queryable object
func EvaluateQuery(env utils.Environment, query *ContactQuery, queryable Queryable) (bool, error) {
return query.Evaluate(env, queryable)
}
Expand All @@ -26,7 +29,9 @@ func icontains(s string, substr string) bool {
return strings.Contains(strings.ToLower(s), strings.ToLower(substr))
}

func stringComparison(objectVal string, comparator string, queryVal string) (bool, error) {
func textComparison(objectVal string, comparator string, queryVal string) (bool, error) {
fmt.Printf("textComparison(%s, %s, %s)", objectVal, comparator, queryVal)

switch comparator {
case "=":
return strings.ToLower(objectVal) == strings.ToLower(queryVal), nil
Expand All @@ -36,7 +41,7 @@ func stringComparison(objectVal string, comparator string, queryVal string) (boo
return false, errors.Errorf("can't query text fields with %s", comparator)
}

func decimalComparison(objectVal decimal.Decimal, comparator string, queryVal decimal.Decimal) (bool, error) {
func numberComparison(objectVal decimal.Decimal, comparator string, queryVal decimal.Decimal) (bool, error) {
switch comparator {
case "=":
return objectVal.Equal(queryVal), nil
Expand All @@ -49,7 +54,7 @@ func decimalComparison(objectVal decimal.Decimal, comparator string, queryVal de
case "<=":
return objectVal.LessThanOrEqual(queryVal), nil
}
return false, errors.Errorf("can't query text fields with %s", comparator)
return false, errors.Errorf("can't query number fields with %s", comparator)
}

func dateComparison(objectVal time.Time, comparator string, queryVal time.Time) (bool, error) {
Expand All @@ -67,5 +72,5 @@ func dateComparison(objectVal time.Time, comparator string, queryVal time.Time)
case "<=":
return objectVal.Before(utcDayEnd), nil
}
return false, errors.Errorf("can't query location fields with %s", comparator)
return false, errors.Errorf("can't query datetime fields with %s", comparator)
}
4 changes: 2 additions & 2 deletions contactql/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,14 @@ func (c *Condition) Evaluate(env utils.Environment, queryable Queryable) (bool,
func (c *Condition) evaluateValue(env utils.Environment, val interface{}) (bool, error) {
switch val.(type) {
case string:
return stringComparison(val.(string), c.comparator, c.value)
return textComparison(val.(string), c.comparator, c.value)

case decimal.Decimal:
asDecimal, err := decimal.NewFromString(c.value)
if err != nil {
return false, err
}
return decimalComparison(val.(decimal.Decimal), c.comparator, asDecimal)
return numberComparison(val.(decimal.Decimal), c.comparator, asDecimal)

case time.Time:
asDate, err := utils.DateFromString(env, c.value, false)
Expand Down
54 changes: 31 additions & 23 deletions contactql/parser_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package contactql

import (
"strconv"
"testing"
"time"

Expand Down Expand Up @@ -74,7 +73,7 @@ func TestEvaluateQuery(t *testing.T) {
testObj := &TestQueryable{}

tests := []struct {
text string
query string
result bool
}{
// URN condition
Expand Down Expand Up @@ -139,39 +138,48 @@ func TestEvaluateQuery(t *testing.T) {
{`(age = 36) AND (gender = male)`, true},
{`age = 36 AND gender = female`, false},
{`age = 36 OR gender = female`, true},
{`age = 35 OR gender = female`, false},
{`(age = 36 OR gender = female) AND age > 35`, true},
}

for _, test := range tests {
parsed, err := ParseQuery(test.text)
if err != nil {
t.Errorf("Error parsing query '%s'\n Error: %s\n", test.text, err.Error())
continue
}
parsed, err := ParseQuery(test.query)
assert.NoError(t, err, "unexpected error parsing '%s'", test.query)

actualResult, err := EvaluateQuery(env, parsed, testObj)
if err != nil {
t.Errorf("Error evaluating query '%s'\n Error: %s\n", test.text, err.Error())
continue
}
if actualResult != test.result {
t.Errorf("Error evaluating query '%s'\n Expected: %s Got: %s\n", test.text, strconv.FormatBool(test.result), strconv.FormatBool(actualResult))
}
assert.NoError(t, err, "unexpected error evaluating '%s'", test.query)
assert.Equal(t, test.result, actualResult, "unexpected result for '%s'", test.query)
}
}

func TestQueryErrors(t *testing.T) {
func TestParsingErrors(t *testing.T) {
_, err := ParseQuery("name = ")
assert.EqualError(t, err, "mismatched input '<EOF>' expecting {TEXT, STRING}")
}

func TestEvaluationErrors(t *testing.T) {
env := utils.NewDefaultEnvironment()
testObj := &TestQueryable{}

// a syntax eror
_, err := ParseQuery("name = ")
assert.EqualError(t, err, "mismatched input '<EOF>' expecting {TEXT, STRING}")
tests := []struct {
query string
errMsg string
}{
{`Bob`, "dynamic group queries can't contain implicit conditions"},
{`gender > Male`, "can't query text fields with >"},
{`age ~ 32`, "can't query number fields with ~"},
{`dob = 32`, "string '32' couldn't be parsed as a date"},
{`dob = 32 AND name = Bob`, "string '32' couldn't be parsed as a date"},
{`name = Bob OR dob = 32`, "string '32' couldn't be parsed as a date"},
{`dob ~ 2018-12-31`, "can't query datetime fields with ~"},
}

// an evaluation error
parsed, err := ParseQuery("Bob")
assert.NoError(t, err)
for _, test := range tests {
parsed, err := ParseQuery(test.query)
assert.NoError(t, err, "unexpected error parsing '%s'", test.query)

_, err = EvaluateQuery(env, parsed, testObj)
assert.EqualError(t, err, "dynamic group queries can't contain implicit conditions")
actualResult, err := EvaluateQuery(env, parsed, testObj)
assert.EqualError(t, err, test.errMsg, "unexpected error evaluating '%s'", test.query)
assert.False(t, actualResult, "unexpected non-false result for '%s'", test.query)
}
}
2 changes: 1 addition & 1 deletion utils/dates.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func dateFromFormats(env Environment, currentYear int, pattern *regexp.Regexp, d
return time.Date(year, time.Month(month), day, 0, 0, 0, 0, env.Timezone()), nil
}

return ZeroTime, errors.Errorf("No date found in string: %s", str)
return ZeroTime, errors.Errorf("string '%s' couldn't be parsed as a date", str)
}

// DaysBetween returns the number of calendar days (an int) between the two dates. Note
Expand Down

0 comments on commit 7e09702

Please sign in to comment.