blob: aab85fa90724e571e4d1ba6c33c2d69c30f94393 [file] [log] [blame]
// 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
//
// https://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 adapt
import (
"testing"
"cloud.google.com/go/bigquery"
"cloud.google.com/go/internal/testutil"
"github.com/google/go-cmp/cmp"
storagepb "google.golang.org/genproto/googleapis/cloud/bigquery/storage/v1beta2"
"google.golang.org/protobuf/testing/protocmp"
)
func TestFieldConversions(t *testing.T) {
testCases := []struct {
desc string
bq *bigquery.FieldSchema
proto *storagepb.TableFieldSchema
}{
{
desc: "nil",
bq: nil,
proto: nil,
},
{
desc: "string field",
bq: &bigquery.FieldSchema{
Name: "name",
Type: bigquery.StringFieldType,
Description: "description",
},
proto: &storagepb.TableFieldSchema{
Name: "name",
Type: storagepb.TableFieldSchema_STRING,
Description: "description",
Mode: storagepb.TableFieldSchema_NULLABLE,
},
},
{
desc: "required integer field",
bq: &bigquery.FieldSchema{
Name: "name",
Type: bigquery.IntegerFieldType,
Description: "description",
Required: true,
},
proto: &storagepb.TableFieldSchema{
Name: "name",
Type: storagepb.TableFieldSchema_INT64,
Description: "description",
Mode: storagepb.TableFieldSchema_REQUIRED,
},
},
{
desc: "struct with repeated bytes subfield",
bq: &bigquery.FieldSchema{
Name: "name",
Type: bigquery.RecordFieldType,
Description: "description",
Required: true,
Schema: bigquery.Schema{
&bigquery.FieldSchema{
Name: "inner1",
Repeated: true,
Description: "repeat",
Type: bigquery.BytesFieldType,
},
},
},
proto: &storagepb.TableFieldSchema{
Name: "name",
Type: storagepb.TableFieldSchema_STRUCT,
Description: "description",
Mode: storagepb.TableFieldSchema_REQUIRED,
Fields: []*storagepb.TableFieldSchema{
{
Name: "inner1",
Mode: storagepb.TableFieldSchema_REPEATED,
Description: "repeat",
Type: storagepb.TableFieldSchema_BYTES,
},
},
},
},
}
for _, tc := range testCases {
// first, bq to proto
converted, err := bqFieldToProto(tc.bq)
if err != nil {
t.Errorf("case (%s) failed conversion from bq: %v", tc.desc, err)
}
if diff := cmp.Diff(converted, tc.proto, protocmp.Transform()); diff != "" {
t.Errorf("conversion to proto diff (%s):\n%v", tc.desc, diff)
}
// reverse conversion, proto to bq
reverse, err := protoToBQField(tc.proto)
if err != nil {
t.Errorf("case (%s) failed conversion from proto: %v", tc.desc, err)
}
if diff := cmp.Diff(reverse, tc.bq); diff != "" {
t.Errorf("conversion to BQ diff (%s):\n%v", tc.desc, diff)
}
}
}
func TestSchemaConversion(t *testing.T) {
testCases := []struct {
description string
bqSchema bigquery.Schema
storageSchema *storagepb.TableSchema
}{
{
description: "nil",
bqSchema: nil,
storageSchema: nil,
},
{
description: "scalars",
bqSchema: bigquery.Schema{
{Name: "f1", Type: bigquery.StringFieldType},
{Name: "f2", Type: bigquery.IntegerFieldType},
{Name: "f3", Type: bigquery.BooleanFieldType},
},
storageSchema: &storagepb.TableSchema{
Fields: []*storagepb.TableFieldSchema{
{Name: "f1", Type: storagepb.TableFieldSchema_STRING, Mode: storagepb.TableFieldSchema_NULLABLE},
{Name: "f2", Type: storagepb.TableFieldSchema_INT64, Mode: storagepb.TableFieldSchema_NULLABLE},
{Name: "f3", Type: storagepb.TableFieldSchema_BOOL, Mode: storagepb.TableFieldSchema_NULLABLE},
},
},
},
{
description: "array",
bqSchema: bigquery.Schema{
{Name: "arr", Type: bigquery.NumericFieldType, Repeated: true},
{Name: "big", Type: bigquery.BigNumericFieldType, Required: true},
},
storageSchema: &storagepb.TableSchema{
Fields: []*storagepb.TableFieldSchema{
{Name: "arr", Type: storagepb.TableFieldSchema_NUMERIC, Mode: storagepb.TableFieldSchema_REPEATED},
{Name: "big", Type: storagepb.TableFieldSchema_BIGNUMERIC, Mode: storagepb.TableFieldSchema_REQUIRED},
},
},
},
{
description: "nested",
bqSchema: bigquery.Schema{
{Name: "struct1", Type: bigquery.RecordFieldType, Schema: []*bigquery.FieldSchema{
{Name: "leaf1", Type: bigquery.DateFieldType},
{Name: "leaf2", Type: bigquery.DateTimeFieldType},
}},
{Name: "field2", Type: bigquery.StringFieldType},
},
storageSchema: &storagepb.TableSchema{
Fields: []*storagepb.TableFieldSchema{
{Name: "struct1",
Type: storagepb.TableFieldSchema_STRUCT,
Mode: storagepb.TableFieldSchema_NULLABLE,
Fields: []*storagepb.TableFieldSchema{
{Name: "leaf1", Type: storagepb.TableFieldSchema_DATE, Mode: storagepb.TableFieldSchema_NULLABLE},
{Name: "leaf2", Type: storagepb.TableFieldSchema_DATETIME, Mode: storagepb.TableFieldSchema_NULLABLE},
}},
{Name: "field2", Type: storagepb.TableFieldSchema_STRING, Mode: storagepb.TableFieldSchema_NULLABLE},
},
},
},
}
for _, tc := range testCases {
// BQ -> Storage
storageS, err := BQSchemaToStorageTableSchema(tc.bqSchema)
if err != nil {
t.Errorf("BQSchemaToStorageTableSchema(%s): %v", tc.description, err)
}
if diff := testutil.Diff(storageS, tc.storageSchema); diff != "" {
t.Fatalf("BQSchemaToStorageTableSchema(%s): -got, +want:\n%s", tc.description, diff)
}
// Storage -> BQ
bqS, err := StorageTableSchemaToBQSchema(tc.storageSchema)
if err != nil {
t.Errorf("StorageTableSchemaToBQSchema(%s): %v", tc.description, err)
}
if diff := testutil.Diff(bqS, tc.bqSchema); diff != "" {
t.Fatalf("StorageTableSchemaToBQSchema(%s): -got, +want:\n%s", tc.description, diff)
}
}
}