package govaluate import ( "fmt" "regexp/syntax" "strings" "testing" ) const ( UNEXPECTED_END string = "Unexpected end of expression" INVALID_TOKEN_TRANSITION = "Cannot transition token types" INVALID_TOKEN_KIND = "Invalid token" UNCLOSED_QUOTES = "Unclosed string literal" UNCLOSED_BRACKETS = "Unclosed parameter bracket" UNBALANCED_PARENTHESIS = "Unbalanced parenthesis" INVALID_NUMERIC = "Unable to parse numeric value" UNDEFINED_FUNCTION = "Undefined function" ) /* Represents a test for parsing failures */ type ParsingFailureTest struct { Name string Input string Expected string } func TestParsingFailure(test *testing.T) { parsingTests := []ParsingFailureTest{ ParsingFailureTest{ Name: "Invalid equality comparator", Input: "1 = 1", Expected: INVALID_TOKEN_KIND, }, ParsingFailureTest{ Name: "Invalid equality comparator", Input: "1 === 1", Expected: INVALID_TOKEN_KIND, }, ParsingFailureTest{ Name: "Too many characters for logical operator", Input: "true &&& false", Expected: INVALID_TOKEN_KIND, }, ParsingFailureTest{ Name: "Too many characters for logical operator", Input: "true ||| false", Expected: INVALID_TOKEN_KIND, }, ParsingFailureTest{ Name: "Premature end to expression, via modifier", Input: "10 > 5 +", Expected: UNEXPECTED_END, }, ParsingFailureTest{ Name: "Premature end to expression, via comparator", Input: "10 + 5 >", Expected: UNEXPECTED_END, }, ParsingFailureTest{ Name: "Premature end to expression, via logical operator", Input: "10 > 5 &&", Expected: UNEXPECTED_END, }, ParsingFailureTest{ Name: "Premature end to expression, via ternary operator", Input: "true ?", Expected: UNEXPECTED_END, }, ParsingFailureTest{ Name: "Hanging REQ", Input: "'wat' =~", Expected: UNEXPECTED_END, }, ParsingFailureTest{ Name: "Invalid operator change to REQ", Input: " / =~", Expected: INVALID_TOKEN_TRANSITION, }, ParsingFailureTest{ Name: "Invalid starting token, comparator", Input: "> 10", Expected: INVALID_TOKEN_TRANSITION, }, ParsingFailureTest{ Name: "Invalid starting token, modifier", Input: "+ 5", Expected: INVALID_TOKEN_TRANSITION, }, ParsingFailureTest{ Name: "Invalid starting token, logical operator", Input: "&& 5 < 10", Expected: INVALID_TOKEN_TRANSITION, }, ParsingFailureTest{ Name: "Invalid NUMERIC transition", Input: "10 10", Expected: INVALID_TOKEN_TRANSITION, }, ParsingFailureTest{ Name: "Invalid STRING transition", Input: "'foo' 'foo'", Expected: INVALID_TOKEN_TRANSITION, }, ParsingFailureTest{ Name: "Invalid operator transition", Input: "10 > < 10", Expected: INVALID_TOKEN_TRANSITION, }, ParsingFailureTest{ Name: "Starting with unbalanced parens", Input: " ) ( arg2", Expected: INVALID_TOKEN_TRANSITION, }, ParsingFailureTest{ Name: "Unclosed bracket", Input: "[foo bar", Expected: UNCLOSED_BRACKETS, }, ParsingFailureTest{ Name: "Unclosed quote", Input: "foo == 'responseTime", Expected: UNCLOSED_QUOTES, }, ParsingFailureTest{ Name: "Constant regex pattern fail to compile", Input: "foo =~ '[abc'", Expected: string(syntax.ErrMissingBracket), }, ParsingFailureTest{ Name: "Unbalanced parenthesis", Input: "10 > (1 + 50", Expected: UNBALANCED_PARENTHESIS, }, ParsingFailureTest{ Name: "Multiple radix", Input: "127.0.0.1", Expected: INVALID_NUMERIC, }, ParsingFailureTest{ Name: "Undefined function", Input: "foobar()", Expected: UNDEFINED_FUNCTION, }, } runParsingFailureTests(parsingTests, test) } func runParsingFailureTests(parsingTests []ParsingFailureTest, test *testing.T) { var err error fmt.Printf("Running %d parsing test cases...\n", len(parsingTests)) for _, testCase := range parsingTests { _, err = NewEvaluableExpression(testCase.Input) if err == nil { test.Logf("Test '%s' failed", testCase.Name) test.Logf("Expected a parsing error, found no error.") test.Fail() continue } if !strings.Contains(err.Error(), testCase.Expected) { test.Logf("Test '%s' failed", testCase.Name) test.Logf("Got error: '%s', expected '%s'", err.Error(), testCase.Expected) test.Fail() continue } } }