Files
hetty/pkg/search/parser_test.go
2022-01-21 11:45:54 +01:00

250 lines
6.3 KiB
Go

package search
import (
"errors"
"reflect"
"regexp"
"testing"
)
func TestParseQuery(t *testing.T) {
t.Parallel()
tests := []struct {
name string
input string
expectedExpression Expression
expectedError error
}{
{
name: "empty query",
input: "",
expectedExpression: nil,
expectedError: errors.New("search: unexpected EOF"),
},
{
name: "string literal expression",
input: "foobar",
expectedExpression: StringLiteral{Value: "foobar"},
expectedError: nil,
},
{
name: "boolean expression with equal operator",
input: "foo = bar",
expectedExpression: InfixExpression{
Operator: TokOpEq,
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
expectedError: nil,
},
{
name: "boolean expression with not equal operator",
input: "foo != bar",
expectedExpression: InfixExpression{
Operator: TokOpNotEq,
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
expectedError: nil,
},
{
name: "boolean expression with greater than operator",
input: "foo > bar",
expectedExpression: InfixExpression{
Operator: TokOpGt,
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
expectedError: nil,
},
{
name: "boolean expression with less than operator",
input: "foo < bar",
expectedExpression: InfixExpression{
Operator: TokOpLt,
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
expectedError: nil,
},
{
name: "boolean expression with greater than or equal operator",
input: "foo >= bar",
expectedExpression: InfixExpression{
Operator: TokOpGtEq,
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
expectedError: nil,
},
{
name: "boolean expression with less than or equal operator",
input: "foo <= bar",
expectedExpression: InfixExpression{
Operator: TokOpLtEq,
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
expectedError: nil,
},
{
name: "boolean expression with regular expression operator",
input: "foo =~ bar",
expectedExpression: InfixExpression{
Operator: TokOpRe,
Left: StringLiteral{Value: "foo"},
Right: regexp.MustCompile("bar"),
},
expectedError: nil,
},
{
name: "boolean expression with not regular expression operator",
input: "foo !~ bar",
expectedExpression: InfixExpression{
Operator: TokOpNotRe,
Left: StringLiteral{Value: "foo"},
Right: regexp.MustCompile("bar"),
},
expectedError: nil,
},
{
name: "boolean expression with AND, OR and NOT operators",
input: "foo AND bar OR NOT baz",
expectedExpression: InfixExpression{
Operator: TokOpAnd,
Left: StringLiteral{Value: "foo"},
Right: InfixExpression{
Operator: TokOpOr,
Left: StringLiteral{Value: "bar"},
Right: PrefixExpression{
Operator: TokOpNot,
Right: StringLiteral{Value: "baz"},
},
},
},
expectedError: nil,
},
{
name: "boolean expression with nested group",
input: "(foo AND bar) OR NOT baz",
expectedExpression: InfixExpression{
Operator: TokOpOr,
Left: InfixExpression{
Operator: TokOpAnd,
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
Right: PrefixExpression{
Operator: TokOpNot,
Right: StringLiteral{Value: "baz"},
},
},
expectedError: nil,
},
{
name: "implicit boolean expression with string literal operands",
input: "foo bar baz",
expectedExpression: InfixExpression{
Operator: TokOpAnd,
Left: InfixExpression{
Operator: TokOpAnd,
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
Right: StringLiteral{Value: "baz"},
},
expectedError: nil,
},
{
name: "implicit boolean expression nested in group",
input: "(foo bar)",
expectedExpression: InfixExpression{
Operator: TokOpAnd,
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
expectedError: nil,
},
{
name: "implicit and explicit boolean expression with string literal operands",
input: "foo bar OR baz yolo",
expectedExpression: InfixExpression{
Operator: TokOpAnd,
Left: InfixExpression{
Operator: TokOpAnd,
Left: StringLiteral{Value: "foo"},
Right: InfixExpression{
Operator: TokOpOr,
Left: StringLiteral{Value: "bar"},
Right: StringLiteral{Value: "baz"},
},
},
Right: StringLiteral{Value: "yolo"},
},
expectedError: nil,
},
{
name: "implicit boolean expression with comparison operands",
input: "foo=bar baz=~yolo",
expectedExpression: InfixExpression{
Operator: TokOpAnd,
Left: InfixExpression{
Operator: TokOpEq,
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
Right: InfixExpression{
Operator: TokOpRe,
Left: StringLiteral{Value: "baz"},
Right: regexp.MustCompile("yolo"),
},
},
expectedError: nil,
},
{
name: "eq operator takes precedence over boolean ops",
input: "foo=bar OR baz=yolo",
expectedExpression: InfixExpression{
Operator: TokOpOr,
Left: InfixExpression{
Operator: TokOpEq,
Left: StringLiteral{Value: "foo"},
Right: StringLiteral{Value: "bar"},
},
Right: InfixExpression{
Operator: TokOpEq,
Left: StringLiteral{Value: "baz"},
Right: StringLiteral{Value: "yolo"},
},
},
expectedError: nil,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got, err := ParseQuery(tt.input)
assertError(t, tt.expectedError, err)
if !reflect.DeepEqual(tt.expectedExpression, got) {
t.Errorf("expected: %v, got: %v", tt.expectedExpression, got)
}
})
}
}
func assertError(t *testing.T, exp, got error) {
t.Helper()
switch {
case exp == nil && got != nil:
t.Fatalf("expected: nil, got: %v", got)
case exp != nil && got == nil:
t.Fatalf("expected: %v, got: nil", exp.Error())
case exp != nil && got != nil && exp.Error() != got.Error():
t.Fatalf("expected: %v, got: %v", exp.Error(), got.Error())
}
}