From c2156d6803bf304ad710509bfbd706ae265fef3d Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 15 Nov 2023 15:03:06 +0000 Subject: [PATCH] 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 --- ...oundryWebFluxEndpointIntegrationTests.java | 28 ++++++------ ...FoundryMvcWebEndpointIntegrationTests.java | 43 +++++++++---------- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointIntegrationTests.java index 56d245eba51..013f2d4053e 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointIntegrationTests.java @@ -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"); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryMvcWebEndpointIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryMvcWebEndpointIntegrationTests.java index 112b843a089..3001204137a 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryMvcWebEndpointIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryMvcWebEndpointIntegrationTests.java @@ -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 clientConsumer) { + BiConsumer 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 clientConsumer) { - BiConsumer 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"); }