[OLINGO-568] Added tests and exception messages

This commit is contained in:
mibo 2015-11-20 14:43:24 +01:00
parent 16a856eaea
commit 69ef9f5194
8 changed files with 126 additions and 56 deletions

View File

@ -54,7 +54,12 @@ public class SearchParser {
if (token == null) { if (token == null) {
throw new SearchParserException("No search String", SearchParserException.MessageKeys.NO_EXPRESSION_FOUND); throw new SearchParserException("No search String", SearchParserException.MessageKeys.NO_EXPRESSION_FOUND);
} }
return processSearchExpression(null); SearchExpression se = processSearchExpression(null);
if(!isEof()) {
throw new SearchParserException("Token left after end of search query parsing.",
SearchParserException.MessageKeys.INVALID_END_OF_QUERY_TOKEN_LEFT, token.getToken().name());
}
return se;
} }
private SearchExpression processSearchExpression(SearchExpression left) throws SearchParserException { private SearchExpression processSearchExpression(SearchExpression left) throws SearchParserException {
@ -71,16 +76,15 @@ public class SearchParser {
if (isToken(SearchQueryToken.Token.OPEN)) { if (isToken(SearchQueryToken.Token.OPEN)) {
processOpen(); processOpen();
expression = processSearchExpression(left); expression = processSearchExpression(left);
validateToken(SearchQueryToken.Token.CLOSE); if (expression == null) {
throw new SearchParserException("Brackets must contain an expression.",
SearchParserException.MessageKeys.NO_EXPRESSION_FOUND);
}
processClose(); processClose();
} else if (isTerm()) { } else if (isTerm()) {
expression = processTerm(); expression = processTerm();
} }
if (expression == null) {
throw new SearchParserException("Brackets must contain an expression.",
SearchParserException.MessageKeys.NO_EXPRESSION_FOUND);
}
if (isToken(SearchQueryToken.Token.AND) || isToken(SearchQueryToken.Token.OPEN) || isTerm()) { if (isToken(SearchQueryToken.Token.AND) || isToken(SearchQueryToken.Token.OPEN) || isTerm()) {
expression = processAnd(expression); expression = processAnd(expression);
@ -106,18 +110,15 @@ public class SearchParser {
return token != null && token.getToken() == toCheckToken; return token != null && token.getToken() == toCheckToken;
} }
private void validateToken(SearchQueryToken.Token toValidateToken) throws SearchParserException { private void processClose() throws SearchParserException {
if (!isToken(toValidateToken)) { if (isToken(Token.CLOSE)) {
String actualToken = token == null ? "null" : token.getToken().toString(); nextToken();
throw new SearchParserException("Expected " + toValidateToken + " but was " + actualToken, } else {
SearchParserException.MessageKeys.EXPECTED_DIFFERENT_TOKEN, toValidateToken.toString(), actualToken); throw new SearchParserException("Missing close bracket after open bracket.",
SearchParserException.MessageKeys.MISSING_CLOSE);
} }
} }
private void processClose() {
nextToken();
}
private void processOpen() { private void processOpen() {
nextToken(); nextToken();
} }
@ -137,7 +138,10 @@ public class SearchParser {
} else { } else {
if (isToken(SearchQueryToken.Token.AND) || isToken(SearchQueryToken.Token.OR)) { if (isToken(SearchQueryToken.Token.AND) || isToken(SearchQueryToken.Token.OR)) {
throw new SearchParserException("Operators must not be followed by an AND or an OR", throw new SearchParserException("Operators must not be followed by an AND or an OR",
SearchParserException.MessageKeys.INVALID_OPERATOR_AFTER_AND, token.getToken().toString()); SearchParserException.MessageKeys.INVALID_OPERATOR_AFTER_AND, token.getToken().name());
} else if(isEof()) {
throw new SearchParserException("Missing search expression after AND (found end of search query)",
SearchParserException.MessageKeys.INVALID_END_OF_QUERY, Token.AND.name());
} }
se = processSearchExpression(se); se = processSearchExpression(se);
return new SearchBinaryImpl(left, SearchBinaryOperatorKind.AND, se); return new SearchBinaryImpl(left, SearchBinaryOperatorKind.AND, se);
@ -148,6 +152,10 @@ public class SearchParser {
if (isToken(SearchQueryToken.Token.OR)) { if (isToken(SearchQueryToken.Token.OR)) {
nextToken(); nextToken();
} }
if(isEof()) {
throw new SearchParserException("Missing search expression after OR (found end of search query)",
SearchParserException.MessageKeys.INVALID_END_OF_QUERY, Token.OR.name());
}
SearchExpression se = processSearchExpression(left); SearchExpression se = processSearchExpression(left);
return new SearchBinaryImpl(left, SearchBinaryOperatorKind.OR, se); return new SearchBinaryImpl(left, SearchBinaryOperatorKind.OR, se);
} }

View File

@ -25,6 +25,7 @@ public class SearchParserException extends UriParserSyntaxException {
private static final long serialVersionUID = 5781553037561337795L; private static final long serialVersionUID = 5781553037561337795L;
public enum MessageKeys implements MessageKey { public enum MessageKeys implements MessageKey {
NO_EXPRESSION_FOUND,
/** parameter: message */ /** parameter: message */
TOKENIZER_EXCEPTION, TOKENIZER_EXCEPTION,
/** parameter: tokenCharacter */ /** parameter: tokenCharacter */
@ -33,9 +34,14 @@ public class SearchParserException extends UriParserSyntaxException {
INVALID_BINARY_OPERATOR_POSITION, INVALID_BINARY_OPERATOR_POSITION,
/** parameter: operatorkind */ /** parameter: operatorkind */
INVALID_NOT_OPERAND, INVALID_NOT_OPERAND,
/** parameters: - */
MISSING_CLOSE,
/** parameters: expectedToken actualToken */ /** parameters: expectedToken actualToken */
EXPECTED_DIFFERENT_TOKEN, EXPECTED_DIFFERENT_TOKEN,
NO_EXPRESSION_FOUND, /** parameters: actualToken */
INVALID_END_OF_QUERY,
/** parameters: left_over_token */
INVALID_END_OF_QUERY_TOKEN_LEFT,
/** parameter: operatorkind */ /** parameter: operatorkind */
INVALID_OPERATOR_AFTER_AND; INVALID_OPERATOR_AFTER_AND;

View File

@ -84,6 +84,7 @@ public class SearchTokenizer {
this.finished = true; this.finished = true;
return this; return this;
} }
public State finishAs(Token token) { public State finishAs(Token token) {
this.finished = true; this.finished = true;
return changeToken(token); return changeToken(token);
@ -97,6 +98,13 @@ public class SearchTokenizer {
return token; return token;
} }
public String getTokenName() {
if(token == null) {
return "NULL";
}
return token.name();
}
public State close() throws SearchTokenizerException { public State close() throws SearchTokenizerException {
return this; return this;
} }
@ -260,7 +268,7 @@ public class SearchTokenizer {
@Override @Override
public String toString() { public String toString() {
return this.getToken() + "=>{" + getLiteral() + "}"; return getToken() + "=>{" + getLiteral() + "}";
} }
} }
@ -292,7 +300,7 @@ public class SearchTokenizer {
public State init(char c) throws SearchTokenizerException { public State init(char c) throws SearchTokenizerException {
if (isFinished()) { if (isFinished()) {
throw new SearchTokenizerException(toString() + " is already finished.", throw new SearchTokenizerException(toString() + " is already finished.",
SearchTokenizerException.MessageKeys.ALREADY_FINISHED); SearchTokenizerException.MessageKeys.ALREADY_FINISHED, getTokenName());
} }
literal.append(c); literal.append(c);
return this; return this;
@ -348,10 +356,9 @@ public class SearchTokenizer {
public SearchWordState(State toConsume) throws SearchTokenizerException { public SearchWordState(State toConsume) throws SearchTokenizerException {
super(Token.WORD, toConsume.getLiteral()); super(Token.WORD, toConsume.getLiteral());
char[] chars = literal.toString().toCharArray(); for (int i = 0; i < literal.length(); i++) {
for (char aChar : chars) { if (!isAllowedWord(literal.charAt(i))) {
if (!isAllowedWord(aChar)) { forbidden(literal.charAt(i));
forbidden(aChar);
} }
} }
} }
@ -479,7 +486,8 @@ public class SearchTokenizer {
changeToken(Token.WORD).finish(); changeToken(Token.WORD).finish();
return new RwsState(); return new RwsState();
} }
return new SearchWordState(this).nextChar(c); literal.append(c);
return new SearchWordState(this);
} }
@Override @Override
public State close() throws SearchTokenizerException { public State close() throws SearchTokenizerException {
@ -511,7 +519,8 @@ public class SearchTokenizer {
changeToken(Token.WORD).finish(); changeToken(Token.WORD).finish();
return new RwsState(); return new RwsState();
} }
return new SearchWordState(this).nextChar(c); literal.append(c);
return new SearchWordState(this);
} }
@Override @Override
public State close() throws SearchTokenizerException { public State close() throws SearchTokenizerException {
@ -540,7 +549,8 @@ public class SearchTokenizer {
changeToken(Token.WORD).finish(); changeToken(Token.WORD).finish();
return new RwsState(); return new RwsState();
} }
return new SearchWordState(this).nextChar(c); literal.append(c);
return new SearchWordState(this);
} }
@Override @Override
public State close() throws SearchTokenizerException { public State close() throws SearchTokenizerException {
@ -620,7 +630,7 @@ public class SearchTokenizer {
states.add(state); states.add(state);
} else { } else {
throw new SearchTokenizerException("Last parsed state '" + state.toString() + "' is not finished.", throw new SearchTokenizerException("Last parsed state '" + state.toString() + "' is not finished.",
SearchTokenizerException.MessageKeys.NOT_FINISHED_QUERY); SearchTokenizerException.MessageKeys.NOT_FINISHED_QUERY, state.getTokenName());
} }
return states; return states;

View File

@ -25,15 +25,15 @@ public class SearchTokenizerException extends UriParserSyntaxException {
private static final long serialVersionUID = -8295456415309640166L; private static final long serialVersionUID = -8295456415309640166L;
public enum MessageKeys implements MessageKey { public enum MessageKeys implements MessageKey {
/** parameter: character */ /** parameter: character, TOKEN */
FORBIDDEN_CHARACTER, FORBIDDEN_CHARACTER,
/** parameter: TOKEN */ /** parameter: TOKEN */
NOT_EXPECTED_TOKEN, NOT_EXPECTED_TOKEN,
/** parameter: - */ /** parameter: TOKEN */
NOT_FINISHED_QUERY, NOT_FINISHED_QUERY,
/** parameter: - */ /** parameter: TOKEN */
INVALID_TOKEN_STATE, INVALID_TOKEN_STATE,
/** parameter: - */ /** parameter: TOKEN */
ALREADY_FINISHED; ALREADY_FINISHED;
@Override @Override

View File

@ -44,6 +44,15 @@ SearchParserException.INVALID_NOT_OPERAND=Invalid not operand for kind '%1$s' fo
SearchParserException.EXPECTED_DIFFERENT_TOKEN=Expected token '%1$s' but found '%2$s'. SearchParserException.EXPECTED_DIFFERENT_TOKEN=Expected token '%1$s' but found '%2$s'.
SearchParserException.NO_EXPRESSION_FOUND=No expression found. SearchParserException.NO_EXPRESSION_FOUND=No expression found.
SearchParserException.INVALID_OPERATOR_AFTER_AND=Invalid operator after AND found of kind '%1$s'. SearchParserException.INVALID_OPERATOR_AFTER_AND=Invalid operator after AND found of kind '%1$s'.
SearchParserException.INVALID_END_OF_QUERY=Invalid end of search query after '%1$s' (query must end with a search phrase or word).
SearchParserException.INVALID_END_OF_QUERY_TOKEN_LEFT=Invalid end of search query. Found not processed token '%1$s' at the end.
SearchParserException.MISSING_CLOSE=Missing closing bracket after an opening bracket.
SearchTokenizerException.FORBIDDEN_CHARACTER=Not allowed character '%1$s' found for token '%2$s'.
SearchTokenizerException.NOT_EXPECTED_TOKEN=Not expected token '%1$s' found.
SearchTokenizerException.NOT_FINISHED_QUERY=Search query end in an invalid state after token '%1$s'.
SearchTokenizerException.INVALID_TOKEN_STATE=Token '%1$s' is in an invalid state.
SearchTokenizerException.ALREADY_FINISHED=Token '%1$s' is already in finished state.
UriParserSemanticException.FUNCTION_NOT_FOUND=The function import '%1$s' has no function with parameters '%2$s'. UriParserSemanticException.FUNCTION_NOT_FOUND=The function import '%1$s' has no function with parameters '%2$s'.

View File

@ -18,7 +18,6 @@
*/ */
package org.apache.olingo.server.core.uri.parser.search; package org.apache.olingo.server.core.uri.parser.search;
import org.apache.olingo.server.api.ODataLibraryException;
import org.apache.olingo.server.api.uri.queryoption.SearchOption; import org.apache.olingo.server.api.uri.queryoption.SearchOption;
import org.apache.olingo.server.api.uri.queryoption.search.SearchExpression; import org.apache.olingo.server.api.uri.queryoption.search.SearchExpression;
import org.junit.Assert; import org.junit.Assert;
@ -71,6 +70,22 @@ public class SearchParserAndTokenizerTest {
assertQuery("NOT").resultsIn(SearchParserException.MessageKeys.INVALID_NOT_OPERAND); assertQuery("NOT").resultsIn(SearchParserException.MessageKeys.INVALID_NOT_OPERAND);
assertQuery("AND").resultsIn(SearchParserException.MessageKeys.INVALID_BINARY_OPERATOR_POSITION); assertQuery("AND").resultsIn(SearchParserException.MessageKeys.INVALID_BINARY_OPERATOR_POSITION);
assertQuery("OR").resultsIn(SearchParserException.MessageKeys.INVALID_BINARY_OPERATOR_POSITION); assertQuery("OR").resultsIn(SearchParserException.MessageKeys.INVALID_BINARY_OPERATOR_POSITION);
assertQuery("NOT a AND").resultsIn(SearchParserException.MessageKeys.INVALID_END_OF_QUERY);
assertQuery("NOT a OR").resultsIn(SearchParserException.MessageKeys.INVALID_END_OF_QUERY);
assertQuery("a AND").resultsIn(SearchParserException.MessageKeys.INVALID_END_OF_QUERY);
assertQuery("a OR").resultsIn(SearchParserException.MessageKeys.INVALID_END_OF_QUERY);
assertQuery("a OR b)").resultsIn(SearchParserException.MessageKeys.INVALID_END_OF_QUERY_TOKEN_LEFT);
assertQuery("a NOT b)").resultsIn(SearchParserException.MessageKeys.INVALID_END_OF_QUERY_TOKEN_LEFT);
assertQuery("a AND b)").resultsIn(SearchParserException.MessageKeys.INVALID_END_OF_QUERY_TOKEN_LEFT);
assertQuery("(a OR b").resultsIn(SearchParserException.MessageKeys.MISSING_CLOSE);
assertQuery("(a NOT b").resultsIn(SearchParserException.MessageKeys.MISSING_CLOSE);
assertQuery("((a AND b)").resultsIn(SearchParserException.MessageKeys.MISSING_CLOSE);
assertQuery("((a AND b OR c)").resultsIn(SearchParserException.MessageKeys.MISSING_CLOSE);
assertQuery("a AND (b OR c").resultsIn(SearchParserException.MessageKeys.MISSING_CLOSE);
assertQuery("(a AND ((b OR c)").resultsIn(SearchParserException.MessageKeys.MISSING_CLOSE);
} }
private static Validator assertQuery(String searchQuery) { private static Validator assertQuery(String searchQuery) {
@ -95,13 +110,17 @@ public class SearchParserAndTokenizerTest {
return this; return this;
} }
private void resultsIn(ODataLibraryException.MessageKey key) private void resultsIn(SearchParserException.MessageKey key)
throws SearchTokenizerException { throws SearchTokenizerException {
try { try {
resultsIn(searchQuery); resultsIn(searchQuery);
} catch (ODataLibraryException e) { } catch (SearchParserException e) {
Assert.assertEquals(SearchParserException.class, e.getClass()); Assert.assertEquals("SearchParserException with unexpected message '" + e.getMessage() +
Assert.assertEquals(key, e.getMessageKey()); "' was thrown.", key, e.getMessageKey());
if(log) {
System.out.println("Caught SearchParserException with message key " +
e.getMessageKey() + " and message " + e.getMessage());
}
return; return;
} }
Assert.fail("SearchParserException with message key " + key.getKey() + " was not thrown."); Assert.fail("SearchParserException with message key " + key.getKey() + " was not thrown.");

View File

@ -187,6 +187,15 @@ public class SearchParserTest extends SearchParser {
runEx(SearchParserException.MessageKeys.INVALID_OPERATOR_AFTER_AND, Token.WORD, Token.AND, Token.AND, Token.WORD); runEx(SearchParserException.MessageKeys.INVALID_OPERATOR_AFTER_AND, Token.WORD, Token.AND, Token.AND, Token.WORD);
} }
@Test
public void invalidQueryEnds() {
runEx(MessageKeys.INVALID_END_OF_QUERY, Token.WORD, Token.AND);
runEx(MessageKeys.INVALID_END_OF_QUERY, Token.WORD, Token.OR);
runEx(MessageKeys.INVALID_END_OF_QUERY, Token.NOT, Token.WORD, Token.OR);
runEx(MessageKeys.INVALID_END_OF_QUERY, Token.NOT, Token.WORD, Token.AND);
runEx(MessageKeys.INVALID_END_OF_QUERY_TOKEN_LEFT, Token.WORD, Token.AND, Token.WORD, Token.CLOSE);
}
@Test @Test
public void singleAnd() { public void singleAnd() {
runEx(SearchParserException.MessageKeys.INVALID_BINARY_OPERATOR_POSITION, Token.AND); runEx(SearchParserException.MessageKeys.INVALID_BINARY_OPERATOR_POSITION, Token.AND);
@ -194,7 +203,7 @@ public class SearchParserTest extends SearchParser {
@Test @Test
public void singleOpenBracket() { public void singleOpenBracket() {
runEx(SearchParserException.MessageKeys.EXPECTED_DIFFERENT_TOKEN, Token.OPEN); runEx(SearchParserException.MessageKeys.NO_EXPRESSION_FOUND, Token.OPEN);
} }
@Test @Test

View File

@ -290,11 +290,14 @@ public class SearchTokenizerTest {
@Test @Test
public void tokenizeInvalid() throws SearchTokenizerException { public void tokenizeInvalid() throws SearchTokenizerException {
// //
assertQuery("( abc AND) OR something").resultsIn(SearchTokenizerException.class); assertQuery("( abc AND) OR something").resultsIn(SearchTokenizerException.MessageKeys.FORBIDDEN_CHARACTER);
assertQuery("\"phrase\"word").resultsIn(SearchTokenizerException.class); assertQuery("\"phrase\"word").resultsIn(SearchTokenizerException.MessageKeys.FORBIDDEN_CHARACTER);
assertQuery("\"p\"w").resultsIn(SearchTokenizerException.class); assertQuery("\"p\"w").resultsIn(SearchTokenizerException.MessageKeys.FORBIDDEN_CHARACTER);
assertQuery("\"\"").resultsIn(SearchTokenizerException.class); assertQuery("\"\"").resultsIn(SearchTokenizerException.MessageKeys.INVALID_TOKEN_STATE);
assertQuery("some AND)").resultsIn(SearchTokenizerException.MessageKeys.FORBIDDEN_CHARACTER);
assertQuery("some OR)").resultsIn(SearchTokenizerException.MessageKeys.FORBIDDEN_CHARACTER);
assertQuery("some NOT)").enableLogging().resultsIn(SearchTokenizerException.MessageKeys.FORBIDDEN_CHARACTER);
} }
@Test @Test
@ -302,9 +305,15 @@ public class SearchTokenizerTest {
assertQuery("AND").resultsIn(AND); assertQuery("AND").resultsIn(AND);
assertQuery("OR").resultsIn(OR); assertQuery("OR").resultsIn(OR);
assertQuery("NOT").resultsIn(NOT); assertQuery("NOT").resultsIn(NOT);
assertQuery("a AND").resultsIn(WORD, AND);
assertQuery("o OR").resultsIn(WORD, OR);
assertQuery("n NOT").resultsIn(WORD, NOT);
assertQuery("NOT AND").resultsIn(NOT, AND); assertQuery("NOT AND").resultsIn(NOT, AND);
assertQuery("NOT and AND").resultsIn(NOT, WORD, AND);
assertQuery("NOT OR").resultsIn(NOT, OR); assertQuery("NOT OR").resultsIn(NOT, OR);
assertQuery("NOT a OR").resultsIn(NOT, WORD, OR);
assertQuery("NOT NOT").resultsIn(NOT, NOT); assertQuery("NOT NOT").resultsIn(NOT, NOT);
assertQuery("some AND other)").resultsIn(WORD, AND, WORD, CLOSE);
assertQuery("abc AND OR something").resultsIn(WORD, AND, OR, WORD); assertQuery("abc AND OR something").resultsIn(WORD, AND, OR, WORD);
assertQuery("abc AND \"something\" )").resultsIn(WORD, AND, PHRASE, CLOSE); assertQuery("abc AND \"something\" )").resultsIn(WORD, AND, PHRASE, CLOSE);
} }
@ -359,10 +368,6 @@ public class SearchTokenizerTest {
this.searchQuery = searchQuery; this.searchQuery = searchQuery;
} }
private static Validator init(String searchQuery) {
return new Validator(searchQuery);
}
@SuppressWarnings("unused") @SuppressWarnings("unused")
private Validator enableLogging() { private Validator enableLogging() {
log = true; log = true;
@ -378,22 +383,26 @@ public class SearchTokenizerTest {
} }
return this; return this;
} }
private void resultsIn(Class<? extends Exception> exception) throws SearchTokenizerException { // private void resultsIn(Class<? extends Exception> exception) throws SearchTokenizerException {
try { // try {
new SearchTokenizer().tokenize(searchQuery); // validate();
} catch (Exception e) { // } catch (Exception e) {
Assert.assertEquals(exception, e.getClass()); // Assert.assertEquals(exception, e.getClass());
return; // return;
} // }
Assert.fail("Expected exception " + exception.getClass().getSimpleName() + " was not thrown."); // Assert.fail("Expected exception " + exception.getClass().getSimpleName() + " was not thrown.");
} // }
private void resultsIn(SearchTokenizerException.MessageKey key) private void resultsIn(SearchTokenizerException.MessageKey key)
throws SearchTokenizerException { throws SearchTokenizerException {
try { try {
init(searchQuery).validate(); validate();
} catch (SearchTokenizerException e) { } catch (SearchTokenizerException e) {
Assert.assertEquals("SearchTokenizerException with unexpected message was thrown.", key, e.getMessageKey()); Assert.assertEquals("SearchTokenizerException with unexpected message was thrown.", key, e.getMessageKey());
if(log) {
System.out.println("Caught SearchTokenizerException with message key " +
e.getMessageKey() + " and message " + e.getMessage());
}
return; return;
} }
Assert.fail("No SearchTokenizerException was not thrown."); Assert.fail("No SearchTokenizerException was not thrown.");