/*
* Copyright 2015-2017 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.web.querydsl;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.web.querydsl.QuerydslPredicateArgumentResolver.*;
import java.util.Optional;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.core.MethodParameter;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.querydsl.QUser;
import org.springframework.data.querydsl.SimpleEntityPathResolver;
import org.springframework.data.querydsl.User;
import org.springframework.data.querydsl.binding.QuerydslBinderCustomizer;
import org.springframework.data.querydsl.binding.QuerydslBindings;
import org.springframework.data.querydsl.binding.QuerydslBindingsFactory;
import org.springframework.data.querydsl.binding.QuerydslPredicate;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.hateoas.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.servlet.ModelAndView;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.BooleanExpression;
/**
* Unit tests for {@link QuerydslPredicateArgumentResolver}.
*
* @author Christoph Strobl
* @author Oliver Gierke
*/
public class QuerydslPredicateArgumentResolverUnitTests {
static final TypeInformation<?> USER_TYPE = ClassTypeInformation.from(User.class);
public @Rule ExpectedException exception = ExpectedException.none();
QuerydslPredicateArgumentResolver resolver;
MockHttpServletRequest request;
@Before
public void setUp() {
resolver = new QuerydslPredicateArgumentResolver(new QuerydslBindingsFactory(SimpleEntityPathResolver.INSTANCE),
Optional.empty());
request = new MockHttpServletRequest();
}
@Test // DATACMNS-669
public void supportsParameterReturnsTrueWhenMethodParameterIsPredicateAndAnnotatedCorrectly() {
assertThat(resolver.supportsParameter(getMethodParameterFor("simpleFind", Predicate.class))).isTrue();
}
@Test // DATACMNS-669
public void supportsParameterReturnsTrueWhenMethodParameterIsPredicateButNotAnnotatedAsSuch() {
assertThat(resolver.supportsParameter(getMethodParameterFor("predicateWithoutAnnotation", Predicate.class)))
.isTrue();
}
@Test(expected = IllegalArgumentException.class) // DATACMNS-669
public void supportsParameterShouldThrowExceptionWhenMethodParameterIsNoPredicateButAnnotatedAsSuch() {
resolver.supportsParameter(getMethodParameterFor("nonPredicateWithAnnotation", String.class));
}
@Test // DATACMNS-669
public void supportsParameterReturnsFalseWhenMethodParameterIsNoPredicate() {
assertThat(resolver.supportsParameter(getMethodParameterFor("nonPredicateWithoutAnnotation", String.class)))
.isFalse();
}
@Test // DATACMNS-669
public void resolveArgumentShouldCreateSingleStringParameterPredicateCorrectly() throws Exception {
request.addParameter("firstname", "rand");
Predicate predicate = resolver.resolveArgument(getMethodParameterFor("simpleFind", Predicate.class), null,
new ServletWebRequest(request), null);
assertThat(predicate).isEqualTo((Predicate) QUser.user.firstname.eq("rand"));
}
@Test // DATACMNS-669
public void resolveArgumentShouldCreateMultipleParametersPredicateCorrectly() throws Exception {
request.addParameter("firstname", "rand");
request.addParameter("lastname", "al'thor");
Predicate predicate = resolver.resolveArgument(getMethodParameterFor("simpleFind", Predicate.class), null,
new ServletWebRequest(request), null);
assertThat(predicate).isEqualTo((Predicate) QUser.user.firstname.eq("rand").and(QUser.user.lastname.eq("al'thor")));
}
@Test // DATACMNS-669
public void resolveArgumentShouldCreateNestedObjectPredicateCorrectly() throws Exception {
request.addParameter("address.city", "two rivers");
Predicate predicate = resolver.resolveArgument(getMethodParameterFor("simpleFind", Predicate.class), null,
new ServletWebRequest(request), null);
BooleanExpression eq = QUser.user.address.city.eq("two rivers");
assertThat(predicate).isEqualTo((Predicate) eq);
}
@Test // DATACMNS-669
public void resolveArgumentShouldResolveTypePropertyFromPageCorrectly() throws Exception {
request.addParameter("address.city", "tar valon");
Predicate predicate = resolver.resolveArgument(getMethodParameterFor("pagedFind", Predicate.class, Pageable.class),
null, new ServletWebRequest(request), null);
assertThat(predicate).isEqualTo((Predicate) QUser.user.address.city.eq("tar valon"));
}
@Test // DATACMNS-669
public void resolveArgumentShouldHonorCustomSpecification() throws Exception {
request.addParameter("firstname", "egwene");
request.addParameter("lastname", "al'vere");
Predicate predicate = resolver.resolveArgument(getMethodParameterFor("specificFind", Predicate.class), null,
new ServletWebRequest(request), null);
assertThat(predicate).isEqualTo(
QUser.user.firstname.eq("egwene".toUpperCase()).and(QUser.user.lastname.toLowerCase().eq("al'vere")));
}
@Test // DATACMNS-669
public void shouldCreatePredicateForNonStringPropertyCorrectly() throws Exception {
request.addParameter("inceptionYear", "978");
Predicate predicate = resolver.resolveArgument(getMethodParameterFor("specificFind", Predicate.class), null,
new ServletWebRequest(request), null);
assertThat(predicate).isEqualTo((Predicate) QUser.user.inceptionYear.eq(978L));
}
@Test // DATACMNS-669
public void shouldCreatePredicateForNonStringListPropertyCorrectly() throws Exception {
request.addParameter("inceptionYear", new String[] { "978", "998" });
Predicate predicate = resolver.resolveArgument(getMethodParameterFor("specificFind", Predicate.class), null,
new ServletWebRequest(request), null);
assertThat(predicate).isEqualTo((Predicate) QUser.user.inceptionYear.in(978L, 998L));
}
@Test // DATACMNS-669
public void shouldExcludePropertiesCorrectly() throws Exception {
request.addParameter("address.street", "downhill");
request.addParameter("inceptionYear", "973");
Object predicate = resolver.resolveArgument(getMethodParameterFor("specificFind", Predicate.class), null,
new ServletWebRequest(request), null);
assertThat(predicate.toString()).isEqualTo(QUser.user.inceptionYear.eq(973L).toString());
}
@Test // DATACMNS-669
@SuppressWarnings("rawtypes")
public void extractTypeInformationShouldUseTypeExtractedFromMethodReturnTypeIfPredicateNotAnnotated() {
TypeInformation<?> type = ReflectionTestUtils.invokeMethod(resolver, "extractTypeInfo",
getMethodParameterFor("predicateWithoutAnnotation", Predicate.class));
assertThat(type).isEqualTo((TypeInformation) ClassTypeInformation.from(User.class));
}
@Test // DATACMNS-669
@SuppressWarnings("rawtypes")
public void detectsDomainTypesCorrectly() {
TypeInformation USER_TYPE = ClassTypeInformation.from(User.class);
TypeInformation MODELA_AND_VIEW_TYPE = ClassTypeInformation.from(ModelAndView.class);
assertThat(extractTypeInfo(getMethodParameterFor("forEntity"))).isEqualTo(USER_TYPE);
assertThat(extractTypeInfo(getMethodParameterFor("forResourceOfUser"))).isEqualTo(USER_TYPE);
assertThat(extractTypeInfo(getMethodParameterFor("forModelAndView"))).isEqualTo(MODELA_AND_VIEW_TYPE);
}
private static MethodParameter getMethodParameterFor(String methodName, Class<?>... args) throws RuntimeException {
try {
return new MethodParameter(Sample.class.getMethod(methodName, args), args.length == 0 ? -1 : 0);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
static class SpecificBinding implements QuerydslBinderCustomizer<QUser> {
public void customize(QuerydslBindings bindings, QUser user) {
bindings.bind(user.firstname).firstOptional((path, value) -> value.map(it -> path.eq(it.toUpperCase())));
bindings.bind(user.lastname).first((path, value) -> path.toLowerCase().eq(value));
bindings.excluding(user.address);
}
}
static interface Sample {
User predicateWithoutAnnotation(Predicate predicate);
User nonPredicateWithAnnotation(@QuerydslPredicate String predicate);
User nonPredicateWithoutAnnotation(String predicate);
User simpleFind(@QuerydslPredicate Predicate predicate);
Page<User> pagedFind(@QuerydslPredicate Predicate predicate, Pageable page);
User specificFind(@QuerydslPredicate(bindings = SpecificBinding.class) Predicate predicate);
HttpEntity<User> forEntity();
ModelAndView forModelAndView();
ResponseEntity<Resource<User>> forResourceOfUser();
}
public static class SampleRepo implements QuerydslBinderCustomizer<QUser> {
@Override
public void customize(QuerydslBindings bindings, QUser user) {
bindings.bind(QUser.user.firstname).first((path, value) -> path.contains(value));
}
}
}