Skip to content

Commit 03579c4

Browse files
authored
Avoid implementing the OCI Monitoring interface and instead use Mockito to mock it. (#8474)
The goal of this test fix is to remove implementing Monitoring interface as a way to mock Monitoring client, as it is causing updates to be made if that interface has changed during an upgrade to OCI SDK is made. Instead, the OCIExtension is now mocked and inside of it, a mocked Monitoring bean using Proxy is injected. With this approach, there is no need to implement all the methods of Monitoring interface, but only mock those that are needed for the test.
1 parent 1a228e2 commit 03579c4

File tree

3 files changed

+106
-184
lines changed

3 files changed

+106
-184
lines changed

integrations/oci/metrics/cdi/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@
4242
<artifactId>helidon-metrics</artifactId>
4343
<scope>test</scope>
4444
</dependency>
45+
<dependency>
46+
<groupId>io.helidon.integrations.oci.sdk</groupId>
47+
<artifactId>helidon-integrations-oci-sdk-cdi</artifactId>
48+
<scope>test</scope>
49+
</dependency>
4550
<dependency>
4651
<groupId>org.junit.jupiter</groupId>
4752
<artifactId>junit-jupiter-params</artifactId>

integrations/oci/metrics/cdi/src/test/java/io/helidon/integrations/oci/metrics/cdi/OciMetricsCdiExtensionTest.java

Lines changed: 87 additions & 182 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,12 @@
1515
*/
1616
package io.helidon.integrations.oci.metrics.cdi;
1717

18+
import java.lang.annotation.Annotation;
19+
import java.lang.reflect.Proxy;
20+
import java.util.Set;
1821
import java.util.concurrent.CountDownLatch;
1922
import java.util.concurrent.TimeUnit;
2023

21-
import com.oracle.bmc.monitoring.requests.CreateAlarmSuppressionRequest;
22-
import com.oracle.bmc.monitoring.requests.DeleteAlarmSuppressionRequest;
23-
import com.oracle.bmc.monitoring.requests.GetAlarmSuppressionRequest;
24-
import com.oracle.bmc.monitoring.requests.ListAlarmSuppressionsRequest;
25-
import com.oracle.bmc.monitoring.requests.SummarizeAlarmSuppressionHistoryRequest;
26-
import com.oracle.bmc.monitoring.responses.CreateAlarmSuppressionResponse;
27-
import com.oracle.bmc.monitoring.responses.DeleteAlarmSuppressionResponse;
28-
import com.oracle.bmc.monitoring.responses.GetAlarmSuppressionResponse;
29-
import com.oracle.bmc.monitoring.responses.ListAlarmSuppressionsResponse;
30-
import com.oracle.bmc.monitoring.responses.SummarizeAlarmSuppressionHistoryResponse;
3124
import io.helidon.config.Config;
3225
import io.helidon.integrations.oci.metrics.OciMetricsSupport;
3326
import io.helidon.metrics.api.Counter;
@@ -43,38 +36,22 @@
4336
import io.helidon.microprofile.testing.junit5.DisableDiscovery;
4437
import io.helidon.microprofile.testing.junit5.HelidonTest;
4538

46-
import com.oracle.bmc.Region;
4739
import com.oracle.bmc.monitoring.Monitoring;
48-
import com.oracle.bmc.monitoring.MonitoringPaginators;
49-
import com.oracle.bmc.monitoring.MonitoringWaiters;
5040
import com.oracle.bmc.monitoring.model.MetricDataDetails;
5141
import com.oracle.bmc.monitoring.model.PostMetricDataDetails;
52-
import com.oracle.bmc.monitoring.requests.ChangeAlarmCompartmentRequest;
53-
import com.oracle.bmc.monitoring.requests.CreateAlarmRequest;
54-
import com.oracle.bmc.monitoring.requests.DeleteAlarmRequest;
55-
import com.oracle.bmc.monitoring.requests.GetAlarmHistoryRequest;
56-
import com.oracle.bmc.monitoring.requests.GetAlarmRequest;
57-
import com.oracle.bmc.monitoring.requests.ListAlarmsRequest;
58-
import com.oracle.bmc.monitoring.requests.ListAlarmsStatusRequest;
59-
import com.oracle.bmc.monitoring.requests.ListMetricsRequest;
6042
import com.oracle.bmc.monitoring.requests.PostMetricDataRequest;
61-
import com.oracle.bmc.monitoring.requests.RemoveAlarmSuppressionRequest;
62-
import com.oracle.bmc.monitoring.requests.RetrieveDimensionStatesRequest;
63-
import com.oracle.bmc.monitoring.requests.SummarizeMetricsDataRequest;
64-
import com.oracle.bmc.monitoring.requests.UpdateAlarmRequest;
65-
import com.oracle.bmc.monitoring.responses.ChangeAlarmCompartmentResponse;
66-
import com.oracle.bmc.monitoring.responses.CreateAlarmResponse;
67-
import com.oracle.bmc.monitoring.responses.DeleteAlarmResponse;
68-
import com.oracle.bmc.monitoring.responses.GetAlarmHistoryResponse;
69-
import com.oracle.bmc.monitoring.responses.GetAlarmResponse;
70-
import com.oracle.bmc.monitoring.responses.ListAlarmsResponse;
71-
import com.oracle.bmc.monitoring.responses.ListAlarmsStatusResponse;
72-
import com.oracle.bmc.monitoring.responses.ListMetricsResponse;
7343
import com.oracle.bmc.monitoring.responses.PostMetricDataResponse;
74-
import com.oracle.bmc.monitoring.responses.RemoveAlarmSuppressionResponse;
75-
import com.oracle.bmc.monitoring.responses.RetrieveDimensionStatesResponse;
76-
import com.oracle.bmc.monitoring.responses.SummarizeMetricsDataResponse;
77-
import com.oracle.bmc.monitoring.responses.UpdateAlarmResponse;
44+
45+
import jakarta.enterprise.context.ApplicationScoped;
46+
import jakarta.enterprise.event.Observes;
47+
48+
import jakarta.enterprise.inject.Default;
49+
import jakarta.enterprise.inject.spi.AfterBeanDiscovery;
50+
import jakarta.enterprise.inject.spi.Extension;
51+
import jakarta.enterprise.inject.spi.InjectionPoint;
52+
import jakarta.enterprise.inject.spi.ProcessInjectionPoint;
53+
import jakarta.enterprise.inject.spi.configurator.BeanConfigurator;
54+
import org.glassfish.jersey.ext.cdi1x.internal.CdiComponentProvider;
7855
import org.junit.jupiter.api.AfterEach;
7956
import org.junit.jupiter.api.Test;
8057

@@ -84,15 +61,15 @@
8461
import static org.junit.jupiter.api.Assertions.fail;
8562

8663
@HelidonTest(resetPerTest = true)
87-
@AddBean(OciMetricsCdiExtensionTest.MockMonitoring.class)
88-
@AddBean(OciMetricsCdiExtensionTest.MockOciMetricsBean.class)
8964
@DisableDiscovery
65+
@AddBean(OciMetricsCdiExtensionTest.MockOciMetricsBean.class)
9066
// Helidon MP Extensions
9167
@AddExtension(ServerCdiExtension.class)
9268
@AddExtension(JaxRsCdiExtension.class)
9369
@AddExtension(ConfigCdiExtension.class)
94-
// OciMetricsCdiExtension
95-
@AddExtension(OciMetricsCdiExtension.class)
70+
@AddExtension(CdiComponentProvider.class)
71+
// Add an extension that will simulate a mocked OciExtension that will inject a mocked Monitoring object
72+
@AddExtension(OciMetricsCdiExtensionTest.MockOciMonitoringExtension.class)
9673
// ConfigSources
9774
@AddConfig(key = "ocimetrics.compartmentId",
9875
value = OciMetricsCdiExtensionTest.MetricDataDetailsOCIParams.compartmentId)
@@ -101,8 +78,9 @@
10178
@AddConfig(key = "ocimetrics.resourceGroup",
10279
value = OciMetricsCdiExtensionTest.MetricDataDetailsOCIParams.resourceGroup)
10380
@AddConfig(key = "ocimetrics.initialDelay", value = "1")
104-
@AddConfig(key = "ocimetrics.delay", value = "2")
81+
@AddConfig(key = "ocimetrics.delay", value = "1")
10582
class OciMetricsCdiExtensionTest {
83+
private static String METRIC_NAME_SUFFIX = "DummyCounter";
10684
private static volatile int testMetricCount = 0;
10785
private static CountDownLatch countDownLatch = new CountDownLatch(1);
10886
private static PostMetricDataDetails postMetricDataDetails;
@@ -134,13 +112,13 @@ void testDisableOciMetrics() throws InterruptedException {
134112
}
135113

136114
private void validateOciMetricsSupport(boolean enabled) throws InterruptedException {
137-
Counter c1 = registry.getOrCreate(Counter.builder("baseDummyCounter")
115+
Counter c1 = registry.getOrCreate(Counter.builder(Meter.Scope.BASE + METRIC_NAME_SUFFIX)
138116
.scope(Meter.Scope.BASE));
139117
c1.increment();
140-
Counter c2 = registry.getOrCreate(Counter.builder("vendorDummyCounter")
118+
Counter c2 = registry.getOrCreate(Counter.builder(Meter.Scope.VENDOR + METRIC_NAME_SUFFIX)
141119
.scope(Meter.Scope.VENDOR));
142120
c2.increment();
143-
Counter c3 = registry.getOrCreate(Counter.builder("appDummyCounter")
121+
Counter c3 = registry.getOrCreate(Counter.builder(Meter.Scope.APPLICATION + METRIC_NAME_SUFFIX)
144122
.scope(Meter.Scope.APPLICATION));
145123
c3.increment();
146124

@@ -156,12 +134,9 @@ private void validateOciMetricsSupport(boolean enabled) throws InterruptedExcept
156134
assertThat(activateOciMetricsSupportIsInvoked, is(true));
157135
// System meters in the registry might vary over time. Instead of looking for a specific number of meters,
158136
// make sure the three we added are in the OCI metric data.
159-
long dummyCounterCount = postMetricDataDetails.getMetricData().stream()
160-
.filter(details -> details.getName().contains("DummyCounter"))
161-
.count();
162-
assertThat(dummyCounterCount, is(3L));
137+
assertThat(testMetricCount, is(3));
163138

164-
MetricDataDetails metricDataDetails = postMetricDataDetails.getMetricData().get(0);
139+
MetricDataDetails metricDataDetails = postMetricDataDetails.getMetricData().getFirst();
165140
assertThat(metricDataDetails.getCompartmentId(),
166141
is(MetricDataDetailsOCIParams.compartmentId));
167142
assertThat(metricDataDetails.getNamespace(), is(MetricDataDetailsOCIParams.namespace));
@@ -183,140 +158,70 @@ interface MetricDataDetailsOCIParams {
183158
String resourceGroup = "dummy_resourceGroup";
184159
}
185160

186-
static class MockMonitoring implements Monitoring {
187-
@Override
188-
public String getEndpoint() {
189-
return "http://www.DummyEndpoint.com";
190-
}
191-
192-
@Override
193-
public void setEndpoint(String s) {
194-
}
195-
196-
@Override
197-
public void setRegion(Region region) {
198-
}
199-
200-
@Override
201-
public void setRegion(String s) {
202-
}
203-
204-
@Override
205-
public void useRealmSpecificEndpointTemplate(boolean b) {
206-
}
207-
208-
@Override
209-
public void refreshClient() {
210-
}
211-
212-
@Override
213-
public ChangeAlarmCompartmentResponse changeAlarmCompartment(ChangeAlarmCompartmentRequest changeAlarmCompartmentRequest) {
214-
return null;
215-
}
216-
217-
@Override
218-
public CreateAlarmResponse createAlarm(CreateAlarmRequest createAlarmRequest) {
219-
return null;
220-
}
221-
222-
@Override
223-
public CreateAlarmSuppressionResponse createAlarmSuppression(CreateAlarmSuppressionRequest createAlarmSuppressionRequest) {
224-
return null;
225-
}
226-
227-
@Override
228-
public DeleteAlarmResponse deleteAlarm(DeleteAlarmRequest deleteAlarmRequest) {
229-
return null;
230-
}
231-
232-
@Override
233-
public DeleteAlarmSuppressionResponse deleteAlarmSuppression(DeleteAlarmSuppressionRequest deleteAlarmSuppressionRequest) {
234-
return null;
235-
}
236-
237-
@Override
238-
public GetAlarmResponse getAlarm(GetAlarmRequest getAlarmRequest) {
239-
return null;
240-
}
241-
242-
@Override
243-
public GetAlarmHistoryResponse getAlarmHistory(GetAlarmHistoryRequest getAlarmHistoryRequest) {
244-
return null;
245-
}
246-
247-
@Override
248-
public GetAlarmSuppressionResponse getAlarmSuppression(GetAlarmSuppressionRequest getAlarmSuppressionRequest) {
249-
return null;
250-
}
251-
252-
@Override
253-
public ListAlarmSuppressionsResponse listAlarmSuppressions(ListAlarmSuppressionsRequest listAlarmSuppressionsRequest) {
254-
return null;
255-
}
256-
257-
@Override
258-
public ListAlarmsResponse listAlarms(ListAlarmsRequest listAlarmsRequest) {
259-
return null;
260-
}
261-
262-
@Override
263-
public ListAlarmsStatusResponse listAlarmsStatus(ListAlarmsStatusRequest listAlarmsStatusRequest) {
264-
return null;
265-
}
266-
267-
@Override
268-
public ListMetricsResponse listMetrics(ListMetricsRequest listMetricsRequest) {
269-
return null;
270-
}
271-
272-
@Override
273-
public PostMetricDataResponse postMetricData(PostMetricDataRequest postMetricDataRequest) {
274-
postMetricDataDetails = postMetricDataRequest.getPostMetricDataDetails();
275-
testMetricCount = postMetricDataDetails.getMetricData().size();
276-
// Give signal that testMetricCount was retrieved
277-
countDownLatch.countDown();
278-
return PostMetricDataResponse.builder()
279-
.__httpStatusCode__(200)
280-
.build();
281-
}
282-
283-
@Override
284-
public RemoveAlarmSuppressionResponse removeAlarmSuppression(RemoveAlarmSuppressionRequest removeAlarmSuppressionRequest) {
285-
return null;
286-
}
287-
288-
@Override
289-
public RetrieveDimensionStatesResponse retrieveDimensionStates(RetrieveDimensionStatesRequest retrieveDimensionStatesRequest) {
290-
return null;
291-
}
292-
293-
@Override
294-
public SummarizeAlarmSuppressionHistoryResponse summarizeAlarmSuppressionHistory(SummarizeAlarmSuppressionHistoryRequest summarizeAlarmSuppressionHistoryRequest) {
295-
return null;
296-
}
297-
298-
@Override
299-
public SummarizeMetricsDataResponse summarizeMetricsData(SummarizeMetricsDataRequest summarizeMetricsDataRequest) {
300-
return null;
301-
}
302-
303-
@Override
304-
public UpdateAlarmResponse updateAlarm(UpdateAlarmRequest updateAlarmRequest) {
305-
return null;
306-
}
307-
308-
@Override
309-
public MonitoringWaiters getWaiters() {
310-
return null;
311-
}
312-
313-
@Override
314-
public MonitoringPaginators getPaginators() {
315-
return null;
161+
// Use this to replace OciExtension, but will only process OCI Monitoring annotation. If the Monitoring
162+
// annotation is found, a Mocked Monitoring object will be injected as a bean
163+
static public class MockOciMonitoringExtension implements Extension {
164+
boolean monitoringFound;
165+
Set<Annotation> monitoringQualifiers;
166+
167+
void processInjectionPoint(@Observes final ProcessInjectionPoint<?, ?> event) {
168+
if (event != null) {
169+
InjectionPoint ip = event.getInjectionPoint();
170+
Class<?> c = (Class<?>) ip.getAnnotated().getBaseType();
171+
final Set<Annotation> existingQualifiers = ip.getQualifiers();
172+
if (c == Monitoring.class && existingQualifiers != null && !existingQualifiers.isEmpty()) {
173+
monitoringFound = true;
174+
monitoringQualifiers = existingQualifiers;
175+
}
176+
}
316177
}
317178

318-
@Override
319-
public void close() throws Exception {
179+
Monitoring getMockedMonitoring() {
180+
// Use Proxy to mock only getEndPoint() and postMetricDataDetails() methods of the Monitoring interface,
181+
// as those are the only ones needed by the test
182+
return
183+
(Monitoring) Proxy.newProxyInstance(
184+
Monitoring.class.getClassLoader(),
185+
new Class[] {Monitoring.class},
186+
(proxy, method, args) -> {
187+
if (method.getName().equals("getEndpoint")) {
188+
return "http://www.DummyEndpoint.com";
189+
} else if (method.getName().equals("postMetricData")) {
190+
PostMetricDataRequest postMetricDataRequest = (PostMetricDataRequest) args[0];
191+
postMetricDataDetails = postMetricDataRequest.getPostMetricDataDetails();
192+
testMetricCount = (int) postMetricDataDetails.getMetricData().stream()
193+
.filter(details -> details.getName().contains(METRIC_NAME_SUFFIX))
194+
.count();
195+
PostMetricDataResponse response = PostMetricDataResponse.builder()
196+
.__httpStatusCode__(200)
197+
.build();
198+
199+
// Give signal that metrics will be posted if the right no. of metrics has been retrieved.
200+
// If not, that means that the metrics have not been registered yet, so try again on the
201+
// next invocation.
202+
if (testMetricCount > 0) {
203+
countDownLatch.countDown();
204+
}
205+
return response;
206+
}
207+
return null;
208+
});
209+
}
210+
211+
void afterBeanDiscovery(@Observes AfterBeanDiscovery event) {
212+
if (monitoringFound) {
213+
BeanConfigurator<Object> beanConfigurator = event.addBean()
214+
.types(Monitoring.class)
215+
.scope(ApplicationScoped.class)
216+
.addQualifiers(monitoringQualifiers);
217+
beanConfigurator = monitoringQualifiers != null ? beanConfigurator.addQualifiers(monitoringQualifiers) :
218+
beanConfigurator.addQualifier(Default.Literal.INSTANCE);
219+
// Add the mocked Monitoring as a bean
220+
beanConfigurator.produceWith(obj -> getMockedMonitoring());
221+
} else {
222+
throw new IllegalStateException("Monitoring was never injected. Check if OciMetricsBean.registerOciMetrics() "
223+
+ "has changed and does not inject Monitoring anymore.");
224+
}
320225
}
321226
}
322227

integrations/oci/metrics/cdi/src/test/java/io/helidon/integrations/oci/metrics/cdi/TestOverridingBean.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,13 @@
1818
import java.lang.reflect.Field;
1919

2020
import io.helidon.integrations.oci.metrics.OciMetricsSupport;
21+
import io.helidon.microprofile.config.ConfigCdiExtension;
22+
import io.helidon.microprofile.server.JaxRsCdiExtension;
23+
import io.helidon.microprofile.server.ServerCdiExtension;
2124
import io.helidon.microprofile.testing.junit5.AddBean;
2225
import io.helidon.microprofile.testing.junit5.AddConfig;
26+
import io.helidon.microprofile.testing.junit5.AddExtension;
27+
import io.helidon.microprofile.testing.junit5.DisableDiscovery;
2328
import io.helidon.microprofile.testing.junit5.HelidonTest;
2429

2530
import jakarta.inject.Inject;
@@ -31,10 +36,17 @@
3136
import static org.hamcrest.Matchers.is;
3237

3338
@HelidonTest
39+
@DisableDiscovery
3440
@AddBean(OverridingOciMetricsBean.class)
41+
// Use OciMetricsCdiExtensionTest.MockOciMonitoringExtension to avoid Monitoring client from being instantiated
42+
// with OCI authentication
43+
@AddExtension(OciMetricsCdiExtensionTest.MockOciMonitoringExtension.class)
44+
// Supporting Extensions for CDI
45+
@AddExtension(ServerCdiExtension.class)
46+
@AddExtension(JaxRsCdiExtension.class)
47+
@AddExtension(ConfigCdiExtension.class)
3548
@AddConfig(key = "oci.metrics.product", value = TestOverridingBean.PRODUCT)
3649
@AddConfig(key = "oci.metrics.fleet", value = TestOverridingBean.FLEET)
37-
@AddBean(OciMetricsCdiExtensionTest.MockMonitoring.class)
3850
class TestOverridingBean {
3951

4052
static final String PRODUCT = "overriding-product-name";
@@ -54,7 +66,7 @@ void checkOverriding() throws NoSuchFieldException, IllegalAccessException {
5466
String resourceGroup = getStringField("resourceGroup", ociMetricsSupport);
5567

5668
assertThat("Effective namespace", namespace, is(equalTo(PRODUCT)));
57-
assertThat("Effective namespace", resourceGroup, is(equalTo(FLEET)));
69+
assertThat("Effective resourceGroup", resourceGroup, is(equalTo(FLEET)));
5870
}
5971

6072
private String getStringField(String fieldName, OciMetricsSupport ociMetricsSupport)

0 commit comments

Comments
 (0)