From e1ac4e0579ed6394b75792ee556f4f7c77cad4a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Thu, 3 Feb 2022 20:18:39 +0100 Subject: [PATCH] test(spanner): enable more tests on emulator The emulator now supports all data types that are also supported by Cloud Spanner. The integration tests also unnecessarily skipped a large number of tests that tried to insert untyped null values as it was assumed that this was not supported. That is however supported for most data types, and only DATE and TIMESTAMP need to be skipped. Updates #https://github.com/GoogleCloudPlatform/cloud-spanner-emulator/issues/31 --- spanner/integration_test.go | 126 ++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/spanner/integration_test.go b/spanner/integration_test.go index c6968e1be5f4..089a0cfd7d1a 100644 --- a/spanner/integration_test.go +++ b/spanner/integration_test.go @@ -1569,23 +1569,21 @@ func TestIntegration_BasicTypes(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) defer cancel() - stmts := singerDBStatements - if !isEmulatorEnvSet() { - stmts = []string{ - `CREATE TABLE Singers ( + stmts := []string{ + `CREATE TABLE Singers ( SingerId INT64 NOT NULL, FirstName STRING(1024), LastName STRING(1024), SingerInfo BYTES(MAX) ) PRIMARY KEY (SingerId)`, - `CREATE INDEX SingerByName ON Singers(FirstName, LastName)`, - `CREATE TABLE Accounts ( + `CREATE INDEX SingerByName ON Singers(FirstName, LastName)`, + `CREATE TABLE Accounts ( AccountId INT64 NOT NULL, Nickname STRING(100), Balance INT64 NOT NULL, ) PRIMARY KEY (AccountId)`, - `CREATE INDEX AccountByNickname ON Accounts(Nickname) STORING (Balance)`, - `CREATE TABLE Types ( + `CREATE INDEX AccountByNickname ON Accounts(Nickname) STORING (Balance)`, + `CREATE TABLE Types ( RowID INT64 NOT NULL, String STRING(MAX), StringArray ARRAY, @@ -1606,7 +1604,6 @@ func TestIntegration_BasicTypes(t *testing.T) { JSON JSON, JSONArray ARRAY ) PRIMARY KEY (RowID)`, - } } client, _, cleanup := prepareIntegrationTest(ctx, t, DefaultSessionPoolConfig, stmts) defer cleanup() @@ -1647,7 +1644,6 @@ func TestIntegration_BasicTypes(t *testing.T) { {col: "String", val: "foo", want: NullString{"foo", true}}, {col: "String", val: NullString{"bar", true}, want: "bar"}, {col: "String", val: NullString{"bar", false}, want: NullString{"", false}}, - {col: "String", val: nil, want: NullString{}}, {col: "StringArray", val: []string(nil), want: []NullString(nil)}, {col: "StringArray", val: []string{}, want: []NullString{}}, {col: "StringArray", val: []string{"foo", "bar"}, want: []NullString{{"foo", true}, {"bar", true}}}, @@ -1668,7 +1664,6 @@ func TestIntegration_BasicTypes(t *testing.T) { {col: "Int64a", val: NullInt64{5, true}, want: int64(5)}, {col: "Int64a", val: NullInt64{6, true}, want: int64(6)}, {col: "Int64a", val: NullInt64{7, false}, want: NullInt64{0, false}}, - {col: "Int64a", val: nil, want: NullInt64{}}, {col: "Int64Array", val: []int(nil), want: []NullInt64(nil)}, {col: "Int64Array", val: []int{}, want: []NullInt64{}}, {col: "Int64Array", val: []int{1, 2}, want: []NullInt64{{1, true}, {2, true}}}, @@ -1684,7 +1679,6 @@ func TestIntegration_BasicTypes(t *testing.T) { {col: "Bool", val: true, want: NullBool{true, true}}, {col: "Bool", val: NullBool{true, true}}, {col: "Bool", val: NullBool{false, false}}, - {col: "Bool", val: nil, want: NullBool{}}, {col: "BoolArray", val: []bool(nil), want: []NullBool(nil)}, {col: "BoolArray", val: []bool{}, want: []NullBool{}}, {col: "BoolArray", val: []bool{true, false}, want: []NullBool{{true, true}, {false, true}}}, @@ -1700,7 +1694,6 @@ func TestIntegration_BasicTypes(t *testing.T) { {col: "Float64", val: NullFloat64{2.71, true}, want: 2.71}, {col: "Float64", val: NullFloat64{1.41, true}, want: NullFloat64{1.41, true}}, {col: "Float64", val: NullFloat64{0, false}}, - {col: "Float64", val: nil, want: NullFloat64{}}, {col: "Float64Array", val: []float64(nil), want: []NullFloat64(nil)}, {col: "Float64Array", val: []float64{}, want: []NullFloat64{}}, {col: "Float64Array", val: []float64{2.72, 3.14, math.Inf(1)}, want: []NullFloat64{{2.72, true}, {3.14, true}, {math.Inf(1), true}}}, @@ -1720,7 +1713,6 @@ func TestIntegration_BasicTypes(t *testing.T) { {col: "Timestamp", val: NullTime{t1, true}}, {col: "Timestamp", val: NullTime{t1, true}, want: t1}, {col: "Timestamp", val: NullTime{}}, - {col: "Timestamp", val: nil, want: NullTime{}}, {col: "TimestampArray", val: []time.Time(nil), want: []NullTime(nil)}, {col: "TimestampArray", val: []time.Time{}, want: []NullTime{}}, {col: "TimestampArray", val: []time.Time{t1, t2, t3}, want: []NullTime{{t1, true}, {t2, true}, {t3, true}}}, @@ -1731,13 +1723,17 @@ func TestIntegration_BasicTypes(t *testing.T) { {col: "Numeric", val: NullNumeric{n1, true}, want: n1}, {col: "Numeric", val: NullNumeric{n1, true}, want: NullNumeric{n1, true}}, {col: "Numeric", val: NullNumeric{n0, false}}, - {col: "Numeric", val: nil, want: NullNumeric{}}, {col: "NumericArray", val: []big.Rat(nil), want: []NullNumeric(nil)}, {col: "NumericArray", val: []big.Rat{}, want: []NullNumeric{}}, {col: "NumericArray", val: []big.Rat{n1, n2}, want: []NullNumeric{{n1, true}, {n2, true}}}, {col: "NumericArray", val: []NullNumeric(nil)}, {col: "NumericArray", val: []NullNumeric{}}, {col: "NumericArray", val: []NullNumeric{{n1, true}, {n2, true}, {}}}, + {col: "JSON", val: NullJSON{msg, true}, want: NullJSON{unmarshalledJSONstruct, true}}, + {col: "JSON", val: NullJSON{msg, false}, want: NullJSON{}}, + {col: "JSONArray", val: []NullJSON(nil)}, + {col: "JSONArray", val: []NullJSON{}}, + {col: "JSONArray", val: []NullJSON{{msg, true}, {msg, true}, {}}, want: []NullJSON{{unmarshalledJSONstruct, true}, {unmarshalledJSONstruct, true}, {}}}, {col: "String", val: nil, want: NullString{}}, {col: "StringArray", val: nil, want: []NullString(nil)}, {col: "Bytes", val: nil, want: []byte(nil)}, @@ -1748,67 +1744,71 @@ func TestIntegration_BasicTypes(t *testing.T) { {col: "BoolArray", val: nil, want: []NullBool(nil)}, {col: "Float64", val: nil, want: NullFloat64{}}, {col: "Float64Array", val: nil, want: []NullFloat64(nil)}, - {col: "Date", val: nil, want: NullDate{}}, - {col: "DateArray", val: nil, want: []NullDate(nil)}, - {col: "Timestamp", val: nil, want: NullTime{}}, - {col: "TimestampArray", val: nil, want: []NullTime(nil)}, {col: "Numeric", val: nil, want: NullNumeric{}}, {col: "NumericArray", val: nil, want: []NullNumeric(nil)}, + {col: "JSON", val: nil, want: NullJSON{}}, + {col: "JSONArray", val: nil, want: []NullJSON(nil)}, } - // Write rows into table first using DML. Only do this on real Spanner - // as the emulator does not support untyped parameters. - // TODO: Remove when the emulator supports untyped parameters. - if !isEmulatorEnvSet() { - statements := make([]Statement, 0) - for i, test := range tests { - stmt := NewStatement(fmt.Sprintf("INSERT INTO Types (RowId, `%s`) VALUES (@id, @value)", test.col)) - // Note: We are not setting the parameter type here to ensure that it - // can be automatically recognized when it is actually needed. - stmt.Params["id"] = i - stmt.Params["value"] = test.val - statements = append(statements, stmt) - } - _, err := client.ReadWriteTransaction(ctx, func(ctx context.Context, tx *ReadWriteTransaction) error { - rowCounts, err := tx.BatchUpdate(ctx, statements) - if err != nil { - return err - } - if len(rowCounts) != len(tests) { - return fmt.Errorf("rowCounts length mismatch\nGot: %v\nWant: %v", len(rowCounts), len(tests)) - } - for i, c := range rowCounts { - if c != 1 { - return fmt.Errorf("row count mismatch for row %v:\nGot: %v\nWant: %v", i, c, 1) - } - } - return nil - }) - if err != nil { - t.Fatalf("failed to insert values using DML: %v", err) - } - // Delete all the rows so we can insert them using mutations as well. - _, err = client.Apply(ctx, []*Mutation{Delete("Types", AllKeys())}) - if err != nil { - t.Fatalf("failed to delete all rows: %v", err) - } - } - + // See https://github.com/GoogleCloudPlatform/cloud-spanner-emulator/issues/31 if !isEmulatorEnvSet() { tests = append(tests, []struct { col string val interface{} want interface{} }{ - {col: "JSON", val: NullJSON{msg, true}, want: NullJSON{unmarshalledJSONstruct, true}}, - {col: "JSON", val: NullJSON{msg, false}, want: NullJSON{}}, - {col: "JSON", val: nil, want: NullJSON{}}, - {col: "JSONArray", val: []NullJSON(nil)}, - {col: "JSONArray", val: []NullJSON{}}, - {col: "JSONArray", val: []NullJSON{{msg, true}, {msg, true}, {}}, want: []NullJSON{{unmarshalledJSONstruct, true}, {unmarshalledJSONstruct, true}, {}}}, + {col: "Date", val: nil, want: NullDate{}}, + {col: "Timestamp", val: nil, want: NullTime{}}, }...) } + // Write rows into table first using DML. + statements := make([]Statement, 0) + for i, test := range tests { + stmt := NewStatement(fmt.Sprintf("INSERT INTO Types (RowId, `%s`) VALUES (@id, @value)", test.col)) + // Note: We are not setting the parameter type here to ensure that it + // can be automatically recognized when it is actually needed. + stmt.Params["id"] = i + stmt.Params["value"] = test.val + statements = append(statements, stmt) + } + _, err := client.ReadWriteTransaction(ctx, func(ctx context.Context, tx *ReadWriteTransaction) error { + rowCounts, err := tx.BatchUpdate(ctx, statements) + if err != nil { + return err + } + if len(rowCounts) != len(tests) { + return fmt.Errorf("rowCounts length mismatch\nGot: %v\nWant: %v", len(rowCounts), len(tests)) + } + for i, c := range rowCounts { + if c != 1 { + return fmt.Errorf("row count mismatch for row %v:\nGot: %v\nWant: %v", i, c, 1) + } + } + return nil + }) + if err != nil { + t.Fatalf("failed to insert values using DML: %v", err) + } + // Delete all the rows so we can insert them using mutations as well. + _, err = client.Apply(ctx, []*Mutation{Delete("Types", AllKeys())}) + if err != nil { + t.Fatalf("failed to delete all rows: %v", err) + } + + tests = append(tests, []struct { + col string + val interface{} + want interface{} + }{ + {col: "JSON", val: NullJSON{msg, true}, want: NullJSON{unmarshalledJSONstruct, true}}, + {col: "JSON", val: NullJSON{msg, false}, want: NullJSON{}}, + {col: "JSON", val: nil, want: NullJSON{}}, + {col: "JSONArray", val: []NullJSON(nil)}, + {col: "JSONArray", val: []NullJSON{}}, + {col: "JSONArray", val: []NullJSON{{msg, true}, {msg, true}, {}}, want: []NullJSON{{unmarshalledJSONstruct, true}, {unmarshalledJSONstruct, true}, {}}}, + }...) + // Verify that we can insert the rows using mutations. var muts []*Mutation for i, test := range tests {