|
6 | 6 | package logic
|
7 | 7 |
|
8 | 8 | import (
|
| 9 | + "context" |
| 10 | + gosql "database/sql" |
9 | 11 | "strings"
|
10 | 12 | "testing"
|
11 | 13 |
|
12 | 14 | "github.com/stretchr/testify/require"
|
| 15 | + "github.com/stretchr/testify/suite" |
| 16 | + |
| 17 | + "github.com/testcontainers/testcontainers-go" |
| 18 | + "github.com/testcontainers/testcontainers-go/wait" |
13 | 19 |
|
14 | 20 | "github.com/github/gh-ost/go/base"
|
15 | 21 | "github.com/github/gh-ost/go/binlog"
|
| 22 | + "github.com/github/gh-ost/go/mysql" |
16 | 23 | "github.com/github/gh-ost/go/sql"
|
17 | 24 | )
|
18 | 25 |
|
@@ -185,3 +192,181 @@ func TestApplierInstantDDL(t *testing.T) {
|
185 | 192 | require.Equal(t, "ALTER /* gh-ost */ TABLE `test`.`mytable` ADD INDEX (foo), ALGORITHM=INSTANT", stmt)
|
186 | 193 | })
|
187 | 194 | }
|
| 195 | + |
| 196 | +type ApplierTestSuite struct { |
| 197 | + suite.Suite |
| 198 | + |
| 199 | + mysqlContainer testcontainers.Container |
| 200 | +} |
| 201 | + |
| 202 | +func (suite *ApplierTestSuite) SetupSuite() { |
| 203 | + ctx := context.Background() |
| 204 | + req := testcontainers.ContainerRequest{ |
| 205 | + Image: "mysql:8.0", |
| 206 | + Env: map[string]string{"MYSQL_ROOT_PASSWORD": "root-password"}, |
| 207 | + WaitingFor: wait.ForLog("port: 3306 MySQL Community Server - GPL"), |
| 208 | + } |
| 209 | + |
| 210 | + mysqlContainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ |
| 211 | + ContainerRequest: req, |
| 212 | + Started: true, |
| 213 | + }) |
| 214 | + suite.Require().NoError(err) |
| 215 | + |
| 216 | + suite.mysqlContainer = mysqlContainer |
| 217 | +} |
| 218 | + |
| 219 | +func (suite *ApplierTestSuite) TeardownSuite() { |
| 220 | + ctx := context.Background() |
| 221 | + |
| 222 | + suite.Require().NoError(suite.mysqlContainer.Terminate(ctx)) |
| 223 | +} |
| 224 | + |
| 225 | +func (suite *ApplierTestSuite) SetupTest() { |
| 226 | + ctx := context.Background() |
| 227 | + |
| 228 | + rc, _, err := suite.mysqlContainer.Exec(ctx, []string{"mysql", "-uroot", "-proot-password", "-e", "CREATE DATABASE test;"}) |
| 229 | + suite.Require().NoError(err) |
| 230 | + suite.Require().Equalf(0, rc, "failed to created database: expected exit code 0, got %d", rc) |
| 231 | + |
| 232 | + rc, _, err = suite.mysqlContainer.Exec(ctx, []string{"mysql", "-uroot", "-proot-password", "-e", "CREATE TABLE test.testing (id INT, item_id INT);"}) |
| 233 | + suite.Require().NoError(err) |
| 234 | + suite.Require().Equalf(0, rc, "failed to created table: expected exit code 0, got %d", rc) |
| 235 | +} |
| 236 | + |
| 237 | +func (suite *ApplierTestSuite) TearDownTest() { |
| 238 | + ctx := context.Background() |
| 239 | + |
| 240 | + rc, _, err := suite.mysqlContainer.Exec(ctx, []string{"mysql", "-uroot", "-proot-password", "-e", "DROP DATABASE test;"}) |
| 241 | + suite.Require().NoError(err) |
| 242 | + suite.Require().Equalf(0, rc, "failed to created database: expected exit code 0, got %d", rc) |
| 243 | +} |
| 244 | + |
| 245 | +func (suite *ApplierTestSuite) TestInitDBConnections() { |
| 246 | + ctx := context.Background() |
| 247 | + |
| 248 | + host, err := suite.mysqlContainer.ContainerIP(ctx) |
| 249 | + suite.Require().NoError(err) |
| 250 | + |
| 251 | + migrationContext := base.NewMigrationContext() |
| 252 | + migrationContext.ApplierConnectionConfig = mysql.NewConnectionConfig() |
| 253 | + migrationContext.ApplierConnectionConfig.Key.Hostname = host |
| 254 | + migrationContext.ApplierConnectionConfig.Key.Port = 3306 |
| 255 | + migrationContext.ApplierConnectionConfig.User = "root" |
| 256 | + migrationContext.ApplierConnectionConfig.Password = "root-password" |
| 257 | + migrationContext.DatabaseName = "test" |
| 258 | + migrationContext.OriginalTableName = "testing" |
| 259 | + migrationContext.SetConnectionConfig("innodb") |
| 260 | + |
| 261 | + applier := NewApplier(migrationContext) |
| 262 | + defer applier.Teardown() |
| 263 | + |
| 264 | + err = applier.InitDBConnections() |
| 265 | + suite.Require().NoError(err) |
| 266 | + |
| 267 | + suite.Require().Equal("8.0.40", migrationContext.ApplierMySQLVersion) |
| 268 | + suite.Require().Equal(int64(28800), migrationContext.ApplierWaitTimeout) |
| 269 | + suite.Require().Equal("SYSTEM", migrationContext.ApplierTimeZone) |
| 270 | + |
| 271 | + suite.Require().Equal(sql.NewColumnList([]string{"id", "item_id"}), migrationContext.OriginalTableColumnsOnApplier) |
| 272 | +} |
| 273 | + |
| 274 | +func (suite *ApplierTestSuite) TestApplyDMLEventQueries() { |
| 275 | + ctx := context.Background() |
| 276 | + |
| 277 | + host, err := suite.mysqlContainer.ContainerIP(ctx) |
| 278 | + suite.Require().NoError(err) |
| 279 | + |
| 280 | + migrationContext := base.NewMigrationContext() |
| 281 | + migrationContext.ApplierConnectionConfig = mysql.NewConnectionConfig() |
| 282 | + migrationContext.ApplierConnectionConfig.Key.Hostname = host |
| 283 | + migrationContext.ApplierConnectionConfig.Key.Port = 3306 |
| 284 | + migrationContext.ApplierConnectionConfig.User = "root" |
| 285 | + migrationContext.ApplierConnectionConfig.Password = "root-password" |
| 286 | + migrationContext.DatabaseName = "test" |
| 287 | + migrationContext.OriginalTableName = "testing" |
| 288 | + migrationContext.SetConnectionConfig("innodb") |
| 289 | + |
| 290 | + migrationContext.OriginalTableColumns = sql.NewColumnList([]string{"id", "item_id"}) |
| 291 | + migrationContext.SharedColumns = sql.NewColumnList([]string{"id", "item_id"}) |
| 292 | + migrationContext.MappedSharedColumns = sql.NewColumnList([]string{"id", "item_id"}) |
| 293 | + |
| 294 | + applier := NewApplier(migrationContext) |
| 295 | + defer applier.Teardown() |
| 296 | + |
| 297 | + err = applier.InitDBConnections() |
| 298 | + suite.Require().NoError(err) |
| 299 | + |
| 300 | + rc, _, err := suite.mysqlContainer.Exec(ctx, []string{"mysql", "-uroot", "-proot-password", "-e", "CREATE TABLE test._testing_gho (id INT, item_id INT);"}) |
| 301 | + suite.Require().NoError(err) |
| 302 | + suite.Require().Equalf(0, rc, "failed to created table: expected exit code 0, got %d", rc) |
| 303 | + |
| 304 | + dmlEvents := []*binlog.BinlogDMLEvent{ |
| 305 | + { |
| 306 | + DatabaseName: "test", |
| 307 | + TableName: "testing", |
| 308 | + DML: binlog.InsertDML, |
| 309 | + NewColumnValues: sql.ToColumnValues([]interface{}{123456, 42}), |
| 310 | + }, |
| 311 | + } |
| 312 | + err = applier.ApplyDMLEventQueries(dmlEvents) |
| 313 | + suite.Require().NoError(err) |
| 314 | + |
| 315 | + // Check that the row was inserted |
| 316 | + db, err := gosql.Open("mysql", "root:root-password@tcp("+host+":3306)/test") |
| 317 | + suite.Require().NoError(err) |
| 318 | + defer db.Close() |
| 319 | + |
| 320 | + rows, err := db.Query("SELECT * FROM test._testing_gho") |
| 321 | + suite.Require().NoError(err) |
| 322 | + defer rows.Close() |
| 323 | + |
| 324 | + var count, id, item_id int |
| 325 | + for rows.Next() { |
| 326 | + err = rows.Scan(&id, &item_id) |
| 327 | + suite.Require().NoError(err) |
| 328 | + count += 1 |
| 329 | + } |
| 330 | + suite.Require().NoError(rows.Err()) |
| 331 | + |
| 332 | + suite.Require().Equal(1, count) |
| 333 | + suite.Require().Equal(123456, id) |
| 334 | + suite.Require().Equal(42, item_id) |
| 335 | + |
| 336 | + suite.Require().Equal(int64(1), migrationContext.TotalDMLEventsApplied) |
| 337 | + suite.Require().Equal(int64(0), migrationContext.RowsDeltaEstimate) |
| 338 | +} |
| 339 | + |
| 340 | +func (suite *ApplierTestSuite) TestValidateOrDropExistingTables() { |
| 341 | + ctx := context.Background() |
| 342 | + |
| 343 | + host, err := suite.mysqlContainer.ContainerIP(ctx) |
| 344 | + suite.Require().NoError(err) |
| 345 | + |
| 346 | + migrationContext := base.NewMigrationContext() |
| 347 | + migrationContext.ApplierConnectionConfig = mysql.NewConnectionConfig() |
| 348 | + migrationContext.ApplierConnectionConfig.Key.Hostname = host |
| 349 | + migrationContext.ApplierConnectionConfig.Key.Port = 3306 |
| 350 | + migrationContext.ApplierConnectionConfig.User = "root" |
| 351 | + migrationContext.ApplierConnectionConfig.Password = "root-password" |
| 352 | + migrationContext.DatabaseName = "test" |
| 353 | + migrationContext.OriginalTableName = "testing" |
| 354 | + migrationContext.SetConnectionConfig("innodb") |
| 355 | + |
| 356 | + migrationContext.OriginalTableColumns = sql.NewColumnList([]string{"id", "item_id"}) |
| 357 | + migrationContext.SharedColumns = sql.NewColumnList([]string{"id", "item_id"}) |
| 358 | + migrationContext.MappedSharedColumns = sql.NewColumnList([]string{"id", "item_id"}) |
| 359 | + |
| 360 | + applier := NewApplier(migrationContext) |
| 361 | + defer applier.Teardown() |
| 362 | + |
| 363 | + err = applier.InitDBConnections() |
| 364 | + suite.Require().NoError(err) |
| 365 | + |
| 366 | + err = applier.ValidateOrDropExistingTables() |
| 367 | + suite.Require().NoError(err) |
| 368 | +} |
| 369 | + |
| 370 | +func TestApplier(t *testing.T) { |
| 371 | + suite.Run(t, new(ApplierTestSuite)) |
| 372 | +} |
0 commit comments