mirror of
https://github.com/dstotijn/hetty.git
synced 2025-07-01 18:47:29 -04:00
250 lines
6.3 KiB
Go
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())
|
|
}
|
|
}
|