diff --git a/.github/workflows/integration-tests-against-emulator.yaml b/.github/workflows/integration-tests-against-emulator.yaml index b28ec38740..96688fda07 100644 --- a/.github/workflows/integration-tests-against-emulator.yaml +++ b/.github/workflows/integration-tests-against-emulator.yaml @@ -31,7 +31,7 @@ jobs: services: spanner_emulator: - image: gcr.io/cloud-spanner-emulator/emulator:1.2.0 + image: gcr.io/cloud-spanner-emulator/emulator:1.4.0 ports: - 9010:9010 - 9020:9020 diff --git a/test_data/mysqldump.test.out b/test_data/mysqldump.test.out index 06c6980718..3a44f3296e 100644 --- a/test_data/mysqldump.test.out +++ b/test_data/mysqldump.test.out @@ -88,6 +88,32 @@ LOCK TABLES `products` WRITE; INSERT INTO `products` VALUES ('abc-123','Blue suede shoes',141.99,'2020-06-06'),('axd-673','Antique typewriter',99.99,'2020-06-07'),('zxi-631','Glass vase',55.50,'2020-06-10'); /*!40000 ALTER TABLE `products` ENABLE KEYS */; UNLOCK TABLES; + +-- +-- Table structure for table `customers` +-- + +DROP TABLE IF EXISTS `customers`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `customers` ( + `c_id` varchar(20) NOT NULL, + `customer_profile` json DEFAULT NULL, + PRIMARY KEY (`c_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `customers` +-- + +LOCK TABLES `customers` WRITE; +/*!40000 ALTER TABLE `customers` DISABLE KEYS */; +INSERT INTO `customers` VALUES +('svd-124','{"first_name": "Lola", "last_name": "Dog", "location": "NYC", "online" : true, "friends" : 547}'), +('tel-595','{"first_name": "Ernie", "status": "Looking for treats", "location" : "Brooklyn"}'); +/*!40000 ALTER TABLE `customers` ENABLE KEYS */; +UNLOCK TABLES; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; diff --git a/testing/mysql/integration_test.go b/testing/mysql/integration_test.go index c1367183f5..c1979b6b88 100644 --- a/testing/mysql/integration_test.go +++ b/testing/mysql/integration_test.go @@ -16,18 +16,21 @@ package mysql_test import ( "context" + "encoding/json" "flag" "fmt" "io/ioutil" "log" "os" "path/filepath" + "strings" "testing" "time" "github.com/cloudspannerecosystem/harbourbridge/common/constants" "github.com/cloudspannerecosystem/harbourbridge/common/utils" "github.com/cloudspannerecosystem/harbourbridge/testing/common" + "github.com/stretchr/testify/assert" "cloud.google.com/go/spanner" database "cloud.google.com/go/spanner/admin/database/apiv1" @@ -123,7 +126,7 @@ func TestIntegration_MYSQLDUMP_Command(t *testing.T) { // Drop the database later. defer dropDatabase(t, dbURI) - checkResults(t, dbURI) + checkResults(t, dbURI, false) } func TestIntegration_MYSQL_SchemaAndDataSubcommand(t *testing.T) { @@ -148,7 +151,7 @@ func TestIntegration_MYSQL_SchemaAndDataSubcommand(t *testing.T) { // Drop the database later. defer dropDatabase(t, dbURI) - checkResults(t, dbURI) + checkResults(t, dbURI, true) } func TestIntegration_MYSQL_Command(t *testing.T) { @@ -171,7 +174,7 @@ func TestIntegration_MYSQL_Command(t *testing.T) { // Drop the database later. defer dropDatabase(t, dbURI) - checkResults(t, dbURI) + checkResults(t, dbURI, true) } func TestIntegration_MySQLInterleaveTable_DataOnlyWithSessionFile(t *testing.T) { @@ -185,7 +188,7 @@ func TestIntegration_MySQLInterleaveTable_DataOnlyWithSessionFile(t *testing.T) dbURI := fmt.Sprintf("projects/%s/instances/%s/databases/%s", projectID, instanceID, dbName) runDataOnlySubcommandForSessionFile(t, dbName, dbURI, sessionFile) defer dropDatabase(t, dbURI) - checkResults(t, dbURI) + checkResults(t, dbURI, true) } func runSchemaOnly(t *testing.T, dbName, filePrefix, sessionFile, dumpFilePath string) { @@ -241,7 +244,7 @@ func TestIntegration_MySQLDUMP_DataOnly(t *testing.T) { dbURI := fmt.Sprintf("projects/%s/instances/%s/databases/%s", projectID, instanceID, dbName) runDataOnly(t, dbName, dbURI, filePrefix, sessionFile, dumpFilePath) defer dropDatabase(t, dbURI) - checkResults(t, dbURI) + checkResults(t, dbURI, false) } func runSchemaSubcommand(t *testing.T, dbName, filePrefix, sessionFile, dumpFilePath string) { @@ -314,7 +317,7 @@ func TestIntegration_MySQLDUMP_DataSubcommand(t *testing.T) { dbURI := fmt.Sprintf("projects/%s/instances/%s/databases/%s", projectID, instanceID, dbName) runDataSubcommand(t, dbName, dbURI, filePrefix, sessionFile, dumpFilePath) defer dropDatabase(t, dbURI) - checkResults(t, dbURI) + checkResults(t, dbURI, false) } func TestIntegration_MySQLDUMP_SchemaAndDataSubcommand(t *testing.T) { @@ -329,10 +332,10 @@ func TestIntegration_MySQLDUMP_SchemaAndDataSubcommand(t *testing.T) { dbURI := fmt.Sprintf("projects/%s/instances/%s/databases/%s", projectID, instanceID, dbName) runSchemaAndDataSubcommand(t, dbName, dbURI, filePrefix, dumpFilePath) defer dropDatabase(t, dbURI) - checkResults(t, dbURI) + checkResults(t, dbURI, false) } -func checkResults(t *testing.T, dbURI string) { +func checkResults(t *testing.T, dbURI string, skipJson bool) { // Make a query to check results. client, err := spanner.NewClient(ctx, dbURI) if err != nil { @@ -341,6 +344,9 @@ func checkResults(t *testing.T, dbURI string) { defer client.Close() checkBigInt(ctx, t, client) + if !skipJson { + checkJson(ctx, t, client, dbURI) + } } func checkBigInt(ctx context.Context, t *testing.T, client *spanner.Client) { @@ -364,6 +370,36 @@ func checkBigInt(ctx context.Context, t *testing.T, client *spanner.Client) { } } +func checkJson(ctx context.Context, t *testing.T, client *spanner.Client, dbURI string) { + resp, err := databaseAdmin.GetDatabaseDdl(ctx, &databasepb.GetDatabaseDdlRequest{Database: dbURI}) + if err != nil { + t.Fatalf("Could not read DDL from database %s: %v", dbURI, err) + } + for _, stmt := range resp.Statements { + if strings.Contains(stmt, "CREATE TABLE customers") { + assert.True(t, strings.Contains(stmt, "customer_profile JSON")) + } + } + got_profile := spanner.NullJSON{} + iter := client.Single().Read(ctx, "customers", spanner.Key{"tel-595"}, []string{"customer_profile"}) + defer iter.Stop() + for { + row, err := iter.Next() + if err == iterator.Done { + break + } + if err != nil { + t.Fatal(err) + } + if err := row.Columns(&got_profile); err != nil { + t.Fatal(err) + } + } + want_profile := spanner.NullJSON{Valid: true} + json.Unmarshal([]byte("{\"first_name\": \"Ernie\", \"status\": \"Looking for treats\", \"location\" : \"Brooklyn\"}"), &want_profile.Value) + assert.Equal(t, got_profile, want_profile) +} + func onlyRunForEmulatorTest(t *testing.T) { if os.Getenv("SPANNER_EMULATOR_HOST") == "" { t.Skip("Skipping tests only running against the emulator.")