1616
1717package 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 ;
2120import static org .mockito .Mockito .mock ;
22- import static org .mockito .Mockito .when ;
2321
2422import 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 ;
2526import com .google .cloud .bigquery .Field ;
2627import com .google .cloud .bigquery .Schema ;
2728import com .google .cloud .bigquery .StandardSQLTypeName ;
29+ import com .google .cloud .bigquery .storage .v1 .AppendRowsResponse ;
30+ import com .google .cloud .bigquery .storage .v1 .BatchCommitWriteStreamsResponse ;
2831import com .google .cloud .bigquery .storage .v1 .BigQueryWriteClient ;
2932import 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 ;
3136import com .google .cloud .bigquery .storage .v1 .TableName ;
3237import com .google .cloud .bigquery .storage .v1 .TableSchema ;
3338import 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 ;
3541import java .lang .reflect .Method ;
3642import java .sql .Types ;
43+ import java .util .Arrays ;
44+ import java .util .UUID ;
45+ import org .junit .jupiter .api .AfterEach ;
3746import org .junit .jupiter .api .BeforeEach ;
3847import 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 */
4556public 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