Skip to content

Commit 514f548

Browse files
rachitsghclaude
andcommitted
test(bigquery-jdbc): verify null-value fix via real Storage Write round trip
Address review feedback on catching NullPointerException as an "expected" outcome in the regression test. Replace the reflection-based try/catch with a full round trip through bulkInsertWithWriteAPI against an in-process fake Storage Write service (MockBigQueryWrite + gax-grpc testlib), so the test now asserts the method completes normally instead of merely checking which exception type comes out. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 349009d commit 514f548

2 files changed

Lines changed: 81 additions & 29 deletions

File tree

java-bigquery-jdbc/pom.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,20 @@
456456
<version>2.96.0-SNAPSHOT</version><!-- {x-version-update:google-cloud-trace:current} -->
457457
<scope>test</scope>
458458
</dependency>
459+
<dependency>
460+
<groupId>com.google.cloud</groupId>
461+
<artifactId>google-cloud-bigquerystorage</artifactId>
462+
<version>3.32.0-SNAPSHOT</version><!-- {x-version-update:google-cloud-bigquerystorage:current} -->
463+
<classifier>tests</classifier>
464+
<type>test-jar</type>
465+
<scope>test</scope>
466+
</dependency>
467+
<dependency>
468+
<groupId>com.google.api</groupId>
469+
<artifactId>gax-grpc</artifactId>
470+
<classifier>testlib</classifier>
471+
<scope>test</scope>
472+
</dependency>
459473
</dependencies>
460474

461475
<profiles>

java-bigquery-jdbc/src/test/java/com/google/cloud/bigquery/jdbc/BigQueryPreparedStatementBulkInsertNullValueTest.java

Lines changed: 67 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,58 +16,96 @@
1616

1717
package com.google.cloud.bigquery.jdbc;
1818

19-
import static org.junit.jupiter.api.Assertions.assertFalse;
20-
import static org.mockito.ArgumentMatchers.any;
19+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
2120
import static org.mockito.Mockito.mock;
22-
import static org.mockito.Mockito.when;
2321

2422
import com.google.api.gax.core.NoCredentialsProvider;
23+
import com.google.api.gax.grpc.testing.LocalChannelProvider;
24+
import com.google.api.gax.grpc.testing.MockGrpcService;
25+
import com.google.api.gax.grpc.testing.MockServiceHelper;
2526
import com.google.cloud.bigquery.Field;
2627
import com.google.cloud.bigquery.Schema;
2728
import com.google.cloud.bigquery.StandardSQLTypeName;
29+
import com.google.cloud.bigquery.storage.v1.AppendRowsResponse;
30+
import com.google.cloud.bigquery.storage.v1.BatchCommitWriteStreamsResponse;
2831
import com.google.cloud.bigquery.storage.v1.BigQueryWriteClient;
2932
import com.google.cloud.bigquery.storage.v1.BigQueryWriteSettings;
30-
import com.google.cloud.bigquery.storage.v1.CreateWriteStreamRequest;
33+
import com.google.cloud.bigquery.storage.v1.FinalizeWriteStreamResponse;
34+
import com.google.cloud.bigquery.storage.v1.MockBigQueryWrite;
35+
import com.google.cloud.bigquery.storage.v1.TableFieldSchema;
3136
import com.google.cloud.bigquery.storage.v1.TableName;
3237
import com.google.cloud.bigquery.storage.v1.TableSchema;
3338
import com.google.cloud.bigquery.storage.v1.WriteStream;
34-
import java.lang.reflect.InvocationTargetException;
39+
import com.google.protobuf.Int64Value;
40+
import com.google.protobuf.Timestamp;
3541
import java.lang.reflect.Method;
3642
import java.sql.Types;
43+
import java.util.Arrays;
44+
import java.util.UUID;
45+
import org.junit.jupiter.api.AfterEach;
3746
import org.junit.jupiter.api.BeforeEach;
3847
import org.junit.jupiter.api.Test;
3948

4049
/**
4150
* Regression test for https://github.com/googleapis/google-cloud-java/issues/14066: a
4251
* null-valued STRING parameter in a Storage Write API batch insert used to throw NPE while
43-
* building the row's JSON representation, instead of writing a JSON null.
52+
* building the row's JSON representation, instead of writing a JSON null. Drives the real
53+
* bulkInsertWithWriteAPI code path end-to-end against an in-process fake Storage Write service so
54+
* the fix is verified without any real network dependency.
4455
*/
4556
public class BigQueryPreparedStatementBulkInsertNullValueTest {
4657

47-
private BigQueryConnection connection;
58+
private static final String STREAM_NAME =
59+
"projects/test-project/datasets/test_dataset/tables/test_table/streams/test-stream";
60+
61+
private MockBigQueryWrite mockBigQueryWrite;
62+
private MockServiceHelper serviceHelper;
63+
private BigQueryWriteClient writeClient;
4864
private BigQueryPreparedStatement preparedStatement;
49-
private BigQueryWriteClient mockWriteClient;
5065

5166
@BeforeEach
5267
public void setUp() throws Exception {
53-
connection = mock(BigQueryConnection.class);
54-
mockWriteClient = mock(BigQueryWriteClient.class);
68+
mockBigQueryWrite = new MockBigQueryWrite();
69+
serviceHelper =
70+
new MockServiceHelper(
71+
UUID.randomUUID().toString(), Arrays.<MockGrpcService>asList(mockBigQueryWrite));
72+
serviceHelper.start();
5573

56-
BigQueryWriteSettings mockSettings =
57-
BigQueryWriteSettings.newBuilder()
58-
.setCredentialsProvider(NoCredentialsProvider.create())
59-
.build();
60-
when(mockWriteClient.getSettings()).thenReturn(mockSettings);
74+
LocalChannelProvider channelProvider = serviceHelper.createChannelProvider();
75+
writeClient =
76+
BigQueryWriteClient.create(
77+
BigQueryWriteSettings.newBuilder()
78+
.setTransportChannelProvider(channelProvider)
79+
.setCredentialsProvider(NoCredentialsProvider.create())
80+
.build());
6181

62-
WriteStream stream =
82+
TableFieldSchema nameField =
83+
TableFieldSchema.newBuilder()
84+
.setName("name")
85+
.setType(TableFieldSchema.Type.STRING)
86+
.setMode(TableFieldSchema.Mode.NULLABLE)
87+
.build();
88+
WriteStream writeStream =
6389
WriteStream.newBuilder()
64-
.setName(
65-
"projects/test-project/datasets/test_dataset/tables/test_table/streams/_default")
66-
.setTableSchema(TableSchema.newBuilder().build())
90+
.setName(STREAM_NAME)
91+
.setTableSchema(TableSchema.newBuilder().addFields(nameField).build())
6792
.build();
68-
when(mockWriteClient.createWriteStream(any(CreateWriteStreamRequest.class)))
69-
.thenReturn(stream);
7093

94+
// Responses are consumed in call order by bulkInsertWithWriteAPI: createWriteStream, then
95+
// append, then finalizeWriteStream, then batchCommitWriteStreams.
96+
mockBigQueryWrite.addResponse(writeStream);
97+
mockBigQueryWrite.addResponse(
98+
AppendRowsResponse.newBuilder()
99+
.setAppendResult(
100+
AppendRowsResponse.AppendResult.newBuilder().setOffset(Int64Value.of(0)))
101+
.build());
102+
mockBigQueryWrite.addResponse(FinalizeWriteStreamResponse.newBuilder().setRowCount(1).build());
103+
mockBigQueryWrite.addResponse(
104+
BatchCommitWriteStreamsResponse.newBuilder()
105+
.setCommitTime(Timestamp.newBuilder().build())
106+
.build());
107+
108+
BigQueryConnection connection = mock(BigQueryConnection.class);
71109
preparedStatement =
72110
new BigQueryPreparedStatement(connection, "INSERT INTO test_table (name) VALUES (?)");
73111
preparedStatement.insertSchema = Schema.of(Field.of("name", StandardSQLTypeName.STRING));
@@ -79,8 +117,14 @@ public void setUp() throws Exception {
79117
preparedStatement, TableName.of("test-project", "test_dataset", "test_table"));
80118
}
81119

120+
@AfterEach
121+
public void tearDown() throws Exception {
122+
writeClient.close();
123+
serviceHelper.stop();
124+
}
125+
82126
@Test
83-
public void testBulkInsertWithWriteApiDoesNotThrowNpeForNullStringParameter() throws Exception {
127+
public void testBulkInsertWithWriteApiCompletesForNullStringParameter() throws Exception {
84128
preparedStatement.setNull(1, Types.VARCHAR);
85129
preparedStatement.addBatch();
86130

@@ -89,12 +133,6 @@ public void testBulkInsertWithWriteApiDoesNotThrowNpeForNullStringParameter() th
89133
"bulkInsertWithWriteAPI", BigQueryWriteClient.class);
90134
bulkInsertMethod.setAccessible(true);
91135

92-
try {
93-
bulkInsertMethod.invoke(preparedStatement, mockWriteClient);
94-
} catch (InvocationTargetException e) {
95-
assertFalse(
96-
e.getCause() instanceof NullPointerException,
97-
"Row construction should not NPE on a null STRING parameter, but got: " + e.getCause());
98-
}
136+
assertDoesNotThrow(() -> bulkInsertMethod.invoke(preparedStatement, writeClient));
99137
}
100138
}

0 commit comments

Comments
 (0)