Skip to content

Commit 20eb72d

Browse files
committed
test: overhaul automated compatibility coverage
1 parent df7aef2 commit 20eb72d

File tree

55 files changed

+4189
-88
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+4189
-88
lines changed

.github/workflows/build.yml

Lines changed: 150 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515
#
16-
# This workflow will build a Java project with Maven
17-
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
1816

1917
name: build
2018

@@ -25,40 +23,170 @@ on:
2523
branches: [ main ]
2624

2725
jobs:
28-
build:
26+
compile-matrix:
2927
runs-on: ubuntu-latest
3028
strategy:
3129
matrix:
3230
jdk: [8, 11, 17]
3331
steps:
34-
- uses: actions/checkout@v2
32+
- uses: actions/checkout@v4
3533
- name: Set up JDK
36-
uses: actions/setup-java@v1
34+
uses: actions/setup-java@v4
3735
with:
36+
distribution: temurin
3837
java-version: ${{ matrix.jdk }}
3938
- name: Cache Maven packages
4039
uses: actions/cache@v4
4140
with:
4241
path: ~/.m2/repository
43-
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
42+
key: ${{ runner.os }}-maven-compile-${{ matrix.jdk }}-${{ hashFiles('**/pom.xml') }}
4443
restore-keys: |
44+
${{ runner.os }}-maven-compile-${{ matrix.jdk }}-
4545
${{ runner.os }}-maven-
46-
- name: JDK 8
47-
if: matrix.jdk == '8'
48-
uses: nick-fields/retry@v3
49-
with:
50-
timeout_minutes: 3
51-
max_attempts: 3
52-
retry_wait_seconds: 1
53-
command: mvn -B clean package -P travis jacoco:report -Dmaven.gitcommitid.skip=true
54-
- name: JDK 11
55-
if: matrix.jdk == '11'
56-
run: mvn -B clean compile -Dmaven.gitcommitid.skip=true
57-
- name: JDK 17
58-
if: matrix.jdk == '17'
46+
- name: Compile
5947
run: mvn -B clean compile -Dmaven.gitcommitid.skip=true
48+
49+
unit-integration-pr:
50+
runs-on: ubuntu-latest
51+
steps:
52+
- uses: actions/checkout@v4
53+
- name: Set up JDK 8
54+
uses: actions/setup-java@v4
55+
with:
56+
distribution: temurin
57+
java-version: 8
58+
- name: Cache Maven packages
59+
uses: actions/cache@v4
60+
with:
61+
path: ~/.m2/repository
62+
key: ${{ runner.os }}-maven-unit-integration-${{ hashFiles('**/pom.xml') }}
63+
restore-keys: |
64+
${{ runner.os }}-maven-unit-integration-
65+
${{ runner.os }}-maven-
66+
- name: Run unit and integration tests
67+
run: |
68+
mvn -B clean test -P travis jacoco:report \
69+
-Dmaven.gitcommitid.skip=true
6070
- name: Upload coverage to Codecov
61-
if: matrix.jdk == '8'
62-
uses: codecov/codecov-action@v1
71+
uses: codecov/codecov-action@v4
72+
with:
73+
files: ${{ github.workspace }}/apollo-*/target/site/jacoco/jacoco.xml
74+
75+
compat-api:
76+
runs-on: ubuntu-latest
77+
steps:
78+
- uses: actions/checkout@v4
79+
- name: Set up JDK 8
80+
uses: actions/setup-java@v4
81+
with:
82+
distribution: temurin
83+
java-version: 8
84+
- name: Cache Maven packages
85+
uses: actions/cache@v4
86+
with:
87+
path: ~/.m2/repository
88+
key: ${{ runner.os }}-maven-compat-api-${{ hashFiles('**/pom.xml') }}
89+
restore-keys: |
90+
${{ runner.os }}-maven-compat-api-
91+
${{ runner.os }}-maven-
92+
- name: Build local artifacts for api compatibility
93+
run: |
94+
mvn -B -pl apollo-core,apollo-client,apollo-mockserver -am \
95+
-DskipTests install -Dmaven.gitcommitid.skip=true
96+
- name: Run api compatibility tests
97+
run: |
98+
mvn -B -f apollo-compat-tests/pom.xml -pl apollo-api-compat-it \
99+
test -Dmaven.gitcommitid.skip=true
100+
101+
compat-spring:
102+
runs-on: ubuntu-latest
103+
strategy:
104+
matrix:
105+
include:
106+
- name: spring-3.1.1-jdk8
107+
java: 8
108+
spring_framework: 3.1.1.RELEASE
109+
java_version_prop: 1.8
110+
- name: spring-6.1-jdk17
111+
java: 17
112+
spring_framework: 6.1.18
113+
java_version_prop: 17
114+
name: compat-spring-${{ matrix.name }}
115+
steps:
116+
- uses: actions/checkout@v4
117+
- name: Set up JDK
118+
uses: actions/setup-java@v4
119+
with:
120+
distribution: temurin
121+
java-version: ${{ matrix.java }}
122+
- name: Cache Maven packages
123+
uses: actions/cache@v4
124+
with:
125+
path: ~/.m2/repository
126+
key: ${{ runner.os }}-maven-compat-spring-${{ matrix.name }}-${{ hashFiles('**/pom.xml') }}
127+
restore-keys: |
128+
${{ runner.os }}-maven-compat-spring-${{ matrix.name }}-
129+
${{ runner.os }}-maven-
130+
- name: Build local artifacts for compat tests
131+
run: |
132+
mvn -B -pl apollo-core,apollo-client,apollo-mockserver -am \
133+
-DskipTests install -Dmaven.gitcommitid.skip=true
134+
- name: Run spring compatibility tests
135+
run: |
136+
mvn -B -f apollo-compat-tests/pom.xml -pl apollo-spring-compat-it \
137+
-Dspring.framework.version=${{ matrix.spring_framework }} \
138+
-Djava.version=${{ matrix.java_version_prop }} \
139+
test -Dmaven.gitcommitid.skip=true
140+
141+
compat-spring-boot:
142+
runs-on: ubuntu-latest
143+
strategy:
144+
matrix:
145+
include:
146+
- name: spring-boot-2.7-jdk8
147+
java: 8
148+
spring_boot: 2.7.18
149+
java_version_prop: 1.8
150+
compat_slf4j: 1.7.36
151+
compat_vintage: 5.7.0
152+
- name: spring-boot-3.3-jdk17
153+
java: 17
154+
spring_boot: 3.3.10
155+
java_version_prop: 17
156+
compat_slf4j: 2.0.17
157+
compat_vintage: 5.10.5
158+
- name: spring-boot-4.0-jdk17
159+
java: 17
160+
spring_boot: 4.0.0
161+
java_version_prop: 17
162+
compat_slf4j: 2.0.17
163+
compat_vintage: 6.0.1
164+
name: compat-spring-boot-${{ matrix.name }}
165+
steps:
166+
- uses: actions/checkout@v4
167+
- name: Set up JDK
168+
uses: actions/setup-java@v4
63169
with:
64-
file: ${{ github.workspace }}/apollo-*/target/site/jacoco/jacoco.xml
170+
distribution: temurin
171+
java-version: ${{ matrix.java }}
172+
- name: Cache Maven packages
173+
uses: actions/cache@v4
174+
with:
175+
path: ~/.m2/repository
176+
key: ${{ runner.os }}-maven-compat-spring-boot-${{ matrix.name }}-${{ hashFiles('**/pom.xml') }}
177+
restore-keys: |
178+
${{ runner.os }}-maven-compat-spring-boot-${{ matrix.name }}-
179+
${{ runner.os }}-maven-
180+
- name: Build local artifacts for spring boot compatibility
181+
run: |
182+
mvn -B -pl apollo-core,apollo-client,apollo-mockserver,apollo-client-config-data -am \
183+
-DskipTests \
184+
install -Dmaven.gitcommitid.skip=true
185+
- name: Run spring boot compatibility tests
186+
run: |
187+
mvn -B -f apollo-compat-tests/pom.xml -pl apollo-spring-boot-compat-it \
188+
-Dspring-boot.version=${{ matrix.spring_boot }} \
189+
-Djava.version=${{ matrix.java_version_prop }} \
190+
-Dcompat.slf4j.version=${{ matrix.compat_slf4j }} \
191+
-Dcompat.junit.vintage.version=${{ matrix.compat_vintage }} \
192+
test -Dmaven.gitcommitid.skip=true

apollo-client-config-data/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,15 @@
7373
</exclusion>
7474
</exclusions>
7575
</dependency>
76+
<dependency>
77+
<groupId>com.ctrip.framework.apollo</groupId>
78+
<artifactId>apollo-mockserver</artifactId>
79+
<scope>test</scope>
80+
</dependency>
81+
<dependency>
82+
<groupId>io.projectreactor.netty</groupId>
83+
<artifactId>reactor-netty-http</artifactId>
84+
<scope>test</scope>
85+
</dependency>
7686
</dependencies>
7787
</project>

apollo-client-config-data/src/main/java/com/ctrip/framework/apollo/config/data/extension/initialize/ApolloClientPropertiesFactory.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package com.ctrip.framework.apollo.config.data.extension.initialize;
1818

1919
import com.ctrip.framework.apollo.config.data.extension.properties.ApolloClientProperties;
20-
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties;
2120
import org.springframework.boot.context.properties.bind.BindHandler;
2221
import org.springframework.boot.context.properties.bind.Bindable;
2322
import org.springframework.boot.context.properties.bind.Binder;
@@ -35,10 +34,4 @@ public ApolloClientProperties createApolloClientProperties(
3534
return binder.bind(PROPERTIES_PREFIX,
3635
Bindable.of(ApolloClientProperties.class), bindHandler).orElse(null);
3736
}
38-
39-
public OAuth2ClientProperties createOauth2ClientProperties(Binder binder,
40-
BindHandler bindHandler) {
41-
return binder.bind("spring.security.oauth2.client", Bindable.of(OAuth2ClientProperties.class),
42-
bindHandler).orElse(null);
43-
}
4437
}

apollo-client-config-data/src/main/java/com/ctrip/framework/apollo/config/data/extension/webclient/ApolloClientLongPollingExtensionInitializer.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@
2323
import com.ctrip.framework.apollo.util.http.HttpClient;
2424
import com.ctrip.framework.foundation.internals.ServiceBootstrap;
2525
import java.util.List;
26+
import java.util.function.Consumer;
2627
import org.apache.commons.logging.Log;
2728
import org.springframework.boot.context.properties.bind.BindHandler;
2829
import org.springframework.boot.context.properties.bind.Binder;
2930
import org.springframework.boot.logging.DeferredLogFactory;
30-
import org.springframework.boot.web.reactive.function.client.WebClientCustomizer;
3131
import org.springframework.util.CollectionUtils;
3232
import org.springframework.web.reactive.function.client.WebClient;
3333

@@ -55,11 +55,11 @@ public void initialize(ApolloClientProperties apolloClientProperties, Binder bin
5555
.loadAllOrdered(ApolloClientWebClientCustomizerFactory.class);
5656
if (!CollectionUtils.isEmpty(factories)) {
5757
for (ApolloClientWebClientCustomizerFactory factory : factories) {
58-
WebClientCustomizer webClientCustomizer = factory
58+
Consumer<WebClient.Builder> webClientCustomizer = factory
5959
.createWebClientCustomizer(apolloClientProperties, binder, bindHandler, this.log,
6060
this.bootstrapContext);
6161
if (webClientCustomizer != null) {
62-
webClientCustomizer.customize(webClientBuilder);
62+
webClientCustomizer.accept(webClientBuilder);
6363
}
6464
}
6565
}

apollo-client-config-data/src/main/java/com/ctrip/framework/apollo/config/data/extension/webclient/ApolloWebClientHttpClient.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@
2222
import com.ctrip.framework.apollo.util.http.HttpRequest;
2323
import com.ctrip.framework.apollo.util.http.HttpResponse;
2424
import com.google.gson.Gson;
25+
import java.lang.reflect.Method;
2526
import java.lang.reflect.Type;
2627
import java.net.URI;
2728
import java.util.Map;
2829
import org.springframework.http.HttpStatus;
2930
import org.springframework.util.CollectionUtils;
31+
import org.springframework.web.reactive.function.client.ClientResponse;
3032
import org.springframework.web.reactive.function.client.WebClient;
3133
import reactor.core.publisher.Mono;
3234

@@ -64,15 +66,16 @@ private <T> HttpResponse<T> doGetInternal(HttpRequest httpRequest, Type response
6466
}
6567
}
6668
return requestHeadersSpec.exchangeToMono(clientResponse -> {
67-
if (HttpStatus.OK.equals(clientResponse.statusCode())) {
69+
int statusCode = this.resolveStatusCode(clientResponse);
70+
if (HttpStatus.OK.value() == statusCode) {
6871
return clientResponse.bodyToMono(String.class)
6972
.map(body -> new HttpResponse<T>(HttpStatus.OK.value(),
7073
gson.fromJson(body, responseType)));
7174
}
72-
if (HttpStatus.NOT_MODIFIED.equals(clientResponse.statusCode())) {
75+
if (HttpStatus.NOT_MODIFIED.value() == statusCode) {
7376
return Mono.just(new HttpResponse<T>(HttpStatus.NOT_MODIFIED.value(), null));
7477
}
75-
return Mono.error(new ApolloConfigStatusCodeException(clientResponse.rawStatusCode(),
78+
return Mono.error(new ApolloConfigStatusCodeException(statusCode,
7679
String.format("Get operation failed for %s", httpRequest.getUrl())));
7780
}).block();
7881
}
@@ -82,4 +85,28 @@ public <T> HttpResponse<T> doGet(HttpRequest httpRequest, Type responseType)
8285
throws ApolloConfigException {
8386
return this.doGetInternal(httpRequest, responseType);
8487
}
88+
89+
/**
90+
* Resolve HTTP status code across Spring WebFlux 5/6/7.
91+
*
92+
* <p>ClientResponse#statusCode has different return types across major versions
93+
* (HttpStatus in Spring 5, HttpStatusCode in Spring 6/7). Calling it directly would bind
94+
* to one method descriptor at compile time and could fail on another runtime version.
95+
* Reflection keeps this bridge binary-compatible for Boot 2/3/4 compatibility tests.
96+
*/
97+
private int resolveStatusCode(Object clientResponse) {
98+
try {
99+
Method statusCodeMethod = ClientResponse.class.getMethod("statusCode");
100+
Object statusCode = statusCodeMethod.invoke(clientResponse);
101+
if (statusCode == null) {
102+
throw new ApolloConfigException("Failed to resolve response status code: statusCode is null");
103+
}
104+
// Both HttpStatus and HttpStatusCode expose value(), so resolve it reflectively.
105+
Method valueMethod = statusCode.getClass().getMethod("value");
106+
Object value = valueMethod.invoke(statusCode);
107+
return ((Number) value).intValue();
108+
} catch (Exception ex) {
109+
throw new ApolloConfigException("Failed to resolve response status code", ex);
110+
}
111+
}
85112
}

apollo-client-config-data/src/main/java/com/ctrip/framework/apollo/config/data/extension/webclient/customizer/spi/ApolloClientWebClientCustomizerFactory.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,20 @@
1818

1919
import com.ctrip.framework.apollo.config.data.extension.properties.ApolloClientProperties;
2020
import com.ctrip.framework.apollo.core.spi.Ordered;
21+
import java.util.function.Consumer;
2122
import org.apache.commons.logging.Log;
2223
import org.springframework.boot.context.properties.bind.BindHandler;
2324
import org.springframework.boot.context.properties.bind.Binder;
24-
import org.springframework.boot.web.reactive.function.client.WebClientCustomizer;
2525
import org.springframework.lang.Nullable;
26+
import org.springframework.web.reactive.function.client.WebClient;
2627

2728
/**
2829
* @author vdisk <vdisk@foxmail.com>
2930
*/
3031
public interface ApolloClientWebClientCustomizerFactory extends Ordered {
3132

3233
/**
33-
* create a WebClientCustomizer instance
34+
* create a webclient builder customizer
3435
*
3536
* @param apolloClientProperties apollo client binded properties
3637
* @param binder properties binder
@@ -41,10 +42,10 @@ public interface ApolloClientWebClientCustomizerFactory extends Ordered {
4142
* Spring Boot 3.x or
4243
* org.springframework.boot.bootstrap.ConfigurableBootstrapContext
4344
* for Spring Boot 4.x)
44-
* @return WebClientCustomizer instance or null
45+
* @return customizer instance or null
4546
*/
4647
@Nullable
47-
WebClientCustomizer createWebClientCustomizer(ApolloClientProperties apolloClientProperties,
48+
Consumer<WebClient.Builder> createWebClientCustomizer(ApolloClientProperties apolloClientProperties,
4849
Binder binder, BindHandler bindHandler, Log log,
4950
Object bootstrapContext);
5051
}

0 commit comments

Comments
 (0)