Skip to content

Commit

Permalink
test: BigDecmialByteStringConverter E2E Test (#979)
Browse files Browse the repository at this point in the history
* Add E2E test for BigDecimalByteStringConverter. Fix JsonToProtoMessage
to support these changes. Fix all affected tests

* Cleaning up logs and unnecessary code

* Encode numerics in WriteManualClientTest

* Lint

* Catch errors with JSONArray in JsonToProtoMessage, cover with a test. Resolving other comments

* Lint

* Removing comment

* Add unit test for new Byte changes

* Lint

* Adding unit tests for repeated BYTES field changes

* Lint

* Fixing test error

* Moved new tests into existing test for better clarity and readability

* Cleaning up
  • Loading branch information
JacobStocklass committed Apr 6, 2021
1 parent 2bf05e2 commit 5c8c4e7
Show file tree
Hide file tree
Showing 9 changed files with 254 additions and 23 deletions.
Expand Up @@ -51,7 +51,7 @@ public class BQTableSchemaToProtoDescriptor {
.put(TableFieldSchema.Type.DOUBLE, FieldDescriptorProto.Type.TYPE_DOUBLE)
.put(TableFieldSchema.Type.GEOGRAPHY, FieldDescriptorProto.Type.TYPE_STRING)
.put(TableFieldSchema.Type.INT64, FieldDescriptorProto.Type.TYPE_INT64)
.put(TableFieldSchema.Type.NUMERIC, FieldDescriptorProto.Type.TYPE_STRING)
.put(TableFieldSchema.Type.NUMERIC, FieldDescriptorProto.Type.TYPE_BYTES)
.put(TableFieldSchema.Type.STRING, FieldDescriptorProto.Type.TYPE_STRING)
.put(TableFieldSchema.Type.STRUCT, FieldDescriptorProto.Type.TYPE_MESSAGE)
.put(TableFieldSchema.Type.TIME, FieldDescriptorProto.Type.TYPE_INT64)
Expand Down
Expand Up @@ -17,6 +17,7 @@

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.protobuf.ByteString;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.DynamicMessage;
Expand Down Expand Up @@ -138,7 +139,10 @@ private static void fillField(
}
break;
case BYTES:
if (val instanceof String) {
if (val instanceof ByteString) {
protoMsg.setField(fieldDescriptor, ((ByteString) val).toByteArray());
return;
} else if (val instanceof String) {
protoMsg.setField(fieldDescriptor, ((String) val).getBytes());
return;
}
Expand Down Expand Up @@ -234,7 +238,25 @@ private static void fillRepeatedField(
break;
case BYTES:
if (val instanceof String) {
// TODO(jstocklass): If string, decode it and pass in the byte array. Will need to
// update tests to ensure that strings passed in are properly encoded as well.
protoMsg.addRepeatedField(fieldDescriptor, ((String) val).getBytes());
} else if (val instanceof JSONArray) {
try {
byte[] bytes = new byte[((JSONArray) val).length()];
for (int j = 0; j < ((JSONArray) val).length(); j++) {
bytes[j] = (byte) ((byte) (((JSONArray) val).get(j)) & 0xFF);
}
protoMsg.addRepeatedField(fieldDescriptor, bytes);
} catch (ClassCastException e) {
throw new IllegalArgumentException(
String.format(
"Error: "
+ currentScope
+ "["
+ index
+ "] could not be converted to byte[]."));
}
} else {
fail = true;
}
Expand Down
Expand Up @@ -20,6 +20,7 @@

import com.google.cloud.bigquery.storage.test.JsonTest.*;
import com.google.cloud.bigquery.storage.test.SchemaTest.*;
import com.google.cloud.bigquery.storage.v1alpha2.Table.TableFieldSchema;
import com.google.common.collect.ImmutableMap;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
Expand Down Expand Up @@ -183,7 +184,7 @@ public void testStructComplex() throws Exception {
.build();
final Table.TableFieldSchema TEST_NUMERIC =
Table.TableFieldSchema.newBuilder()
.setType(Table.TableFieldSchema.Type.NUMERIC)
.setType(TableFieldSchema.Type.BYTES)
.setMode(Table.TableFieldSchema.Mode.NULLABLE)
.setName("test_numeric")
.build();
Expand Down
Expand Up @@ -42,7 +42,7 @@ public class BQTableSchemaToProtoDescriptorTest {
.put(TableFieldSchema.Type.DOUBLE, DoubleType.getDescriptor())
.put(TableFieldSchema.Type.GEOGRAPHY, StringType.getDescriptor())
.put(TableFieldSchema.Type.INT64, Int64Type.getDescriptor())
.put(TableFieldSchema.Type.NUMERIC, StringType.getDescriptor())
.put(TableFieldSchema.Type.NUMERIC, BytesType.getDescriptor())
.put(TableFieldSchema.Type.STRING, StringType.getDescriptor())
.put(TableFieldSchema.Type.TIME, Int64Type.getDescriptor())
.put(TableFieldSchema.Type.TIMESTAMP, Int64Type.getDescriptor())
Expand Down
Expand Up @@ -36,6 +36,7 @@
import com.google.protobuf.Int64Value;
import com.google.protobuf.Timestamp;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.logging.Logger;
Expand Down Expand Up @@ -408,7 +409,8 @@ public void testSingleAppendComplexJson() throws Exception {
com.google.cloud.bigquery.storage.test.JsonTest.ComplexLvl2.newBuilder()
.setTestInt(3)
.build())
.setTestNumeric("1.23456")
.setTestNumeric(
BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("1.23456")))
.setTestGeo("POINT(1,1)")
.setTestTimestamp(12345678)
.setTestTime(CivilTimeEncoder.encodePacked64TimeMicros(LocalTime.of(1, 0, 1)))
Expand All @@ -429,7 +431,9 @@ public void testSingleAppendComplexJson() throws Exception {
json.put("test_date", 1);
json.put("complex_lvl1", complex_lvl1);
json.put("complex_lvl2", complex_lvl2);
json.put("test_numeric", "1.23456");
json.put(
"test_numeric",
BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("1.23456")));
json.put("test_geo", "POINT(1,1)");
json.put("test_timestamp", 12345678);
json.put("test_time", CivilTimeEncoder.encodePacked64TimeMicros(LocalTime.of(1, 0, 1)));
Expand Down
Expand Up @@ -16,6 +16,7 @@
package com.google.cloud.bigquery.storage.v1beta2;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import com.google.cloud.bigquery.storage.test.JsonTest.*;
import com.google.cloud.bigquery.storage.test.SchemaTest.*;
Expand All @@ -24,8 +25,10 @@
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.DynamicMessage;
import com.google.protobuf.Message;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Map;
import java.util.logging.Logger;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.Assert;
Expand All @@ -35,6 +38,7 @@

@RunWith(JUnit4.class)
public class JsonToProtoMessageTest {
private static final Logger LOG = Logger.getLogger(JsonToProtoMessageTest.class.getName());
private static ImmutableMap<Descriptor, String> AllTypesToDebugMessageTest =
new ImmutableMap.Builder<Descriptor, String>()
.put(BoolType.getDescriptor(), "boolean")
Expand Down Expand Up @@ -97,15 +101,15 @@ public class JsonToProtoMessageTest {
})
.build();

private static ImmutableMap<Descriptor, String> AllRepeatedTypesToDebugMessageTest =
new ImmutableMap.Builder<Descriptor, String>()
.put(RepeatedBool.getDescriptor(), "boolean")
.put(RepeatedBytes.getDescriptor(), "string")
.put(RepeatedInt64.getDescriptor(), "int64")
.put(RepeatedInt32.getDescriptor(), "int32")
.put(RepeatedDouble.getDescriptor(), "double")
.put(RepeatedString.getDescriptor(), "string")
.put(RepeatedObject.getDescriptor(), "object")
private static ImmutableMap<Descriptor, String[]> AllRepeatedTypesToDebugMessageTest =
new ImmutableMap.Builder<Descriptor, String[]>()
.put(RepeatedBool.getDescriptor(), new String[] {"boolean"})
.put(RepeatedBytes.getDescriptor(), new String[] {"string", "bytes"})
.put(RepeatedInt64.getDescriptor(), new String[] {"int64"})
.put(RepeatedInt32.getDescriptor(), new String[] {"int32"})
.put(RepeatedDouble.getDescriptor(), new String[] {"double"})
.put(RepeatedString.getDescriptor(), new String[] {"string"})
.put(RepeatedObject.getDescriptor(), new String[] {"object"})
.build();

private static ImmutableMap<Descriptor, Message[]> AllRepeatedTypesToCorrectProto =
Expand All @@ -121,6 +125,13 @@ public class JsonToProtoMessageTest {
RepeatedBytes.newBuilder()
.addTestRepeated(ByteString.copyFrom("hello".getBytes()))
.addTestRepeated(ByteString.copyFrom("test".getBytes()))
.build(),
RepeatedBytes.newBuilder()
.addTestRepeated(
BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("0")))
.addTestRepeated(
BigDecimalByteStringEncoder.encodeToNumericByteString(
new BigDecimal("1.2")))
.build()
})
.put(
Expand Down Expand Up @@ -243,6 +254,18 @@ public class JsonToProtoMessageTest {
.put("test_repeated", new JSONArray(new Float[] {Float.MAX_VALUE, Float.MIN_VALUE})),
new JSONObject().put("test_repeated", new JSONArray(new Boolean[] {true, false})),
new JSONObject().put("test_repeated", new JSONArray(new String[] {"hello", "test"})),
new JSONObject()
.put(
"test_repeated",
new JSONArray(
new byte[][] {
BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("0"))
.toByteArray(),
BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("1.2"))
.toByteArray()
})),
new JSONObject().put("test_repeated", new JSONArray(new char[][] {{'a', 'b'}, {'c'}})),
new JSONObject().put("test_repeated", new JSONArray(new String[][] {{"hello"}, {"test"}})),
new JSONObject()
.put(
"test_repeated",
Expand Down Expand Up @@ -347,7 +370,7 @@ public void testAllTypes() throws Exception {

@Test
public void testAllRepeatedTypesWithLimits() throws Exception {
for (Map.Entry<Descriptor, String> entry : AllRepeatedTypesToDebugMessageTest.entrySet()) {
for (Map.Entry<Descriptor, String[]> entry : AllRepeatedTypesToDebugMessageTest.entrySet()) {
int success = 0;
for (JSONObject json : simpleJSONArrays) {
try {
Expand All @@ -356,13 +379,19 @@ public void testAllRepeatedTypesWithLimits() throws Exception {
assertEquals(protoMsg, AllRepeatedTypesToCorrectProto.get(entry.getKey())[success]);
success += 1;
} catch (IllegalArgumentException e) {
assertEquals(
"JSONObject does not have a " + entry.getValue() + " field at root.test_repeated[0].",
e.getMessage());
assertTrue(
e.getMessage()
.equals(
"JSONObject does not have a "
+ entry.getValue()[0]
+ " field at root.test_repeated[0].")
|| e.getMessage()
.equals("Error: root.test_repeated[0] could not be converted to byte[]."));
}
}
if (entry.getKey() == RepeatedInt64.getDescriptor()
|| entry.getKey() == RepeatedDouble.getDescriptor()) {
|| entry.getKey() == RepeatedDouble.getDescriptor()
|| entry.getKey() == RepeatedBytes.getDescriptor()) {
assertEquals(2, success);
} else {
assertEquals(1, success);
Expand Down
@@ -0,0 +1,158 @@
/*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.cloud.bigquery.storage.v1beta2.it;

import static org.junit.Assert.assertEquals;

import com.google.api.core.ApiFuture;
import com.google.cloud.ServiceOptions;
import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.DatasetInfo;
import com.google.cloud.bigquery.Field.Mode;
import com.google.cloud.bigquery.FieldValueList;
import com.google.cloud.bigquery.Schema;
import com.google.cloud.bigquery.StandardSQLTypeName;
import com.google.cloud.bigquery.StandardTableDefinition;
import com.google.cloud.bigquery.TableId;
import com.google.cloud.bigquery.TableInfo;
import com.google.cloud.bigquery.TableResult;
import com.google.cloud.bigquery.storage.v1beta2.AppendRowsResponse;
import com.google.cloud.bigquery.storage.v1beta2.AppendRowsResponse.AppendResult;
import com.google.cloud.bigquery.storage.v1beta2.BigDecimalByteStringEncoder;
import com.google.cloud.bigquery.storage.v1beta2.BigQueryWriteClient;
import com.google.cloud.bigquery.storage.v1beta2.JsonStreamWriter;
import com.google.cloud.bigquery.storage.v1beta2.TableName;
import com.google.cloud.bigquery.testing.RemoteBigQueryHelper;
import com.google.protobuf.Descriptors;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Iterator;
import java.util.concurrent.ExecutionException;
import java.util.logging.Logger;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

public class ITBigQueryBigDecimalByteStringEncoderTest {
private static final Logger LOG =
Logger.getLogger(ITBigQueryBigDecimalByteStringEncoderTest.class.getName());
private static final String DATASET = RemoteBigQueryHelper.generateDatasetName();
private static final String TABLE = "testtable";
private static final String DESCRIPTION = "BigQuery Write Java manual client test dataset";

private static BigQueryWriteClient client;
private static TableInfo tableInfo;
private static BigQuery bigquery;

@BeforeClass
public static void beforeClass() throws IOException {
client = BigQueryWriteClient.create();

RemoteBigQueryHelper bigqueryHelper = RemoteBigQueryHelper.create();
bigquery = bigqueryHelper.getOptions().getService();
DatasetInfo datasetInfo =
DatasetInfo.newBuilder(/* datasetId = */ DATASET).setDescription(DESCRIPTION).build();
bigquery.create(datasetInfo);
tableInfo =
TableInfo.newBuilder(
TableId.of(DATASET, TABLE),
StandardTableDefinition.of(
Schema.of(
com.google.cloud.bigquery.Field.newBuilder(
"test_numeric_zero", StandardSQLTypeName.NUMERIC)
.build(),
com.google.cloud.bigquery.Field.newBuilder(
"test_numeric_one", StandardSQLTypeName.NUMERIC)
.build(),
com.google.cloud.bigquery.Field.newBuilder(
"test_numeric_repeated", StandardSQLTypeName.NUMERIC)
.setMode(Mode.REPEATED)
.build())))
.build();
bigquery.create(tableInfo);
}

@AfterClass
public static void afterClass() {
if (client != null) {
client.close();
}
if (bigquery != null) {
RemoteBigQueryHelper.forceDelete(bigquery, DATASET);
}
}

@Test
public void TestBigDecimalEncoding()
throws IOException, InterruptedException, ExecutionException,
Descriptors.DescriptorValidationException {
TableName parent = TableName.of(ServiceOptions.getDefaultProjectId(), DATASET, TABLE);
try (JsonStreamWriter jsonStreamWriter =
JsonStreamWriter.newBuilder(parent.toString(), tableInfo.getDefinition().getSchema())
.createDefaultStream()
.build()) {
JSONObject row = new JSONObject();
row.put(
"test_numeric_zero",
BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("0")));
row.put(
"test_numeric_one",
BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("1.2")));
row.put(
"test_numeric_repeated",
new JSONArray(
new byte[][] {
BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("0"))
.toByteArray(),
BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("1.2"))
.toByteArray(),
BigDecimalByteStringEncoder.encodeToNumericByteString(new BigDecimal("-1.2"))
.toByteArray(),
BigDecimalByteStringEncoder.encodeToNumericByteString(
new BigDecimal("99999999999999999999999999999.999999999"))
.toByteArray(),
BigDecimalByteStringEncoder.encodeToNumericByteString(
new BigDecimal("-99999999999999999999999999999.999999999"))
.toByteArray(),
}));
JSONArray jsonArr = new JSONArray(new JSONObject[] {row});
ApiFuture<AppendRowsResponse> response = jsonStreamWriter.append(jsonArr, -1);
AppendRowsResponse arr = response.get();
AppendResult ar = arr.getAppendResult();
boolean ho = ar.hasOffset();
TableResult result =
bigquery.listTableData(
tableInfo.getTableId(), BigQuery.TableDataListOption.startIndex(0L));
Iterator<FieldValueList> iter = result.getValues().iterator();
FieldValueList currentRow;
currentRow = iter.next();
assertEquals("0", currentRow.get(0).getStringValue());
assertEquals("1.2", currentRow.get(1).getStringValue());
assertEquals("0", currentRow.get(2).getRepeatedValue().get(0).getStringValue());
assertEquals("1.2", currentRow.get(2).getRepeatedValue().get(1).getStringValue());
assertEquals("-1.2", currentRow.get(2).getRepeatedValue().get(2).getStringValue());
assertEquals(
"99999999999999999999999999999.999999999",
currentRow.get(2).getRepeatedValue().get(3).getStringValue());
assertEquals(
"-99999999999999999999999999999.999999999",
currentRow.get(2).getRepeatedValue().get(4).getStringValue());
}
}
}

0 comments on commit 5c8c4e7

Please sign in to comment.