Remove static state from CF web endpoint integration tests

The mocks being static meant that their state was shared across each
test in the class. This resulted in the tests being order dependent.
This commit uses instance variables to hold the mocks, thereby
ensuring that they're recreated for each test as part of the standard
JUnit lifecycle.

Closes gh-38363
This commit is contained in:
Andy Wilkinson 2023-11-15 15:03:06 +00:00
parent 9b8bcec33e
commit c2156d6803
2 changed files with 36 additions and 35 deletions

View File

@ -75,22 +75,23 @@ import static org.mockito.Mockito.mock;
*/
class CloudFoundryWebFluxEndpointIntegrationTests {
private static ReactiveTokenValidator tokenValidator = mock(ReactiveTokenValidator.class);
private final ReactiveTokenValidator tokenValidator = mock(ReactiveTokenValidator.class);
private static ReactiveCloudFoundrySecurityService securityService = mock(
ReactiveCloudFoundrySecurityService.class);
private final ReactiveCloudFoundrySecurityService securityService = mock(ReactiveCloudFoundrySecurityService.class);
private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner(
AnnotationConfigReactiveWebServerApplicationContext::new)
.withConfiguration(AutoConfigurations.of(WebFluxAutoConfiguration.class, HttpHandlerAutoConfiguration.class,
ReactiveWebServerFactoryAutoConfiguration.class))
.withUserConfiguration(TestEndpointConfiguration.class)
.withBean(ReactiveTokenValidator.class, () -> this.tokenValidator)
.withBean(ReactiveCloudFoundrySecurityService.class, () -> this.securityService)
.withPropertyValues("server.port=0");
@Test
void operationWithSecurityInterceptorForbidden() {
given(tokenValidator.validate(any())).willReturn(Mono.empty());
given(securityService.getAccessLevel(any(), eq("app-id"))).willReturn(Mono.just(AccessLevel.RESTRICTED));
given(this.tokenValidator.validate(any())).willReturn(Mono.empty());
given(this.securityService.getAccessLevel(any(), eq("app-id"))).willReturn(Mono.just(AccessLevel.RESTRICTED));
this.contextRunner.run(withWebTestClient((client) -> client.get()
.uri("/cfApplication/test")
.accept(MediaType.APPLICATION_JSON)
@ -102,8 +103,8 @@ class CloudFoundryWebFluxEndpointIntegrationTests {
@Test
void operationWithSecurityInterceptorSuccess() {
given(tokenValidator.validate(any())).willReturn(Mono.empty());
given(securityService.getAccessLevel(any(), eq("app-id"))).willReturn(Mono.just(AccessLevel.FULL));
given(this.tokenValidator.validate(any())).willReturn(Mono.empty());
given(this.securityService.getAccessLevel(any(), eq("app-id"))).willReturn(Mono.just(AccessLevel.FULL));
this.contextRunner.run(withWebTestClient((client) -> client.get()
.uri("/cfApplication/test")
.accept(MediaType.APPLICATION_JSON)
@ -131,8 +132,8 @@ class CloudFoundryWebFluxEndpointIntegrationTests {
@Test
void linksToOtherEndpointsWithFullAccess() {
given(tokenValidator.validate(any())).willReturn(Mono.empty());
given(securityService.getAccessLevel(any(), eq("app-id"))).willReturn(Mono.just(AccessLevel.FULL));
given(this.tokenValidator.validate(any())).willReturn(Mono.empty());
given(this.securityService.getAccessLevel(any(), eq("app-id"))).willReturn(Mono.just(AccessLevel.FULL));
this.contextRunner.run(withWebTestClient((client) -> client.get()
.uri("/cfApplication")
.accept(MediaType.APPLICATION_JSON)
@ -169,7 +170,7 @@ class CloudFoundryWebFluxEndpointIntegrationTests {
void linksToOtherEndpointsForbidden() {
CloudFoundryAuthorizationException exception = new CloudFoundryAuthorizationException(Reason.INVALID_TOKEN,
"invalid-token");
willThrow(exception).given(tokenValidator).validate(any());
willThrow(exception).given(this.tokenValidator).validate(any());
this.contextRunner.run(withWebTestClient((client) -> client.get()
.uri("/cfApplication")
.accept(MediaType.APPLICATION_JSON)
@ -181,8 +182,8 @@ class CloudFoundryWebFluxEndpointIntegrationTests {
@Test
void linksToOtherEndpointsWithRestrictedAccess() {
given(tokenValidator.validate(any())).willReturn(Mono.empty());
given(securityService.getAccessLevel(any(), eq("app-id"))).willReturn(Mono.just(AccessLevel.RESTRICTED));
given(this.tokenValidator.validate(any())).willReturn(Mono.empty());
given(this.securityService.getAccessLevel(any(), eq("app-id"))).willReturn(Mono.just(AccessLevel.RESTRICTED));
this.contextRunner.run(withWebTestClient((client) -> client.get()
.uri("/cfApplication")
.accept(MediaType.APPLICATION_JSON)
@ -232,7 +233,8 @@ class CloudFoundryWebFluxEndpointIntegrationTests {
static class CloudFoundryReactiveConfiguration {
@Bean
CloudFoundrySecurityInterceptor interceptor() {
CloudFoundrySecurityInterceptor interceptor(ReactiveTokenValidator tokenValidator,
ReactiveCloudFoundrySecurityService securityService) {
return new CloudFoundrySecurityInterceptor(tokenValidator, securityService, "app-id");
}

View File

@ -42,6 +42,7 @@ import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.ApplicationContext;
@ -70,13 +71,13 @@ import static org.mockito.Mockito.mock;
*/
class CloudFoundryMvcWebEndpointIntegrationTests {
private static TokenValidator tokenValidator = mock(TokenValidator.class);
private final TokenValidator tokenValidator = mock(TokenValidator.class);
private static CloudFoundrySecurityService securityService = mock(CloudFoundrySecurityService.class);
private final CloudFoundrySecurityService securityService = mock(CloudFoundrySecurityService.class);
@Test
void operationWithSecurityInterceptorForbidden() {
given(securityService.getAccessLevel(any(), eq("app-id"))).willReturn(AccessLevel.RESTRICTED);
given(this.securityService.getAccessLevel(any(), eq("app-id"))).willReturn(AccessLevel.RESTRICTED);
load(TestEndpointConfiguration.class,
(client) -> client.get()
.uri("/cfApplication/test")
@ -89,7 +90,7 @@ class CloudFoundryMvcWebEndpointIntegrationTests {
@Test
void operationWithSecurityInterceptorSuccess() {
given(securityService.getAccessLevel(any(), eq("app-id"))).willReturn(AccessLevel.FULL);
given(this.securityService.getAccessLevel(any(), eq("app-id"))).willReturn(AccessLevel.FULL);
load(TestEndpointConfiguration.class,
(client) -> client.get()
.uri("/cfApplication/test")
@ -119,7 +120,7 @@ class CloudFoundryMvcWebEndpointIntegrationTests {
@Test
void linksToOtherEndpointsWithFullAccess() {
given(securityService.getAccessLevel(any(), eq("app-id"))).willReturn(AccessLevel.FULL);
given(this.securityService.getAccessLevel(any(), eq("app-id"))).willReturn(AccessLevel.FULL);
load(TestEndpointConfiguration.class,
(client) -> client.get()
.uri("/cfApplication")
@ -157,7 +158,7 @@ class CloudFoundryMvcWebEndpointIntegrationTests {
void linksToOtherEndpointsForbidden() {
CloudFoundryAuthorizationException exception = new CloudFoundryAuthorizationException(Reason.INVALID_TOKEN,
"invalid-token");
willThrow(exception).given(tokenValidator).validate(any());
willThrow(exception).given(this.tokenValidator).validate(any());
load(TestEndpointConfiguration.class,
(client) -> client.get()
.uri("/cfApplication")
@ -170,7 +171,7 @@ class CloudFoundryMvcWebEndpointIntegrationTests {
@Test
void linksToOtherEndpointsWithRestrictedAccess() {
given(securityService.getAccessLevel(any(), eq("app-id"))).willReturn(AccessLevel.RESTRICTED);
given(this.securityService.getAccessLevel(any(), eq("app-id"))).willReturn(AccessLevel.RESTRICTED);
load(TestEndpointConfiguration.class,
(client) -> client.get()
.uri("/cfApplication")
@ -198,26 +199,23 @@ class CloudFoundryMvcWebEndpointIntegrationTests {
.doesNotExist());
}
private AnnotationConfigServletWebServerApplicationContext createApplicationContext(Class<?>... config) {
return new AnnotationConfigServletWebServerApplicationContext(config);
private void load(Class<?> configuration, Consumer<WebTestClient> clientConsumer) {
BiConsumer<ApplicationContext, WebTestClient> consumer = (context, client) -> clientConsumer.accept(client);
new WebApplicationContextRunner(AnnotationConfigServletWebServerApplicationContext::new)
.withUserConfiguration(configuration, CloudFoundryMvcConfiguration.class)
.withBean(TokenValidator.class, () -> this.tokenValidator)
.withBean(CloudFoundrySecurityService.class, () -> this.securityService)
.run((context) -> consumer.accept(context, WebTestClient.bindToServer()
.baseUrl("http://localhost:" + getPort(
(AnnotationConfigServletWebServerApplicationContext) context.getSourceApplicationContext()))
.responseTimeout(Duration.ofMinutes(5))
.build()));
}
private int getPort(AnnotationConfigServletWebServerApplicationContext context) {
return context.getWebServer().getPort();
}
private void load(Class<?> configuration, Consumer<WebTestClient> clientConsumer) {
BiConsumer<ApplicationContext, WebTestClient> consumer = (context, client) -> clientConsumer.accept(client);
try (AnnotationConfigServletWebServerApplicationContext context = createApplicationContext(configuration,
CloudFoundryMvcConfiguration.class)) {
consumer.accept(context,
WebTestClient.bindToServer()
.baseUrl("http://localhost:" + getPort(context))
.responseTimeout(Duration.ofMinutes(5))
.build());
}
}
private String mockAccessToken() {
return "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0b3B0YWwu"
+ "Y29tIiwiZXhwIjoxNDI2NDIwODAwLCJhd2Vzb21lIjp0cnVlfQ."
@ -229,7 +227,8 @@ class CloudFoundryMvcWebEndpointIntegrationTests {
static class CloudFoundryMvcConfiguration {
@Bean
CloudFoundrySecurityInterceptor interceptor() {
CloudFoundrySecurityInterceptor interceptor(TokenValidator tokenValidator,
CloudFoundrySecurityService securityService) {
return new CloudFoundrySecurityInterceptor(tokenValidator, securityService, "app-id");
}