Skip to content

Commit ae6a4b2

Browse files
committed
Add unit test implementation
1 parent 3a59d50 commit ae6a4b2

File tree

3 files changed

+258
-1
lines changed

3 files changed

+258
-1
lines changed

gcp-auth-extension/build.gradle.kts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,19 @@ dependencies {
2020
// Only dependencies added to `implementation` configuration will be picked up by Shadow plugin
2121
implementation("com.google.auth:google-auth-library-oauth2-http:1.30.1")
2222

23-
testImplementation("org.junit.jupiter:junit-jupiter")
23+
// Test dependencies
24+
testCompileOnly("com.google.auto.service:auto-service-annotations")
2425
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
26+
testImplementation("org.junit.jupiter:junit-jupiter-api")
27+
28+
testImplementation("io.opentelemetry:opentelemetry-api")
29+
testImplementation("io.opentelemetry:opentelemetry-exporter-otlp")
30+
testImplementation("io.opentelemetry:opentelemetry-sdk-testing")
31+
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
32+
33+
testImplementation("org.awaitility:awaitility")
34+
testImplementation("org.mockito:mockito-inline")
35+
testImplementation("org.mockito:mockito-junit-jupiter")
2536
}
2637

2738
tasks.test {
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.gcp.auth;
7+
8+
import static io.opentelemetry.contrib.gcp.auth.GcpAuthAutoConfigurationCustomizerProvider.GCP_USER_PROJECT_ID_KEY;
9+
import static io.opentelemetry.contrib.gcp.auth.GcpAuthAutoConfigurationCustomizerProvider.QUOTA_USER_PROJECT_HEADER;
10+
import static org.assertj.core.api.Assertions.assertThat;
11+
import static org.junit.jupiter.api.Assertions.assertEquals;
12+
import static org.junit.jupiter.api.Assertions.assertTrue;
13+
14+
import com.google.auth.oauth2.AccessToken;
15+
import com.google.auth.oauth2.GoogleCredentials;
16+
import com.google.common.collect.ImmutableMap;
17+
import io.opentelemetry.api.common.AttributeKey;
18+
import io.opentelemetry.api.trace.Span;
19+
import io.opentelemetry.context.Scope;
20+
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
21+
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporterBuilder;
22+
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
23+
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder;
24+
import io.opentelemetry.sdk.OpenTelemetrySdk;
25+
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
26+
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder;
27+
import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil;
28+
import io.opentelemetry.sdk.autoconfigure.internal.ComponentLoader;
29+
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
30+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
31+
import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider;
32+
import io.opentelemetry.sdk.common.CompletableResultCode;
33+
import io.opentelemetry.sdk.trace.data.SpanData;
34+
import io.opentelemetry.sdk.trace.export.SpanExporter;
35+
import java.time.Duration;
36+
import java.time.Instant;
37+
import java.util.AbstractMap.SimpleEntry;
38+
import java.util.ArrayList;
39+
import java.util.Collections;
40+
import java.util.Date;
41+
import java.util.List;
42+
import java.util.Map;
43+
import java.util.Map.Entry;
44+
import java.util.Set;
45+
import java.util.concurrent.TimeUnit;
46+
import java.util.function.Supplier;
47+
import org.junit.jupiter.api.BeforeEach;
48+
import org.junit.jupiter.api.Test;
49+
import org.junit.jupiter.api.extension.ExtendWith;
50+
import org.mockito.ArgumentCaptor;
51+
import org.mockito.Captor;
52+
import org.mockito.Mock;
53+
import org.mockito.MockedStatic;
54+
import org.mockito.Mockito;
55+
import org.mockito.MockitoAnnotations;
56+
import org.mockito.junit.jupiter.MockitoExtension;
57+
58+
@ExtendWith(MockitoExtension.class)
59+
class GcpAuthAutoConfigurationCustomizerProviderTest {
60+
61+
@Mock private GoogleCredentials mockedGoogleCredentials;
62+
63+
@Captor private ArgumentCaptor<Supplier<Map<String, String>>> headerSupplierCaptor;
64+
65+
private static final ImmutableMap<String, String> otelProperties =
66+
ImmutableMap.of(
67+
"otel.traces.exporter",
68+
"otlp",
69+
"otel.metrics.exporter",
70+
"none",
71+
"otel.logs.exporter",
72+
"none",
73+
"otel.resource.attributes",
74+
"foo=bar");
75+
76+
@BeforeEach
77+
@SuppressWarnings("CannotMockMethod")
78+
public void setup() {
79+
MockitoAnnotations.openMocks(this);
80+
Mockito.when(mockedGoogleCredentials.getQuotaProjectId()).thenReturn("test-project");
81+
Mockito.when(mockedGoogleCredentials.getAccessToken())
82+
.thenReturn(new AccessToken("fake", Date.from(Instant.now())));
83+
}
84+
85+
@Test
86+
public void testCustomizerOtlpHttp() {
87+
OtlpHttpSpanExporter mockOtlpHttpSpanExporter = Mockito.mock(OtlpHttpSpanExporter.class);
88+
OtlpHttpSpanExporterBuilder otlpSpanExporterBuilder = OtlpHttpSpanExporter.builder();
89+
OtlpHttpSpanExporterBuilder spyOtlpHttpSpanExporterBuilder =
90+
Mockito.spy(otlpSpanExporterBuilder);
91+
Mockito.when(spyOtlpHttpSpanExporterBuilder.build()).thenReturn(mockOtlpHttpSpanExporter);
92+
93+
Mockito.when(mockOtlpHttpSpanExporter.shutdown()).thenReturn(CompletableResultCode.ofSuccess());
94+
List<SpanData> exportedSpans = new ArrayList<>();
95+
Mockito.when(mockOtlpHttpSpanExporter.export(Mockito.anyCollection()))
96+
.thenAnswer(
97+
invocationOnMock -> {
98+
exportedSpans.addAll(invocationOnMock.getArgument(0));
99+
return CompletableResultCode.ofSuccess();
100+
});
101+
Mockito.when(mockOtlpHttpSpanExporter.toBuilder()).thenReturn(spyOtlpHttpSpanExporterBuilder);
102+
103+
try (MockedStatic<GoogleCredentials> googleCredentialsMockedStatic =
104+
Mockito.mockStatic(GoogleCredentials.class)) {
105+
googleCredentialsMockedStatic
106+
.when(GoogleCredentials::getApplicationDefault)
107+
.thenReturn(mockedGoogleCredentials);
108+
109+
OpenTelemetrySdk sdk = buildOpenTelemetrySdkWithExporter(mockOtlpHttpSpanExporter);
110+
generateTestSpan(sdk);
111+
CompletableResultCode code = sdk.shutdown();
112+
CompletableResultCode joinResult = code.join(10, TimeUnit.SECONDS);
113+
assertTrue(joinResult.isSuccess());
114+
115+
Mockito.verify(mockOtlpHttpSpanExporter, Mockito.times(1)).toBuilder();
116+
Mockito.verify(spyOtlpHttpSpanExporterBuilder, Mockito.times(1))
117+
.setHeaders(headerSupplierCaptor.capture());
118+
assertEquals(2, headerSupplierCaptor.getValue().get().size());
119+
assertThat(verifyAuthHeaders(headerSupplierCaptor.getValue().get())).isTrue();
120+
121+
Mockito.verify(mockOtlpHttpSpanExporter, Mockito.atLeast(1)).export(Mockito.anyCollection());
122+
123+
assertThat(exportedSpans)
124+
.hasSizeGreaterThan(0)
125+
.allSatisfy(
126+
spanData -> {
127+
assertThat(spanData.getResource().getAttributes().asMap())
128+
.containsEntry(AttributeKey.stringKey(GCP_USER_PROJECT_ID_KEY), "test-project")
129+
.containsEntry(AttributeKey.stringKey("foo"), "bar");
130+
assertThat(spanData.getAttributes().asMap())
131+
.containsKey(AttributeKey.longKey("work_loop"));
132+
});
133+
}
134+
}
135+
136+
@Test
137+
public void testCustomizerOtlpGrpc() {
138+
OtlpGrpcSpanExporter mockOtlpGrpcSpanExporter = Mockito.mock(OtlpGrpcSpanExporter.class);
139+
OtlpGrpcSpanExporterBuilder otlpSpanExporterBuilder = OtlpGrpcSpanExporter.builder();
140+
OtlpGrpcSpanExporterBuilder spyOtlpGrpcSpanExporterBuilder =
141+
Mockito.spy(otlpSpanExporterBuilder);
142+
Mockito.when(spyOtlpGrpcSpanExporterBuilder.build()).thenReturn(mockOtlpGrpcSpanExporter);
143+
Mockito.when(mockOtlpGrpcSpanExporter.shutdown()).thenReturn(CompletableResultCode.ofSuccess());
144+
List<SpanData> exportedSpans = new ArrayList<>();
145+
Mockito.when(mockOtlpGrpcSpanExporter.export(Mockito.anyCollection()))
146+
.thenAnswer(
147+
invocationOnMock -> {
148+
exportedSpans.addAll(invocationOnMock.getArgument(0));
149+
return CompletableResultCode.ofSuccess();
150+
});
151+
Mockito.when(mockOtlpGrpcSpanExporter.toBuilder()).thenReturn(spyOtlpGrpcSpanExporterBuilder);
152+
153+
try (MockedStatic<GoogleCredentials> googleCredentialsMockedStatic =
154+
Mockito.mockStatic(GoogleCredentials.class)) {
155+
googleCredentialsMockedStatic
156+
.when(GoogleCredentials::getApplicationDefault)
157+
.thenReturn(mockedGoogleCredentials);
158+
159+
OpenTelemetrySdk sdk = buildOpenTelemetrySdkWithExporter(mockOtlpGrpcSpanExporter);
160+
generateTestSpan(sdk);
161+
CompletableResultCode code = sdk.shutdown();
162+
CompletableResultCode joinResult = code.join(10, TimeUnit.SECONDS);
163+
assertTrue(joinResult.isSuccess());
164+
165+
Mockito.verify(mockOtlpGrpcSpanExporter, Mockito.times(1)).toBuilder();
166+
Mockito.verify(spyOtlpGrpcSpanExporterBuilder, Mockito.times(1))
167+
.setHeaders(headerSupplierCaptor.capture());
168+
assertEquals(2, headerSupplierCaptor.getValue().get().size());
169+
verifyAuthHeaders(headerSupplierCaptor.getValue().get());
170+
171+
Mockito.verify(mockOtlpGrpcSpanExporter, Mockito.atLeast(1)).export(Mockito.anyCollection());
172+
173+
assertThat(exportedSpans)
174+
.hasSizeGreaterThan(0)
175+
.allSatisfy(
176+
spanData -> {
177+
assertThat(spanData.getResource().getAttributes().asMap())
178+
.containsEntry(AttributeKey.stringKey(GCP_USER_PROJECT_ID_KEY), "test-project")
179+
.containsEntry(AttributeKey.stringKey("foo"), "bar");
180+
assertThat(spanData.getAttributes().asMap())
181+
.containsKey(AttributeKey.longKey("work_loop"));
182+
});
183+
}
184+
}
185+
186+
private OpenTelemetrySdk buildOpenTelemetrySdkWithExporter(SpanExporter spanExporter) {
187+
SpiHelper spiHelper =
188+
SpiHelper.create(GcpAuthAutoConfigurationCustomizerProviderTest.class.getClassLoader());
189+
AutoConfiguredOpenTelemetrySdkBuilder builder =
190+
AutoConfiguredOpenTelemetrySdk.builder().addPropertiesSupplier(() -> otelProperties);
191+
AutoConfigureUtil.setComponentLoader(
192+
builder,
193+
new ComponentLoader() {
194+
@SuppressWarnings("unchecked")
195+
@Override
196+
public <T> List<T> load(Class<T> spiClass) {
197+
if (spiClass == ConfigurableSpanExporterProvider.class) {
198+
return Collections.singletonList(
199+
(T)
200+
new ConfigurableSpanExporterProvider() {
201+
@Override
202+
public SpanExporter createExporter(ConfigProperties configProperties) {
203+
return spanExporter;
204+
}
205+
206+
@Override
207+
public String getName() {
208+
return "otlp";
209+
}
210+
});
211+
}
212+
return spiHelper.load(spiClass);
213+
}
214+
});
215+
return builder.build().getOpenTelemetrySdk();
216+
}
217+
218+
private static boolean verifyAuthHeaders(Map<String, String> headers) {
219+
Set<Entry<String, String>> headerEntrySet = headers.entrySet();
220+
return headerEntrySet.contains(new SimpleEntry<>(QUOTA_USER_PROJECT_HEADER, "test-project"))
221+
&& headerEntrySet.contains(new SimpleEntry<>("Authorization", "Bearer fake"));
222+
}
223+
224+
private static void generateTestSpan(OpenTelemetrySdk openTelemetrySdk) {
225+
Span span = openTelemetrySdk.getTracer("test").spanBuilder("sample").startSpan();
226+
try (Scope ignored = span.makeCurrent()) {
227+
long workOutput = busyloop();
228+
span.setAttribute("work_loop", workOutput);
229+
} finally {
230+
span.end();
231+
}
232+
}
233+
234+
// loop to simulate work done
235+
private static long busyloop() {
236+
Instant start = Instant.now();
237+
Instant end;
238+
long counter = 0;
239+
do {
240+
counter++;
241+
end = Instant.now();
242+
} while (Duration.between(start, end).toMillis() < 1000);
243+
return counter;
244+
}
245+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
mock-maker-inline

0 commit comments

Comments
 (0)