Skip to content

Commit 6a3884f

Browse files
committed
test(showcase): add integration tests for resumable upload
1 parent 920e237 commit 6a3884f

1 file changed

Lines changed: 349 additions & 0 deletions

File tree

Lines changed: 349 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,349 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.showcase.v1beta1.it;
18+
19+
import static com.google.common.truth.Truth.assertThat;
20+
import static org.junit.jupiter.api.Assertions.assertThrows;
21+
22+
import com.google.api.client.http.javanet.NetHttpTransport;
23+
import com.google.api.gax.core.FixedCredentialsProvider;
24+
import com.google.api.gax.core.NoCredentialsProvider;
25+
import com.google.api.gax.rpc.FailedPreconditionException;
26+
import com.google.api.gax.rpc.ResumableUploadFuture;
27+
import com.google.api.gax.rpc.TransportChannelProvider;
28+
import com.google.auth.Credentials;
29+
import com.google.auth.oauth2.AccessToken;
30+
import com.google.auth.oauth2.OAuth2Credentials;
31+
import com.google.showcase.v1beta1.ResumableUploadServiceClient;
32+
import com.google.showcase.v1beta1.ResumableUploadServiceSettings;
33+
import com.google.showcase.v1beta1.UploadMediaRequest;
34+
import com.google.showcase.v1beta1.UploadMediaResponse;
35+
import com.google.showcase.v1beta1.it.util.TestClientInitializer;
36+
import io.grpc.ManagedChannelBuilder;
37+
import java.io.IOException;
38+
import java.io.InputStream;
39+
import java.nio.charset.StandardCharsets;
40+
import java.nio.file.Files;
41+
import java.nio.file.Path;
42+
import java.util.Date;
43+
import java.util.concurrent.TimeUnit;
44+
import org.junit.jupiter.api.AfterAll;
45+
import org.junit.jupiter.api.BeforeAll;
46+
import org.junit.jupiter.api.Test;
47+
import org.junit.jupiter.api.io.TempDir;
48+
49+
/**
50+
* Integration tests for generated {@link ResumableUploadServiceClient} against the Showcase server.
51+
*/
52+
class ITResumableUpload {
53+
54+
private static final int SHOWCASE_CHUNK_SIZE = 256 * 1024; // 256KB
55+
private static final Credentials DUMMY_CREDENTIALS =
56+
OAuth2Credentials.create(new AccessToken("fake-token", new Date(Long.MAX_VALUE)));
57+
private static ResumableUploadServiceClient client;
58+
59+
@BeforeAll
60+
static void createClients() throws Exception {
61+
ResumableUploadServiceSettings.Builder settingsBuilder =
62+
ResumableUploadServiceSettings.newHttpJsonBuilder();
63+
settingsBuilder
64+
.setCredentialsProvider(NoCredentialsProvider.create())
65+
.setTransportChannelProvider(
66+
ResumableUploadServiceSettings.defaultHttpJsonTransportProviderBuilder()
67+
.setHttpTransport(new NetHttpTransport.Builder().doNotValidateCertificate().build())
68+
.setEndpoint(TestClientInitializer.DEFAULT_HTTPJSON_ENDPOINT)
69+
.build());
70+
settingsBuilder.uploadMediaSettings().setChunkSize(SHOWCASE_CHUNK_SIZE);
71+
client = ResumableUploadServiceClient.create(settingsBuilder.build());
72+
}
73+
74+
@AfterAll
75+
static void destroyClients() throws InterruptedException {
76+
if (client != null) {
77+
client.close();
78+
client.awaitTermination(TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS);
79+
}
80+
}
81+
82+
@Test
83+
void testGeneratedClient_uploadMedia_synchronousConvenienceMethod(@TempDir Path tempDir)
84+
throws Exception {
85+
Path file =
86+
createTempFile(
87+
tempDir,
88+
"it-client-sync.txt",
89+
"Hello from generated ResumableUploadServiceClient synchronous convenience method!"
90+
.getBytes(StandardCharsets.UTF_8));
91+
UploadMediaRequest request =
92+
UploadMediaRequest.newBuilder().setName("it-client-sync.txt").build();
93+
94+
try (InputStream stream = Files.newInputStream(file)) {
95+
UploadMediaResponse response = client.uploadMedia(request, stream);
96+
assertThat(response.getName()).isEqualTo("it-client-sync.txt");
97+
assertThat(response.getSize()).isEqualTo(Files.size(file));
98+
}
99+
}
100+
101+
@Test
102+
void testGeneratedClient_uploadMediaCallable_asynchronousFutureCall(@TempDir Path tempDir)
103+
throws Exception {
104+
Path file =
105+
createTempFile(
106+
tempDir,
107+
"it-client-callable.txt",
108+
"Hello from generated ResumableUploadServiceClient callable futureCall!"
109+
.getBytes(StandardCharsets.UTF_8));
110+
UploadMediaRequest request =
111+
UploadMediaRequest.newBuilder().setName("it-client-callable.txt").build();
112+
113+
try (InputStream stream = Files.newInputStream(file)) {
114+
ResumableUploadFuture<UploadMediaResponse> future =
115+
client.uploadMediaCallable().futureCall(request, stream);
116+
117+
UploadMediaResponse response = future.get(10, TimeUnit.SECONDS);
118+
assertThat(future.isDone()).isTrue();
119+
assertThat(future.isCancelled()).isFalse();
120+
assertThat(future.getUploadSessionUrl()).isNotNull();
121+
assertThat(response.getName()).isEqualTo("it-client-callable.txt");
122+
assertThat(response.getSize()).isEqualTo(Files.size(file));
123+
}
124+
}
125+
126+
@Test
127+
void testGeneratedClient_multiChunkUpload(@TempDir Path tempDir) throws Exception {
128+
// 600KB payload = 2 full 256KB chunks + 1 partial 88KB chunk
129+
int totalBytes = 600 * 1024;
130+
Path file = createTempFile(tempDir, "it-client-multi-chunk.txt", totalBytes);
131+
UploadMediaRequest request =
132+
UploadMediaRequest.newBuilder().setName("it-client-multi-chunk.txt").build();
133+
134+
try (InputStream stream = Files.newInputStream(file)) {
135+
UploadMediaResponse response = client.uploadMedia(request, stream);
136+
assertThat(response.getName()).isEqualTo("it-client-multi-chunk.txt");
137+
assertThat(response.getSize()).isEqualTo(Files.size(file));
138+
}
139+
}
140+
141+
@Test
142+
void testGeneratedClient_zeroByteUpload(@TempDir Path tempDir) throws Exception {
143+
Path file = tempDir.resolve("it-client-zero-byte.txt");
144+
Files.createFile(file);
145+
UploadMediaRequest request =
146+
UploadMediaRequest.newBuilder().setName("it-client-zero-byte.txt").build();
147+
148+
try (InputStream stream = Files.newInputStream(file)) {
149+
UploadMediaResponse response = client.uploadMedia(request, stream);
150+
assertThat(response.getName()).isEqualTo("it-client-zero-byte.txt");
151+
assertThat(response.getSize()).isEqualTo(0);
152+
}
153+
}
154+
155+
@Test
156+
void testGeneratedClient_exactChunkBoundaryUpload(@TempDir Path tempDir) throws Exception {
157+
// Exactly 2 full 256KB chunks (512KB total) -> triggers 0-byte finalize request
158+
int totalBytes = 512 * 1024;
159+
Path file = createTempFile(tempDir, "it-client-exact-chunks.txt", totalBytes);
160+
UploadMediaRequest request =
161+
UploadMediaRequest.newBuilder().setName("it-client-exact-chunks.txt").build();
162+
163+
try (InputStream stream = Files.newInputStream(file)) {
164+
UploadMediaResponse response = client.uploadMedia(request, stream);
165+
assertThat(response.getName()).isEqualTo("it-client-exact-chunks.txt");
166+
assertThat(response.getSize()).isEqualTo(Files.size(file));
167+
}
168+
}
169+
170+
@Test
171+
void testGeneratedClient_grpcClientDelegation_uploadMedia(@TempDir Path tempDir)
172+
throws Exception {
173+
TransportChannelProvider grpcTransportChannelProvider =
174+
new TransportChannelProvider() {
175+
private final TransportChannelProvider delegate =
176+
ResumableUploadServiceSettings.defaultGrpcTransportProviderBuilder()
177+
.setChannelConfigurator(ManagedChannelBuilder::usePlaintext)
178+
.setEndpoint(TestClientInitializer.DEFAULT_GRPC_ENDPOINT)
179+
.build();
180+
181+
@Override
182+
public boolean shouldAutoClose() {
183+
return delegate.shouldAutoClose();
184+
}
185+
186+
@Override
187+
public boolean needsExecutor() {
188+
return delegate.needsExecutor();
189+
}
190+
191+
@Override
192+
public TransportChannelProvider withExecutor(java.util.concurrent.Executor executor) {
193+
return delegate.withExecutor(executor);
194+
}
195+
196+
@Override
197+
public TransportChannelProvider withExecutor(
198+
java.util.concurrent.ScheduledExecutorService executor) {
199+
return delegate.withExecutor(executor);
200+
}
201+
202+
@Override
203+
public boolean needsHeaders() {
204+
return delegate.needsHeaders();
205+
}
206+
207+
@Override
208+
public TransportChannelProvider withHeaders(java.util.Map<String, String> headers) {
209+
return delegate.withHeaders(headers);
210+
}
211+
212+
@Override
213+
public boolean needsEndpoint() {
214+
return false;
215+
}
216+
217+
@Override
218+
public TransportChannelProvider withEndpoint(String endpoint) {
219+
return this;
220+
}
221+
222+
@Override
223+
public boolean acceptsPoolSize() {
224+
return delegate.acceptsPoolSize();
225+
}
226+
227+
@Override
228+
public TransportChannelProvider withPoolSize(int size) {
229+
return delegate.withPoolSize(size);
230+
}
231+
232+
@Override
233+
public boolean needsCredentials() {
234+
return delegate.needsCredentials();
235+
}
236+
237+
@Override
238+
public TransportChannelProvider withCredentials(Credentials credentials) {
239+
return delegate.withCredentials(credentials);
240+
}
241+
242+
@Override
243+
public com.google.api.gax.rpc.TransportChannel getTransportChannel() throws IOException {
244+
return delegate.getTransportChannel();
245+
}
246+
247+
@Override
248+
public String getTransportName() {
249+
return delegate.getTransportName();
250+
}
251+
252+
@Override
253+
public String getEndpoint() {
254+
return null;
255+
}
256+
};
257+
258+
ResumableUploadServiceSettings.Builder grpcSettingsBuilder =
259+
ResumableUploadServiceSettings.newBuilder()
260+
.setCredentialsProvider(FixedCredentialsProvider.create(DUMMY_CREDENTIALS))
261+
.setTransportChannelProvider(grpcTransportChannelProvider)
262+
.setEndpoint(TestClientInitializer.DEFAULT_HTTPJSON_ENDPOINT);
263+
grpcSettingsBuilder.uploadMediaSettings().setChunkSize(SHOWCASE_CHUNK_SIZE);
264+
265+
Path file =
266+
createTempFile(
267+
tempDir,
268+
"it-grpc-delegation.txt",
269+
"Hello from generated ResumableUploadServiceClient gRPC delegation!"
270+
.getBytes(StandardCharsets.UTF_8));
271+
UploadMediaRequest request =
272+
UploadMediaRequest.newBuilder().setName("it-grpc-delegation.txt").build();
273+
274+
try (ResumableUploadServiceClient grpcClient =
275+
ResumableUploadServiceClient.create(grpcSettingsBuilder.build())) {
276+
// 1. Synchronous convenience method delegation
277+
try (InputStream stream = Files.newInputStream(file)) {
278+
UploadMediaResponse response = grpcClient.uploadMedia(request, stream);
279+
assertThat(response.getName()).isEqualTo("it-grpc-delegation.txt");
280+
assertThat(response.getSize()).isEqualTo(Files.size(file));
281+
}
282+
283+
// 2. Asynchronous callable futureCall delegation
284+
try (InputStream stream = Files.newInputStream(file)) {
285+
ResumableUploadFuture<UploadMediaResponse> future =
286+
grpcClient.uploadMediaCallable().futureCall(request, stream);
287+
UploadMediaResponse response = future.get(10, TimeUnit.SECONDS);
288+
assertThat(future.isDone()).isTrue();
289+
assertThat(future.isCancelled()).isFalse();
290+
assertThat(future.getUploadSessionUrl()).isNotNull();
291+
assertThat(response.getName()).isEqualTo("it-grpc-delegation.txt");
292+
assertThat(response.getSize()).isEqualTo(Files.size(file));
293+
}
294+
}
295+
}
296+
297+
@Test
298+
void testChannelGuards_grpcChannelOnly_throwsFailedPreconditionException(@TempDir Path tempDir)
299+
throws Exception {
300+
ResumableUploadServiceSettings grpcSettings =
301+
ResumableUploadServiceSettings.newBuilder()
302+
.setCredentialsProvider(NoCredentialsProvider.create())
303+
.setTransportChannelProvider(
304+
ResumableUploadServiceSettings.defaultGrpcTransportProviderBuilder()
305+
.setChannelConfigurator(ManagedChannelBuilder::usePlaintext)
306+
.build())
307+
.setEndpoint(TestClientInitializer.DEFAULT_GRPC_ENDPOINT)
308+
.build();
309+
310+
Path file =
311+
createTempFile(
312+
tempDir, "guard-test.txt", "guard test data".getBytes(StandardCharsets.UTF_8));
313+
314+
try (ResumableUploadServiceClient grpcClient =
315+
ResumableUploadServiceClient.create(grpcSettings)) {
316+
UploadMediaRequest request =
317+
UploadMediaRequest.newBuilder().setName("guard-test.txt").build();
318+
319+
try (InputStream stream1 = Files.newInputStream(file)) {
320+
// 1. Verify synchronous convenience call fails fast
321+
FailedPreconditionException syncException =
322+
assertThrows(
323+
FailedPreconditionException.class, () -> grpcClient.uploadMedia(request, stream1));
324+
assertThat(syncException.getMessage())
325+
.contains("Resumable uploads execute over HTTP/REST and require credentials");
326+
}
327+
328+
// 2. Verify callable getter fails fast
329+
FailedPreconditionException callableException =
330+
assertThrows(FailedPreconditionException.class, () -> grpcClient.uploadMediaCallable());
331+
assertThat(callableException.getMessage())
332+
.contains("Resumable uploads execute over HTTP/REST and require credentials");
333+
}
334+
}
335+
336+
private static Path createTempFile(Path dir, String fileName, byte[] data) throws IOException {
337+
Path path = dir.resolve(fileName);
338+
Files.write(path, data);
339+
return path;
340+
}
341+
342+
private static Path createTempFile(Path dir, String fileName, int size) throws IOException {
343+
byte[] data = new byte[size];
344+
for (int i = 0; i < size; i++) {
345+
data[i] = (byte) (i % 256);
346+
}
347+
return createTempFile(dir, fileName, data);
348+
}
349+
}

0 commit comments

Comments
 (0)