Skip to content

Commit

Permalink
test both value expressions and query method expressions in datastore
Browse files Browse the repository at this point in the history
  • Loading branch information
diegomarquezp committed Dec 31, 2024
1 parent 2afede2 commit e858017
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.Mockito;
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.query.Parameter;
Expand All @@ -49,21 +51,32 @@ class DatastoreQueryLookupStrategyTests {

private DatastoreQueryMethod queryMethod;

private DatastoreQueryLookupStrategy datastoreQueryLookupStrategy;
private DatastoreQueryLookupStrategy[] lookupStrategies;

private ValueExpressionDelegate valueExpressionDelegate;

private QueryMethodEvaluationContextProvider evaluationContextProvider;

@BeforeEach
void initMocks() {
this.datastoreTemplate = mock(DatastoreTemplate.class);
this.datastoreMappingContext = new DatastoreMappingContext();
this.queryMethod = mock(DatastoreQueryMethod.class);
this.valueExpressionDelegate = mock(ValueExpressionDelegate.class);
this.datastoreQueryLookupStrategy = getDatastoreQueryLookupStrategy();
this.evaluationContextProvider = mock(QueryMethodEvaluationContextProvider.class);
this.lookupStrategies = new DatastoreQueryLookupStrategy[] {
getDatastoreQueryLookupStrategy(this.valueExpressionDelegate),
getDatastoreQueryLookupStrategy(this.evaluationContextProvider),
};
}

@Test
void resolveSqlQueryTest() {
/**
* int parameters are used as indexes of the two lookup strategies we use
*/
@ParameterizedTest
@ValueSource(ints = {0, 1})
void resolveSqlQueryTest(int lookupStrategyIndex) {
DatastoreQueryLookupStrategy lookupStrategy = this.lookupStrategies[lookupStrategyIndex];
String queryName = "fakeNamedQueryName";
String query = "fake query";
when(this.queryMethod.getNamedQueryName()).thenReturn(queryName);
Expand All @@ -89,23 +102,37 @@ void resolveSqlQueryTest() {
when(namedQueries.getQuery(queryName)).thenReturn(query);
when(valueExpressionDelegate.getEvaluationContextAccessor()).thenReturn(mock(QueryMethodValueEvaluationContextAccessor.class));

this.datastoreQueryLookupStrategy.resolveQuery(null, null, null, namedQueries);
lookupStrategy.resolveQuery(null, null, null, namedQueries);

verify(this.datastoreQueryLookupStrategy, times(1))
verify(lookupStrategy, times(1))
.createGqlDatastoreQuery(eq(Object.class), same(this.queryMethod), eq(query));
}

private DatastoreQueryLookupStrategy getDatastoreQueryLookupStrategy() {
DatastoreQueryLookupStrategy spannerQueryLookupStrategy =
private DatastoreQueryLookupStrategy getDatastoreQueryLookupStrategy(ValueExpressionDelegate valueExpressionDelegate) {
DatastoreQueryLookupStrategy lookupStrategy =
spy(
new DatastoreQueryLookupStrategy(
this.datastoreMappingContext,
this.datastoreTemplate,
this.valueExpressionDelegate));
doReturn(Object.class).when(spannerQueryLookupStrategy).getEntityType(any());
valueExpressionDelegate));
return prepareDatastoreQueryLookupStrategy(lookupStrategy);
}

private DatastoreQueryLookupStrategy getDatastoreQueryLookupStrategy(QueryMethodEvaluationContextProvider evaluationContextProvider) {
DatastoreQueryLookupStrategy lookupStrategy =
spy(
new DatastoreQueryLookupStrategy(
this.datastoreMappingContext,
this.datastoreTemplate,
evaluationContextProvider));
return prepareDatastoreQueryLookupStrategy(lookupStrategy);
}

private DatastoreQueryLookupStrategy prepareDatastoreQueryLookupStrategy(DatastoreQueryLookupStrategy base) {
doReturn(Object.class).when(base).getEntityType(any());
doReturn(this.queryMethod)
.when(spannerQueryLookupStrategy)
.when(base)
.createQueryMethod(any(), any(), any());
return spannerQueryLookupStrategy;
return base;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
import org.assertj.core.data.Offset;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
Expand All @@ -66,8 +68,11 @@
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
import org.springframework.data.repository.query.ValueExpressionDelegate;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.support.StandardEvaluationContext;

/** Tests for the GQL Query Method. */
class GqlDatastoreQueryTests {
Expand All @@ -87,6 +92,8 @@ class GqlDatastoreQueryTests {

private ValueExpressionDelegate valueExpressionDelegate;

private QueryMethodEvaluationContextProvider evaluationContextProvider;

@BeforeEach
void initMocks() {
this.queryMethod = mock(DatastoreQueryMethod.class);
Expand All @@ -100,26 +107,45 @@ void initMocks() {
when(this.datastoreEntityConverter.getConversions()).thenReturn(this.readWriteConversions);
this.valueExpressionDelegate = mock(ValueExpressionDelegate.class);
when(valueExpressionDelegate.getEvaluationContextAccessor()).thenReturn(mock(QueryMethodValueEvaluationContextAccessor.class));
this.evaluationContextProvider = mock(QueryMethodEvaluationContextProvider.class);
}

private GqlDatastoreQuery<Trade> createQuerySpy(
String gql, boolean isPageQuery, boolean isSliceQuery, boolean useValueExpressionDelegate) {
GqlDatastoreQuery<Trade> spy;
if (useValueExpressionDelegate) {
spy =
spy(
new GqlDatastoreQuery<>(
Trade.class,
this.queryMethod,
this.datastoreTemplate,
gql,
this.valueExpressionDelegate,
this.datastoreMappingContext));
} else {
spy = spy(new GqlDatastoreQuery<>(
Trade.class,
this.queryMethod,
this.datastoreTemplate,
gql,
this.evaluationContextProvider,
this.datastoreMappingContext));
}
return spy;
}

private GqlDatastoreQuery<Trade> createQuery(
String gql, boolean isPageQuery, boolean isSliceQuery) {
GqlDatastoreQuery<Trade> spy =
spy(
new GqlDatastoreQuery<>(
Trade.class,
this.queryMethod,
this.datastoreTemplate,
gql,
this.valueExpressionDelegate,
this.datastoreMappingContext));
String gql, boolean isPageQuery, boolean isSliceQuery, boolean useValueExpressionDelegate) {
GqlDatastoreQuery<Trade> spy = createQuerySpy(gql, isPageQuery, isSliceQuery, useValueExpressionDelegate);
doReturn(isPageQuery).when(spy).isPageQuery();
doReturn(isSliceQuery).when(spy).isSliceQuery();
return spy;
}

@Test
void compoundNameConventionTest() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
void compoundNameConventionTest(boolean useValueExpressionDelegate) {

String gql =
"SELECT * FROM "
Expand Down Expand Up @@ -169,9 +195,20 @@ void compoundNameConventionTest() {

doReturn(key).when(this.datastoreTemplate).getKey(any());

// to be used when using a query method evaluation context
EvaluationContext evaluationContext = new StandardEvaluationContext();
for (int i = 0; i < paramVals.length; i++) {
evaluationContext.setVariable(paramNames[i], paramVals[i]);
}
when(this.evaluationContextProvider.getEvaluationContext(any(), any()))
.thenReturn(evaluationContext);
when(this.evaluationContextProvider.getEvaluationContext(any(), any(), any()))
.thenReturn(evaluationContext);

// to be used when testing with a value expression delegate
this.valueExpressionDelegate = ValueExpressionDelegate.create();

GqlDatastoreQuery gqlDatastoreQuery = createQuery(gql, false, false);
GqlDatastoreQuery gqlDatastoreQuery = createQuery(gql, false, false, useValueExpressionDelegate);

doAnswer(
invocation -> {
Expand Down Expand Up @@ -223,8 +260,9 @@ void compoundNameConventionTest() {
verify(this.datastoreTemplate, times(1)).queryKeysOrEntities(any(), eq(Trade.class));
}

@Test
void pageableTest() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
void pageableTest(boolean useValueExpressionDelegate) {

String gql = "SELECT * FROM trades WHERE price=@price";

Expand All @@ -237,7 +275,7 @@ void pageableTest() {
when(parameters.hasPageableParameter()).thenReturn(true);
when(parameters.getPageableIndex()).thenReturn(1);

GqlDatastoreQuery gqlDatastoreQuery = createQuery(gql, false, false);
GqlDatastoreQuery gqlDatastoreQuery = createQuery(gql, false, false, useValueExpressionDelegate);

doAnswer(
invocation -> {
Expand All @@ -264,8 +302,9 @@ void pageableTest() {
verify(this.datastoreTemplate, times(1)).queryKeysOrEntities(any(), eq(Trade.class));
}

@Test
void pageableTestSort() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
void pageableTestSort(boolean useValueExpressionDelegate) {

String gql = "SELECT * FROM trades WHERE price=@price";

Expand All @@ -278,7 +317,7 @@ void pageableTestSort() {
when(parameters.hasSortParameter()).thenReturn(true);
when(parameters.getSortIndex()).thenReturn(1);

GqlDatastoreQuery gqlDatastoreQuery = createQuery(gql, false, false);
GqlDatastoreQuery gqlDatastoreQuery = createQuery(gql, false, false, useValueExpressionDelegate);

doAnswer(
invocation -> {
Expand All @@ -303,8 +342,9 @@ void pageableTestSort() {
verify(this.datastoreTemplate, times(1)).queryKeysOrEntities(any(), eq(Trade.class));
}

@Test
void pageableTestSlice() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
void pageableTestSlice(boolean useValueExpressionDelegate) {

String gql = "SELECT * FROM trades WHERE price=@price";

Expand All @@ -318,7 +358,7 @@ void pageableTestSlice() {
when(parameters.hasPageableParameter()).thenReturn(true);
when(parameters.getPageableIndex()).thenReturn(1);

GqlDatastoreQuery gqlDatastoreQuery = createQuery(gql, false, true);
GqlDatastoreQuery gqlDatastoreQuery = createQuery(gql, false, true, useValueExpressionDelegate);

Cursor cursor = Cursor.copyFrom("abc".getBytes());
List<Map> params = new ArrayList<>();
Expand Down Expand Up @@ -358,8 +398,9 @@ void pageableTestSlice() {
assertThat(params.get(1)).containsEntry("offset", cursor);
}

@Test
void pageableTestPage() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
void pageableTestPage(boolean useValueExpressionDelegate) {

String gql = "SELECT * FROM trades WHERE price=@price";
String expected = "SELECT * FROM trades WHERE price=@price LIMIT @limit OFFSET @offset";
Expand All @@ -374,7 +415,7 @@ void pageableTestPage() {
when(parameters.hasPageableParameter()).thenReturn(true);
when(parameters.getPageableIndex()).thenReturn(1);

GqlDatastoreQuery gqlDatastoreQuery = createQuery(gql, true, true);
GqlDatastoreQuery gqlDatastoreQuery = createQuery(gql, true, true, useValueExpressionDelegate);

Cursor cursor = Cursor.copyFrom("abc".getBytes());

Expand Down Expand Up @@ -419,8 +460,9 @@ void pageableTestPage() {
verify(this.datastoreTemplate, times(2)).queryKeysOrEntities(any(), eq(Trade.class));
}

@Test
void pageableTestPageCursor() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
void pageableTestPageCursor(boolean useValueExpressionDelegate) {
String gql = "SELECT * FROM trades WHERE price=@price";
String expected = "SELECT * FROM trades WHERE price=@price LIMIT @limit OFFSET @offset";

Expand All @@ -439,7 +481,7 @@ void pageableTestPageCursor() {
when(parameters.hasPageableParameter()).thenReturn(true);
when(parameters.getPageableIndex()).thenReturn(1);

GqlDatastoreQuery gqlDatastoreQuery = createQuery(gql, true, true);
GqlDatastoreQuery gqlDatastoreQuery = createQuery(gql, true, true, useValueExpressionDelegate);

Cursor cursor = Cursor.copyFrom("abc".getBytes());

Expand Down Expand Up @@ -475,8 +517,9 @@ void pageableTestPageCursor() {
verify(this.datastoreTemplate, times(1)).queryKeysOrEntities(any(), eq(Trade.class));
}

@Test
void streamResultTest() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
void streamResultTest(boolean useValueExpressionDelegate) {
Mockito.<Class>when(this.queryMethod.getReturnedObjectType()).thenReturn(Trade.class);
Parameters parameters = mock(Parameters.class);
when(this.queryMethod.getParameters()).thenReturn(parameters);
Expand All @@ -500,7 +543,7 @@ void streamResultTest() {
.when(this.datastoreTemplate)
.queryKeysOrEntities(any(), eq(Trade.class));

GqlDatastoreQuery gqlDatastoreQuery = createQuery("unusedGqlString", false, false);
GqlDatastoreQuery gqlDatastoreQuery = createQuery("unusedGqlString", false, false, useValueExpressionDelegate);

Object result = gqlDatastoreQuery.execute(new Parameters[0]);
assertThat(result).isInstanceOf(Stream.class);
Expand Down

0 comments on commit e858017

Please sign in to comment.