Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 7cc8892

Browse files
committedMar 4, 2025·
add unit test
1 parent c2eb21f commit 7cc8892

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed
 

‎services/src/main/java/io/grpc/protobuf/services/HealthServiceImpl.java

+14
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@
2828
import io.grpc.health.v1.HealthCheckResponse.ServingStatus;
2929
import io.grpc.health.v1.HealthGrpc;
3030
import io.grpc.stub.StreamObserver;
31+
import java.util.Collections;
3132
import java.util.HashMap;
3233
import java.util.IdentityHashMap;
3334
import java.util.Map;
35+
import java.util.Set;
3436
import java.util.concurrent.ConcurrentHashMap;
3537
import java.util.logging.Level;
3638
import java.util.logging.Logger;
@@ -169,6 +171,18 @@ int numWatchersForTest(String service) {
169171
}
170172
}
171173

174+
@VisibleForTesting
175+
Set<StreamObserver<HealthCheckResponse>> watchersForTest(String service) {
176+
synchronized (watchLock) {
177+
IdentityHashMap<StreamObserver<HealthCheckResponse>, Boolean> serviceWatchers =
178+
watchers.get(service);
179+
if (serviceWatchers == null) {
180+
return Collections.emptySet();
181+
}
182+
return serviceWatchers.keySet();
183+
}
184+
}
185+
172186
@GuardedBy("watchLock")
173187
private void notifyWatchers(String service, @Nullable ServingStatus status) {
174188
HealthCheckResponse response = getResponseForWatch(status);

‎services/src/test/java/io/grpc/protobuf/services/HealthStatusManagerTest.java

+60
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import io.grpc.stub.StreamObserver;
3232
import io.grpc.testing.GrpcServerRule;
3333
import java.util.ArrayDeque;
34+
import java.util.Set;
3435
import java.util.concurrent.TimeUnit;
3536
import org.junit.After;
3637
import org.junit.Before;
@@ -109,6 +110,46 @@ public void enterTerminalState_watch() throws Exception {
109110
assertThat(obs.responses).isEmpty();
110111
}
111112

113+
@Test
114+
public void watchNotifyException() throws Exception {
115+
manager.setStatus(SERVICE1, ServingStatus.SERVING);
116+
CancellableRespObserver cobs1 = new CancellableRespObserver();
117+
service.watch(HealthCheckRequest.newBuilder().setService(SERVICE1).build(), cobs1);
118+
assertThat(cobs1.responses).hasSize(1);
119+
HealthCheckResponse resp = (HealthCheckResponse) cobs1.responses.poll();
120+
assertThat(resp.getStatus()).isEqualTo(ServingStatus.SERVING);
121+
cobs1.responses.clear();
122+
123+
CancellableRespObserver cobs2 = new CancellableRespObserver();
124+
service.watch(HealthCheckRequest.newBuilder().setService(SERVICE1).build(), cobs2);
125+
assertThat(cobs2.responses).hasSize(1);
126+
resp = (HealthCheckResponse) cobs2.responses.poll();
127+
assertThat(resp.getStatus()).isEqualTo(ServingStatus.SERVING);
128+
cobs2.responses.clear();
129+
130+
// assert that both observers are in watchers
131+
Set<StreamObserver<HealthCheckResponse>> observers = service.watchersForTest(SERVICE1);
132+
assertThat(observers).hasSize(2);
133+
134+
// cancel first Observer
135+
cobs1 = (CancellableRespObserver) observers.stream().findFirst().get();
136+
cobs1.setCancelled(true);
137+
138+
cobs2 = (CancellableRespObserver) observers.stream().skip(1).findFirst().get();
139+
140+
manager.setStatus(SERVICE1, ServingStatus.NOT_SERVING);
141+
142+
// first observer should notify exception
143+
assertThat(cobs1.responses).isEmpty();
144+
145+
// second observer should be notified
146+
assertThat(cobs2.responses).hasSize(1);
147+
resp = (HealthCheckResponse) cobs2.responses.poll();
148+
assertThat(resp.getStatus()).isEqualTo(ServingStatus.NOT_SERVING);
149+
cobs2.responses.clear();
150+
151+
}
152+
112153
@Test
113154
public void enterTerminalState_ignoreClear() throws Exception {
114155
manager.setStatus(SERVICE1, ServingStatus.SERVING);
@@ -311,4 +352,23 @@ public void onCompleted() {
311352
responses.add("onCompleted");
312353
}
313354
}
355+
356+
private static class CancellableRespObserver extends RespObserver {
357+
358+
boolean cancelled = false;
359+
360+
public void setCancelled(boolean cancelled) {
361+
this.cancelled = cancelled;
362+
}
363+
364+
@Override
365+
public void onNext(HealthCheckResponse value) {
366+
if (cancelled) {
367+
throw Status.CANCELLED
368+
.withDescription("call already cancelled.")
369+
.asRuntimeException();
370+
}
371+
super.onNext(value);
372+
}
373+
}
314374
}

0 commit comments

Comments
 (0)
Please sign in to comment.