Skip to content

Commit

Permalink
Added some trivial integration tests
Browse files Browse the repository at this point in the history
Just ported some very basic tests for now, looking to port more tests.
  • Loading branch information
shuhaowu committed Sep 25, 2018
1 parent c422ceb commit 78bdc7d
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 0 deletions.
51 changes: 51 additions & 0 deletions test/integration/cases/trivial_integration_tests.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
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_state_cleanup do
use_datawriter
interrupt_ghostferry_when_some_batches_are_copied

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_state_cleanup do
use_datawriter
@ghostferry.run(dumped_state)

assert_test_table_is_identical
end
end
end
128 changes: 128 additions & 0 deletions test/integration/ruby/ghostferry_integration/test_case.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
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)
@logger.level = Logger::INFO
@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

@datawriter = DataWriter.new(@dbs.source_db_config, logger: @logger)
end

def after_teardown
terminate_datawriter_and_ghostferry
end

######################
# Test Setup Helpers #
######################

# This should be a no op if ghostferry and datawriter have already been
# stopped.
def terminate_datawriter_and_ghostferry
@datawriter.stop
@datawriter.join

@ghostferry.stop_and_cleanup
@datawriter = DataWriter.new(@dbs.source_db_config, logger: @logger)
end

# This is useful if we need to run Ghostferry multiple times in during a
# single run, such as during an interrupt + resume cycle.
def with_state_cleanup
@datawriter = DataWriter.new(@dbs.source_db_config, logger: @logger)
@ghostferry.reset_state
yield
terminate_datawriter_and_ghostferry
end

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 use_datawriter(&on_write)
start_datawriter_with_ghostferry(&on_write)
stop_datawriter_during_cutover
end

def interrupt_ghostferry_when_some_batches_are_copied(batches: 2)
batches_written = 0
@ghostferry.on_status(Ghostferry::Status::AFTER_ROW_COPY) do
batches_written += 1
if batches_written >= batches
@ghostferry.send_signal("TERM")
end
end
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
end
end
6 changes: 6 additions & 0 deletions test/integration/test.rb
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"

0 comments on commit 78bdc7d

Please sign in to comment.