Skip to content

Commit d2966e1

Browse files
committed
Polish "Replace calls to verifyComplete() to avoid indefinite blocking"
See gh-35915
1 parent f9da30f commit d2966e1

File tree

25 files changed

+159
-89
lines changed

25 files changed

+159
-89
lines changed

buildSrc/src/main/java/org/springframework/boot/build/architecture/ArchitectureCheck.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ void checkArchitecture() throws IOException {
7474
.importPaths(this.classes.getFiles().stream().map(File::toPath).collect(Collectors.toList()));
7575
List<EvaluationResult> violations = Stream.of(allPackagesShouldBeFreeOfTangles(),
7676
allBeanPostProcessorBeanMethodsShouldBeStaticAndHaveParametersThatWillNotCausePrematureInitialization(),
77-
allBeanFactoryPostProcessorBeanMethodsShouldBeStaticAndHaveNoParameters())
77+
allBeanFactoryPostProcessorBeanMethodsShouldBeStaticAndHaveNoParameters(),
78+
noClassesShouldCallStepVerifierStepVerifyComplete(),
79+
noClassesShouldConfigureDefaultStepVerifierTimeout())
7880
.map((rule) -> rule.evaluate(javaClasses))
7981
.filter(EvaluationResult::hasViolation)
8082
.collect(Collectors.toList());
@@ -163,6 +165,20 @@ public void check(JavaMethod item, ConditionEvents events) {
163165
};
164166
}
165167

168+
private ArchRule noClassesShouldCallStepVerifierStepVerifyComplete() {
169+
return ArchRuleDefinition.noClasses()
170+
.should()
171+
.callMethod("reactor.test.StepVerifier$Step", "verifyComplete")
172+
.because("it can block indefinitely and expectComplete().verify(Duration) should be used instead");
173+
}
174+
175+
private ArchRule noClassesShouldConfigureDefaultStepVerifierTimeout() {
176+
return ArchRuleDefinition.noClasses()
177+
.should()
178+
.callMethod("reactor.test.StepVerifier", "setDefaultTimeout", "java.time.Duration")
179+
.because("expectComplete().verify(Duration) should be used instead");
180+
}
181+
166182
public void setClasses(FileCollection classes) {
167183
this.classes = classes;
168184
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundrySecurityInterceptorTests.java

+12-8
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.cloudfoundry.reactive;
1818

19+
import java.time.Duration;
20+
1921
import org.junit.jupiter.api.BeforeEach;
2022
import org.junit.jupiter.api.Test;
2123
import org.junit.jupiter.api.extension.ExtendWith;
@@ -33,8 +35,6 @@
3335
import org.springframework.mock.web.server.MockServerWebExchange;
3436
import org.springframework.util.Base64Utils;
3537

36-
import java.time.Duration;
37-
3838
import static org.assertj.core.api.Assertions.assertThat;
3939
import static org.mockito.ArgumentMatchers.any;
4040
import static org.mockito.BDDMockito.given;
@@ -68,7 +68,8 @@ void preHandleWhenRequestIsPreFlightShouldBeOk() {
6868
.build());
6969
StepVerifier.create(this.interceptor.preHandle(request, "/a"))
7070
.consumeNextWith((response) -> assertThat(response.getStatus()).isEqualTo(HttpStatus.OK))
71-
.expectComplete().verify(Duration.ofSeconds(5));
71+
.expectComplete()
72+
.verify(Duration.ofSeconds(30));
7273
}
7374

7475
@Test
@@ -77,7 +78,8 @@ void preHandleWhenTokenIsMissingShouldReturnMissingAuthorization() {
7778
StepVerifier.create(this.interceptor.preHandle(request, "/a"))
7879
.consumeNextWith(
7980
(response) -> assertThat(response.getStatus()).isEqualTo(Reason.MISSING_AUTHORIZATION.getStatus()))
80-
.expectComplete().verify(Duration.ofSeconds(5));
81+
.expectComplete()
82+
.verify(Duration.ofSeconds(30));
8183
}
8284

8385
@Test
@@ -87,7 +89,8 @@ void preHandleWhenTokenIsNotBearerShouldReturnMissingAuthorization() {
8789
StepVerifier.create(this.interceptor.preHandle(request, "/a"))
8890
.consumeNextWith(
8991
(response) -> assertThat(response.getStatus()).isEqualTo(Reason.MISSING_AUTHORIZATION.getStatus()))
90-
.expectComplete().verify(Duration.ofSeconds(5));
92+
.expectComplete()
93+
.verify(Duration.ofSeconds(30));
9194
}
9295

9396
@Test
@@ -123,7 +126,8 @@ void preHandleWhenAccessIsNotAllowedShouldReturnAccessDenied() {
123126
.build());
124127
StepVerifier.create(this.interceptor.preHandle(request, "/a"))
125128
.consumeNextWith((response) -> assertThat(response.getStatus()).isEqualTo(Reason.ACCESS_DENIED.getStatus()))
126-
.expectComplete().verify(Duration.ofSeconds(5));
129+
.expectComplete()
130+
.verify(Duration.ofSeconds(30));
127131
}
128132

129133
@Test
@@ -137,7 +141,7 @@ void preHandleSuccessfulWithFullAccess() {
137141
StepVerifier.create(this.interceptor.preHandle(exchange, "/a")).consumeNextWith((response) -> {
138142
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK);
139143
assertThat((AccessLevel) exchange.getAttribute("cloudFoundryAccessLevel")).isEqualTo(AccessLevel.FULL);
140-
}).expectComplete().verify(Duration.ofSeconds(5));
144+
}).expectComplete().verify(Duration.ofSeconds(30));
141145
}
142146

143147
@Test
@@ -153,7 +157,7 @@ void preHandleSuccessfulWithRestrictedAccess() {
153157
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK);
154158
assertThat((AccessLevel) exchange.getAttribute("cloudFoundryAccessLevel"))
155159
.isEqualTo(AccessLevel.RESTRICTED);
156-
}).expectComplete().verify(Duration.ofSeconds(5));
160+
}).expectComplete().verify(Duration.ofSeconds(30));
157161
}
158162

159163
private String mockAccessToken() {

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveTokenValidatorTests.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ void validateTokenWhenKidValidationSucceedsInTheSecondAttempt() throws Exception
122122
String claims = "{\"exp\": 2147483647, \"iss\": \"http://localhost:8080/uaa/oauth/token\", \"scope\": [\"actuator.read\"]}";
123123
StepVerifier
124124
.create(this.tokenValidator.validate(new Token(getSignedToken(header.getBytes(), claims.getBytes()))))
125-
.expectComplete().verify(Duration.ofSeconds(5));
125+
.expectComplete()
126+
.verify(Duration.ofSeconds(30));
126127
assertThat(this.tokenValidator).hasFieldOrPropertyWithValue("cachedTokenKeys", VALID_KEYS);
127128
fetchTokenKeys.assertWasSubscribed();
128129
}
@@ -136,7 +137,8 @@ void validateTokenWhenCacheIsEmptyShouldFetchTokenKeys() throws Exception {
136137
String claims = "{\"exp\": 2147483647, \"iss\": \"http://localhost:8080/uaa/oauth/token\", \"scope\": [\"actuator.read\"]}";
137138
StepVerifier
138139
.create(this.tokenValidator.validate(new Token(getSignedToken(header.getBytes(), claims.getBytes()))))
139-
.expectComplete().verify(Duration.ofSeconds(5));
140+
.expectComplete()
141+
.verify(Duration.ofSeconds(30));
140142
assertThat(this.tokenValidator).hasFieldOrPropertyWithValue("cachedTokenKeys", VALID_KEYS);
141143
fetchTokenKeys.assertWasSubscribed();
142144
}
@@ -168,7 +170,8 @@ void validateTokenWhenCacheValidShouldNotFetchTokenKeys() throws Exception {
168170
String claims = "{\"exp\": 2147483647, \"iss\": \"http://localhost:8080/uaa/oauth/token\", \"scope\": [\"actuator.read\"]}";
169171
StepVerifier
170172
.create(this.tokenValidator.validate(new Token(getSignedToken(header.getBytes(), claims.getBytes()))))
171-
.expectComplete().verify(Duration.ofSeconds(5));
173+
.expectComplete()
174+
.verify(Duration.ofSeconds(30));
172175
fetchTokenKeys.assertWasNotSubscribed();
173176
}
174177

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cassandra/CassandraDriverReactiveHealthIndicatorTests.java

+17-10
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ void healthWithOneHealthyNodeShouldReturnUp() {
6262
Mono<Health> health = healthIndicator.health();
6363
StepVerifier.create(health)
6464
.consumeNextWith((h) -> assertThat(h.getStatus()).isEqualTo(Status.UP))
65-
.expectComplete().verify(Duration.ofSeconds(5));
65+
.expectComplete()
66+
.verify(Duration.ofSeconds(30));
6667
}
6768

6869
@Test
@@ -72,7 +73,8 @@ void healthWithOneUnhealthyNodeShouldReturnDown() {
7273
Mono<Health> health = healthIndicator.health();
7374
StepVerifier.create(health)
7475
.consumeNextWith((h) -> assertThat(h.getStatus()).isEqualTo(Status.DOWN))
75-
.expectComplete().verify(Duration.ofSeconds(5));
76+
.expectComplete()
77+
.verify(Duration.ofSeconds(30));
7678
}
7779

7880
@Test
@@ -82,7 +84,8 @@ void healthWithOneUnknownNodeShouldReturnDown() {
8284
Mono<Health> health = healthIndicator.health();
8385
StepVerifier.create(health)
8486
.consumeNextWith((h) -> assertThat(h.getStatus()).isEqualTo(Status.DOWN))
85-
.expectComplete().verify(Duration.ofSeconds(5));
87+
.expectComplete()
88+
.verify(Duration.ofSeconds(30));
8689
}
8790

8891
@Test
@@ -92,7 +95,8 @@ void healthWithOneForcedDownNodeShouldReturnDown() {
9295
Mono<Health> health = healthIndicator.health();
9396
StepVerifier.create(health)
9497
.consumeNextWith((h) -> assertThat(h.getStatus()).isEqualTo(Status.DOWN))
95-
.expectComplete().verify(Duration.ofSeconds(5));
98+
.expectComplete()
99+
.verify(Duration.ofSeconds(30));
96100
}
97101

98102
@Test
@@ -102,7 +106,8 @@ void healthWithOneHealthyNodeAndOneUnhealthyNodeShouldReturnUp() {
102106
Mono<Health> health = healthIndicator.health();
103107
StepVerifier.create(health)
104108
.consumeNextWith((h) -> assertThat(h.getStatus()).isEqualTo(Status.UP))
105-
.expectComplete().verify(Duration.ofSeconds(5));
109+
.expectComplete()
110+
.verify(Duration.ofSeconds(30));
106111
}
107112

108113
@Test
@@ -112,7 +117,8 @@ void healthWithOneHealthyNodeAndOneUnknownNodeShouldReturnUp() {
112117
Mono<Health> health = healthIndicator.health();
113118
StepVerifier.create(health)
114119
.consumeNextWith((h) -> assertThat(h.getStatus()).isEqualTo(Status.UP))
115-
.expectComplete().verify(Duration.ofSeconds(5));
120+
.expectComplete()
121+
.verify(Duration.ofSeconds(30));
116122
}
117123

118124
@Test
@@ -122,7 +128,8 @@ void healthWithOneHealthyNodeAndOneForcedDownNodeShouldReturnUp() {
122128
Mono<Health> health = healthIndicator.health();
123129
StepVerifier.create(health)
124130
.consumeNextWith((h) -> assertThat(h.getStatus()).isEqualTo(Status.UP))
125-
.expectComplete().verify(Duration.ofSeconds(5));
131+
.expectComplete()
132+
.verify(Duration.ofSeconds(30));
126133
}
127134

128135
@Test
@@ -140,7 +147,7 @@ void healthWithNodeVersionShouldAddVersionDetail() {
140147
assertThat(h.getStatus()).isEqualTo(Status.UP);
141148
assertThat(h.getDetails()).containsOnlyKeys("version");
142149
assertThat(h.getDetails().get("version")).isEqualTo(Version.V4_0_0);
143-
}).expectComplete().verify(Duration.ofSeconds(5));
150+
}).expectComplete().verify(Duration.ofSeconds(30));
144151
}
145152

146153
@Test
@@ -151,7 +158,7 @@ void healthWithoutNodeVersionShouldNotAddVersionDetail() {
151158
StepVerifier.create(health).consumeNextWith((h) -> {
152159
assertThat(h.getStatus()).isEqualTo(Status.UP);
153160
assertThat(h.getDetails().get("version")).isNull();
154-
}).expectComplete().verify(Duration.ofSeconds(5));
161+
}).expectComplete().verify(Duration.ofSeconds(30));
155162
}
156163

157164
@Test
@@ -166,7 +173,7 @@ void healthWithCassandraDownShouldReturnDown() {
166173
assertThat(h.getDetails()).containsOnlyKeys("error");
167174
assertThat(h.getDetails().get("error"))
168175
.isEqualTo(DriverTimeoutException.class.getName() + ": Test Exception");
169-
}).expectComplete().verify(Duration.ofSeconds(5));
176+
}).expectComplete().verify(Duration.ofSeconds(30));
170177
}
171178

172179
private CqlSession mockCqlSessionWithNodeState(NodeState... nodeStates) {

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/HealthIndicatorReactiveAdapterTests.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ void delegateReturnsHealth() {
3737
HealthIndicatorReactiveAdapter adapter = new HealthIndicatorReactiveAdapter(delegate);
3838
Health status = Health.up().build();
3939
given(delegate.health()).willReturn(status);
40-
StepVerifier.create(adapter.health()).expectNext(status).expectComplete().verify(Duration.ofSeconds(5));
40+
StepVerifier.create(adapter.health()).expectNext(status).expectComplete().verify(Duration.ofSeconds(30));
4141
}
4242

4343
@Test
@@ -55,7 +55,10 @@ void delegateRunsOnTheElasticScheduler() {
5555
.status(Thread.currentThread().getName().equals(currentThread) ? Status.DOWN : Status.UP)
5656
.build();
5757
HealthIndicatorReactiveAdapter adapter = new HealthIndicatorReactiveAdapter(delegate);
58-
StepVerifier.create(adapter.health()).expectNext(Health.status(Status.UP).build()).expectComplete().verify(Duration.ofSeconds(5));
58+
StepVerifier.create(adapter.health())
59+
.expectNext(Health.status(Status.UP).build())
60+
.expectComplete()
61+
.verify(Duration.ofSeconds(30));
5962
}
6063

6164
}

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/ReactiveHealthIndicatorImplementationTests.java

+8-5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.boot.actuate.health;
1818

19+
import java.time.Duration;
20+
1921
import org.junit.jupiter.api.Test;
2022
import org.junit.jupiter.api.extension.ExtendWith;
2123
import reactor.core.publisher.Mono;
@@ -25,8 +27,6 @@
2527
import org.springframework.boot.test.system.CapturedOutput;
2628
import org.springframework.boot.test.system.OutputCaptureExtension;
2729

28-
import java.time.Duration;
29-
3030
import static org.assertj.core.api.Assertions.assertThat;
3131

3232
/**
@@ -42,7 +42,8 @@ class ReactiveHealthIndicatorImplementationTests {
4242
void healthUp(CapturedOutput output) {
4343
StepVerifier.create(new SimpleReactiveHealthIndicator().health())
4444
.consumeNextWith((health) -> assertThat(health).isEqualTo(Health.up().build()))
45-
.expectComplete().verify(Duration.ofSeconds(5));
45+
.expectComplete()
46+
.verify(Duration.ofSeconds(30));
4647
assertThat(output).doesNotContain("Health check failed for simple");
4748
}
4849

@@ -51,15 +52,17 @@ void healthDownWithCustomErrorMessage(CapturedOutput output) {
5152
StepVerifier.create(new CustomErrorMessageReactiveHealthIndicator().health())
5253
.consumeNextWith(
5354
(health) -> assertThat(health).isEqualTo(Health.down(new UnsupportedOperationException()).build()))
54-
.expectComplete().verify(Duration.ofSeconds(5));
55+
.expectComplete()
56+
.verify(Duration.ofSeconds(30));
5557
assertThat(output).contains("Health check failed for custom");
5658
}
5759

5860
@Test
5961
void healthDownWithCustomErrorMessageFunction(CapturedOutput output) {
6062
StepVerifier.create(new CustomErrorMessageFunctionReactiveHealthIndicator().health())
6163
.consumeNextWith((health) -> assertThat(health).isEqualTo(Health.down(new RuntimeException()).build()))
62-
.expectComplete().verify(Duration.ofSeconds(5));
64+
.expectComplete()
65+
.verify(Duration.ofSeconds(30));
6366
assertThat(output).contains("Health check failed with RuntimeException");
6467
}
6568

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/r2dbc/ConnectionPoolMetricsTests.java

+12-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -60,7 +60,7 @@ void init() {
6060
@AfterEach
6161
void close() {
6262
if (this.connectionFactory != null) {
63-
StepVerifier.create(this.connectionFactory.close()).expectComplete().verify(Duration.ofSeconds(5));
63+
StepVerifier.create(this.connectionFactory.close()).expectComplete().verify(Duration.ofSeconds(30));
6464
}
6565
}
6666

@@ -73,8 +73,16 @@ void connectionFactoryIsInstrumented() {
7373
Tags.of(testTag, regionTag));
7474
metrics.bindTo(registry);
7575
// acquire two connections
76-
connectionPool.create().as(StepVerifier::create).expectNextCount(1).expectComplete().verify(Duration.ofSeconds(5));
77-
connectionPool.create().as(StepVerifier::create).expectNextCount(1).expectComplete().verify(Duration.ofSeconds(5));
76+
connectionPool.create()
77+
.as(StepVerifier::create)
78+
.expectNextCount(1)
79+
.expectComplete()
80+
.verify(Duration.ofSeconds(30));
81+
connectionPool.create()
82+
.as(StepVerifier::create)
83+
.expectNextCount(1)
84+
.expectComplete()
85+
.verify(Duration.ofSeconds(30));
7886
assertGauge(registry, "r2dbc.pool.acquired", 2);
7987
assertGauge(registry, "r2dbc.pool.allocated", 3);
8088
assertGauge(registry, "r2dbc.pool.idle", 1);

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/web/reactive/server/MetricsWebFilterTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ void disconnectedExceptionShouldProduceMetrics() {
162162
exchange.getResponse().setRawStatusCode(500);
163163
return exchange.getResponse().setComplete();
164164
});
165-
StepVerifier.create(processing).expectComplete().verify(Duration.ofSeconds(5));
165+
StepVerifier.create(processing).expectComplete().verify(Duration.ofSeconds(30));
166166
assertMetricsContainsTag("uri", "/projects/{project}");
167167
assertMetricsContainsTag("status", "500");
168168
assertMetricsContainsTag("outcome", "UNKNOWN");

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/mongo/MongoReactiveHealthIndicatorTests.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.boot.actuate.mongo;
1818

19+
import java.time.Duration;
20+
1921
import com.mongodb.MongoException;
2022
import org.bson.Document;
2123
import org.junit.jupiter.api.Test;
@@ -26,8 +28,6 @@
2628
import org.springframework.boot.actuate.health.Status;
2729
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
2830

29-
import java.time.Duration;
30-
3131
import static org.assertj.core.api.Assertions.assertThat;
3232
import static org.mockito.BDDMockito.given;
3333
import static org.mockito.Mockito.mock;
@@ -52,7 +52,7 @@ void testMongoIsUp() {
5252
assertThat(h.getStatus()).isEqualTo(Status.UP);
5353
assertThat(h.getDetails()).containsOnlyKeys("version");
5454
assertThat(h.getDetails().get("version")).isEqualTo("2.6.4");
55-
}).expectComplete().verify(Duration.ofSeconds(5));
55+
}).expectComplete().verify(Duration.ofSeconds(30));
5656
}
5757

5858
@Test
@@ -67,7 +67,7 @@ void testMongoIsDown() {
6767
assertThat(h.getStatus()).isEqualTo(Status.DOWN);
6868
assertThat(h.getDetails()).containsOnlyKeys("error");
6969
assertThat(h.getDetails().get("error")).isEqualTo(MongoException.class.getName() + ": Connection failed");
70-
}).expectComplete().verify(Duration.ofSeconds(5));
70+
}).expectComplete().verify(Duration.ofSeconds(30));
7171
}
7272

7373
}

0 commit comments

Comments
 (0)