From 5c3461724fdbf5f1d62a7405c7130fb1bfd09a21 Mon Sep 17 00:00:00 2001 From: John Blum Date: Thu, 22 Sep 2016 22:28:57 -0700 Subject: [PATCH] SGF-534 - Fix ordered GemfireRepository.findAll(:Sort) queries. (cherry picked from commit a68a31a0521e3e63bfb283f4b7c5dc15f7d0f8cc) Signed-off-by: John Blum --- build.gradle | 1 + gradle.properties | 3 +- pom.xml | 8 + .../repository/query/QueryBuilder.java | 71 ++++- .../gemfire/repository/query/QueryString.java | 138 ++++++--- .../repository/query/support/OqlKeyword.java | 111 +++++++ .../support/SimpleGemfireRepository.java | 22 +- .../repository/OqlKeywordUnitTests.java | 99 +++++++ ...erTest.java => QueryBuilderUnitTests.java} | 83 +++--- .../query/QueryStringUnitTests.java | 278 +++++++++++------- .../repository/sample/PersonRepository.java | 5 +- ... => PersonRepositoryIntegrationTests.java} | 124 +++++--- 12 files changed, 695 insertions(+), 248 deletions(-) create mode 100644 src/main/java/org/springframework/data/gemfire/repository/query/support/OqlKeyword.java create mode 100644 src/test/java/org/springframework/data/gemfire/repository/OqlKeywordUnitTests.java rename src/test/java/org/springframework/data/gemfire/repository/query/{QueryBuilderTest.java => QueryBuilderUnitTests.java} (60%) rename src/test/java/org/springframework/data/gemfire/repository/sample/{PersonRepositoryTest.java => PersonRepositoryIntegrationTests.java} (63%) diff --git a/build.gradle b/build.gradle index 3fee192f..8ab14bbc 100644 --- a/build.gradle +++ b/build.gradle @@ -121,6 +121,7 @@ dependencies { testCompile("org.springframework:spring-test:$springVersion") { exclude group: "commons-logging", module: "commons-logging" } + testCompile "org.assertj:assertj-core:$assertjVersion" testCompile "junit:junit:$junitVersion" testCompile "org.hamcrest:hamcrest-core:$hamcrestVersion" testCompile "org.hamcrest:hamcrest-library:$hamcrestVersion" diff --git a/gradle.properties b/gradle.properties index 7b96194d..3835875f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,6 @@ -aspectjVersion=1.8.9 antlrVersion=2.7.7 +aspectjVersion=1.8.9 +assertjVersion=3.5.2 cdiVersion=1.0 gemfireVersion=1.0.0-incubating.M3 googleCodeFindbugsVersion=2.0.2 diff --git a/pom.xml b/pom.xml index 1179253d..e0f9f709 100644 --- a/pom.xml +++ b/pom.xml @@ -20,6 +20,7 @@ SGF 2.7.7 + 3.5.2 1.0.0-incubating.M3 2.0.2 1.01 @@ -160,6 +161,13 @@ test + + org.assertj + assertj-core + ${assertj.version} + test + + org.apache.openwebbeans.test cditest-owb diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/QueryBuilder.java b/src/main/java/org/springframework/data/gemfire/repository/query/QueryBuilder.java index dead037d..df8a0d1a 100644 --- a/src/main/java/org/springframework/data/gemfire/repository/query/QueryBuilder.java +++ b/src/main/java/org/springframework/data/gemfire/repository/query/QueryBuilder.java @@ -13,14 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.data.gemfire.repository.query; import org.springframework.data.gemfire.mapping.GemfirePersistentEntity; +import org.springframework.data.gemfire.repository.query.support.OqlKeyword; import org.springframework.data.repository.query.parser.PartTree; import org.springframework.util.Assert; /** - * The QueryBuilder class is used to build a QueryString. + * The QueryBuilder class is used to build a {@link QueryString}. * * @author Oliver Gierke * @author David Turanski @@ -30,22 +32,73 @@ import org.springframework.util.Assert; class QueryBuilder { static final String DEFAULT_ALIAS = "x"; + static final String SELECT_OQL_TEMPLATE = "SELECT %1$s * FROM /%2$s %3$s"; + static final String WHERE_CLAUSE_TEMPLATE = "%1$s WHERE %2$s"; private final String query; - public QueryBuilder(String source) { - Assert.hasText(source, "The OQL Query must be specified"); - this.query = source; + /* (non-Javadoc) */ + static String asQuery(GemfirePersistentEntity entity, PartTree tree) { + return String.format(SELECT_OQL_TEMPLATE, (tree.isDistinct() ? OqlKeyword.DISTINCT : ""), + entity.getRegionName(), DEFAULT_ALIAS).replaceAll("\\s{2,}", " "); } + /* (non-Javadoc) */ + static String validateQuery(String query) { + Assert.hasText(query, "An OQL Query must be specified"); + return query; + } + + /** + * Constructs an instance of {@link QueryBuilder} initialized with the given query {@link String}. + * + * @param query {@link String} containing the base OQL query. + * @see #validateQuery(String) + */ + public QueryBuilder(String query) { + this.query = validateQuery(query); + } + + /** + * Constructs an instance of {@link QueryBuilder} with the given {@link GemfirePersistentEntity} + * and {@link PartTree} that determines the GemFire {@link com.gemstone.gemfire.cache.Region} + * to query and whether the query should capture unique results. + * + * @param entity {@link GemfirePersistentEntity} used to determine the GemFire + * {@link com.gemstone.gemfire.cache.Region} to query. + * @param tree {@link PartTree} containing parts of the OQL Query for determining things + * like uniqueness. + * @see org.springframework.data.gemfire.mapping.GemfirePersistentEntity + * @see org.springframework.data.repository.query.parser.PartTree + */ public QueryBuilder(GemfirePersistentEntity entity, PartTree tree) { - this(String.format("SELECT%1$s * FROM /%2$s %3$s", (tree.isDistinct() ? " DISTINCT" : ""), - entity.getRegionName(), DEFAULT_ALIAS)); + this(asQuery(entity, tree)); } + /** + * Constructs a {@link QueryString} with the given {@link Predicate}. + * + * @param predicate {@link Predicate} to append to the query. + * @return a {@link QueryString} with the base OQL query and {@link Predicate}. + * @see org.springframework.data.gemfire.repository.query.Predicate + * @see #withPredicate(String, Predicate) + */ public QueryString create(Predicate predicate) { - return new QueryString(predicate != null ? String.format("%1$s WHERE %2$s", query, - predicate.toString(DEFAULT_ALIAS)) : query); + return new QueryString(withPredicate(this.query, predicate)); + } + + /** + * Appends the given {@link Predicate} to the given {@code query} {@link String}. + * + * @param query {@link String} containing the query. + * @param predicate {@link Predicate} to append to the query. + * @return a {@link String} containing the query with the {@link Predicate} appended, + * or just a {@link String} containing the query if the {@link Predicate} is {@literal null}. + * @see org.springframework.data.gemfire.repository.query.Predicate + */ + protected String withPredicate(String query, Predicate predicate) { + return (predicate == null ? query + : String.format(WHERE_CLAUSE_TEMPLATE, query, predicate.toString(DEFAULT_ALIAS))); } /* @@ -54,6 +107,6 @@ class QueryBuilder { */ @Override public String toString() { - return query; + return this.query; } } diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/QueryString.java b/src/main/java/org/springframework/data/gemfire/repository/query/QueryString.java index 9a6a9f86..628bfdf2 100644 --- a/src/main/java/org/springframework/data/gemfire/repository/query/QueryString.java +++ b/src/main/java/org/springframework/data/gemfire/repository/query/QueryString.java @@ -21,15 +21,16 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.gemstone.gemfire.cache.Region; + import org.springframework.data.domain.Sort; +import org.springframework.data.gemfire.repository.query.support.OqlKeyword; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; -import com.gemstone.gemfire.cache.Region; - /** - * Value object to work with OQL query strings. + * The QueryString class is a utility class for working with GemFire OQL query statement syntax. * * @author Oliver Gierke * @author David Turanski @@ -43,52 +44,74 @@ public class QueryString { protected static final Pattern TRACE_PATTERN = Pattern.compile(""); // OQL Query Templates - private static final String HINTS_QUERY_TEMPLATE = " %2$s"; - private static final String IMPORT_QUERY_TEMPLATE = "IMPORT %1$s; %2$s"; - private static final String LIMIT_QUERY_TEMPLATE = "%1$s LIMIT %2$d"; - private static final String SELECT_QUERY_TEMPLATE = "SELECT %1$s FROM /%2$s"; - private static final String TRACE_QUERY_TEMPLATE = " %1$s"; + private static final String HINTS_OQL_TEMPLATE = " %2$s"; + private static final String IMPORT_OQL_TEMPLATE = "IMPORT %1$s; %2$s"; + private static final String LIMIT_OQL_TEMPLATE = "%1$s LIMIT %2$d"; + private static final String SELECT_OQL_TEMPLATE = "SELECT %1$s FROM /%2$s"; + private static final String TRACE_OQL_TEMPLATE = " %1$s"; - // Query Regular Expression Patterns + // OQL Query Regular Expression Patterns private static final String IN_PATTERN = "(?<=IN (SET|LIST) )\\$\\d"; private static final String IN_PARAMETER_PATTERN = "(?<=IN (SET|LIST) \\$)\\d"; private static final String REGION_PATTERN = "\\/(\\/?\\w)+"; private final String query; - /** - * Creates a {@link QueryString} from the given {@link String} query. - * - * @param source a String containing the OQL Query. - */ - public QueryString(String source) { - Assert.hasText(source, "The OQL statement (Query) to execute must be specified!"); - this.query = source; + /* (non-Javadoc) */ + static String asQuery(Class domainType, boolean isCountQuery) { + return String.format(SELECT_OQL_TEMPLATE, (isCountQuery ? "count(*)" : "*"), + validateDomainType(domainType).getSimpleName()); + } + + /* (non-Javadoc) */ + static Class validateDomainType(Class domainType) { + Assert.notNull(domainType, "domainType must not be null"); + return domainType; + } + + /* (non-Javadoc) */ + static String validateQuery(String query) { + Assert.hasText(query, "An OQL Query must be specified"); + return query; } /** - * Creates a {@literal SELECT} query for the given domain class. + * Constructs an instance of {@link QueryString} initialized with the given GemFire OQL Query {@link String}. * - * @param domainClass must not be {@literal null}. + * @param query {@link String} specifying the GemFire OQL Query. + * @throws IllegalArgumentException if the query string is unspecified (null or empty). + */ + public QueryString(String query) { + this.query = validateQuery(query); + } + + /** + * Constructs a GemFire OQL {@literal SELECT} Query for the given domain class. + * + * @param domainType application domain object type to query; must not be {@literal null}. + * @see #QueryString(Class, boolean) */ @SuppressWarnings("unused") - public QueryString(Class domainClass) { - this(domainClass, false); + public QueryString(Class domainType) { + this(domainType, false); } /** - * Creates a {@literal SELECT} query for the given domain class. + * Constructs a GemFire OQL {@literal SELECT} Query for the given domain class. {@code isCountQuery} indicates + * whether to select a count or select the contents of the objects of the given domain object type. * - * @param domainClass must not be {@literal null}. - * @param isCountQuery indicates if this is a count query + * @param domainType application domain object type to query; must not be {@literal null}. + * @param isCountQuery boolean value to indicate if this is a count query. + * @throws IllegalArgumentException if {@code domainType} is null. + * @see #QueryString(String) */ - public QueryString(Class domainClass, boolean isCountQuery) { - this(String.format(SELECT_QUERY_TEMPLATE, (isCountQuery ? "count(*)" : "*"), domainClass.getSimpleName())); + public QueryString(Class domainType, boolean isCountQuery) { + this(asQuery(domainType, isCountQuery)); } /** - * Binds the given values to the {@literal IN} parameter keyword by expanding the given values into a comma-separated - * {@link String}. + * Binds the given {@link Collection} of values into the {@literal IN} parameters of the OQL Query by expanding + * the given values into a comma-separated {@link String}. * * @param values the values to bind, returns the {@link QueryString} as is if {@literal null} is given. * @return a Query String having "in" parameters bound with values. @@ -96,7 +119,7 @@ public class QueryString { public QueryString bindIn(Collection values) { if (values != null) { String valueString = StringUtils.collectionToDelimitedString(values, ", ", "'", "'"); - return new QueryString(query.replaceFirst(IN_PATTERN, String.format("(%s)", valueString))); + return new QueryString(this.query.replaceFirst(IN_PATTERN, String.format("(%s)", valueString))); } return this; @@ -113,7 +136,7 @@ public class QueryString { */ @SuppressWarnings("unused") public QueryString forRegion(Class domainClass, Region region) { - return new QueryString(query.replaceAll(REGION_PATTERN, region.getFullPath())); + return new QueryString(this.query.replaceAll(REGION_PATTERN, region.getFullPath())); } /** @@ -135,11 +158,11 @@ public class QueryString { } /** - * Appends the Sort order to this GemFire OQL Query string. + * Appends the {@link Sort} order to this GemFire OQL Query string. * - * @param sort the Sort object indicating the order by criteria. - * @return this QueryString with an ORDER BY clause if the Sort object is not null, or this QueryString as-is - * if the Sort object is null. + * @param sort {@link Sort} indicating the order of the query results. + * @return a new {@link QueryString} with an ORDER BY clause if {@link Sort} is not {@literal null}, + * or this {@link QueryString} as-is if {@link Sort} is {@literal null}. * @see org.springframework.data.domain.Sort * @see org.springframework.data.gemfire.repository.query.QueryString */ @@ -153,12 +176,30 @@ public class QueryString { orderClause.append(String.format("%1$s %2$s", order.getProperty(), order.getDirection())); } - return new QueryString(String.format("%1$s %2$s", this.query, orderClause.toString())); + return new QueryString(String.format("%1$s %2$s", makeDistinct(this.query), orderClause.toString())); } return this; } + /** + * Replaces the SELECT query with a SELECT DISTINCT query if the query does not contain the DISTINCT OQL keyword. + * + * @param query {@link String} containing the query to evaluate. + * @return a SELECT DISTINCT query if {@code query} does not contain the DISTINCT OQL keyword. + */ + String makeDistinct(String query) { + return (query.contains(OqlKeyword.DISTINCT.getKeyword()) ? query + : query.replaceFirst(OqlKeyword.SELECT.getKeyword(), + String.format("%1$s %2$s", OqlKeyword.SELECT.getKeyword(), OqlKeyword.DISTINCT.getKeyword()))); + } + + /** + * Applies HINTS to the OQL Query. + * + * @param hints array of {@link String Strings} containing query hints. + * @return a new {@link QueryString} if hints are not null or empty, or return this {@link QueryString}. + */ public QueryString withHints(String... hints) { if (!ObjectUtils.isEmpty(hints)) { StringBuilder builder = new StringBuilder(); @@ -168,23 +209,40 @@ public class QueryString { builder.append(String.format("'%1$s'", hint)); } - return new QueryString(String.format(HINTS_QUERY_TEMPLATE, builder.toString(), query)); + return new QueryString(String.format(HINTS_OQL_TEMPLATE, builder.toString(), query)); } return this; } + /** + * Applies an IMPORT to the OQL Query. + * + * @param importExpression {@link String} containing the import clause. + * @return a new {@link QueryString} if an import was declared, or return this {@link QueryString}. + */ public QueryString withImport(String importExpression) { - return (StringUtils.hasText(importExpression) ? new QueryString( - String.format(IMPORT_QUERY_TEMPLATE, importExpression, query)) : this); + return (StringUtils.hasText(importExpression) ? + new QueryString(String.format(IMPORT_OQL_TEMPLATE, importExpression, query)) : this); } + /** + * Applies a LIMIT to the OQL Query. + * + * @param limit {@link Integer} indicating the number of results to return from the query. + * @return a new {@link QueryString} if a limit was specified, or return this {@link QueryString}. + */ public QueryString withLimit(Integer limit) { - return (limit != null ? new QueryString(String.format(LIMIT_QUERY_TEMPLATE, query, limit)) : this); + return (limit != null ? new QueryString(String.format(LIMIT_OQL_TEMPLATE, query, limit)) : this); } + /** + * Applies TRACE logging to the OQL Query. + * + * @return a new {@link QueryString} with tracing enabled. + */ public QueryString withTrace() { - return new QueryString(String.format(TRACE_QUERY_TEMPLATE, query)); + return new QueryString(String.format(TRACE_OQL_TEMPLATE, query)); } /* diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/support/OqlKeyword.java b/src/main/java/org/springframework/data/gemfire/repository/query/support/OqlKeyword.java new file mode 100644 index 00000000..b824832c --- /dev/null +++ b/src/main/java/org/springframework/data/gemfire/repository/query/support/OqlKeyword.java @@ -0,0 +1,111 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.data.gemfire.repository.query.support; + +import org.springframework.util.StringUtils; + +/** + * The OqlKeyword enum represents the range of keywords (Reserved Words) + * in GemFire's Object Query Language (OQL). + * + * @author John Blum + * @see Supported Keywords + * @since 1.0.0 + */ +public enum OqlKeyword { + AND, + AS, + COUNT, + DISTINCT, + ELEMENT, + FROM, + HINT, + IMPORT, + IN, + IS_DEFINED, + IS_UNDEFINED, + LIMIT, + LIKE, + NOT, + NVL, + OR, + ORDER_BY("ORDER BY"), + SELECT, + SET, + TRACE, + TO_DATE, + TYPE, + WHERE; + + private final String keyword; + + /** + * Constructs an instance of the GemFire {@link OqlKeyword} enumerate value with an unspecified keyword. + * When the keyword is unspecified, it defaults to the {@link #name()} of the {@link OqlKeyword}. + * + * @see #OqlKeyword(String) + */ + OqlKeyword() { + this(null); + } + + /** + * Constructs an {@link OqlKeyword} enumerated value with the given GemFire OQL Keyword. + * + * @param keyword {@link String} specifying the GemFire OQL Keyword; + * can be {@literal null}. + */ + OqlKeyword(String keyword) { + this.keyword = keyword; + } + + /** + * Looks up an {@link OqlKeyword} for the given {@code keyword} {@link String}. + * + * @param keyword name of the GemFire OQL Keyword to lookup. + * @return an {@link OqlKeyword} enumerated value for the given {@code keyword} {@link String}. + * @throws IllegalArgumentException if {@code keyword} is not a valid GemFire OQL Keyword. + */ + public static OqlKeyword valueOfIgnoreCase(String keyword) { + for (OqlKeyword oqlKeyword : values()) { + if (oqlKeyword.getKeyword().equalsIgnoreCase(StringUtils.trimWhitespace(keyword))) { + return oqlKeyword; + } + } + + throw new IllegalArgumentException(String.format("[%s] is not a valid GemFire OQL Keyword", keyword)); + } + + /** + * Returns name of this GemFire OQL Keyword enumerated value. The keyword may have been + * explicitly defined when the {@link OqlKeyword} enumerated value was constructed, in which case + * this value is returned, otherwise this method returns {@link OqlKeyword#name()}. + * + * @return a {@link String} name for this GemFire OQL Keyword enumerated value. + * @see #name() + */ + public String getKeyword() { + return (StringUtils.hasText(this.keyword) ? this.keyword : name()); + } + + /* (non-Javadoc) */ + @Override + public String toString() { + return getKeyword(); + } +} diff --git a/src/main/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepository.java b/src/main/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepository.java index 2bc50a49..3764baff 100644 --- a/src/main/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepository.java +++ b/src/main/java/org/springframework/data/gemfire/repository/support/SimpleGemfireRepository.java @@ -39,7 +39,7 @@ import com.gemstone.gemfire.cache.query.SelectResults; /** * Basic Repository implementation for GemFire. - * + * * @author Oliver Gierke * @author David Turanski * @author John Blum @@ -56,12 +56,12 @@ public class SimpleGemfireRepository implements Gemf /** * Creates a new {@link SimpleGemfireRepository}. - * + * * @param template must not be {@literal null}. * @param entityInformation must not be {@literal null}. */ public SimpleGemfireRepository(GemfireTemplate template, EntityInformation entityInformation) { - + Assert.notNull(template); Assert.notNull(entityInformation); @@ -71,7 +71,7 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * + * * @see org.springframework.data.repository.CrudRepository#save(S) */ @Override @@ -82,7 +82,7 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * + * * @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable) */ @Override @@ -123,7 +123,7 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * + * * @see org.springframework.data.repository.CrudRepository#exists(java.io.Serializable) */ @Override @@ -144,7 +144,7 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * + * * @see org.springframework.data.repository.CrudRepository#findAll() */ @Override @@ -158,7 +158,7 @@ public class SimpleGemfireRepository implements Gemf * @see org.springframework.data.gemfire.repository.GemfireRepository.sort(:org.springframework.data.domain.Sort) */ @Override - public Iterable findAll(final Sort sort) { + public Iterable findAll(Sort sort) { QueryString query = new QueryString("SELECT * FROM /RegionPlaceholder") .forRegion(entityInformation.getJavaType(), template.getRegion()) .orderBy(sort); @@ -197,7 +197,7 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * + * * @see * org.springframework.data.repository.CrudRepository#delete(java.lang.Object) */ @@ -208,7 +208,7 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * + * * @see * org.springframework.data.repository.CrudRepository#delete(java.lang.Iterable) */ @@ -267,7 +267,7 @@ public class SimpleGemfireRepository implements Gemf /* * (non-Javadoc) - * + * * @see org.springframework.data.repository.CrudRepository#deleteAll() */ @Override diff --git a/src/test/java/org/springframework/data/gemfire/repository/OqlKeywordUnitTests.java b/src/test/java/org/springframework/data/gemfire/repository/OqlKeywordUnitTests.java new file mode 100644 index 00000000..3312a3de --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/repository/OqlKeywordUnitTests.java @@ -0,0 +1,99 @@ +/* + * Copyright 2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.data.gemfire.repository; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.springframework.data.gemfire.repository.query.support.OqlKeyword; + +/** + * Test suite of test cases testing the contract and functionality + * of the {@link OqlKeyword} enumerated type. + * + * @author John Blum + * @see org.junit.Test + * @see org.springframework.data.gemfire.repository.query.support.OqlKeyword + * @since 1.0.0 + */ +public class OqlKeywordUnitTests { + + @Rule + public ExpectedException exception = ExpectedException.none(); + + @Test + public void valueOfIgnoreCaseWithEnumeratedValuesIsSuccessful() { + for (OqlKeyword oqlKeyword : OqlKeyword.values()) { + assertThat(OqlKeyword.valueOfIgnoreCase(oqlKeyword.getKeyword())).isEqualTo(oqlKeyword); + } + } + + @Test + public void valueOfIgnoreCaseWithUnconventionalValuesIsSuccessful() { + assertThat(OqlKeyword.valueOfIgnoreCase("and")).isEqualTo(OqlKeyword.AND); + assertThat(OqlKeyword.valueOfIgnoreCase("As")).isEqualTo(OqlKeyword.AS); + assertThat(OqlKeyword.valueOfIgnoreCase("CoUnT")).isEqualTo(OqlKeyword.COUNT); + assertThat(OqlKeyword.valueOfIgnoreCase(" DISTINCT ")).isEqualTo(OqlKeyword.DISTINCT); + assertThat(OqlKeyword.valueOfIgnoreCase(" Order BY ")).isEqualTo(OqlKeyword.ORDER_BY); + } + + @Test + public void valueOfIgnoreCaseWithIllegalValuesThrowsIllegalArgumentException() { + assertIllegalOqlKeyword("AN"); + assertIllegalOqlKeyword("ASS"); + assertIllegalOqlKeyword("CNT"); + assertIllegalOqlKeyword("EXTINCT"); + assertIllegalOqlKeyword("TO"); + assertIllegalOqlKeyword("EXPORT"); + assertIllegalOqlKeyword("OUT"); + assertIllegalOqlKeyword("IS DEFINED"); + assertIllegalOqlKeyword("IS-UNDEFINED"); + assertIllegalOqlKeyword("UNLIKE"); + assertIllegalOqlKeyword("NIL"); + assertIllegalOqlKeyword("NULL VALUE"); + assertIllegalOqlKeyword("XOR"); + assertIllegalOqlKeyword("ORDER_BY"); + assertIllegalOqlKeyword("INSERT"); + assertIllegalOqlKeyword("UPDATE"); + assertIllegalOqlKeyword("LIST"); + assertIllegalOqlKeyword("CLASS"); + assertIllegalOqlKeyword("WHAT"); + assertIllegalOqlKeyword("WHEN"); + } + + protected void assertIllegalOqlKeyword(String keyword) { + exception.expect(IllegalArgumentException.class); + exception.expectCause(is(nullValue(Throwable.class))); + exception.expectMessage(String.format("[%s] is not a valid GemFire OQL Keyword", keyword)); + + OqlKeyword.valueOfIgnoreCase(keyword); + } + + @Test + public void getKeywordEqualsNameExceptForOrderBy() { + for (OqlKeyword oqlKeyword : OqlKeyword.values()) { + if (!OqlKeyword.ORDER_BY.equals(oqlKeyword)) { + assertThat(oqlKeyword.getKeyword()).isEqualTo(oqlKeyword.name()); + } + } + } +} diff --git a/src/test/java/org/springframework/data/gemfire/repository/query/QueryBuilderTest.java b/src/test/java/org/springframework/data/gemfire/repository/query/QueryBuilderUnitTests.java similarity index 60% rename from src/test/java/org/springframework/data/gemfire/repository/query/QueryBuilderTest.java rename to src/test/java/org/springframework/data/gemfire/repository/query/QueryBuilderUnitTests.java index c1eb85f5..4f8f400d 100644 --- a/src/test/java/org/springframework/data/gemfire/repository/query/QueryBuilderTest.java +++ b/src/test/java/org/springframework/data/gemfire/repository/query/QueryBuilderUnitTests.java @@ -16,11 +16,10 @@ package org.springframework.data.gemfire.repository.query; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -34,48 +33,46 @@ import org.springframework.data.gemfire.mapping.GemfirePersistentEntity; import org.springframework.data.repository.query.parser.PartTree; /** - * The QueryBuilderTest class is a test suite of test cases testing the contract and functionality of the QueryBuilder - * class. + * Test suite of test cases testing the contract and functionality of the {@link QueryBuilder} class. * * @author John Blum - * @see org.junit.Rule * @see org.junit.Test * @see org.mockito.Mockito * @see org.springframework.data.gemfire.repository.query.QueryBuilder * @since 1.7.0 */ -public class QueryBuilderTest { +public class QueryBuilderUnitTests { @Rule - public ExpectedException expectedException = ExpectedException.none(); + public ExpectedException exception = ExpectedException.none(); @Test - public void createQueryBuilderNonDistinct() { - GemfirePersistentEntity mockPersistentEntity = mock(GemfirePersistentEntity.class, "MockGemfirePersistentEntity"); - PartTree mockPartTree = mock(PartTree.class, "MockPartTree"); - - when(mockPersistentEntity.getRegionName()).thenReturn("Example"); - when(mockPartTree.isDistinct()).thenReturn(false); - - QueryBuilder queryBuilder = new QueryBuilder(mockPersistentEntity, mockPartTree); - - assertThat(queryBuilder.toString(), is(equalTo("SELECT * FROM /Example x"))); - - verify(mockPersistentEntity, times(1)).getRegionName(); - verify(mockPartTree, times(1)).isDistinct(); - } - - @Test - public void createQueryBuilderWithDistinct() { - GemfirePersistentEntity mockPersistentEntity = mock(GemfirePersistentEntity.class, "MockGemfirePersistentEntity"); - PartTree mockPartTree = mock(PartTree.class, "MockPartTree"); + public void createQueryBuilderWithDistinctQuery() { + GemfirePersistentEntity mockPersistentEntity = mock(GemfirePersistentEntity.class); + PartTree mockPartTree = mock(PartTree.class); when(mockPersistentEntity.getRegionName()).thenReturn("Example"); when(mockPartTree.isDistinct()).thenReturn(true); QueryBuilder queryBuilder = new QueryBuilder(mockPersistentEntity, mockPartTree); - assertThat(queryBuilder.toString(), is(equalTo("SELECT DISTINCT * FROM /Example x"))); + assertThat(queryBuilder.toString()).isEqualTo("SELECT DISTINCT * FROM /Example x"); + + verify(mockPersistentEntity, times(1)).getRegionName(); + verify(mockPartTree, times(1)).isDistinct(); + } + + @Test + public void createQueryBuilderWithNonDistinctQuery() { + GemfirePersistentEntity mockPersistentEntity = mock(GemfirePersistentEntity.class); + PartTree mockPartTree = mock(PartTree.class); + + when(mockPersistentEntity.getRegionName()).thenReturn("Example"); + when(mockPartTree.isDistinct()).thenReturn(false); + + QueryBuilder queryBuilder = new QueryBuilder(mockPersistentEntity, mockPartTree); + + assertThat(queryBuilder.toString()).isEqualTo("SELECT * FROM /Example x"); verify(mockPersistentEntity, times(1)).getRegionName(); verify(mockPartTree, times(1)).isDistinct(); @@ -83,28 +80,38 @@ public class QueryBuilderTest { @Test public void createQueryBuilderWithNullQueryString() { - expectedException.expect(IllegalArgumentException.class); - expectedException.expectCause(is(nullValue(Throwable.class))); - expectedException.expectMessage(is(equalTo("The OQL Query must be specified"))); + exception.expect(IllegalArgumentException.class); + exception.expectCause(is(nullValue(Throwable.class))); + exception.expectMessage(is(equalTo("An OQL Query must be specified"))); new QueryBuilder(null); } @Test + @SuppressWarnings("all") public void createWithPredicate() { - Predicate mockPredicate = mock(Predicate.class, "MockPredicate"); + Predicate mockPredicate = mock(Predicate.class); when(mockPredicate.toString(eq(QueryBuilder.DEFAULT_ALIAS))).thenReturn("x.id = 1"); - QueryBuilder queryBuilder = new QueryBuilder(String.format("SELECT * FROM /Example %s", - QueryBuilder.DEFAULT_ALIAS)); + QueryBuilder queryBuilder = new QueryBuilder( + String.format("SELECT * FROM /Example %s", QueryBuilder.DEFAULT_ALIAS)); QueryString queryString = queryBuilder.create(mockPredicate); - assertThat(queryString, is(notNullValue())); - assertThat(queryString.toString(), is(equalTo("SELECT * FROM /Example x WHERE x.id = 1"))); + assertThat(queryString).isNotNull(); + assertThat(queryString.toString()).isEqualTo("SELECT * FROM /Example x WHERE x.id = 1"); verify(mockPredicate, times(1)).toString(eq(QueryBuilder.DEFAULT_ALIAS)); } + @Test + public void createWithNullPredicate() { + QueryBuilder queryBuilder = new QueryBuilder("SELECT * FROM /Example"); + + QueryString queryString = queryBuilder.create(null); + + assertThat(queryString).isNotNull(); + assertThat(queryString.toString()).isEqualTo("SELECT * FROM /Example"); + } } diff --git a/src/test/java/org/springframework/data/gemfire/repository/query/QueryStringUnitTests.java b/src/test/java/org/springframework/data/gemfire/repository/query/QueryStringUnitTests.java index fd4962ac..59667177 100644 --- a/src/test/java/org/springframework/data/gemfire/repository/query/QueryStringUnitTests.java +++ b/src/test/java/org/springframework/data/gemfire/repository/query/QueryStringUnitTests.java @@ -16,19 +16,26 @@ package org.springframework.data.gemfire.repository.query; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.CoreMatchers.sameInstance; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.never; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.springframework.data.gemfire.repository.query.QueryString.HINT_PATTERN; +import static org.springframework.data.gemfire.repository.query.QueryString.IMPORT_PATTERN; +import static org.springframework.data.gemfire.repository.query.QueryString.LIMIT_PATTERN; +import static org.springframework.data.gemfire.repository.query.QueryString.TRACE_PATTERN; import java.util.Arrays; -import java.util.List; +import java.util.regex.Pattern; +import com.gemstone.gemfire.cache.Region; + +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; @@ -36,250 +43,299 @@ import org.springframework.data.domain.Sort; import org.springframework.data.gemfire.repository.sample.Person; import org.springframework.data.gemfire.repository.sample.RootUser; -import com.gemstone.gemfire.cache.Region; - /** - * + * Test suite of test cases testing the contract and functionality of the {@link QueryString} class. + * * @author Oliver Gierke * @author John Blum + * @see org.junit.Test + * @see org.mockito.Mock + * @see org.springframework.data.gemfire.repository.query.QueryString */ @RunWith(MockitoJUnitRunner.class) public class QueryStringUnitTests { + @Rule + public ExpectedException exception = ExpectedException.none(); + @Mock @SuppressWarnings("rawtypes") Region region; - protected Sort.Order createOrder(final String property) { - return createOrder(property, Sort.Direction.ASC); + protected Sort.Order newSortOrder(String property) { + return newSortOrder(property, Sort.Direction.ASC); } - protected Sort.Order createOrder(final String property, final Sort.Direction direction) { + protected Sort.Order newSortOrder(String property, Sort.Direction direction) { return new Sort.Order(direction, property); } - protected Sort createSort(Sort.Order... orders) { + protected Sort newSort(Sort.Order... orders) { return new Sort(orders); } @Test - public void createQueryStringWithCount() { - QueryString queryString = new QueryString(Person.class, true); - - assertThat(queryString, is(notNullValue())); - assertThat(queryString.toString(), is(equalTo("SELECT count(*) FROM /Person"))); + public void createQueryStringWithDomainType() { + assertThat(new QueryString(Person.class).toString()).isEqualTo("SELECT * FROM /Person"); } @Test - public void createQueryStringWithoutCount() { - QueryString queryString = new QueryString(Person.class); + public void createQueryStringWithDomainTypeHavingCount() { + assertThat(new QueryString(Person.class, true).toString()).isEqualTo("SELECT count(*) FROM /Person"); + } - assertThat(queryString, is(notNullValue())); - assertThat(queryString.toString(), is(equalTo("SELECT * FROM /Person"))); + @Test + public void createQueryStringWithNullDomainType() { + exception.expect(IllegalArgumentException.class); + exception.expectCause(is(nullValue(Throwable.class))); + exception.expectMessage(is(equalTo("domainType must not be null"))); + + new QueryString((Class) null); + } + + @Test + public void createQueryStringWithQuery() { + String query = "SELECT * FROM /Example"; + + assertThat(new QueryString(query).toString()).isEqualTo(query); + } + + @Test + public void createQueryStringWithUnspecifiedQueryThrowsIllegalArgumentException() { + assertUnspecifiedQueryThrowsIllegalArgumentException(""); + assertUnspecifiedQueryThrowsIllegalArgumentException(" "); + assertUnspecifiedQueryThrowsIllegalArgumentException(null); + } + + protected void assertUnspecifiedQueryThrowsIllegalArgumentException(String query) { + exception.expect(IllegalArgumentException.class); + exception.expectCause(is(nullValue(Throwable.class))); + exception.expectMessage(is(equalTo("An OQL Query must be specified"))); + + new QueryString(query); } @Test public void hintPatternMatches() { - assertThat(QueryString.HINT_PATTERN.matcher("").find(), is(true)); - assertThat(QueryString.HINT_PATTERN.matcher(" SELECT * FROM /Example WHERE id = $1").find(), is(true)); - assertThat(QueryString.HINT_PATTERN.matcher(" SELECT * FROM /Person WHERE lastName = $1 AND birthDate = $2").find(), is(true)); + assertThat(matches(HINT_PATTERN, "")).isTrue(); + assertThat(matches(HINT_PATTERN, " SELECT * FROM /Example WHERE id = $1")).isTrue(); + assertThat(matches(HINT_PATTERN, " SELECT * FROM /Person WHERE lastName = $1 AND birthDate = $2")).isTrue(); } @Test public void hintPatternNoMatches() { - assertThat(QueryString.HINT_PATTERN.matcher("HINT").find(), is(false)); - assertThat(QueryString.HINT_PATTERN.matcher("").find(), is(false)); - assertThat(QueryString.HINT_PATTERN.matcher("").find(), is(false)); - assertThat(QueryString.HINT_PATTERN.matcher("").find(), is(false)); - assertThat(QueryString.HINT_PATTERN.matcher("").find(), is(false)); - assertThat(QueryString.HINT_PATTERN.matcher("SELECT * FROM /Example").find(), is(false)); - assertThat(QueryString.HINT_PATTERN.matcher("SELECT * FROM /Hint").find(), is(false)); - assertThat(QueryString.HINT_PATTERN.matcher("SELECT x.hint FROM /Clues x WHERE x.hint > $1").find(), is(false)); + assertThat(matches(HINT_PATTERN, "HINT")).isFalse(); + assertThat(matches(HINT_PATTERN, "")).isFalse(); + assertThat(matches(HINT_PATTERN, "")).isFalse(); + assertThat(matches(HINT_PATTERN, "")).isFalse(); + assertThat(matches(HINT_PATTERN, "")).isFalse(); + assertThat(matches(HINT_PATTERN, "")).isFalse(); + assertThat(matches(HINT_PATTERN, "SELECT * FROM /Example")).isFalse(); + assertThat(matches(HINT_PATTERN, "SELECT * FROM /Hint")).isFalse(); + assertThat(matches(HINT_PATTERN, "SELECT x.hint FROM /Clues x WHERE x.hint > $1")).isFalse(); } @Test public void importPatternMatches() { - assertThat(QueryString.IMPORT_PATTERN.matcher("IMPORT *;").find(), is(true)); - assertThat(QueryString.IMPORT_PATTERN.matcher("IMPORT org.example.*;").find(), is(true)); - assertThat(QueryString.IMPORT_PATTERN.matcher("IMPORT org.example.Type;").find(), is(true)); - assertThat(QueryString.IMPORT_PATTERN.matcher("IMPORT org.example.app.DomainType; SELECT * FROM /Domain").find(), is(true)); + assertThat(matches(IMPORT_PATTERN, "IMPORT *;")).isTrue(); + assertThat(matches(IMPORT_PATTERN, "IMPORT org.example.*;")).isTrue(); + assertThat(matches(IMPORT_PATTERN, "IMPORT org.example.Type;")).isTrue(); + assertThat(matches(IMPORT_PATTERN, "IMPORT org.example.app.domain.DomainType; SELECT * FROM /DomainType")).isTrue(); } @Test public void importPatternNoMatches() { - assertThat(QueryString.IMPORT_PATTERN.matcher("IMPORT").find(), is(false)); - assertThat(QueryString.IMPORT_PATTERN.matcher("IMPORT ;").find(), is(false)); - assertThat(QueryString.IMPORT_PATTERN.matcher("IMPORT *").find(), is(false)); - assertThat(QueryString.IMPORT_PATTERN.matcher("SELECT * FROM /Example").find(), is(false)); + assertThat(matches(IMPORT_PATTERN, "IMPORT")).isFalse(); + assertThat(matches(IMPORT_PATTERN, "IMPORT ;")).isFalse(); + assertThat(matches(IMPORT_PATTERN, "IMPORT *")).isFalse(); + assertThat(matches(IMPORT_PATTERN, "IMPORT *:")).isFalse(); + assertThat(matches(IMPORT_PATTERN, "SELECT * FROM /Example")).isFalse(); } @Test public void limitPatternMatches() { - assertThat(QueryString.LIMIT_PATTERN.matcher("LIMIT 10").find(), is(true)); - assertThat(QueryString.LIMIT_PATTERN.matcher("SELECT * FROM /Example LIMIT 10").find(), is(true)); - assertThat(QueryString.LIMIT_PATTERN.matcher("SELECT * FROM /Example WHERE id = $1 LIMIT 10").find(), is(true)); + assertThat(matches(LIMIT_PATTERN, "LIMIT 0")).isTrue(); + assertThat(matches(LIMIT_PATTERN, "LIMIT 1")).isTrue(); + assertThat(matches(LIMIT_PATTERN, "LIMIT 10")).isTrue(); + assertThat(matches(LIMIT_PATTERN, "SELECT * FROM /Example LIMIT 10")).isTrue(); + assertThat(matches(LIMIT_PATTERN, "SELECT * FROM /Example WHERE id = $1 LIMIT 10")).isTrue(); } @Test public void limitPatternNoMatches() { - assertThat(QueryString.LIMIT_PATTERN.matcher("LIMIT").find(), is(false)); - assertThat(QueryString.LIMIT_PATTERN.matcher("LIMIT lO").find(), is(false)); - assertThat(QueryString.LIMIT_PATTERN.matcher("LIMIT FF").find(), is(false)); - assertThat(QueryString.LIMIT_PATTERN.matcher("LIMIT ten").find(), is(false)); - assertThat(QueryString.LIMIT_PATTERN.matcher("SELECT * FROM /Example LIMIT").find(), is(false)); - assertThat(QueryString.LIMIT_PATTERN.matcher("SELECT * FROM /Example WHERE id = $1 LIM 10").find(), is(false)); + assertThat(matches(LIMIT_PATTERN, "LIMIT")).isFalse(); + assertThat(matches(LIMIT_PATTERN, "LIMIT lO")).isFalse(); + assertThat(matches(LIMIT_PATTERN, "LIMIT AF")).isFalse(); + assertThat(matches(LIMIT_PATTERN, "LIMIT ten")).isFalse(); + assertThat(matches(LIMIT_PATTERN, "SELECT * FROM /Example LIMIT")).isFalse(); + assertThat(matches(LIMIT_PATTERN, "SELECT * FROM /Example WHERE id = $1 LIM 10")).isFalse(); } @Test public void tracePatternMatches() { - assertThat(QueryString.TRACE_PATTERN.matcher("").find(), is(true)); - assertThat(QueryString.TRACE_PATTERN.matcher(" SELECT * FROM /Example").find(), is(true)); - assertThat(QueryString.TRACE_PATTERN.matcher("SELECT * FROM /Example").find(), is(true)); - assertThat(QueryString.TRACE_PATTERN.matcher("SELECT * FROM /Example").find(), is(true)); + assertThat(matches(TRACE_PATTERN, "")).isTrue(); + assertThat(matches(TRACE_PATTERN, " SELECT * FROM /Example")).isTrue(); + assertThat(matches(TRACE_PATTERN, "SELECT * FROM /Example")).isTrue(); + assertThat(matches(TRACE_PATTERN, "SELECT * FROM /Example")).isTrue(); + } + + @Test + public void tracePatternNoMatches() { + assertThat(matches(TRACE_PATTERN, "TRACE")).isFalse(); + assertThat(matches(TRACE_PATTERN, "TRACE SELECT * FROM /Example")).isFalse(); + assertThat(matches(TRACE_PATTERN, "")).isFalse(); + } + + protected boolean matches(Pattern pattern, String value) { + return pattern.matcher(value).find(); } // SGF-251 @Test - public void replacesDomainObjectWithRegionPathCorrectly() { - QueryString query = new QueryString("SELECT * FROM /Person p WHERE p.firstname = $1"); + public void replacesDomainTypeSimpleNameWithRegionPathCorrectly() { + QueryString query = new QueryString(Person.class); when(region.getFullPath()).thenReturn("/foo/bar"); - assertThat(query.forRegion(Person.class, region).toString(), is("SELECT * FROM /foo/bar p WHERE p.firstname = $1")); + assertThat(query.forRegion(Person.class, region).toString()).isEqualTo("SELECT * FROM /foo/bar"); - verify(region, never()).getName(); + verify(region, times(1)).getFullPath(); } - - // SGF-156 - // SGF-251 + + // SGF-156, SGF-251 @Test - public void replacesDomainObjectWithPluralRegionPathCorrectly() { - QueryString query = new QueryString("SELECT * FROM /Persons p WHERE p.firstname = $1"); + public void replacesFromClauseWithRegionPathCorrectly() { + QueryString query = new QueryString("SELECT * FROM /Persons p WHERE p.lastname = $1"); when(region.getFullPath()).thenReturn("/People"); - assertThat(query.forRegion(Person.class, region).toString(), - is("SELECT * FROM /People p WHERE p.firstname = $1")); + assertThat(query.forRegion(Person.class, region).toString()) + .isEqualTo("SELECT * FROM /People p WHERE p.lastname = $1"); - verify(region, never()).getName(); + verify(region, times(1)).getFullPath(); } // SGF-252 @Test - public void replacesFullyQualifiedSubRegionReferenceWithRegionPathCorrectly() { + public void replacesFullyQualifiedSubRegionPathWithRegionPathCorrectly() { QueryString query = new QueryString("SELECT * FROM //Local/Root/Users u WHERE u.username = $1"); - when(region.getFullPath()).thenReturn("/Local/Root/Users"); + when(region.getFullPath()).thenReturn("/Remote/Root/Users"); - assertThat(query.forRegion(RootUser.class, region).toString(), is(equalTo( - "SELECT * FROM /Local/Root/Users u WHERE u.username = $1"))); + assertThat(query.forRegion(RootUser.class, region).toString()) + .isEqualTo("SELECT * FROM /Remote/Root/Users u WHERE u.username = $1"); - verify(region, never()).getName(); + verify(region, times(1)).getFullPath(); } @Test public void bindsInValuesCorrectly() { - QueryString query = new QueryString("SELECT * FROM /Person p WHERE p.firstname IN SET $1"); - List values = Arrays.asList(1, 2, 3); - assertThat(query.bindIn(values).toString(), is("SELECT * FROM /Person p WHERE p.firstname IN SET ('1', '2', '3')")); + QueryString query = new QueryString("SELECT * FROM /Collection WHERE elements IN SET $1"); + + assertThat(query.bindIn(Arrays.asList(1, 2, 3)).toString()) + .isEqualTo("SELECT * FROM /Collection WHERE elements IN SET ('1', '2', '3')"); } @Test public void detectsInParameterIndexesCorrectly() { - QueryString query = new QueryString("IN SET $1 OR IN SET $2"); - Iterable indexes = query.getInParameterIndexes(); - assertThat(indexes, is((Iterable) Arrays.asList(1, 2))); + QueryString query = new QueryString("SELECT * FROM /Example WHERE values IN SET $1 OR IN SET $2"); + + assertThat(query.getInParameterIndexes()).isEqualTo(Arrays.asList(1, 2)); } @Test public void addsNoOrderByClauseCorrectly() { QueryString query = new QueryString("SELECT * FROM /People p").orderBy(null); - assertThat(query.toString(), is(equalTo("SELECT * FROM /People p"))); + assertThat(query.toString()).isEqualTo("SELECT * FROM /People p"); } @Test public void addsOrderByClauseCorrectly() { QueryString query = new QueryString("SELECT * FROM /People p WHERE p.lastName = $1") - .orderBy(createSort(createOrder("lastName", Sort.Direction.DESC), createOrder("firstName"))); + .orderBy(newSort(newSortOrder("lastName", Sort.Direction.DESC), newSortOrder("firstName"))); - assertThat(query.toString(), is(equalTo( - "SELECT * FROM /People p WHERE p.lastName = $1 ORDER BY lastName DESC, firstName ASC"))); + assertThat(query.toString()) + .isEqualTo("SELECT DISTINCT * FROM /People p WHERE p.lastName = $1 ORDER BY lastName DESC, firstName ASC"); } @Test public void addsSingleOrderByClauseCorrectly() { QueryString query = new QueryString("SELECT DISTINCT p.lastName FROM /People p WHERE p.firstName = $1") - .orderBy(createSort(createOrder("lastName"))); + .orderBy(newSort(newSortOrder("lastName"))); - assertThat(query.toString(), is(equalTo( - "SELECT DISTINCT p.lastName FROM /People p WHERE p.firstName = $1 ORDER BY lastName ASC"))); + assertThat(query.toString()) + .isEqualTo("SELECT DISTINCT p.lastName FROM /People p WHERE p.firstName = $1 ORDER BY lastName ASC"); } @Test public void withHints() { - assertThat(new QueryString("SELECT * FROM /Example").withHints("IdIdx").toString(), - is(equalTo(" SELECT * FROM /Example"))); - assertThat(new QueryString("SELECT * FROM /Example").withHints("IdIdx", "SpatialIdx", "TxDateIdx").toString(), - is(equalTo(" SELECT * FROM /Example"))); + assertThat(new QueryString("SELECT * FROM /Example").withHints("IdIdx").toString()) + .isEqualTo(" SELECT * FROM /Example"); + + assertThat(new QueryString("SELECT * FROM /Example").withHints("IdIdx", "SpatialIdx", "TxDateIdx").toString()) + .isEqualTo(" SELECT * FROM /Example"); } @Test public void withoutHints() { QueryString expectedQueryString = new QueryString("SELECT * FROM /Example"); - assertThat(expectedQueryString.withHints((String[]) null), is(sameInstance(expectedQueryString))); - assertThat(expectedQueryString.withHints(), is(sameInstance(expectedQueryString))); + assertThat(expectedQueryString.withHints()).isSameAs(expectedQueryString); + assertThat(expectedQueryString.withHints((String[]) null)).isSameAs(expectedQueryString); } @Test public void withImport() { - assertThat(new QueryString("SELECT * FROM /People").withImport("org.example.app.domain.Person").toString(), - is(equalTo("IMPORT org.example.app.domain.Person; SELECT * FROM /People"))); + assertThat(new QueryString("SELECT * FROM /People").withImport("org.example.app.domain.Person").toString()) + .isEqualTo("IMPORT org.example.app.domain.Person; SELECT * FROM /People"); } @Test public void withoutImport() { QueryString expectedQueryString = new QueryString("SELECT * FROM /Example"); - assertThat(expectedQueryString.withImport(null), is(sameInstance(expectedQueryString))); - assertThat(expectedQueryString.withImport(""), is(sameInstance(expectedQueryString))); - assertThat(expectedQueryString.withImport(" "), is(sameInstance(expectedQueryString))); + assertThat(expectedQueryString.withImport(null)).isSameAs(expectedQueryString); + assertThat(expectedQueryString.withImport("")).isSameAs(expectedQueryString); + assertThat(expectedQueryString.withImport(" ")).isSameAs(expectedQueryString); } @Test public void withLimit() { - assertThat(new QueryString("SELECT * FROM /Example").withLimit(10).toString(), - is(equalTo("SELECT * FROM /Example LIMIT 10"))); - assertThat(new QueryString("SELECT * FROM /Example").withLimit(-5).toString(), - is(equalTo("SELECT * FROM /Example LIMIT -5"))); - assertThat(new QueryString("SELECT * FROM /Example").withLimit(0).toString(), - is(equalTo("SELECT * FROM /Example LIMIT 0"))); + assertThat(new QueryString("SELECT * FROM /Example").withLimit(10).toString()) + .isEqualTo("SELECT * FROM /Example LIMIT 10"); + + assertThat(new QueryString("SELECT * FROM /Example").withLimit(0).toString()) + .isEqualTo("SELECT * FROM /Example LIMIT 0"); + + assertThat(new QueryString("SELECT * FROM /Example").withLimit(-5).toString()) + .isEqualTo("SELECT * FROM /Example LIMIT -5"); } @Test public void withoutLimit() { QueryString expectedQueryString = new QueryString("SELECT * FROM /Example"); - assertThat(expectedQueryString.withLimit(null), is(sameInstance(expectedQueryString))); + + assertThat(expectedQueryString.withLimit(null)).isSameAs(expectedQueryString); } @Test public void withTrace() { - assertThat(new QueryString("SELECT * FROM /Example").withTrace().toString(), - is(equalTo(" SELECT * FROM /Example"))); + assertThat(new QueryString("SELECT * FROM /Example").withTrace().toString()) + .isEqualTo(" SELECT * FROM /Example"); } @Test public void withHintAndTrace() { - assertThat(new QueryString("SELECT * FROM /Example").withHints("IdIdx").withTrace().toString(), - is(equalTo(" SELECT * FROM /Example"))); + assertThat(new QueryString("SELECT * FROM /Example").withHints("IdIdx").withTrace().toString()) + .isEqualTo(" SELECT * FROM /Example"); } @Test public void withHintImportLimitAndTrace() { - assertThat(new QueryString("SELECT * FROM /Example").withImport("org.example.domain.Type") - .withHints("IdIdx", "NameIdx").withLimit(20).withTrace().toString(), - is(equalTo(" IMPORT org.example.domain.Type; SELECT * FROM /Example LIMIT 20"))); - } + QueryString query = new QueryString("SELECT * FROM /Example"); + assertThat(query.withImport("org.example.domain.Type").withHints("IdIdx", "NameIdx").withLimit(20).withTrace().toString()) + .isEqualTo(" IMPORT org.example.domain.Type; SELECT * FROM /Example LIMIT 20"); + } } diff --git a/src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepository.java b/src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepository.java index a4c28395..c283817c 100644 --- a/src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepository.java +++ b/src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepository.java @@ -52,11 +52,14 @@ public interface PersonRepository extends GemfireRepository { Collection findByFirstnameAndLastname(String firstName, String lastName); - @Trace Collection findByFirstnameIgnoreCaseAndLastnameIgnoreCase(String firstName, String lastName); + Collection findByFirstnameAndLastnameAllIgnoringCase(String firstName, String lastName); + Collection findByFirstnameOrLastname(String firstName, String lastName); + Collection findDistinctByFirstnameOrLastname(String firstName, String lastName, Sort order); + Person findByLastname(String lastName); Collection findByLastnameEndingWith(String lastName); diff --git a/src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepositoryTest.java b/src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepositoryIntegrationTests.java similarity index 63% rename from src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepositoryTest.java rename to src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepositoryIntegrationTests.java index 88f0e8f2..e802a195 100644 --- a/src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepositoryTest.java +++ b/src/test/java/org/springframework/data/gemfire/repository/sample/PersonRepositoryIntegrationTests.java @@ -16,14 +16,12 @@ package org.springframework.data.gemfire.repository.sample; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Iterator; import java.util.List; import java.util.Properties; import java.util.concurrent.atomic.AtomicLong; @@ -48,7 +46,7 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** - * The PersonRepositoryTest class... + * The PersonRepositoryIntegrationTests class... * * @author John Blum * @see org.junit.Test @@ -60,18 +58,20 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; * @since 1.4.0 */ @RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = PersonRepositoryTest.GemFireConfiguration.class) +@ContextConfiguration(classes = PersonRepositoryIntegrationTests.GemFireConfiguration.class) @SuppressWarnings("unused") -public class PersonRepositoryTest { +public class PersonRepositoryIntegrationTests { - private static final String GEMFIRE_LOG_LEVEL = System.getProperty("gemfire.log-level", "warning"); + private static final String DEFAULT_GEMFIRE_LOG_LEVEL = "warning"; + private static final String GEMFIRE_LOG_LEVEL = System.getProperty("gemfire.log-level", DEFAULT_GEMFIRE_LOG_LEVEL); - protected final AtomicLong ID_SEQUENCE = new AtomicLong(0l); + protected final AtomicLong ID_SEQUENCE = new AtomicLong(0L); private Person cookieDoe = newPerson("Cookie", "Doe"); private Person janeDoe = newPerson("Jane", "Doe"); private Person jonDoe = newPerson("Jon", "Doe"); private Person pieDoe = newPerson("Pie", "Doe"); + private Person sourDoe = newPerson("Sour", "Doe"); private Person jackHandy = newPerson("Jack", "Handy"); private Person sandyHandy = newPerson("Sandy", "Handy"); private Person imaPigg = newPerson("Ima", "Pigg"); @@ -82,6 +82,7 @@ public class PersonRepositoryTest { @Before public void setup() { if (personRepository.count() == 0) { + sourDoe = personRepository.save(sourDoe); sandyHandy = personRepository.save(sandyHandy); jonDoe = personRepository.save(jonDoe); jackHandy = personRepository.save(jackHandy); @@ -91,18 +92,28 @@ public class PersonRepositoryTest { cookieDoe = personRepository.save(cookieDoe); } - assertThat(personRepository.count(), is(equalTo(7L))); + assertThat(personRepository.count()).isEqualTo(8L); + } + + protected List asList(Iterable iterable) { + List list = new ArrayList(); + + for (T element : iterable) { + list.add(element); + } + + return list; } protected Person newPerson(String firstName, String lastName) { return new Person(ID_SEQUENCE.incrementAndGet(), firstName, lastName); } - protected Sort.Order newOrder(String property) { - return newOrder(property, Sort.Direction.ASC); + protected Sort.Order newSortOrder(String property) { + return newSortOrder(property, Sort.Direction.ASC); } - protected Sort.Order newOrder(String property, Sort.Direction direction) { + protected Sort.Order newSortOrder(String property, Sort.Direction direction) { return new Sort.Order(direction, property); } @@ -111,33 +122,71 @@ public class PersonRepositoryTest { } @Test - public void findDistinctPeopleOrderedByFirstnameDescending() { - List actualPeople = personRepository.findDistinctPeopleByOrderByLastnameDesc( - newSort(newOrder("firstname"))); + public void findAllPeopleSorted() { + Iterable people = personRepository.findAll(newSort(newSortOrder("firstname"))); - assertThat(actualPeople, is(notNullValue(List.class))); - assertThat(actualPeople.size(), is(equalTo(7))); - assertThat(actualPeople, is(equalTo(Arrays.asList( - imaPigg, jackHandy, sandyHandy, cookieDoe, janeDoe, jonDoe, pieDoe)))); + assertThat(people).isNotNull(); + + List peopleList = asList(people); + + assertThat(peopleList.size()).isEqualTo(8); + assertThat(peopleList).isEqualTo( + Arrays.asList(cookieDoe, imaPigg, jackHandy, janeDoe, jonDoe, pieDoe, sandyHandy, sourDoe)); } @Test - public void findDistinctPersonWithUnordered() { + public void findDistinctPeopleOrderedByLastnameDescendingFirstnameAscending() { + List actualPeople = personRepository.findDistinctPeopleByOrderByLastnameDesc( + newSort(newSortOrder("firstname"))); + + assertThat(actualPeople).isNotNull(); + assertThat(actualPeople.size()).isEqualTo(8); + assertThat(actualPeople).isEqualTo(Arrays.asList( + imaPigg, jackHandy, sandyHandy, cookieDoe, janeDoe, jonDoe, pieDoe, sourDoe)); + } + + @Test + public void findDistinctPeopleByLastnameUnordered() { List actualPeople = personRepository.findDistinctByLastname("Handy", null); - assertThat(actualPeople, is(notNullValue(List.class))); - assertThat(actualPeople.size(), is(equalTo(2))); - assertThat(String.format("Expected '%1$s'; but was '%2$s'", Arrays.asList(jackHandy, sandyHandy), actualPeople), - actualPeople, contains(jackHandy, sandyHandy)); + assertThat(actualPeople).isNotNull(); + assertThat(actualPeople.size()).isEqualTo(2); + assertThat(actualPeople).containsAll(Arrays.asList(jackHandy, sandyHandy)); + } + + @Test + public void findDistinctPeopleByFirstOrLastNameWithSort() { + Collection people = personRepository.findDistinctByFirstnameOrLastname("Cookie", "Pigg", + newSort(newSortOrder("lastname", Sort.Direction.DESC), newSortOrder("firstname", Sort.Direction.ASC))); + + assertThat(people).isNotNull(); + assertThat(people.size()).isEqualTo(2); + + Iterator peopleIterator = people.iterator(); + + assertThat(peopleIterator.hasNext()).isTrue(); + assertThat(peopleIterator.next()).isEqualTo(imaPigg); + assertThat(peopleIterator.hasNext()).isTrue(); + assertThat(peopleIterator.next()).isEqualTo(cookieDoe); + assertThat(peopleIterator.hasNext()).isFalse(); } @Test public void findPersonByFirstAndLastNameIgnoringCase() { Collection people = personRepository.findByFirstnameIgnoreCaseAndLastnameIgnoreCase("jON", "doE"); - assertThat(people, is(notNullValue(Collection.class))); - assertThat(people.size(), is(equalTo(1))); - assertThat(people.iterator().next(), is(equalTo(jonDoe))); + assertThat(people).isNotNull(); + assertThat(people.size()).isEqualTo(1); + assertThat(people.iterator().next()).isEqualTo(jonDoe); + } + + @Test + public void findByFirstAndLastNameAllIgnoringCase() { + Collection people = personRepository.findByFirstnameAndLastnameAllIgnoringCase("IMa", "PIGg"); + + assertThat(people).isNotNull(); + assertThat(people.size()).isEqualTo(1); + assertThat(people.iterator().next()).isEqualTo(imaPigg); } @Configuration @@ -146,24 +195,25 @@ public class PersonRepositoryTest { value = org.springframework.data.gemfire.repository.sample.PersonRepository.class)) public static class GemFireConfiguration { - String applicationName() { - return PersonRepositoryTest.class.getSimpleName(); - } - - String logLevel() { - return GEMFIRE_LOG_LEVEL; - } - Properties gemfireProperties() { Properties gemfireProperties = new Properties(); gemfireProperties.setProperty("name", applicationName()); gemfireProperties.setProperty("mcast-port", "0"); + gemfireProperties.setProperty("locators", ""); gemfireProperties.setProperty("log-level", logLevel()); return gemfireProperties; } + String applicationName() { + return PersonRepositoryIntegrationTests.class.getSimpleName(); + } + + String logLevel() { + return GEMFIRE_LOG_LEVEL; + } + @Bean CacheFactoryBean gemfireCache() { CacheFactoryBean gemfireCache = new CacheFactoryBean();