diff --git a/internal/spanner/spanner_test.go b/internal/spanner/spanner_test.go index 4315feb..8cd3bfb 100644 --- a/internal/spanner/spanner_test.go +++ b/internal/spanner/spanner_test.go @@ -3,9 +3,12 @@ package spanner import ( "context" "fmt" + "math/big" "os" "testing" + "time" + "cloud.google.com/go/civil" "cloud.google.com/go/spanner" "github.com/google/go-cmp/cmp" @@ -30,6 +33,20 @@ type baz struct { Name string `spanner:"Name"` } +type allTypes struct { + ID string `spanner:"ID"` + StringValue string `spanner:"StringValue"` + BoolValue bool `spanner:"BoolValue"` + Int64Value int64 `spanner:"Int64Value"` + Float64Value float64 `spanner:"Float64Value"` + JSONValue spanner.NullJSON `spanner:"JSONValue"` + BytesValue []byte `spanner:"BytesValue"` + TimestampValue time.Time `spanner:"TimestampValue"` + NumericValue *big.Rat `spanner:"NumericValue"` + DateValue civil.Date `spanner:"DateValue"` + StringArray []string `spanner:"StringArray"` +} + func TestSave(t *testing.T) { t.Parallel() ctx := context.Background() @@ -113,6 +130,27 @@ func TestSave(t *testing.T) { }, }, }, + { + Name: "AllTypes", + Records: []*model.Record{ + { + Values: map[string]interface{}{ + "DateValue": "2022-04-01", + "Float64Value": float64(3.14159), + "ID": "All_Type_Values", + "Int64Value": int64(42), + "JSONValue": `{"test": 1}`, + "NumericValue": "-12345678901234567890123456789.123456789", + "StringValue": "FooBar", + "TimestampValue": "2022-04-01T00:00:00Z", + // Commented out because it is not yet supported. + // "StringArray": []interface{}{"Foo", "Bar"}, + "BoolValue": true, + "BytesValue": "aG9nZQ==", + }, + }, + }, + }, }) if err != nil { t.Errorf("failed to save: %s", err) @@ -217,6 +255,46 @@ func TestSave(t *testing.T) { if diff := cmp.Diff(actualBazs, expectedBazs); diff != "" { t.Errorf("\n(-actual, +expected)\n%s", diff) } + + var actualAllTypes []*allTypes + err = db.client.ReadOnlyTransaction().Query(ctx, spanner.Statement{ + SQL: "SELECT * FROM AllTypes ORDER BY ID", + }).Do(func(row *spanner.Row) error { + b := new(allTypes) + if err = row.ToStruct(b); err != nil { + return fmt.Errorf("failed to populate allTypes by rows: %w", err) + } + actualAllTypes = append(actualAllTypes, b) + + return nil + }) + if err != nil { + t.Errorf("failed to select AllTypes: %s", err) + return + } + expectedNumeric, _ := new(big.Rat).SetString("-12345678901234567890123456789.123456789") + expectedAllTypes := []*allTypes{ + { + ID: "All_Type_Values", + StringValue: "FooBar", + BoolValue: true, + Int64Value: 42, + Float64Value: 3.14159, + JSONValue: spanner.NullJSON{Value: map[string]interface{}{"test": float64(1)}, Valid: true}, + BytesValue: []byte{'h', 'o', 'g', 'e'}, + TimestampValue: time.Date(2022, time.April, 1, 0, 0, 0, 0, time.UTC), + NumericValue: expectedNumeric, + DateValue: civil.Date{2022, time.April, 1}, + }, + } + if diff := cmp.Diff(actualAllTypes, expectedAllTypes, cmp.Comparer(func(x, y *big.Rat) bool { + if x == nil || y == nil { + return false + } + return x.Cmp(y) == 0 + })); diff != "" { + t.Errorf("\n(-actual, +expected)\n%s", diff) + } } func TestSortTablesByDependencies(t *testing.T) { diff --git a/internal/spanner/testdata/schema.sql b/internal/spanner/testdata/schema.sql index 459624b..c107045 100644 --- a/internal/spanner/testdata/schema.sql +++ b/internal/spanner/testdata/schema.sql @@ -24,3 +24,17 @@ CREATE TABLE Boo ( Name STRING(MAX), CONSTRAINT FK_BooBaz FOREIGN KEY (BazID) REFERENCES Baz (BazID) ) PRIMARY KEY(BooID); + +CREATE TABLE AllTypes ( + ID STRING(MAX) NOT NULL, + BoolValue BOOL, + Int64Value INT64, + Float64Value FLOAT64, + TimestampValue TIMESTAMP, + DateValue DATE, + StringValue STRING(MAX), + BytesValue BYTES(MAX), + NumericValue NUMERIC, + JSONValue JSON, + StringArray ARRAY, +) PRIMARY KEY(ID); diff --git a/internal/yaml/testdata/seeds/AllTypes.yaml b/internal/yaml/testdata/seeds/AllTypes.yaml new file mode 100644 index 0000000..7372ff8 --- /dev/null +++ b/internal/yaml/testdata/seeds/AllTypes.yaml @@ -0,0 +1,16 @@ +--- +# Values are need to be encoded as documented. +# https://cloud.google.com/spanner/docs/reference/rpc/google.spanner.v1#google.spanner.v1.TypeCode +- ID: "All_Type_Values" + BoolValue: true + BytesValue: "aG9nZQ==" # base64("hoge") + DateValue: "2022-04-01" + Float64Value: 3.14159 + Int64Value: 42 + JSONValue: '{"test": 1}' + NumericValue: "-12345678901234567890123456789.123456789" + StringValue: "FooBar" + TimestampValue: "2022-04-01T00:00:00Z" + # StringArray: + # - "Foo" + # - "Bar" diff --git a/internal/yaml/yaml_test.go b/internal/yaml/yaml_test.go index 5b2f7e8..1cd4f56 100644 --- a/internal/yaml/yaml_test.go +++ b/internal/yaml/yaml_test.go @@ -21,6 +21,26 @@ func TestLoad(t *testing.T) { } expected := []*model.Table{ + { + Name: "AllTypes", + Records: []*model.Record{ + { + Values: map[string]interface{}{ + "DateValue": "2022-04-01", + "Float64Value": float64(3.14159), + "ID": "All_Type_Values", + "Int64Value": int64(42), + "JSONValue": `{"test": 1}`, + "NumericValue": string("-12345678901234567890123456789.123456789"), + "StringValue": "FooBar", + "TimestampValue": "2022-04-01T00:00:00Z", + // "StringArray": []interface{}{"Foo", "Bar"}, + "BoolValue": true, + "BytesValue": "aG9nZQ==", + }, + }, + }, + }, { Name: "Bar", Records: []*model.Record{