-
Notifications
You must be signed in to change notification settings - Fork 65
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added some trivial integration tests
Ported some very basic tests as well as created two interrupt/resume tests.
- Loading branch information
Showing
3 changed files
with
229 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
require "json" | ||
require "ghostferry_integration" | ||
|
||
class TrivialIntegrationTests < GhostferryIntegration::TestCase | ||
def ghostferry_main_path | ||
"go/minimal.go" | ||
end | ||
|
||
def test_copy_data_without_any_writes_to_source | ||
@dbs.seed_simple_database_with_single_table | ||
@ghostferry.run | ||
assert_test_table_is_identical | ||
end | ||
|
||
def test_copy_data_with_writes_to_source | ||
use_datawriter | ||
|
||
@dbs.seed_simple_database_with_single_table | ||
|
||
@ghostferry.run | ||
assert_test_table_is_identical | ||
end | ||
|
||
def test_interrupt_resume_with_writes_to_source | ||
@dbs.seed_simple_database_with_single_table | ||
|
||
dumped_state = nil | ||
with_isolated_setup_and_teardown do | ||
use_datawriter | ||
|
||
batches_written = 0 | ||
@ghostferry.on_status(Ghostferry::Status::AFTER_ROW_COPY) do | ||
batches_written += 1 | ||
if batches_written >= 2 | ||
@ghostferry.send_signal("TERM") | ||
end | ||
end | ||
|
||
dumped_state = @ghostferry.run_expecting_interrupt | ||
assert_basic_fields_exist_in_dumped_state(dumped_state) | ||
end | ||
|
||
# We want to write some data to the source database while Ghostferry is down | ||
# to verify that it is copied over. | ||
5.times do | ||
@datawriter.insert_data(@dbs.source) | ||
@datawriter.update_data(@dbs.source) | ||
@datawriter.delete_data(@dbs.source) | ||
end | ||
|
||
with_isolated_setup_and_teardown do | ||
use_datawriter | ||
@ghostferry.run(dumped_state) | ||
|
||
assert_test_table_is_identical | ||
end | ||
end | ||
|
||
def test_interrupt_resume_when_table_has_completed | ||
@dbs.seed_simple_database_with_single_table | ||
dumped_state = nil | ||
|
||
results = @dbs.source.query("SELECT COUNT(*) as cnt FROM #{GhostferryIntegration::DbManager::DEFAULT_FULL_TABLE_NAME}") | ||
rows = results.first["cnt"] | ||
|
||
with_isolated_setup_and_teardown do | ||
use_datawriter | ||
|
||
@ghostferry.on_status(Ghostferry::Status::ROW_COPY_COMPLETED) do | ||
@ghostferry.send_signal("TERM") | ||
end | ||
|
||
dumped_state = @ghostferry.run_expecting_interrupt | ||
assert_basic_fields_exist_in_dumped_state(dumped_state) | ||
end | ||
|
||
with_isolated_setup_and_teardown do | ||
use_datawriter | ||
@ghostferry.run(dumped_state) | ||
|
||
assert_test_table_is_identical | ||
end | ||
end | ||
end |
139 changes: 139 additions & 0 deletions
139
test/integration/ruby/ghostferry_integration/test_case.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
require "logger" | ||
require "minitest" | ||
require "minitest/hooks/test" | ||
|
||
module GhostferryIntegration | ||
class TestCase < Minitest::Test | ||
include Minitest::Hooks | ||
include GhostferryIntegration | ||
|
||
############## | ||
# Test Hooks # | ||
############## | ||
|
||
def before_all | ||
@logger = Logger.new(STDOUT) | ||
if ENV["DEBUG"] == "1" | ||
@logger.level = Logger::DEBUG | ||
else | ||
@logger.level = Logger::INFO | ||
end | ||
@ghostferry = Ghostferry.new(ghostferry_main_path, logger: @logger) | ||
end | ||
|
||
def after_all | ||
@ghostferry.remove_binary | ||
end | ||
|
||
def before_setup | ||
@dbs = DbManager.new(logger: @logger) | ||
@dbs.reset_data | ||
|
||
setup_ghostferry_datawriter | ||
end | ||
|
||
def after_teardown | ||
teardown_ghostferry_datawriter | ||
end | ||
|
||
###################### | ||
# Test Setup Helpers # | ||
###################### | ||
|
||
# If multiple Ghostferry runs are needed within a single test, such as in | ||
# the case of interrupt/resume testing, we will need to wrap each | ||
# @ghostferry.run within a block for this method. | ||
# | ||
# This method doesn't destroy the database state like before_setup and | ||
# after_teardown does. | ||
def with_isolated_setup_and_teardown | ||
setup_ghostferry_datawriter | ||
yield | ||
teardown_ghostferry_datawriter | ||
end | ||
|
||
# This setup the datawriter to start when Ghostferry start and stop when | ||
# cutover is about to take place. | ||
# | ||
# The on_write block is called everytime the datawriter writes a row with | ||
# the argument op, id. | ||
# | ||
# op: "INSERT"/"UPDATE"/"DELETE" | ||
# id: the primary id of the row inserted | ||
def use_datawriter(&on_write) | ||
start_datawriter_with_ghostferry(&on_write) | ||
stop_datawriter_during_cutover | ||
end | ||
|
||
##################### | ||
# Assertion Helpers # | ||
##################### | ||
|
||
def assert_test_table_is_identical | ||
source, target = @dbs.source_and_target_table_metrics | ||
|
||
assert source[DbManager::DEFAULT_FULL_TABLE_NAME][:row_count] > 0 | ||
assert target[DbManager::DEFAULT_FULL_TABLE_NAME][:row_count] > 0 | ||
|
||
assert_equal( | ||
source[DbManager::DEFAULT_FULL_TABLE_NAME][:checksum], | ||
target[DbManager::DEFAULT_FULL_TABLE_NAME][:checksum], | ||
) | ||
|
||
assert_equal( | ||
source[DbManager::DEFAULT_FULL_TABLE_NAME][:sample_row], | ||
target[DbManager::DEFAULT_FULL_TABLE_NAME][:sample_row], | ||
) | ||
end | ||
|
||
# Use this method to assert the validity of the structure of the dumped | ||
# state. | ||
# | ||
# To actually assert the validity of the data within the dumped state, you | ||
# have to do it manually. | ||
def assert_basic_fields_exist_in_dumped_state(dumped_state) | ||
refute dumped_state.nil? | ||
refute dumped_state["GhostferryVersion"].nil? | ||
refute dumped_state["LastKnownTableSchemaCache"].nil? | ||
refute dumped_state["LastSuccessfulPrimaryKeys"].nil? | ||
refute dumped_state["CompletedTables"].nil? | ||
refute dumped_state["LastWrittenBinlogPosition"].nil? | ||
end | ||
|
||
protected | ||
def ghostferry_main_path | ||
raise NotImplementedError | ||
end | ||
|
||
private | ||
|
||
def start_datawriter_with_ghostferry(&on_write) | ||
@ghostferry.on_status(Ghostferry::Status::READY) do | ||
@datawriter.start(&on_write) | ||
end | ||
end | ||
|
||
def stop_datawriter_during_cutover | ||
@ghostferry.on_status(Ghostferry::Status::ROW_COPY_COMPLETED) do | ||
# At the start of the cutover phase, we have to set the database to | ||
# read-only. This is done by stopping the datawriter. | ||
@datawriter.stop | ||
@datawriter.join | ||
end | ||
end | ||
|
||
def setup_ghostferry_datawriter | ||
@ghostferry.reset_state | ||
@datawriter = DataWriter.new(@dbs.source_db_config, logger: @logger) | ||
end | ||
|
||
# This should be a no op if ghostferry and datawriter have already been | ||
# stopped. | ||
def teardown_ghostferry_datawriter | ||
@datawriter.stop | ||
@datawriter.join | ||
|
||
@ghostferry.stop_and_cleanup | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
require "minitest/autorun" | ||
|
||
ruby_path = File.join(File.absolute_path(File.dirname(__FILE__)), "ruby") | ||
$LOAD_PATH.unshift(ruby_path) unless $LOAD_PATH.include?(ruby_path) | ||
|
||
require_relative "cases/trivial_integration_tests" |