Skip to content

Commit ec6fa38

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

2 files changed

Lines changed: 274 additions & 0 deletions

File tree

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

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
import com.google.showcase.v1beta1.EchoSettings;
3434
import com.google.showcase.v1beta1.IdentityClient;
3535
import com.google.showcase.v1beta1.IdentitySettings;
36+
import com.google.showcase.v1beta1.ResumableUploadServiceClient;
37+
import com.google.showcase.v1beta1.ResumableUploadServiceSettings;
3638
import com.google.showcase.v1beta1.SequenceServiceClient;
3739
import com.google.showcase.v1beta1.SequenceServiceSettings;
3840
import com.google.showcase.v1beta1.WaitRequest;
@@ -539,4 +541,34 @@ public static SequenceServiceClient createHttpJsonSequenceClientWithRetrySetting
539541
.build());
540542
return SequenceServiceClient.create(settingsBuilder.build());
541543
}
544+
545+
public static ResumableUploadServiceClient createHttpJsonResumableUploadClient(int chunkSize)
546+
throws Exception {
547+
ResumableUploadServiceSettings.Builder settingsBuilder =
548+
ResumableUploadServiceSettings.newHttpJsonBuilder();
549+
settingsBuilder
550+
.setCredentialsProvider(NoCredentialsProvider.create())
551+
.setTransportChannelProvider(
552+
ResumableUploadServiceSettings.defaultHttpJsonTransportProviderBuilder()
553+
.setHttpTransport(new NetHttpTransport.Builder().doNotValidateCertificate().build())
554+
.setEndpoint(DEFAULT_HTTPJSON_ENDPOINT)
555+
.build());
556+
settingsBuilder.uploadMediaSettings().setChunkSize(chunkSize);
557+
return ResumableUploadServiceClient.create(settingsBuilder.build());
558+
}
559+
560+
public static ResumableUploadServiceClient createGrpcResumableUploadClient(int chunkSize)
561+
throws Exception {
562+
ResumableUploadServiceSettings.Builder settingsBuilder =
563+
ResumableUploadServiceSettings.newBuilder()
564+
.setCredentialsProvider(NoCredentialsProvider.create())
565+
.setTransportChannelProvider(
566+
ResumableUploadServiceSettings.defaultGrpcTransportProviderBuilder()
567+
.setEndpoint(DEFAULT_GRPC_ENDPOINT)
568+
.setChannelConfigurator(ManagedChannelBuilder::usePlaintext)
569+
.build())
570+
.setEndpoint(DEFAULT_HTTPJSON_ENDPOINT);
571+
settingsBuilder.uploadMediaSettings().setChunkSize(chunkSize);
572+
return ResumableUploadServiceClient.create(settingsBuilder.build());
573+
}
542574
}

0 commit comments

Comments
 (0)