44import io .grpc .health .v1 .HealthCheckRequest ;
55import io .grpc .health .v1 .HealthCheckResponse ;
66import io .grpc .health .v1 .HealthGrpc ;
7+ import io .grpc .reflection .v1alpha .ServerReflectionGrpc ;
8+ import io .grpc .reflection .v1alpha .ServerReflectionRequest ;
9+ import io .grpc .reflection .v1alpha .ServerReflectionResponse ;
10+ import io .grpc .reflection .v1alpha .ServiceResponse ;
11+ import io .grpc .stub .StreamObserver ;
12+ import lombok .Getter ;
13+ import lombok .extern .slf4j .Slf4j ;
14+ import org .hamcrest .Matchers ;
15+ import org .junit .Rule ;
716import org .junit .Test ;
817import org .junit .runner .RunWith ;
918import org .lognet .springboot .grpc .GRpcService ;
1322import org .springframework .boot .test .context .SpringBootTest ;
1423import org .springframework .boot .test .context .TestConfiguration ;
1524import org .springframework .boot .test .mock .mockito .SpyBean ;
25+ import org .springframework .cloud .commons .util .InetUtils ;
26+ import org .springframework .cloud .commons .util .InetUtilsProperties ;
1627import org .springframework .test .annotation .DirtiesContext ;
1728import org .springframework .test .context .ActiveProfiles ;
1829import org .springframework .test .context .junit4 .SpringRunner ;
30+ import org .testcontainers .containers .Container ;
1931
32+ import java .io .IOException ;
33+ import java .util .ArrayList ;
34+ import java .util .List ;
35+ import java .util .concurrent .CountDownLatch ;
2036import java .util .concurrent .ExecutionException ;
37+ import java .util .concurrent .TimeUnit ;
2138
2239import static org .hamcrest .MatcherAssert .assertThat ;
40+ import static org .hamcrest .Matchers .containsInAnyOrder ;
41+ import static org .hamcrest .Matchers .greaterThan ;
42+ import static org .hamcrest .Matchers .hasSize ;
2343import static org .hamcrest .Matchers .is ;
2444import static org .hamcrest .Matchers .isA ;
2545import static org .springframework .boot .test .context .SpringBootTest .WebEnvironment .NONE ;
2646
2747@ RunWith (SpringRunner .class )
28- @ SpringBootTest (classes = {DemoApp .class , CustomManagedHealthStatusServiceTest .Cfg .class }, webEnvironment = NONE )
48+ @ SpringBootTest (classes = {DemoApp .class , CustomManagedHealthStatusServiceTest .Cfg .class }, webEnvironment = NONE ,
49+ properties = "grpc.enableReflection=true" )
2950@ ActiveProfiles ("disable-security" )
30-
51+ @ Slf4j
3152public class CustomManagedHealthStatusServiceTest extends GrpcServerTestBase {
53+
54+ @ Rule
55+ public GrpcHealthProbeContainer grpcHealthProbe = new GrpcHealthProbeContainer ();
56+
3257 @ TestConfiguration
33- static class Cfg {
58+ static class Cfg {
3459 @ GRpcService
35- static class MyCustomHealthStatusService extends DefaultHealthStatusService {}
60+ static class MyCustomHealthStatusService extends DefaultHealthStatusService {
61+ @ Getter
62+ private final ArrayList <String > registeredServices = new ArrayList <>();
63+
64+ @ Getter
65+ private final ArrayList <String > checkedServices = new ArrayList <>();
66+
67+ @ Override
68+ public void setStatus (String service , HealthCheckResponse .ServingStatus status ) {
69+ synchronized (registeredServices ) {
70+ registeredServices .add (service );
71+ }
72+ super .setStatus (service , status );
73+ }
74+
75+ @ Override
76+ public void check (HealthCheckRequest request , StreamObserver <HealthCheckResponse > responseObserver ) {
77+ synchronized (checkedServices ) {
78+ checkedServices .add (request .getService ());
79+ }
80+ super .check (request , responseObserver );
81+ }
82+ }
3683
3784 }
3885
@@ -42,7 +89,52 @@ static class MyCustomHealthStatusService extends DefaultHealthStatusService {}
4289 @ Test
4390 @ DirtiesContext
4491 public void contextLoads () {
45- assertThat (healthStatusManager ,isA (Cfg .MyCustomHealthStatusService .class ));
92+ assertThat (healthStatusManager , isA (Cfg .MyCustomHealthStatusService .class ));
93+ }
94+
95+ @ Test
96+ @ DirtiesContext
97+ public void grpcHealthProbeTest () throws InterruptedException , IOException {
98+
99+
100+ Cfg .MyCustomHealthStatusService healthManager = (Cfg .MyCustomHealthStatusService ) healthStatusManager ;
101+
102+ assertThat (healthManager .getRegisteredServices (), hasSize (greaterThan (0 )));
103+ final List <String > discovered = discoverServicesNames ();
104+
105+ assertThat (discovered , containsInAnyOrder (healthManager .getRegisteredServices ().toArray ()));
106+
107+ String addressParameter = String .format ("-addr=%s:%d" ,
108+ new InetUtils (new InetUtilsProperties ()).findFirstNonLoopbackHostInfo ().getIpAddress (),
109+ getPort ());
110+
111+
112+ ArrayList <String > allServices = new ArrayList <>(discovered );
113+ allServices .add ("" );
114+
115+ for (String serviceName : allServices ) {
116+ final Container .ExecResult execResult = grpcHealthProbe
117+ .execInContainer ("/bin/grpc_health_probe" ,
118+ addressParameter ,
119+ "-service=" + serviceName
120+ );
121+
122+ assertThat (execResult .getExitCode (), is (0 ));
123+
124+ }
125+ assertThat (healthManager .getCheckedServices (), containsInAnyOrder (allServices .toArray ()));
126+
127+
128+ final Container .ExecResult execResult = grpcHealthProbe
129+ .execInContainer ("/bin/grpc_health_probe" ,
130+ addressParameter ,
131+ "-service=blah"
132+ );
133+
134+ assertThat (execResult .getExitCode (), Matchers .not (0 ));
135+
136+
137+
46138 }
47139
48140 @ Test
@@ -54,7 +146,49 @@ public void testHealthCheck() throws ExecutionException, InterruptedException {
54146
55147 assertThat (servingStatus , is (HealthCheckResponse .ServingStatus .SERVING ));
56148
57- Mockito .verify (healthStatusManager ,Mockito .atLeast (1 ))
58- .setStatus (Mockito .any (String .class ),Mockito .eq (HealthCheckResponse .ServingStatus .SERVING ));
149+ Mockito .verify (healthStatusManager , Mockito .atLeast (1 ))
150+ .setStatus (Mockito .any (String .class ), Mockito .eq (HealthCheckResponse .ServingStatus .SERVING ));
151+ }
152+
153+ @ Test
154+ public void testReflection () throws InterruptedException {
155+
156+ assertThat (discoverServicesNames (), Matchers .not (Matchers .empty ()));
157+ }
158+
159+ private List <String > discoverServicesNames () throws InterruptedException {
160+ List <String > discoveredServiceNames = new ArrayList <>();
161+ ServerReflectionRequest request = ServerReflectionRequest .newBuilder ().setListServices ("services" ).setHost ("localhost" ).build ();
162+ CountDownLatch latch = new CountDownLatch (1 );
163+ ServerReflectionGrpc .newStub (channel ).serverReflectionInfo (new StreamObserver <ServerReflectionResponse >() {
164+ @ Override
165+ public void onNext (ServerReflectionResponse value ) {
166+ List <ServiceResponse > serviceList = value .getListServicesResponse ().getServiceList ();
167+ for (ServiceResponse serviceResponse : serviceList ) {
168+
169+ final String serviceName = serviceResponse .getName ();
170+ if (
171+ !serviceName .equals (ServerReflectionGrpc .getServiceDescriptor ().getName ()) &&
172+ !serviceName .equals (HealthGrpc .getServiceDescriptor ().getName ())
173+ ) {
174+
175+ discoveredServiceNames .add (serviceName );
176+ }
177+ }
178+ }
179+
180+ @ Override
181+ public void onError (Throwable t ) {
182+
183+ }
184+
185+ @ Override
186+ public void onCompleted () {
187+ latch .countDown ();
188+ }
189+ }).onNext (request );
190+
191+ latch .await (3 , TimeUnit .SECONDS );
192+ return discoveredServiceNames ;
59193 }
60194}
0 commit comments