Skip to content

Commit d9d658f

Browse files
committed
添加测试类进行边界条件的测试
1 parent aa8a1dd commit d9d658f

13 files changed

Lines changed: 761 additions & 15 deletions

File tree

.github/workflows/maven.yml

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,39 @@ on:
77
branches: [ "main" ]
88

99
jobs:
10+
unit-tests:
11+
runs-on: ubuntu-latest
12+
timeout-minutes: 20
13+
14+
steps:
15+
- uses: actions/checkout@v4
16+
17+
- name: Set up JDK 17
18+
uses: actions/setup-java@v4
19+
with:
20+
java-version: '17'
21+
distribution: 'temurin'
22+
cache: maven
23+
24+
- name: Run Maven Tests
25+
run: |
26+
set -euo pipefail
27+
mvn -B -ntp clean test
28+
29+
- name: Upload Surefire Reports
30+
if: always()
31+
uses: actions/upload-artifact@v4
32+
with:
33+
name: surefire-reports
34+
path: |
35+
**/target/surefire-reports/*.txt
36+
**/target/surefire-reports/*.xml
37+
1038
integration-test:
1139
runs-on: ubuntu-latest
12-
40+
timeout-minutes: 30
41+
needs: unit-tests
42+
1343
services:
1444
nacos:
1545
image: nacos/nacos-server:v2.2.3
@@ -25,7 +55,6 @@ jobs:
2555
--health-interval 10s
2656
--health-timeout 5s
2757
--health-retries 10
28-
2958
steps:
3059
- uses: actions/checkout@v4
3160

@@ -37,28 +66,26 @@ jobs:
3766
cache: maven
3867

3968
- name: Set up Python
40-
uses: actions/setup-python@v4
69+
uses: actions/setup-python@v5
4170
with:
4271
python-version: '3.x'
4372

44-
4573
- name: Install Python Dependencies
4674
run: |
75+
set -euo pipefail
4776
python -m pip install --upgrade pip
4877
pip install grpcio grpcio-tools protobuf
4978
50-
- name: Build with Maven
51-
run: mvn clean install -DskipTests
52-
53-
- name: Run Unit Tests
79+
- name: Build Jars For Integration Test
5480
run: |
55-
echo "Running unit tests..."
56-
mvn test -pl rpc-core
81+
set -euo pipefail
82+
mvn -B -ntp -DskipTests clean package
5783
5884
- name: Start RPC Provider (Java Server)
5985
run: |
86+
set -euo pipefail
6087
# Build classpath including project jars and dependencies
61-
CLASSPATH="rpc-provider/target/rpc-provider-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout -pl rpc-provider -am)"
88+
CLASSPATH="rpc-provider/target/rpc-provider-1.0-SNAPSHOT.jar:rpc-transport-netty/target/rpc-transport-netty-1.0-SNAPSHOT.jar:rpc-core/target/rpc-core-1.0-SNAPSHOT.jar:rpc-common/target/rpc-common-1.0-SNAPSHOT.jar:rpc-api/target/rpc-api-1.0-SNAPSHOT.jar:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout -pl rpc-provider -am)"
6289
6390
echo "Starting Java Server..."
6491
# Run in background with nohup
@@ -75,5 +102,16 @@ jobs:
75102
- name: Run Python Client
76103
working-directory: python_client
77104
run: |
105+
set -euo pipefail
78106
echo "Running Python Client..."
79-
python client.py
107+
python client.py | tee ../python-client.log
108+
grep -q "Message: Success" ../python-client.log
109+
110+
- name: Upload CI Logs
111+
if: always()
112+
uses: actions/upload-artifact@v4
113+
with:
114+
name: ci-logs
115+
path: |
116+
server.log
117+
python-client.log

rpc-core/src/main/java/com/xiaoyu/rpc/core/client/RpcClient.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.xiaoyu.rpc.core.transport.TransportClient;
99

1010
import java.net.InetSocketAddress;
11+
import java.util.Objects;
1112

1213
public class RpcClient {
1314

@@ -25,6 +26,11 @@ public RpcClient() {
2526
this.transportClient = transport.createClient();
2627
}
2728

29+
RpcClient(TransportClient transportClient, ServiceDiscovery serviceDiscovery) {
30+
this.transportClient = Objects.requireNonNull(transportClient, "transportClient");
31+
this.serviceDiscovery = Objects.requireNonNull(serviceDiscovery, "serviceDiscovery");
32+
}
33+
2834
public java.util.concurrent.CompletableFuture<Object> sendRequest(RpcRequest request, Class<?> returnType) {
2935
try {
3036
// 先做一次服务发现(同步查找,通常会命中本地缓存)

rpc-core/src/main/java/com/xiaoyu/rpc/core/registry/nacos/NacosServiceDiscovery.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,14 @@ public class NacosServiceDiscovery implements ServiceDiscovery {
3232
private static final java.util.Set<String> subscribedServices = java.util.concurrent.ConcurrentHashMap.newKeySet();
3333

3434
public NacosServiceDiscovery() {
35-
this.namingService = NacosUtils.getNacosNamingService();
36-
String loadBalancerCode = RpcConfig.getInstance().getLoadBalancer();
37-
this.loadBalancer = ExtensionLoader.getExtensionLoader(LoadBalancer.class).getExtension(loadBalancerCode);
35+
this(NacosUtils.getNacosNamingService(),
36+
ExtensionLoader.getExtensionLoader(LoadBalancer.class)
37+
.getExtension(RpcConfig.getInstance().getLoadBalancer()));
38+
}
39+
40+
NacosServiceDiscovery(NamingService namingService, LoadBalancer loadBalancer) {
41+
this.namingService = namingService;
42+
this.loadBalancer = loadBalancer;
3843
}
3944

4045
@Override
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package com.xiaoyu.rpc.core.client;
2+
3+
import com.google.protobuf.ByteString;
4+
import com.xiaoyu.rpc.common.extension.ExtensionLoader;
5+
import com.xiaoyu.rpc.common.serialization.Serializer;
6+
import com.xiaoyu.rpc.common.vo.RpcRequest;
7+
import com.xiaoyu.rpc.common.vo.RpcResponse;
8+
import com.xiaoyu.rpc.core.config.RpcConfig;
9+
import com.xiaoyu.rpc.core.registry.ServiceDiscovery;
10+
import com.xiaoyu.rpc.core.transport.TransportClient;
11+
import org.junit.jupiter.api.AfterEach;
12+
import org.junit.jupiter.api.BeforeEach;
13+
import org.junit.jupiter.api.DisplayName;
14+
import org.junit.jupiter.api.Test;
15+
16+
import java.lang.reflect.Field;
17+
import java.net.InetSocketAddress;
18+
import java.util.concurrent.CompletableFuture;
19+
import java.util.concurrent.ExecutionException;
20+
import java.util.concurrent.TimeUnit;
21+
22+
import static org.junit.jupiter.api.Assertions.*;
23+
24+
@DisplayName("RpcClient 异常与边界测试")
25+
public class RpcClientTest {
26+
27+
@BeforeEach
28+
void setUp() throws Exception {
29+
System.setProperty("rpc.serializer", "java");
30+
resetRpcConfigSingleton();
31+
}
32+
33+
@AfterEach
34+
void tearDown() throws Exception {
35+
System.clearProperty("rpc.serializer");
36+
resetRpcConfigSingleton();
37+
}
38+
39+
@Test
40+
@DisplayName("服务发现为空时返回异常 Future")
41+
void testServiceNotFound() {
42+
TransportClient transportClient = (request, address) -> CompletableFuture.completedFuture(null);
43+
ServiceDiscovery serviceDiscovery = serviceName -> null;
44+
RpcClient rpcClient = new RpcClient(transportClient, serviceDiscovery);
45+
46+
CompletableFuture<Object> future = rpcClient.sendRequest(minimalRequest(), String.class);
47+
48+
assertTrue(future.isCompletedExceptionally(), "Future should be completed exceptionally");
49+
ExecutionException ex = assertThrows(ExecutionException.class, () -> future.get(1, TimeUnit.SECONDS));
50+
assertTrue(ex.getCause().getMessage().contains("未发现服务"), "Error should mention service not found");
51+
}
52+
53+
@Test
54+
@DisplayName("传输层抛异常时返回异常 Future")
55+
void testTransportThrows() {
56+
TransportClient transportClient = (request, address) -> {
57+
throw new RuntimeException("transport down");
58+
};
59+
ServiceDiscovery serviceDiscovery = serviceName -> new InetSocketAddress("127.0.0.1", 8080);
60+
RpcClient rpcClient = new RpcClient(transportClient, serviceDiscovery);
61+
62+
CompletableFuture<Object> future = rpcClient.sendRequest(minimalRequest(), String.class);
63+
64+
assertTrue(future.isCompletedExceptionally(), "Future should be completed exceptionally");
65+
ExecutionException ex = assertThrows(ExecutionException.class, () -> future.get(1, TimeUnit.SECONDS));
66+
assertTrue(ex.getCause().getMessage().contains("transport down"), "Error should keep transport failure");
67+
}
68+
69+
@Test
70+
@DisplayName("返回非 RpcResponse 类型时应失败")
71+
void testUnexpectedResponseType() {
72+
TransportClient transportClient = (request, address) -> CompletableFuture.completedFuture("not-rpc-response");
73+
ServiceDiscovery serviceDiscovery = serviceName -> new InetSocketAddress("127.0.0.1", 8080);
74+
RpcClient rpcClient = new RpcClient(transportClient, serviceDiscovery);
75+
76+
CompletableFuture<Object> future = rpcClient.sendRequest(minimalRequest(), String.class);
77+
78+
assertTrue(future.isCompletedExceptionally(), "Future should be completed exceptionally");
79+
ExecutionException ex = assertThrows(ExecutionException.class, () -> future.get(1, TimeUnit.SECONDS));
80+
assertTrue(ex.getCause().getMessage().contains("Unexpected response type"), "Error should mention type mismatch");
81+
}
82+
83+
@Test
84+
@DisplayName("RpcResponse 正常反序列化返回目标类型")
85+
void testSuccessfulDeserialize() throws Exception {
86+
Serializer serializer = ExtensionLoader.getExtensionLoader(Serializer.class).getExtension("java");
87+
byte[] body = serializer.serialize("hello");
88+
RpcResponse response = RpcResponse.newBuilder()
89+
.setRequestId("req-1")
90+
.setMessage("Success")
91+
.setData(ByteString.copyFrom(body))
92+
.build();
93+
94+
TransportClient transportClient = (request, address) -> CompletableFuture.completedFuture(response);
95+
ServiceDiscovery serviceDiscovery = serviceName -> new InetSocketAddress("127.0.0.1", 8080);
96+
RpcClient rpcClient = new RpcClient(transportClient, serviceDiscovery);
97+
98+
Object result = rpcClient.sendRequest(minimalRequest(), String.class).get(1, TimeUnit.SECONDS);
99+
assertEquals("hello", result, "Response payload should be deserialized to String");
100+
}
101+
102+
private static RpcRequest minimalRequest() {
103+
return RpcRequest.newBuilder()
104+
.setInterfaceName("com.example.DemoService")
105+
.setMethodName("ping")
106+
.build();
107+
}
108+
109+
private static void resetRpcConfigSingleton() throws Exception {
110+
Field field = RpcConfig.class.getDeclaredField("instance");
111+
field.setAccessible(true);
112+
field.set(null, null);
113+
}
114+
}

rpc-core/src/test/java/com/xiaoyu/rpc/core/config/RpcConfigTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ void setUp() throws Exception {
2323
System.clearProperty("rpc.registry");
2424
System.clearProperty("rpc.serializer");
2525
System.clearProperty("rpc.server-port");
26+
System.clearProperty("rpc.transport");
2627
}
2728

2829
@AfterEach
@@ -31,6 +32,7 @@ void tearDown() throws Exception {
3132
System.clearProperty("rpc.registry");
3233
System.clearProperty("rpc.serializer");
3334
System.clearProperty("rpc.server-port");
35+
System.clearProperty("rpc.transport");
3436
// 重置单例
3537
resetSingleton();
3638
}
@@ -91,6 +93,16 @@ void testSystemPropertyOverridePort() throws Exception {
9193
assertEquals(9999, config.getServerPort(), "Server port should be overridden by system property");
9294
}
9395

96+
@Test
97+
@DisplayName("测试系统属性覆盖 - 传输层")
98+
void testSystemPropertyOverrideTransport() throws Exception {
99+
System.setProperty("rpc.transport", "netty");
100+
resetSingleton();
101+
102+
RpcConfig config = RpcConfig.getInstance();
103+
assertEquals("netty", config.getTransport(), "Transport should be overridden by system property");
104+
}
105+
94106
@Test
95107
@DisplayName("测试 getSerializerCode 方法")
96108
void testGetSerializerCode() {

rpc-core/src/test/java/com/xiaoyu/rpc/core/registry/LocalRegistryTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.xiaoyu.rpc.core.registry;
22

33
import org.junit.jupiter.api.BeforeEach;
4+
import org.junit.jupiter.api.AfterEach;
45
import org.junit.jupiter.api.Test;
56
import org.junit.jupiter.api.DisplayName;
67

@@ -21,6 +22,11 @@ void setUp() {
2122
registry = new LocalRegistry();
2223
}
2324

25+
@AfterEach
26+
void tearDown() {
27+
registry.clearRegistry();
28+
}
29+
2430
@Test
2531
@DisplayName("测试注册和查找服务")
2632
void testRegisterAndLookupService() {
@@ -80,4 +86,17 @@ void testOverwriteService() {
8086
assertNotNull(result, "Should find service");
8187
assertEquals(9090, result.getPort(), "Port should be updated to new address");
8288
}
89+
90+
@Test
91+
@DisplayName("测试清空注册中心")
92+
void testClearRegistry() {
93+
String serviceName = "com.example.ToBeCleared";
94+
registry.registerService(serviceName, new InetSocketAddress("127.0.0.1", 8088));
95+
96+
assertNotNull(registry.lookupService(serviceName), "Service should exist before clear");
97+
98+
registry.clearRegistry();
99+
100+
assertNull(registry.lookupService(serviceName), "Service should be removed after clear");
101+
}
83102
}

0 commit comments

Comments
 (0)