Test Controller that uses #QueryDsl Predicate and Page - spring-boot

I have a Controller, that is annotated with #RestController. For GET requests it is implemented like this:
#GetMapping
public Map<String, Object> getFilteredCars(
#QuerydslPredicate(root = Car.class) Predicate predicate,
#RequestParam(name = "page", defaultValue = "0") int page,
#RequestParam(name = "size", defaultValue = "150") int size
) {
return this.carService.getFilteredCars(
predicate,
PageRequest.of(page, size)
);
}
The Controller works fine, I can query with different parameters and get a page with a defined size.
Now I want to test the controller.
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest
class CarControllerTest {
#Mock
private CarService carService;
#InjectMock
private CarController carController;
#BeforeEach
void init(){
RestAssured.baseURI = "http://localhost";
RestAssured.port = 8810;
}
#Test
void givenUrl_whenSuccessOnGetCarsAndReturnsPage_thenCorrect() {
when(this.CarService.getFilteredCars(any(),any()))
.thenReturn(expectedResponse);
given()
.standaloneSetup(new CarController(this.carService))
.when()
.get("/cars?color=red&page=0&size=200")
.then()
.statusCode(200);
}
Running this gave me the exception:
No primary or default constructor found for interface com.querydsl.core.types.Predicate
Like in this question I added
#EnableSpringDataWebSupport
Now I get a new exception:
java.lang.IllegalStateException: Failed to load ApplicationContext
...
Caused by: org.springframework.beans.factory.support.BeanDefinitionOverrideException:
Invalid bean definition with name 'pageableResolver' defined in class path resource [org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.class]:
Cannot register bean definition
[Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
factoryMethodName=pageableResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.class]] for bean 'pageableResolver':
There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.data.web.config.HateoasAwareSpringDataWebConfiguration;
factoryMethodName=pageableResolver; initMethodName=null; destroyMethodName=(inferred);
defined in class path resource [org/springframework/data/web/config/HateoasAwareSpringDataWebConfiguration.class]] bound.
...
I don't understand what is happening. Has anyone an idea, how to resolve the problem?

Related

how to disable autoconfiguration in integration test?

I am trying this:
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
)
#EnableAutoConfiguration(
exclude = [
ReactiveOAuth2ClientAutoConfiguration::class,
ReactiveOAuth2ResourceServerAutoConfiguration::class,
]
)
#ActiveProfiles("testcontainers")
internal class RecordIntegrationTest {
#Test
fun contextLoads() {}
}
With testcontainers profile in application.yml:
spring:
config.activate.on-profile: testcontainers
r2dbc.url: r2dbc:tc:postgresql:///databasename?TC_IMAGE_TAG=13.2
flyway:
enabled: false
But then it tries to create repositories twice (and potentially other beans as well):
java.lang.IllegalStateException: Failed to load ApplicationContext
...
Caused by: org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'recordRepository' defined in com.example.RecordRepository defined in #EnableR2dbcRepositories declared on R2dbcRepositoriesAutoConfigureRegistrar.EnableR2dbcRepositoriesConfiguration: Cannot register bean definition [Root bean: class [org.springframework.data.r2dbc.repository.support.R2dbcRepositoryFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in com.example.RecordRepository defined in #EnableR2dbcRepositories declared on R2dbcRepositoriesAutoConfigureRegistrar.EnableR2dbcRepositoriesConfiguration] for bean 'recordRepository': There is already [Root bean: class [org.springframework.data.r2dbc.repository.support.R2dbcRepositoryFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in com.example.RecordRepository defined in #EnableR2dbcRepositories declared on R2dbcRepositoriesAutoConfigureRegistrar.EnableR2dbcRepositoriesConfiguration] bound.
UPDATE
Found out that the test runs fine when I move it to the root package
src/test/kotlin/com/example/backend
But it fails when I put it in the package
`src/test/kotlin/com/example/backend/subfolder`
UPDATE 2
I figured out that if a repository defined under src/main/kotlin is in the same package as the integration test that exception shows up.
E.g.:
data class X(#Id id: Long? = null)
// src/main/kotlin/com/example/x/XRepository.kt
interface XRepository : CoroutineCrudRepository<X, Long> {}
This test fails with the above exception:
// src/test/kotlin/com/example/x/XIntegrationTest.kt
#SpringBootTest(classes = [BackendApplication::class])
#EnableAutoConfiguration(
exclude = [
ReactiveOAuth2ClientAutoConfiguration::class,
ReactiveOAuth2ResourceServerAutoConfiguration::class,
]
)
#ActiveProfiles("testcontainers")
internal class XIntegrationTest {
#Test
fun contextLoads() {
}
}
Deleting the repository or moving the integration test to a different package works fine.
Include DataSourceAutoConfiguration in exclude block used by EnableR2dbcRepositories which disables the checking of datasource while starting up your integration test which is used by Repositories or you can configure the datasource to fix this issue.
The issue is basically due to the unavailability of datasource required by the repositories
Somewhere in your src/main/java you have a class that brings the annotation #EnableAutoConfiguration. This is the class that you want to exclude from your tests, when you have defined your own #EnableAutoConfiguration in some other class in test package.

Registering test bean with same name in Spring Framework 5.1 [duplicate]

This question already has answers here:
SpringBoot - BeanDefinitionOverrideException: Invalid bean definition
(8 answers)
Closed 3 years ago.
I'm having following config in my production files:
#Configuration
internal class Config {
#Bean
fun clock() = Clock.systemUTC()
}
In tests:
#Configuration
class ClockTestConfiguration {
#Bean
fun clock() = SetableClock()
}
My test annotations:
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = [
MyApplication::class,
ClockTestConfiguration::class
]
)
class MyTest {
...
When I was using Spring Boot 2.0.5.RELEASE it worked like a charm. After upgrading to 2.1.0.RELEASE it fails during bean registration.
Caused by: org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'clock' defined in com.foo.clock.ClockTestConfiguration:
Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=clockTestConfiguration; factoryMethodName=clock; initMethodName=null; destroyMethodName=(inferred);
defined in com.foo.clock.ClockTestConfiguration] for bean 'clock': There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=config; factoryMethodName=clock; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [com/foo/clock/Config.class]] bound.
at org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(DefaultListableBeanFactory.java:894)
Is there a clean way to override such bean?
You could use the properties attribute of #SpringBootTest to set spring.main.allow-bean-definition-overriding=true.

Invalid bean definition with name 'configServicePropertySource'

I have defined ConfigServicePropertySourceLocator bean like following
#Primary
#Bean
public ConfigServicePropertySourceLocator configServicePropertySource(
ConfigClientProperties configClientProperties) {
ConfigServicePropertySourceLocator sourceLocator = new ConfigServicePropertySourceLocator(
configClientProperties);
sourceLocator.setRestTemplate(clientOnlyRestTemplate());
return sourceLocator;
}
but i get the following exception (This is how it is printed in docker) although my bean is marked as #Primary
WARN 1 --- [ main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt:
org.springframework.beans.factory.support.BeanDefinitionOverrideException:
Invalid bean definition with name 'configServicePropertySource' defined in de.ig.client.security.configuration.ConfigClientSecurityConfiguration:
Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3;
dependencyCheck=0; autowireCandidate=true; primary=true; factoryBeanName=configClientSecurityConfiguration;
factoryMethodName=configServicePropertySource; initMethodName=null; destroyMethodName=(inferred);
defined in de.ig.client.security.configuration.ConfigClientSecurityConfiguration] for bean 'configServicePropertySource':
There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0;
autowireCandidate=true; primary=false; factoryBeanName=configServiceBootstrapConfiguration;
factoryMethodName=configServicePropertySource; initMethodName=null; destroyMethodName=(inferred);
defined in org.springframework.cloud.config.client.ConfigServiceBootstrapConfiguration] bound.
Solved it by setting
spring.main.allow-bean-definition-overriding to true
New from boot version 2.1

SprinBoot app launched from IDE - SpelEvaluationException

I have a simple SpringBoot app with web security and method security config using a custom SecurityExpressionRoot.
When I launch the SB app from an IDE (e.g. STS or IDEA) and call an endpoint I get
org.springframework.expression.spel.SpelEvaluationException: EL1004E:
Method call: Method hasAnyAccess(java.lang.String) cannot be found on
org.springframework.security.access.expression.method.MethodSecurityExpressionRoot type
When I launch the SB app from CLI with the app jar file the endpoint call succeeds:
INFO 11282 --- [nio-8080-exec-4] c.e.CustomMethodSecurityExpressionRoot :
< CustomMethodSecurityExpressionRoot(): [org.springframework.security.authentication.UsernamePasswordAuthenticationToken#442a8f33: Principal: org.springframework.security.core.userdetails.User#36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#166c8: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 36E17CAA657E897B1682BFF27EA7DA1F; Granted Authorities: ROLE_USER]
INFO 11282 --- [nio-8080-exec-4] c.e.CustomMethodSecurityExpressionRoot :
> hasAnyAccess(): [[FULL_ACCESS]]
ANY help is highly appreciated.
Web security configuration:
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
}
Method security configuration:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
CustomMethodSecurityExpressionHandler expressionHandler = new CustomMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new CustomPermissionEvaluator());
return expressionHandler;
}
}
Method security expression handler:
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
#Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) {
CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(this.trustResolver);
root.setRoleHierarchy(getRoleHierarchy());
return root;
}
}
Security expression root:
public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
public CustomMethodSecurityExpressionRoot(Authentication authentication) {
super(authentication);
LOGGER.info("< CustomMethodSecurityExpressionRoot(): [{}]", authentication);
}
public final boolean hasAnyAccess(String... accessLevels) {
LOGGER.info("> hasAnyAccess(): [{}]", Arrays.asList(accessLevels));
return true;
}
}
Rest controller:
#RestController
#RequestMapping("/")
public class MyController {
#PreAuthorize("hasAnyRole('ROLE_USER') && hasAnyAccess('FULL_ACCESS')")
#RequestMapping(value = "hello", method = RequestMethod.GET)
public String hello(#RequestParam String name) {
return "Hello '" + name + "' at " + System.currentTimeMillis() + "\n";
}
}
Update 1:
The same SpelEvaluationException happens when the SB app is launched with
mvn spring-boot:run
Update 2:
From the logs it seem when launching from IDE my MethodSecurityConfig extension is overridden by GlobalMethodSecurityConfiguration:
INFO 5927 --- [ main] o.s.b.f.s.DefaultListableBeanFactory :
Overriding bean definition for bean 'methodSecurityInterceptor' with a different definition:
replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=methodSecurityConfig; factoryMethodName=methodSecurityInterceptor; initMethodName=null; destroyMethodName=(inferred);
defined in class path resource [com/example/MethodSecurityConfig.class]]
with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; factoryMethodName=methodSecurityInterceptor; initMethodName=null; destroyMethodName=(inferred);
defined in class path resource [org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.class]]
INFO 5927 --- [ main] o.s.b.f.s.DefaultListableBeanFactory :
Overriding bean definition for bean 'methodSecurityMetadataSource' with a different definition:
replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=methodSecurityConfig; factoryMethodName=methodSecurityMetadataSource; initMethodName=null; destroyMethodName=(inferred);
defined in class path resource [com/example/MethodSecurityConfig.class]]
with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; factoryMethodName=methodSecurityMetadataSource; initMethodName=null; destroyMethodName=(inferred);
defined in class path resource [org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.class]]
When launching from JAR my MethodSecurityConfig extension overrides the GlobalMethodSecurityConfiguration:
INFO 6092 --- [ main] o.s.b.f.s.DefaultListableBeanFactory :
Overriding bean definition for bean 'methodSecurityInterceptor' with a different definition:
replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; factoryMethodName=methodSecurityInterceptor; initMethodName=null; destroyMethodName=(inferred);
defined in class path resource [org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.class]]
with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=methodSecurityConfig; factoryMethodName=methodSecurityInterceptor; initMethodName=null; destroyMethodName=(inferred);
defined in class path resource [com/example/MethodSecurityConfig.class]]
INFO 6092 --- [ main] o.s.b.f.s.DefaultListableBeanFactory :
Overriding bean definition for bean 'methodSecurityMetadataSource' with a different definition:
replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; factoryMethodName=methodSecurityMetadataSource; initMethodName=null; destroyMethodName=(inferred);
defined in class path resource [org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.class]]
with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=methodSecurityConfig; factoryMethodName=methodSecurityMetadataSource; initMethodName=null; destroyMethodName=(inferred);
defined in class path resource [com/example/MethodSecurityConfig.class]]
After some investigation it turned out the #EnableGlobalMethodSecurity(prePostEnabled = true) annotation specified for both
WebSecurityConfigurerAdapter extension and
GlobalMethodSecurityConfiguration extension
causes the problem. Removing the annotation from WebSecurityConfigurerAdapter solves the issue.

Spring OAUTH: Override CheckTokenEndpoint 'check_token?token=' response map

I would like to override the CheckTokenEndpoint to provide my own custom output as Map to the resource server. I have tried the following, but not working.
Introducing new custom controller for (/oauth/check_token), but Spring rejects this custom and registers its own.
Overriding bean definition for bean 'checkTokenEndpoint' with a
different definition: replacing [Generic bean: class
[com.datami.auth.security.CheckTokenEndpoint]; scope=singleton;
abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0;
autowireCandidate=true; primary=false; factoryBeanName=null;
factoryMethodName=null; initMethodName=null; destroyMethodName=null;
defined in file
[/usr/local/Cellar/tomcat/8.5.5/libexec/webapps/oauth-server/WEB-INF/classes/com/datami/auth/security/CheckTokenEndpoint.class]]
with [Root bean: class [null]; scope=; abstract=false; lazyInit=false;
autowireMode=3; dependencyCheck=0; autowireCandidate=true;
primary=false;
factoryBeanName=org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
factoryMethodName=checkTokenEndpoint; initMethodName=null;
destroyMethodName=(inferred); defined in class path resource
[org/springframework/security/oauth2/config/annotation/web/configuration/AuthorizationServerEndpointsConfiguration.class]]
Created my own endpoint with (/oauth/check_custom_token) but not sure autowiring resourceServerTokenServices in the below, #autowire doesn't helped me.
#autowire
private ResourceServerTokenServices resourceServerTokenServices;
Spring has autowired this with DefaultTokenServices.
I can also create new DefaultTokenServices() in my code, but then how to autowire the below inside DefaultTokenServices? again the same problem.
private TokenStore tokenStore;
private ClientDetailsService clientDetailsService;
private TokenEnhancer accessTokenEnhancer;
private AuthenticationManager authenticationManager;
Coul you please help me out.
CheckTokenEndpoint depends on its accessTokenConverter instance to create and return the map.
You could create a custom AccessTokenConverter (maybe extending from OOTB DefaultAccessTokenConverter if needed) and use it like so:
#Configuration
#EnableAuthorizationServer
public class MyAuthConfig extends AuthorizationServerConfigurerAdapter {
...
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.accessTokenConverter(new MyAccessTokenConverter())...
....
Of course, you might want to use a factory method to create your accessTokenConverter instance, which allows you to inject a few properties into the instance etc.
Once done, inside AuthorizationServerEndpointsConfiguration.checkTokenEndpoint you can see that the accessTokenConverter you set above will be passed to the OOTB instance of CheckTokenEndpoint and used to create the map.

Resources