Skip to content

Commit 8ad9334

Browse files
committed
test: overhaul automated compatibility coverage
1 parent 0a8c618 commit 8ad9334

File tree

56 files changed

+4257
-83
lines changed

Some content is hidden

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

56 files changed

+4257
-83
lines changed

.github/workflows/build.yml

Lines changed: 150 additions & 17 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,35 +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-
run: mvn -B clean package -P travis jacoco:report -Dmaven.gitcommitid.skip=true
49-
- name: JDK 11
50-
if: matrix.jdk == '11'
51-
run: mvn -B clean compile -Dmaven.gitcommitid.skip=true
52-
- name: JDK 17
53-
if: matrix.jdk == '17'
46+
- name: Compile
5447
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
5570
- name: Upload coverage to Codecov
56-
if: matrix.jdk == '8'
57-
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
5886
with:
59-
file: ${{ github.workspace }}/apollo-*/target/site/jacoco/jacoco.xml
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
169+
with:
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

AGENTS.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Repository Guidelines
2+
3+
## Project Structure & Module Organization
4+
- Multi-module Maven repository with root `pom.xml`.
5+
- Core modules:
6+
- `apollo-core`: shared constants/utilities.
7+
- `apollo-client`: main Java client and Spring integration.
8+
- `apollo-client-config-data`: Spring Boot ConfigData integration.
9+
- `apollo-mockserver`: test/mock support.
10+
- `apollo-openapi`: OpenAPI client.
11+
- `apollo-plugin`: plugin modules.
12+
- Tests follow standard Maven layout: `*/src/test/java` and `*/src/test/resources`.
13+
- CI workflows are in `.github/workflows`.
14+
15+
## Build, Test, and Development Commands
16+
- Build all modules: `mvn -B clean package -Dmaven.gitcommitid.skip=true`
17+
- Run all tests: `mvn clean test`
18+
- Run one module tests: `mvn -pl apollo-client clean test`
19+
- Run targeted tests: `mvn -pl apollo-client -Dtest=ClassNameTest test`
20+
- Compile only: `mvn -B clean compile -Dmaven.gitcommitid.skip=true`
21+
- Notes:
22+
- This repository does **not** configure `spotless`; do not use `spotless:apply` here.
23+
- Some integration tests log connection warnings in local/dev environments; focus on final Maven summary.
24+
25+
## Coding Style & Naming Conventions
26+
- Java style: follow existing codebase conventions (Google-style Java patterns in current files).
27+
- Keep changes minimal and module-scoped.
28+
- Preserve existing package/class naming conventions.
29+
- Add/adjust tests for non-trivial behavior changes.
30+
31+
## Testing Guidelines
32+
- JUnit 4 + JUnit Vintage are both used in this repo.
33+
- For bug fixes:
34+
- Add a regression test first when feasible.
35+
- Run module-level tests for changed modules.
36+
- Prefer full `mvn clean test` before opening PR.
37+
38+
## Commit & Pull Request Guidelines
39+
- Use Conventional Commits, e.g. `fix: ...`, `feat: ...`, `docs: ...`.
40+
- If applicable, include issue linkage in commit message body, e.g. `Fixes #88`.
41+
- Keep PR commits clean:
42+
- Require a single commit in the PR branch before review (squash locally if needed).
43+
- Open PRs with `.github/PULL_REQUEST_TEMPLATE.md` and fill all sections with concrete content.
44+
- PR description should include:
45+
- purpose/root cause
46+
- change summary
47+
- test commands actually run
48+
- PR submission flow:
49+
- Push branch to personal fork remote first (for example `origin`).
50+
- Open PR from `<your-fork>:<branch>` to `apolloconfig/apollo-java:main`.
51+
- If history is rewritten for squash, update remote branch with `--force-with-lease`.
52+
53+
## CHANGES.md Rules
54+
- Update `CHANGES.md` for user-visible fixes/features.
55+
- Use bullet style consistent with existing entries.
56+
- Entry format should be a Markdown link:
57+
- link text = the actual change description
58+
- link target = the PR URL (not issue URL)
59+
- Example:
60+
- `[Fix ... detailed summary](https://github.com/apolloconfig/apollo-java/pull/123)`
61+
62+
## Agent Workflow Hints
63+
- Reproduce first, then fix.
64+
- Prefer targeted module/test runs during iteration, then run broader tests before PR.
65+
- When creating upstream PRs:
66+
- use a branch in personal fork
67+
- base repository is `apolloconfig/apollo-java`
68+
- ensure PR branch is squashed to one commit

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
}

0 commit comments

Comments
 (0)