Skip to content

Commit 8d6758f

Browse files
authored
Merge pull request #51380 from gsmet/restassured-workaround
Clear some internal state of REST Assured between tests
2 parents 658ca9a + c4f145a commit 8d6758f

File tree

10 files changed

+204
-168
lines changed

10 files changed

+204
-168
lines changed

extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/root/ApplicationPathHttpRootTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public class ApplicationPathHttpRootTest {
2626

2727
@Test
2828
public void testResources() {
29-
// Note that /foo is added automatically by RestAssuredURLManager
29+
// Note that /foo is added automatically by RestAssuredStateManager
3030
RestAssured.when().get("/hello/world").then().body(Matchers.is("hello world"));
3131
RestAssured.when().get("/world").then().statusCode(404);
3232
}

extensions/smallrye-openapi/deployment/pom.xml

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -143,16 +143,6 @@
143143
By adding this configuration we ensure that the maven surefire plugin will execute twice, one for the regular **/*Test.java
144144
tests (using the 'default-test' execution), and one for the prod mode tests (this 'dev-mode' execution)
145145
-->
146-
<execution>
147-
<id>default-test</id>
148-
<configuration>
149-
<includes>
150-
<include>**/Test*.java</include>
151-
<include>**/*Test.java</include>
152-
<include>**/*Tests.java</include>
153-
</includes>
154-
</configuration>
155-
</execution>
156146
<execution>
157147
<id>dev-mode</id>
158148
<phase>test</phase>
@@ -163,17 +153,6 @@
163153
<includes>**/*DMT.java</includes>
164154
</configuration>
165155
</execution>
166-
<!-- for now, run the TestCase in a separate surefire execution to try and minimize OOME issues -->
167-
<execution>
168-
<id>test-case</id>
169-
<phase>test</phase>
170-
<goals>
171-
<goal>test</goal>
172-
</goals>
173-
<configuration>
174-
<includes>**/*TestCase.java</includes>
175-
</configuration>
176-
</execution>
177156
</executions>
178157
</plugin>
179158
<plugin>
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
package io.quarkus.test.common;
2+
3+
import java.time.Duration;
4+
import java.util.Optional;
5+
6+
import org.eclipse.microprofile.config.ConfigProvider;
7+
8+
import io.restassured.RestAssured;
9+
import io.restassured.config.HttpClientConfig;
10+
import io.restassured.config.RestAssuredConfig;
11+
import io.restassured.internal.path.json.ConfigurableJsonSlurper;
12+
import io.restassured.path.json.JsonPath;
13+
import io.restassured.specification.RequestSpecification;
14+
15+
/**
16+
* Utility class that sets the rest assured port to the default test port and meaningful timeouts.
17+
* <p>
18+
* This class works whether RestAssured is on the classpath or not - if it is not, invoking the methods of the class are
19+
* essentially NO-OPs
20+
* <p>
21+
* TODO: do we actually want this here, or should it be in a different module?
22+
*/
23+
public class RestAssuredStateManager {
24+
25+
private static final int DEFAULT_HTTP_PORT = 8081;
26+
private static final int DEFAULT_HTTPS_PORT = 8444;
27+
28+
private static int oldPort;
29+
private static String oldBaseURI;
30+
private static String oldBasePath;
31+
private static Object oldRestAssuredConfig; // we can't declare the type here as that would prevent this class for being loaded if RestAssured is not present
32+
private static Object oldRequestSpecification;
33+
34+
private static final boolean REST_ASSURED_PRESENT;
35+
36+
static {
37+
boolean present = false;
38+
try {
39+
Class.forName("io.restassured.RestAssured");
40+
present = true;
41+
} catch (ClassNotFoundException ignored) {
42+
}
43+
REST_ASSURED_PRESENT = present;
44+
}
45+
46+
private RestAssuredStateManager() {
47+
48+
}
49+
50+
private static int getPortFromConfig(int defaultValue, String... keys) {
51+
for (String key : keys) {
52+
Optional<Integer> port = ConfigProvider.getConfig().getOptionalValue(key, Integer.class);
53+
if (port.isPresent())
54+
return port.get();
55+
}
56+
return defaultValue;
57+
}
58+
59+
public static void setURL(boolean useSecureConnection) {
60+
setURL(useSecureConnection, null, null);
61+
}
62+
63+
public static void setURL(boolean useSecureConnection, String additionalPath) {
64+
setURL(useSecureConnection, null, additionalPath);
65+
}
66+
67+
public static void setURL(boolean useSecureConnection, Integer port) {
68+
setURL(useSecureConnection, port, null);
69+
}
70+
71+
public static void setURL(boolean useSecureConnection, Integer port, String additionalPath) {
72+
if (!REST_ASSURED_PRESENT) {
73+
return;
74+
}
75+
76+
oldPort = RestAssured.port;
77+
if (port == null) {
78+
port = useSecureConnection ? getPortFromConfig(DEFAULT_HTTPS_PORT, "quarkus.http.test-ssl-port")
79+
: getPortFromConfig(DEFAULT_HTTP_PORT, "quarkus.lambda.mock-event-server.test-port",
80+
"quarkus.http.test-port");
81+
}
82+
RestAssured.port = port;
83+
84+
oldBaseURI = RestAssured.baseURI;
85+
final String protocol = useSecureConnection ? "https://" : "http://";
86+
String host = ConfigProvider.getConfig().getOptionalValue("quarkus.http.host", String.class)
87+
.orElse("localhost");
88+
if (host.equals("0.0.0.0")) {
89+
host = "localhost";
90+
}
91+
RestAssured.baseURI = protocol + host;
92+
93+
oldBasePath = RestAssured.basePath;
94+
Optional<String> basePath = ConfigProvider.getConfig().getOptionalValue("quarkus.http.root-path",
95+
String.class);
96+
if (basePath.isPresent() || additionalPath != null) {
97+
StringBuilder bp = new StringBuilder();
98+
if (basePath.isPresent()) {
99+
if (basePath.get().startsWith("/")) {
100+
bp.append(basePath.get().substring(1));
101+
} else {
102+
bp.append(basePath.get());
103+
}
104+
if (bp.toString().endsWith("/")) {
105+
bp.setLength(bp.length() - 1);
106+
}
107+
}
108+
if (additionalPath != null) {
109+
if (!additionalPath.startsWith("/")) {
110+
bp.append("/");
111+
}
112+
bp.append(additionalPath);
113+
if (bp.toString().endsWith("/")) {
114+
bp.setLength(bp.length() - 1);
115+
}
116+
}
117+
RestAssured.basePath = bp.toString();
118+
}
119+
120+
oldRestAssuredConfig = RestAssured.config();
121+
122+
Duration timeout = ConfigProvider.getConfig()
123+
.getOptionalValue("quarkus.http.test-timeout", Duration.class).orElse(Duration.ofSeconds(30));
124+
configureTimeouts(timeout);
125+
126+
oldRequestSpecification = RestAssured.requestSpecification;
127+
if (ConfigProvider.getConfig()
128+
.getOptionalValue("quarkus.test.rest-assured.enable-logging-on-failure", Boolean.class).orElse(true)) {
129+
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
130+
}
131+
}
132+
133+
private static void configureTimeouts(Duration d) {
134+
RestAssured.config = RestAssured.config().httpClient(new HttpClientConfig()
135+
.setParam("http.conn-manager.timeout", d.toMillis()) // this needs to be long
136+
.setParam("http.connection.timeout", (int) d.toMillis()) // this needs to be int
137+
.setParam("http.socket.timeout", (int) d.toMillis())); // same here
138+
}
139+
140+
public static void clearState() {
141+
if (!REST_ASSURED_PRESENT) {
142+
return;
143+
}
144+
145+
clearURL();
146+
147+
JsonPath.config = null;
148+
// Reset the numberReturnType ThreadLocal in ConfigurableJsonSlurper as it is causing class loader leaks
149+
try {
150+
java.lang.reflect.Field numberReturnTypeField = ConfigurableJsonSlurper.class.getDeclaredField("numberReturnType");
151+
numberReturnTypeField.setAccessible(true);
152+
ThreadLocal<?> threadLocal = (ThreadLocal<?>) numberReturnTypeField.get(null);
153+
if (threadLocal != null) {
154+
threadLocal.remove();
155+
}
156+
} catch (Exception e) {
157+
// Ignore if the field doesn't exist or can't be accessed
158+
}
159+
}
160+
161+
/**
162+
* @deprecated most probably, you want to call {@link #clearState()}. If it's not the case, please let us know so that we
163+
* can reconsider the deprecation.
164+
* Note: when removing, please move the code to {@link #clearState()}.
165+
*/
166+
@Deprecated(forRemoval = true, since = "3.31")
167+
static void clearURL() {
168+
if (!REST_ASSURED_PRESENT) {
169+
return;
170+
}
171+
172+
RestAssured.port = oldPort;
173+
RestAssured.baseURI = oldBaseURI;
174+
RestAssured.basePath = oldBasePath;
175+
RestAssured.config = (RestAssuredConfig) oldRestAssuredConfig;
176+
RestAssured.requestSpecification = (RequestSpecification) oldRequestSpecification;
177+
}
178+
}
Lines changed: 4 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,11 @@
11
package io.quarkus.test.common;
22

3-
import java.time.Duration;
4-
import java.util.Optional;
5-
6-
import org.eclipse.microprofile.config.ConfigProvider;
7-
8-
import io.restassured.RestAssured;
9-
import io.restassured.config.HttpClientConfig;
10-
import io.restassured.config.RestAssuredConfig;
11-
import io.restassured.specification.RequestSpecification;
12-
133
/**
14-
* Utility class that sets the rest assured port to the default test port and meaningful timeouts.
15-
* <p>
16-
* This class works whether RestAssured is on the classpath or not - if it is not, invoking the methods of the class are
17-
* essentially NO-OPs
18-
* <p>
19-
* TODO: do we actually want this here, or should it be in a different module?
4+
* @deprecated use {@link RestAssuredStateManager} instead
205
*/
6+
@Deprecated(forRemoval = true, since = "3.31")
217
public class RestAssuredURLManager {
228

23-
private static final int DEFAULT_HTTP_PORT = 8081;
24-
private static final int DEFAULT_HTTPS_PORT = 8444;
25-
26-
private static int oldPort;
27-
private static String oldBaseURI;
28-
private static String oldBasePath;
29-
private static Object oldRestAssuredConfig; // we can't declare the type here as that would prevent this class for being loaded if RestAssured is not present
30-
private static Object oldRequestSpecification;
31-
32-
private static final boolean REST_ASSURED_PRESENT;
33-
34-
static {
35-
boolean present = false;
36-
try {
37-
Class.forName("io.restassured.RestAssured");
38-
present = true;
39-
} catch (ClassNotFoundException ignored) {
40-
}
41-
REST_ASSURED_PRESENT = present;
42-
}
43-
44-
private RestAssuredURLManager() {
45-
46-
}
47-
48-
private static int getPortFromConfig(int defaultValue, String... keys) {
49-
for (String key : keys) {
50-
Optional<Integer> port = ConfigProvider.getConfig().getOptionalValue(key, Integer.class);
51-
if (port.isPresent())
52-
return port.get();
53-
}
54-
return defaultValue;
55-
}
56-
579
public static void setURL(boolean useSecureConnection) {
5810
setURL(useSecureConnection, null, null);
5911
}
@@ -67,83 +19,10 @@ public static void setURL(boolean useSecureConnection, Integer port) {
6719
}
6820

6921
public static void setURL(boolean useSecureConnection, Integer port, String additionalPath) {
70-
if (!REST_ASSURED_PRESENT) {
71-
return;
72-
}
73-
74-
oldPort = RestAssured.port;
75-
if (port == null) {
76-
port = useSecureConnection ? getPortFromConfig(DEFAULT_HTTPS_PORT, "quarkus.http.test-ssl-port")
77-
: getPortFromConfig(DEFAULT_HTTP_PORT, "quarkus.lambda.mock-event-server.test-port",
78-
"quarkus.http.test-port");
79-
}
80-
RestAssured.port = port;
81-
82-
oldBaseURI = RestAssured.baseURI;
83-
final String protocol = useSecureConnection ? "https://" : "http://";
84-
String host = ConfigProvider.getConfig().getOptionalValue("quarkus.http.host", String.class)
85-
.orElse("localhost");
86-
if (host.equals("0.0.0.0")) {
87-
host = "localhost";
88-
}
89-
RestAssured.baseURI = protocol + host;
90-
91-
oldBasePath = RestAssured.basePath;
92-
Optional<String> basePath = ConfigProvider.getConfig().getOptionalValue("quarkus.http.root-path",
93-
String.class);
94-
if (basePath.isPresent() || additionalPath != null) {
95-
StringBuilder bp = new StringBuilder();
96-
if (basePath.isPresent()) {
97-
if (basePath.get().startsWith("/")) {
98-
bp.append(basePath.get().substring(1));
99-
} else {
100-
bp.append(basePath.get());
101-
}
102-
if (bp.toString().endsWith("/")) {
103-
bp.setLength(bp.length() - 1);
104-
}
105-
}
106-
if (additionalPath != null) {
107-
if (!additionalPath.startsWith("/")) {
108-
bp.append("/");
109-
}
110-
bp.append(additionalPath);
111-
if (bp.toString().endsWith("/")) {
112-
bp.setLength(bp.length() - 1);
113-
}
114-
}
115-
RestAssured.basePath = bp.toString();
116-
}
117-
118-
oldRestAssuredConfig = RestAssured.config();
119-
120-
Duration timeout = ConfigProvider.getConfig()
121-
.getOptionalValue("quarkus.http.test-timeout", Duration.class).orElse(Duration.ofSeconds(30));
122-
configureTimeouts(timeout);
123-
124-
oldRequestSpecification = RestAssured.requestSpecification;
125-
if (ConfigProvider.getConfig()
126-
.getOptionalValue("quarkus.test.rest-assured.enable-logging-on-failure", Boolean.class).orElse(true)) {
127-
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
128-
}
129-
}
130-
131-
private static void configureTimeouts(Duration d) {
132-
RestAssured.config = RestAssured.config().httpClient(new HttpClientConfig()
133-
.setParam("http.conn-manager.timeout", d.toMillis()) // this needs to be long
134-
.setParam("http.connection.timeout", (int) d.toMillis()) // this needs to be int
135-
.setParam("http.socket.timeout", (int) d.toMillis())); // same here
22+
RestAssuredStateManager.setURL(useSecureConnection, port, additionalPath);
13623
}
13724

13825
public static void clearURL() {
139-
if (!REST_ASSURED_PRESENT) {
140-
return;
141-
}
142-
143-
RestAssured.port = oldPort;
144-
RestAssured.baseURI = oldBaseURI;
145-
RestAssured.basePath = oldBasePath;
146-
RestAssured.config = (RestAssuredConfig) oldRestAssuredConfig;
147-
RestAssured.requestSpecification = (RequestSpecification) oldRequestSpecification;
26+
RestAssuredStateManager.clearURL();
14827
}
14928
}

0 commit comments

Comments
 (0)