Skip to content

Commit 23e5ed6

Browse files
Add more test coverage.
1 parent b771eae commit 23e5ed6

File tree

2 files changed

+196
-40
lines changed

2 files changed

+196
-40
lines changed

go/logic/applier_test.go

Lines changed: 153 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import (
1919

2020
"github.com/github/gh-ost/go/base"
2121
"github.com/github/gh-ost/go/binlog"
22-
"github.com/github/gh-ost/go/mysql"
2322
"github.com/github/gh-ost/go/sql"
2423
)
2524

@@ -198,6 +197,7 @@ type ApplierTestSuite struct {
198197
suite.Suite
199198

200199
mysqlContainer testcontainers.Container
200+
db *gosql.DB
201201
}
202202

203203
func (suite *ApplierTestSuite) SetupSuite() {
@@ -216,49 +216,50 @@ func (suite *ApplierTestSuite) SetupSuite() {
216216
suite.Require().NoError(err)
217217

218218
suite.mysqlContainer = mysqlContainer
219+
220+
dsn, err := GetDSN(ctx, mysqlContainer)
221+
suite.Require().NoError(err)
222+
223+
db, err := gosql.Open("mysql", dsn)
224+
suite.Require().NoError(err)
225+
226+
suite.db = db
219227
}
220228

221229
func (suite *ApplierTestSuite) TeardownSuite() {
222230
ctx := context.Background()
223231

224-
suite.Require().NoError(suite.mysqlContainer.Terminate(ctx))
232+
suite.Assert().NoError(suite.db.Close())
233+
suite.Assert().NoError(suite.mysqlContainer.Terminate(ctx))
225234
}
226235

227236
func (suite *ApplierTestSuite) SetupTest() {
228237
ctx := context.Background()
229238

230-
rc, _, err := suite.mysqlContainer.Exec(ctx, []string{"mysql", "-uroot", "-proot-password", "-e", "CREATE DATABASE test;"})
231-
suite.Require().NoError(err)
232-
suite.Require().Equalf(0, rc, "failed to created database: expected exit code 0, got %d", rc)
233-
234-
rc, _, err = suite.mysqlContainer.Exec(ctx, []string{"mysql", "-uroot", "-proot-password", "-e", "CREATE TABLE test.testing (id INT, item_id INT);"})
239+
_, err := suite.db.ExecContext(ctx, "CREATE DATABASE test")
235240
suite.Require().NoError(err)
236-
suite.Require().Equalf(0, rc, "failed to created table: expected exit code 0, got %d", rc)
237241
}
238242

239243
func (suite *ApplierTestSuite) TearDownTest() {
240244
ctx := context.Background()
241245

242-
rc, _, err := suite.mysqlContainer.Exec(ctx, []string{"mysql", "-uroot", "-proot-password", "-e", "DROP DATABASE test;"})
246+
_, err := suite.db.ExecContext(ctx, "DROP DATABASE test")
243247
suite.Require().NoError(err)
244-
suite.Require().Equalf(0, rc, "failed to created database: expected exit code 0, got %d", rc)
245248
}
246249

247250
func (suite *ApplierTestSuite) TestInitDBConnections() {
248251
ctx := context.Background()
249252

250-
host, err := suite.mysqlContainer.Host(ctx)
253+
var err error
254+
255+
_, err = suite.db.ExecContext(ctx, "CREATE TABLE test.testing (id INT, item_id INT);")
251256
suite.Require().NoError(err)
252257

253-
port, err := suite.mysqlContainer.MappedPort(ctx, "3306")
258+
connectionConfig, err := GetConnectionConfig(ctx, suite.mysqlContainer)
254259
suite.Require().NoError(err)
255260

256261
migrationContext := base.NewMigrationContext()
257-
migrationContext.ApplierConnectionConfig = mysql.NewConnectionConfig()
258-
migrationContext.ApplierConnectionConfig.Key.Hostname = host
259-
migrationContext.ApplierConnectionConfig.Key.Port = port.Int()
260-
migrationContext.ApplierConnectionConfig.User = "root"
261-
migrationContext.ApplierConnectionConfig.Password = "root-password"
262+
migrationContext.ApplierConnectionConfig = connectionConfig
262263
migrationContext.DatabaseName = "test"
263264
migrationContext.SkipPortValidation = true
264265
migrationContext.OriginalTableName = "testing"
@@ -280,18 +281,19 @@ func (suite *ApplierTestSuite) TestInitDBConnections() {
280281
func (suite *ApplierTestSuite) TestApplyDMLEventQueries() {
281282
ctx := context.Background()
282283

283-
host, err := suite.mysqlContainer.Host(ctx)
284+
var err error
285+
286+
_, err = suite.db.ExecContext(ctx, "CREATE TABLE test.testing (id INT, item_id INT);")
287+
suite.Require().NoError(err)
288+
289+
_, err = suite.db.ExecContext(ctx, "CREATE TABLE test._testing_gho (id INT, item_id INT);")
284290
suite.Require().NoError(err)
285291

286-
port, err := suite.mysqlContainer.MappedPort(ctx, "3306")
292+
connectionConfig, err := GetConnectionConfig(ctx, suite.mysqlContainer)
287293
suite.Require().NoError(err)
288294

289295
migrationContext := base.NewMigrationContext()
290-
migrationContext.ApplierConnectionConfig = mysql.NewConnectionConfig()
291-
migrationContext.ApplierConnectionConfig.Key.Hostname = host
292-
migrationContext.ApplierConnectionConfig.Key.Port = port.Int()
293-
migrationContext.ApplierConnectionConfig.User = "root"
294-
migrationContext.ApplierConnectionConfig.Password = "root-password"
296+
migrationContext.ApplierConnectionConfig = connectionConfig
295297
migrationContext.DatabaseName = "test"
296298
migrationContext.SkipPortValidation = true
297299
migrationContext.OriginalTableName = "testing"
@@ -307,10 +309,6 @@ func (suite *ApplierTestSuite) TestApplyDMLEventQueries() {
307309
err = applier.InitDBConnections()
308310
suite.Require().NoError(err)
309311

310-
rc, _, err := suite.mysqlContainer.Exec(ctx, []string{"mysql", "-uroot", "-proot-password", "-e", "CREATE TABLE test._testing_gho (id INT, item_id INT);"})
311-
suite.Require().NoError(err)
312-
suite.Require().Equalf(0, rc, "failed to created table: expected exit code 0, got %d", rc)
313-
314312
dmlEvents := []*binlog.BinlogDMLEvent{
315313
{
316314
DatabaseName: "test",
@@ -323,11 +321,7 @@ func (suite *ApplierTestSuite) TestApplyDMLEventQueries() {
323321
suite.Require().NoError(err)
324322

325323
// Check that the row was inserted
326-
db, err := gosql.Open("mysql", "root:root-password@tcp("+host+":3306)/test")
327-
suite.Require().NoError(err)
328-
defer db.Close()
329-
330-
rows, err := db.Query("SELECT * FROM test._testing_gho")
324+
rows, err := suite.db.Query("SELECT * FROM test._testing_gho")
331325
suite.Require().NoError(err)
332326
defer rows.Close()
333327

@@ -350,18 +344,16 @@ func (suite *ApplierTestSuite) TestApplyDMLEventQueries() {
350344
func (suite *ApplierTestSuite) TestValidateOrDropExistingTables() {
351345
ctx := context.Background()
352346

353-
host, err := suite.mysqlContainer.Host(ctx)
347+
var err error
348+
349+
_, err = suite.db.ExecContext(ctx, "CREATE TABLE test.testing (id INT, item_id INT);")
354350
suite.Require().NoError(err)
355351

356-
port, err := suite.mysqlContainer.MappedPort(ctx, "3306")
352+
connectionConfig, err := GetConnectionConfig(ctx, suite.mysqlContainer)
357353
suite.Require().NoError(err)
358354

359355
migrationContext := base.NewMigrationContext()
360-
migrationContext.ApplierConnectionConfig = mysql.NewConnectionConfig()
361-
migrationContext.ApplierConnectionConfig.Key.Hostname = host
362-
migrationContext.ApplierConnectionConfig.Key.Port = port.Int()
363-
migrationContext.ApplierConnectionConfig.User = "root"
364-
migrationContext.ApplierConnectionConfig.Password = "root-password"
356+
migrationContext.ApplierConnectionConfig = connectionConfig
365357
migrationContext.DatabaseName = "test"
366358
migrationContext.SkipPortValidation = true
367359
migrationContext.OriginalTableName = "testing"
@@ -381,6 +373,127 @@ func (suite *ApplierTestSuite) TestValidateOrDropExistingTables() {
381373
suite.Require().NoError(err)
382374
}
383375

376+
func (suite *ApplierTestSuite) TestValidateOrDropExistingTablesWithGhostTableExisting() {
377+
ctx := context.Background()
378+
379+
var err error
380+
381+
_, err = suite.db.ExecContext(ctx, "CREATE TABLE test.testing (id INT, item_id INT);")
382+
suite.Require().NoError(err)
383+
384+
_, err = suite.db.ExecContext(ctx, "CREATE TABLE test._testing_gho (id INT, item_id INT);")
385+
suite.Require().NoError(err)
386+
387+
connectionConfig, err := GetConnectionConfig(ctx, suite.mysqlContainer)
388+
suite.Require().NoError(err)
389+
390+
migrationContext := base.NewMigrationContext()
391+
migrationContext.ApplierConnectionConfig = connectionConfig
392+
migrationContext.DatabaseName = "test"
393+
migrationContext.SkipPortValidation = true
394+
migrationContext.OriginalTableName = "testing"
395+
migrationContext.SetConnectionConfig("innodb")
396+
397+
migrationContext.OriginalTableColumns = sql.NewColumnList([]string{"id", "item_id"})
398+
migrationContext.SharedColumns = sql.NewColumnList([]string{"id", "item_id"})
399+
migrationContext.MappedSharedColumns = sql.NewColumnList([]string{"id", "item_id"})
400+
401+
applier := NewApplier(migrationContext)
402+
defer applier.Teardown()
403+
404+
err = applier.InitDBConnections()
405+
suite.Require().NoError(err)
406+
407+
err = applier.ValidateOrDropExistingTables()
408+
suite.Require().Error(err)
409+
suite.Require().EqualError(err, "Table `_testing_gho` already exists. Panicking. Use --initially-drop-ghost-table to force dropping it, though I really prefer that you drop it or rename it away")
410+
}
411+
412+
func (suite *ApplierTestSuite) TestValidateOrDropExistingTablesWithGhostTableExistingAndInitiallyDropGhostTableSet() {
413+
ctx := context.Background()
414+
415+
var err error
416+
417+
_, err = suite.db.ExecContext(ctx, "CREATE TABLE test.testing (id INT, item_id INT);")
418+
suite.Require().NoError(err)
419+
420+
_, err = suite.db.ExecContext(ctx, "CREATE TABLE test._testing_gho (id INT, item_id INT);")
421+
suite.Require().NoError(err)
422+
423+
connectionConfig, err := GetConnectionConfig(ctx, suite.mysqlContainer)
424+
suite.Require().NoError(err)
425+
426+
migrationContext := base.NewMigrationContext()
427+
migrationContext.ApplierConnectionConfig = connectionConfig
428+
migrationContext.DatabaseName = "test"
429+
migrationContext.SkipPortValidation = true
430+
migrationContext.OriginalTableName = "testing"
431+
migrationContext.SetConnectionConfig("innodb")
432+
433+
migrationContext.InitiallyDropGhostTable = true
434+
435+
applier := NewApplier(migrationContext)
436+
defer applier.Teardown()
437+
438+
err = applier.InitDBConnections()
439+
suite.Require().NoError(err)
440+
441+
err = applier.ValidateOrDropExistingTables()
442+
suite.Require().NoError(err)
443+
444+
// Check that the ghost table was dropped
445+
var tableName string
446+
err = suite.db.QueryRow("SHOW TABLES IN test LIKE '_testing_gho'").Scan(&tableName)
447+
suite.Require().Error(err)
448+
suite.Require().Equal(gosql.ErrNoRows, err)
449+
}
450+
451+
func (suite *ApplierTestSuite) TestCreateGhostTable() {
452+
ctx := context.Background()
453+
454+
var err error
455+
456+
_, err = suite.db.ExecContext(ctx, "CREATE TABLE test.testing (id INT, item_id INT);")
457+
suite.Require().NoError(err)
458+
459+
connectionConfig, err := GetConnectionConfig(ctx, suite.mysqlContainer)
460+
suite.Require().NoError(err)
461+
462+
migrationContext := base.NewMigrationContext()
463+
migrationContext.ApplierConnectionConfig = connectionConfig
464+
migrationContext.DatabaseName = "test"
465+
migrationContext.SkipPortValidation = true
466+
migrationContext.OriginalTableName = "testing"
467+
migrationContext.SetConnectionConfig("innodb")
468+
469+
migrationContext.OriginalTableColumns = sql.NewColumnList([]string{"id", "item_id"})
470+
migrationContext.SharedColumns = sql.NewColumnList([]string{"id", "item_id"})
471+
migrationContext.MappedSharedColumns = sql.NewColumnList([]string{"id", "item_id"})
472+
473+
migrationContext.InitiallyDropGhostTable = true
474+
475+
applier := NewApplier(migrationContext)
476+
defer applier.Teardown()
477+
478+
err = applier.InitDBConnections()
479+
suite.Require().NoError(err)
480+
481+
err = applier.CreateGhostTable()
482+
suite.Require().NoError(err)
483+
484+
// Check that the ghost table was created
485+
var tableName string
486+
err = suite.db.QueryRow("SHOW TABLES IN test LIKE '_testing_gho'").Scan(&tableName)
487+
suite.Require().NoError(err)
488+
suite.Require().Equal("_testing_gho", tableName)
489+
490+
// Check that the ghost table has the same columns as the original table
491+
var createDDL string
492+
err = suite.db.QueryRow("SHOW CREATE TABLE test._testing_gho").Scan(&tableName, &createDDL)
493+
suite.Require().NoError(err)
494+
suite.Require().Equal("CREATE TABLE `_testing_gho` (\n `id` int DEFAULT NULL,\n `item_id` int DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", createDDL)
495+
}
496+
384497
func TestApplier(t *testing.T) {
385498
suite.Run(t, new(ApplierTestSuite))
386499
}

go/logic/test_utils.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package logic
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/github/gh-ost/go/mysql"
8+
"github.com/testcontainers/testcontainers-go"
9+
)
10+
11+
func GetConnectionConfig(ctx context.Context, container testcontainers.Container) (*mysql.ConnectionConfig, error) {
12+
host, err := container.Host(ctx)
13+
if err != nil {
14+
return nil, err
15+
}
16+
17+
port, err := container.MappedPort(ctx, "3306")
18+
if err != nil {
19+
return nil, err
20+
}
21+
22+
connectionConfig := mysql.NewConnectionConfig()
23+
connectionConfig.Key.Hostname = host
24+
connectionConfig.Key.Port = port.Int()
25+
connectionConfig.User = "root"
26+
connectionConfig.Password = "root-password"
27+
28+
return connectionConfig, nil
29+
}
30+
31+
func GetDSN(ctx context.Context, container testcontainers.Container) (string, error) {
32+
host, err := container.Host(ctx)
33+
if err != nil {
34+
return "", err
35+
}
36+
37+
port, err := container.MappedPort(ctx, "3306")
38+
if err != nil {
39+
return "", err
40+
}
41+
42+
return fmt.Sprintf("root:root-password@tcp(%s:%s)/", host, port), nil
43+
}

0 commit comments

Comments
 (0)