Skip to content

Commit a532f43

Browse files
committed
test(showcase): add integration tests for resumable upload
1 parent 8ea7df8 commit a532f43

2 files changed

Lines changed: 286 additions & 0 deletions

File tree

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
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.gax.grpc.GrpcTransportChannel;
23+
import com.google.api.gax.rpc.FailedPreconditionException;
24+
import com.google.api.gax.rpc.FixedTransportChannelProvider;
25+
import com.google.api.gax.rpc.ResumableUploadCallSettings;
26+
import com.google.api.gax.rpc.ResumableUploadFuture;
27+
import com.google.showcase.v1beta1.ResumableUploadServiceClient;
28+
import com.google.showcase.v1beta1.UploadMediaRequest;
29+
import com.google.showcase.v1beta1.UploadMediaResponse;
30+
import com.google.showcase.v1beta1.it.util.TestClientInitializer;
31+
import java.io.ByteArrayInputStream;
32+
import java.io.IOException;
33+
import java.io.InputStream;
34+
import java.nio.charset.StandardCharsets;
35+
import java.nio.file.Files;
36+
import java.nio.file.Path;
37+
import java.util.concurrent.TimeUnit;
38+
import org.junit.jupiter.api.AfterAll;
39+
import org.junit.jupiter.api.BeforeAll;
40+
import org.junit.jupiter.api.Test;
41+
import org.junit.jupiter.api.io.TempDir;
42+
43+
/**
44+
* Integration tests for generated {@link ResumableUploadServiceClient} against the Showcase server.
45+
*/
46+
class ITResumableUpload {
47+
48+
private static final int SHOWCASE_CHUNK_SIZE = 256 * 1024; // 256KB
49+
private static ResumableUploadServiceClient client;
50+
51+
@BeforeAll
52+
static void createClients() throws Exception {
53+
client = TestClientInitializer.createHttpJsonResumableUploadClient(SHOWCASE_CHUNK_SIZE);
54+
}
55+
56+
@AfterAll
57+
static void destroyClients() throws InterruptedException {
58+
if (client != null) {
59+
client.close();
60+
client.awaitTermination(TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS);
61+
}
62+
}
63+
64+
@Test
65+
void testGeneratedClient_uploadMedia_synchronousConvenienceMethod(@TempDir Path tempDir)
66+
throws Exception {
67+
Path file =
68+
createTempFile(
69+
tempDir,
70+
"it-client-sync.txt",
71+
"Hello from generated ResumableUploadServiceClient synchronous convenience method!"
72+
.getBytes(StandardCharsets.UTF_8));
73+
UploadMediaRequest request =
74+
UploadMediaRequest.newBuilder().setName("it-client-sync.txt").build();
75+
76+
try (InputStream stream = Files.newInputStream(file)) {
77+
UploadMediaResponse response = client.uploadMedia(request, stream);
78+
assertThat(response.getName()).isEqualTo("it-client-sync.txt");
79+
assertThat(response.getSize()).isEqualTo(Files.size(file));
80+
}
81+
}
82+
83+
@Test
84+
void testGeneratedClient_uploadMediaCallable_asynchronousFutureCall(@TempDir Path tempDir)
85+
throws Exception {
86+
Path file =
87+
createTempFile(
88+
tempDir,
89+
"it-client-callable.txt",
90+
"Hello from generated ResumableUploadServiceClient callable futureCall!"
91+
.getBytes(StandardCharsets.UTF_8));
92+
UploadMediaRequest request =
93+
UploadMediaRequest.newBuilder().setName("it-client-callable.txt").build();
94+
95+
try (InputStream stream = Files.newInputStream(file)) {
96+
ResumableUploadFuture<UploadMediaResponse> future =
97+
client
98+
.uploadMediaCallable()
99+
.futureCall(request, stream, (ResumableUploadCallSettings) null);
100+
101+
UploadMediaResponse response = future.get(10, TimeUnit.SECONDS);
102+
assertThat(future.isDone()).isTrue();
103+
assertThat(future.isCancelled()).isFalse();
104+
assertThat(future.getUploadSessionUrl()).isNotNull();
105+
assertThat(future.getUploadSessionUrl()).contains("/resumable/upload");
106+
assertThat(response.getName()).isEqualTo("it-client-callable.txt");
107+
assertThat(response.getSize()).isEqualTo(Files.size(file));
108+
}
109+
}
110+
111+
@Test
112+
void testGeneratedClient_multiChunkUpload(@TempDir Path tempDir) throws Exception {
113+
// 600KB payload = 2 full 256KB chunks + 1 partial 88KB chunk
114+
int totalBytes = 600 * 1024;
115+
Path file = createTempFile(tempDir, "it-client-multi-chunk.txt", totalBytes);
116+
UploadMediaRequest request =
117+
UploadMediaRequest.newBuilder().setName("it-client-multi-chunk.txt").build();
118+
119+
try (InputStream stream = Files.newInputStream(file)) {
120+
UploadMediaResponse response = client.uploadMedia(request, stream);
121+
assertThat(response.getName()).isEqualTo("it-client-multi-chunk.txt");
122+
assertThat(response.getSize()).isEqualTo(Files.size(file));
123+
}
124+
}
125+
126+
@Test
127+
void testGeneratedClient_zeroByteUpload() throws Exception {
128+
UploadMediaRequest request =
129+
UploadMediaRequest.newBuilder().setName("it-client-zero-byte.txt").build();
130+
131+
try (InputStream stream = new ByteArrayInputStream(new byte[0])) {
132+
UploadMediaResponse response = client.uploadMedia(request, stream);
133+
assertThat(response.getName()).isEqualTo("it-client-zero-byte.txt");
134+
assertThat(response.getSize()).isEqualTo(0);
135+
}
136+
}
137+
138+
@Test
139+
void testGeneratedClient_exactChunkBoundaryUpload(@TempDir Path tempDir) throws Exception {
140+
// Exactly 2 full 256KB chunks (512KB total) -> triggers 0-byte finalize request
141+
int totalBytes = 512 * 1024;
142+
Path file = createTempFile(tempDir, "it-client-exact-chunks.txt", totalBytes);
143+
UploadMediaRequest request =
144+
UploadMediaRequest.newBuilder().setName("it-client-exact-chunks.txt").build();
145+
146+
try (InputStream stream = Files.newInputStream(file)) {
147+
UploadMediaResponse response = client.uploadMedia(request, stream);
148+
assertThat(response.getName()).isEqualTo("it-client-exact-chunks.txt");
149+
assertThat(response.getSize()).isEqualTo(Files.size(file));
150+
}
151+
}
152+
153+
@Test
154+
void testGeneratedClient_grpcClientDelegation_uploadMedia(@TempDir Path tempDir)
155+
throws Exception {
156+
Path file =
157+
createTempFile(
158+
tempDir,
159+
"it-grpc-delegation.txt",
160+
"Hello from generated ResumableUploadServiceClient gRPC delegation!"
161+
.getBytes(StandardCharsets.UTF_8));
162+
UploadMediaRequest request =
163+
UploadMediaRequest.newBuilder().setName("it-grpc-delegation.txt").build();
164+
165+
try (ResumableUploadServiceClient grpcClient =
166+
TestClientInitializer.createGrpcResumableUploadClient(SHOWCASE_CHUNK_SIZE)) {
167+
try (InputStream stream = Files.newInputStream(file)) {
168+
UploadMediaResponse response = grpcClient.uploadMedia(request, stream);
169+
assertThat(response.getName()).isEqualTo("it-grpc-delegation.txt");
170+
assertThat(response.getSize()).isEqualTo(Files.size(file));
171+
}
172+
173+
try (InputStream stream = Files.newInputStream(file)) {
174+
ResumableUploadFuture<UploadMediaResponse> future =
175+
grpcClient
176+
.uploadMediaCallable()
177+
.futureCall(request, stream, (ResumableUploadCallSettings) null);
178+
UploadMediaResponse response = future.get(10, TimeUnit.SECONDS);
179+
assertThat(future.isDone()).isTrue();
180+
assertThat(future.isCancelled()).isFalse();
181+
assertThat(future.getUploadSessionUrl()).isNotNull();
182+
assertThat(future.getUploadSessionUrl()).contains("/resumable/upload");
183+
assertThat(response.getName()).isEqualTo("it-grpc-delegation.txt");
184+
assertThat(response.getSize()).isEqualTo(Files.size(file));
185+
}
186+
}
187+
}
188+
189+
@Test
190+
void testChannelGuards_grpcChannelOnly_throwsFailedPreconditionException(@TempDir Path tempDir)
191+
throws Exception {
192+
Path file =
193+
createTempFile(
194+
tempDir, "guard-test.txt", "guard test data".getBytes(StandardCharsets.UTF_8));
195+
196+
try (GrpcTransportChannel grpcChannel = TestClientInitializer.createPlaintextGrpcChannel();
197+
ResumableUploadServiceClient grpcClient =
198+
TestClientInitializer.createGrpcResumableUploadClient(
199+
FixedTransportChannelProvider.create(grpcChannel), SHOWCASE_CHUNK_SIZE)) {
200+
UploadMediaRequest request =
201+
UploadMediaRequest.newBuilder().setName("guard-test.txt").build();
202+
203+
try (InputStream stream1 = Files.newInputStream(file)) {
204+
FailedPreconditionException syncException =
205+
assertThrows(
206+
FailedPreconditionException.class, () -> grpcClient.uploadMedia(request, stream1));
207+
assertThat(syncException.getMessage())
208+
.contains("Resumable uploads execute over HTTP/REST and require credentials");
209+
}
210+
211+
FailedPreconditionException callableException =
212+
assertThrows(FailedPreconditionException.class, () -> grpcClient.uploadMediaCallable());
213+
assertThat(callableException.getMessage())
214+
.contains("Resumable uploads execute over HTTP/REST and require credentials");
215+
}
216+
}
217+
218+
private static Path createTempFile(Path dir, String fileName, byte[] data) throws IOException {
219+
Path path = dir.resolve(fileName);
220+
Files.write(path, data);
221+
return path;
222+
}
223+
224+
private static Path createTempFile(Path dir, String fileName, int size) throws IOException {
225+
byte[] data = new byte[size];
226+
for (int i = 0; i < size; i++) {
227+
data[i] = (byte) (i % 256);
228+
}
229+
return createTempFile(dir, fileName, data);
230+
}
231+
}

‎java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/util/TestClientInitializer.java‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
package com.google.showcase.v1beta1.it.util;
1818

1919
import com.google.api.client.http.javanet.NetHttpTransport;
20+
import com.google.api.gax.core.CredentialsProvider;
2021
import com.google.api.gax.core.NoCredentialsProvider;
22+
import com.google.api.gax.grpc.GrpcTransportChannel;
2123
import com.google.api.gax.httpjson.HttpJsonClientInterceptor;
2224
import com.google.api.gax.longrunning.OperationSnapshot;
2325
import com.google.api.gax.longrunning.OperationTimedPollAlgorithm;
@@ -33,6 +35,8 @@
3335
import com.google.showcase.v1beta1.EchoSettings;
3436
import com.google.showcase.v1beta1.IdentityClient;
3537
import com.google.showcase.v1beta1.IdentitySettings;
38+
import com.google.showcase.v1beta1.ResumableUploadServiceClient;
39+
import com.google.showcase.v1beta1.ResumableUploadServiceSettings;
3640
import com.google.showcase.v1beta1.SequenceServiceClient;
3741
import com.google.showcase.v1beta1.SequenceServiceSettings;
3842
import com.google.showcase.v1beta1.WaitRequest;
@@ -539,4 +543,55 @@ public static SequenceServiceClient createHttpJsonSequenceClientWithRetrySetting
539543
.build());
540544
return SequenceServiceClient.create(settingsBuilder.build());
541545
}
546+
547+
public static ResumableUploadServiceClient createHttpJsonResumableUploadClient(int chunkSize)
548+
throws Exception {
549+
ResumableUploadServiceSettings.Builder settingsBuilder =
550+
ResumableUploadServiceSettings.newHttpJsonBuilder();
551+
settingsBuilder
552+
.setCredentialsProvider(NoCredentialsProvider.create())
553+
.setTransportChannelProvider(
554+
ResumableUploadServiceSettings.defaultHttpJsonTransportProviderBuilder()
555+
.setHttpTransport(new NetHttpTransport.Builder().doNotValidateCertificate().build())
556+
.setEndpoint(DEFAULT_HTTPJSON_ENDPOINT)
557+
.build());
558+
settingsBuilder.uploadMediaSettings().setChunkSize(chunkSize);
559+
return ResumableUploadServiceClient.create(settingsBuilder.build());
560+
}
561+
562+
public static ResumableUploadServiceClient createGrpcResumableUploadClient(int chunkSize)
563+
throws Exception {
564+
return createGrpcResumableUploadClient(NoCredentialsProvider.create(), chunkSize);
565+
}
566+
567+
public static ResumableUploadServiceClient createGrpcResumableUploadClient(
568+
CredentialsProvider credentialsProvider, int chunkSize) throws Exception {
569+
ResumableUploadServiceSettings.Builder settingsBuilder =
570+
ResumableUploadServiceSettings.newBuilder()
571+
.setCredentialsProvider(credentialsProvider)
572+
.setTransportChannelProvider(
573+
ResumableUploadServiceSettings.defaultGrpcTransportProviderBuilder()
574+
.setEndpoint(DEFAULT_GRPC_ENDPOINT)
575+
.setChannelConfigurator(ManagedChannelBuilder::usePlaintext)
576+
.build())
577+
.setEndpoint(DEFAULT_HTTPJSON_ENDPOINT);
578+
settingsBuilder.uploadMediaSettings().setChunkSize(chunkSize);
579+
return ResumableUploadServiceClient.create(settingsBuilder.build());
580+
}
581+
582+
public static ResumableUploadServiceClient createGrpcResumableUploadClient(
583+
TransportChannelProvider transportChannelProvider, int chunkSize) throws Exception {
584+
ResumableUploadServiceSettings.Builder settingsBuilder =
585+
ResumableUploadServiceSettings.newBuilder()
586+
.setCredentialsProvider(NoCredentialsProvider.create())
587+
.setTransportChannelProvider(transportChannelProvider)
588+
.setEndpoint(DEFAULT_HTTPJSON_ENDPOINT);
589+
settingsBuilder.uploadMediaSettings().setChunkSize(chunkSize);
590+
return ResumableUploadServiceClient.create(settingsBuilder.build());
591+
}
592+
593+
public static GrpcTransportChannel createPlaintextGrpcChannel() {
594+
return GrpcTransportChannel.create(
595+
ManagedChannelBuilder.forTarget(DEFAULT_GRPC_ENDPOINT).usePlaintext().build());
596+
}
542597
}

0 commit comments

Comments
 (0)