andyjeffries-rubyrep 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +83 -0
- data/License.txt +20 -0
- data/Manifest.txt +151 -0
- data/README.txt +37 -0
- data/bin/rubyrep +8 -0
- data/lib/rubyrep.rb +72 -0
- data/lib/rubyrep/base_runner.rb +195 -0
- data/lib/rubyrep/command_runner.rb +144 -0
- data/lib/rubyrep/committers/buffered_committer.rb +151 -0
- data/lib/rubyrep/committers/committers.rb +152 -0
- data/lib/rubyrep/configuration.rb +275 -0
- data/lib/rubyrep/connection_extenders/connection_extenders.rb +165 -0
- data/lib/rubyrep/connection_extenders/jdbc_extender.rb +65 -0
- data/lib/rubyrep/connection_extenders/mysql_extender.rb +59 -0
- data/lib/rubyrep/connection_extenders/postgresql_extender.rb +277 -0
- data/lib/rubyrep/database_proxy.rb +52 -0
- data/lib/rubyrep/direct_table_scan.rb +75 -0
- data/lib/rubyrep/generate_runner.rb +105 -0
- data/lib/rubyrep/initializer.rb +39 -0
- data/lib/rubyrep/log_helper.rb +30 -0
- data/lib/rubyrep/logged_change.rb +160 -0
- data/lib/rubyrep/logged_change_loader.rb +197 -0
- data/lib/rubyrep/noisy_connection.rb +80 -0
- data/lib/rubyrep/proxied_table_scan.rb +171 -0
- data/lib/rubyrep/proxy_block_cursor.rb +145 -0
- data/lib/rubyrep/proxy_connection.rb +431 -0
- data/lib/rubyrep/proxy_cursor.rb +44 -0
- data/lib/rubyrep/proxy_row_cursor.rb +43 -0
- data/lib/rubyrep/proxy_runner.rb +89 -0
- data/lib/rubyrep/replication_difference.rb +100 -0
- data/lib/rubyrep/replication_extenders/mysql_replication.rb +271 -0
- data/lib/rubyrep/replication_extenders/postgresql_replication.rb +236 -0
- data/lib/rubyrep/replication_extenders/replication_extenders.rb +26 -0
- data/lib/rubyrep/replication_helper.rb +142 -0
- data/lib/rubyrep/replication_initializer.rb +327 -0
- data/lib/rubyrep/replication_run.rb +142 -0
- data/lib/rubyrep/replication_runner.rb +166 -0
- data/lib/rubyrep/replicators/replicators.rb +42 -0
- data/lib/rubyrep/replicators/two_way_replicator.rb +361 -0
- data/lib/rubyrep/scan_progress_printers/progress_bar.rb +65 -0
- data/lib/rubyrep/scan_progress_printers/scan_progress_printers.rb +65 -0
- data/lib/rubyrep/scan_report_printers/scan_detail_reporter.rb +111 -0
- data/lib/rubyrep/scan_report_printers/scan_report_printers.rb +67 -0
- data/lib/rubyrep/scan_report_printers/scan_summary_reporter.rb +75 -0
- data/lib/rubyrep/scan_runner.rb +25 -0
- data/lib/rubyrep/session.rb +230 -0
- data/lib/rubyrep/sync_helper.rb +121 -0
- data/lib/rubyrep/sync_runner.rb +31 -0
- data/lib/rubyrep/syncers/syncers.rb +112 -0
- data/lib/rubyrep/syncers/two_way_syncer.rb +174 -0
- data/lib/rubyrep/table_scan.rb +54 -0
- data/lib/rubyrep/table_scan_helper.rb +46 -0
- data/lib/rubyrep/table_sorter.rb +70 -0
- data/lib/rubyrep/table_spec_resolver.rb +142 -0
- data/lib/rubyrep/table_sync.rb +90 -0
- data/lib/rubyrep/task_sweeper.rb +77 -0
- data/lib/rubyrep/trigger_mode_switcher.rb +63 -0
- data/lib/rubyrep/type_casting_cursor.rb +31 -0
- data/lib/rubyrep/uninstall_runner.rb +93 -0
- data/lib/rubyrep/version.rb +9 -0
- data/rubyrep +8 -0
- data/rubyrep.bat +4 -0
- data/setup.rb +1585 -0
- data/spec/base_runner_spec.rb +218 -0
- data/spec/buffered_committer_spec.rb +274 -0
- data/spec/command_runner_spec.rb +145 -0
- data/spec/committers_spec.rb +178 -0
- data/spec/configuration_spec.rb +203 -0
- data/spec/connection_extender_interface_spec.rb +141 -0
- data/spec/connection_extenders_registration_spec.rb +164 -0
- data/spec/database_proxy_spec.rb +48 -0
- data/spec/database_rake_spec.rb +40 -0
- data/spec/db_specific_connection_extenders_spec.rb +34 -0
- data/spec/db_specific_replication_extenders_spec.rb +38 -0
- data/spec/direct_table_scan_spec.rb +61 -0
- data/spec/dolphins.jpg +0 -0
- data/spec/generate_runner_spec.rb +84 -0
- data/spec/initializer_spec.rb +46 -0
- data/spec/log_helper_spec.rb +39 -0
- data/spec/logged_change_loader_spec.rb +68 -0
- data/spec/logged_change_spec.rb +470 -0
- data/spec/noisy_connection_spec.rb +78 -0
- data/spec/postgresql_replication_spec.rb +48 -0
- data/spec/postgresql_schema_support_spec.rb +212 -0
- data/spec/postgresql_support_spec.rb +63 -0
- data/spec/progress_bar_spec.rb +77 -0
- data/spec/proxied_table_scan_spec.rb +151 -0
- data/spec/proxy_block_cursor_spec.rb +197 -0
- data/spec/proxy_connection_spec.rb +423 -0
- data/spec/proxy_cursor_spec.rb +56 -0
- data/spec/proxy_row_cursor_spec.rb +66 -0
- data/spec/proxy_runner_spec.rb +70 -0
- data/spec/replication_difference_spec.rb +161 -0
- data/spec/replication_extender_interface_spec.rb +367 -0
- data/spec/replication_extenders_spec.rb +32 -0
- data/spec/replication_helper_spec.rb +178 -0
- data/spec/replication_initializer_spec.rb +509 -0
- data/spec/replication_run_spec.rb +443 -0
- data/spec/replication_runner_spec.rb +254 -0
- data/spec/replicators_spec.rb +36 -0
- data/spec/rubyrep_spec.rb +8 -0
- data/spec/scan_detail_reporter_spec.rb +119 -0
- data/spec/scan_progress_printers_spec.rb +68 -0
- data/spec/scan_report_printers_spec.rb +67 -0
- data/spec/scan_runner_spec.rb +50 -0
- data/spec/scan_summary_reporter_spec.rb +61 -0
- data/spec/session_spec.rb +253 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +305 -0
- data/spec/strange_name_support_spec.rb +135 -0
- data/spec/sync_helper_spec.rb +169 -0
- data/spec/sync_runner_spec.rb +78 -0
- data/spec/syncers_spec.rb +171 -0
- data/spec/table_scan_helper_spec.rb +36 -0
- data/spec/table_scan_spec.rb +49 -0
- data/spec/table_sorter_spec.rb +30 -0
- data/spec/table_spec_resolver_spec.rb +111 -0
- data/spec/table_sync_spec.rb +140 -0
- data/spec/task_sweeper_spec.rb +47 -0
- data/spec/trigger_mode_switcher_spec.rb +83 -0
- data/spec/two_way_replicator_spec.rb +721 -0
- data/spec/two_way_syncer_spec.rb +256 -0
- data/spec/type_casting_cursor_spec.rb +50 -0
- data/spec/uninstall_runner_spec.rb +93 -0
- metadata +190 -0
data/spec/spec.opts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--colour
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'spec'
|
|
3
|
+
rescue LoadError
|
|
4
|
+
require 'rubygems'
|
|
5
|
+
gem 'rspec'
|
|
6
|
+
require 'spec'
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
require 'drb'
|
|
10
|
+
require 'digest/sha1'
|
|
11
|
+
|
|
12
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
|
13
|
+
$LOAD_PATH.unshift File.dirname(__FILE__)
|
|
14
|
+
|
|
15
|
+
require 'rubyrep'
|
|
16
|
+
require 'connection_extender_interface_spec'
|
|
17
|
+
|
|
18
|
+
unless self.class.const_defined?('STRANGE_TABLE')
|
|
19
|
+
if ENV['RR_TEST_DB'] == 'postgres' || ENV['RR_TEST_DB'] == nil
|
|
20
|
+
STRANGE_TABLE = 'table_with.stränge Name山'
|
|
21
|
+
else
|
|
22
|
+
STRANGE_TABLE = 'table_with_stränge Name山'
|
|
23
|
+
end
|
|
24
|
+
STRANGE_COLUMN = 'stränge. Column山'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class Module
|
|
28
|
+
# Used to verify that an instance of the class / module receives a call of the
|
|
29
|
+
# specified method.
|
|
30
|
+
# This is for cases where a method call has to be mocked of an object that is
|
|
31
|
+
# not yet created.
|
|
32
|
+
# (Couldn't find out how to do that using existing rspec mocking features.)
|
|
33
|
+
def any_instance_should_receive(method, &blck)
|
|
34
|
+
tmp_method = "original_before_mocking_#{method}".to_sym
|
|
35
|
+
logger_key = "#{self.name}_#{method}"
|
|
36
|
+
$mock_method_marker ||= {}
|
|
37
|
+
$mock_method_marker[logger_key] = Spec::Mocks::Mock.new("#{name} Instance")
|
|
38
|
+
$mock_method_marker[logger_key].should_receive(method).at_least(:once)
|
|
39
|
+
self.send :alias_method, tmp_method, method
|
|
40
|
+
self.class_eval "def #{method}(*args); $mock_method_marker['#{logger_key}'].#{method}; end"
|
|
41
|
+
blck.call
|
|
42
|
+
ensure
|
|
43
|
+
$mock_method_marker.delete logger_key
|
|
44
|
+
self.send :alias_method, method, tmp_method rescue nil
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Used to verify that an instance of the class / module does not receive a
|
|
48
|
+
# call of the specified method.
|
|
49
|
+
# This is for cases where a method call has to be mocked of an object that is
|
|
50
|
+
# not yet created.
|
|
51
|
+
# (Couldn't find out how to do that using existing rspec mocking features.)
|
|
52
|
+
def any_instance_should_not_receive(method, &blck)
|
|
53
|
+
tmp_method = "original_before_mocking_#{method}".to_sym
|
|
54
|
+
logger_key = "#{self.name}_#{method}"
|
|
55
|
+
$mock_method_marker ||= {}
|
|
56
|
+
$mock_method_marker[logger_key] = Spec::Mocks::Mock.new("#{name} Instance")
|
|
57
|
+
$mock_method_marker[logger_key].should_not_receive(method)
|
|
58
|
+
self.send :alias_method, tmp_method, method
|
|
59
|
+
self.class_eval "def #{method}(*args); $mock_method_marker['#{logger_key}'].#{method}; end"
|
|
60
|
+
blck.call
|
|
61
|
+
ensure
|
|
62
|
+
$mock_method_marker.delete logger_key
|
|
63
|
+
self.send :alias_method, method, tmp_method rescue nil
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class RR::Session
|
|
68
|
+
# To keep rspec output of failed tests managable
|
|
69
|
+
def inspect; 'session'; end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class ActiveRecord::Base
|
|
73
|
+
class << self
|
|
74
|
+
# Hack:
|
|
75
|
+
# The default inspect method (as per activerecord version 2.2.2) tries to
|
|
76
|
+
# send commands to the database.
|
|
77
|
+
# This leads to rcov failing.
|
|
78
|
+
# As workaround this is disabling the attempts to connect to the database.
|
|
79
|
+
def inspect
|
|
80
|
+
super
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# If number_of_calls is :once, mock ActiveRecord for 1 call.
|
|
86
|
+
# If number_of_calls is :twice, mock ActiveRecord for 2 calls.
|
|
87
|
+
def mock_active_record(number_of_calls)
|
|
88
|
+
ConnectionExtenders::DummyActiveRecord.should_receive(:establish_connection).send(number_of_calls) \
|
|
89
|
+
.and_return {|config| $used_config = config}
|
|
90
|
+
|
|
91
|
+
dummy_connection = Object.new
|
|
92
|
+
# We have a spec testing behaviour for non-existing extenders.
|
|
93
|
+
# So extend might not be called in all cases
|
|
94
|
+
dummy_connection.stub!(:extend)
|
|
95
|
+
dummy_connection.stub!(:tables).and_return([])
|
|
96
|
+
dummy_connection.stub!(:initialize_search_path)
|
|
97
|
+
dummy_connection.stub!(:select_one).and_return({'x' => '2'})
|
|
98
|
+
|
|
99
|
+
ConnectionExtenders::DummyActiveRecord.should_receive(:connection).send(number_of_calls) \
|
|
100
|
+
.and_return {dummy_connection}
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Creates a mock ProxyConnection with the given
|
|
104
|
+
# * mock_table: name of the mock table
|
|
105
|
+
# * primary_key_names: array of mock primary column names
|
|
106
|
+
# * column_names: array of mock column names, if nil: doesn't mock this function
|
|
107
|
+
def create_mock_proxy_connection(mock_table, primary_key_names, column_names = nil)
|
|
108
|
+
session = mock("ProxyConnection")
|
|
109
|
+
if primary_key_names
|
|
110
|
+
session.should_receive(:primary_key_names) \
|
|
111
|
+
.with(mock_table) \
|
|
112
|
+
.and_return(primary_key_names)
|
|
113
|
+
end
|
|
114
|
+
if column_names
|
|
115
|
+
session.should_receive(:column_names) \
|
|
116
|
+
.with(mock_table) \
|
|
117
|
+
.and_return(column_names)
|
|
118
|
+
end
|
|
119
|
+
session.should_receive(:quote_value) \
|
|
120
|
+
.any_number_of_times \
|
|
121
|
+
.with(an_instance_of(String), an_instance_of(String), anything) \
|
|
122
|
+
.and_return { |table, column, value| value}
|
|
123
|
+
|
|
124
|
+
session.should_receive(:connection) \
|
|
125
|
+
.any_number_of_times \
|
|
126
|
+
.and_return {dummy_connection}
|
|
127
|
+
|
|
128
|
+
session.should_receive(:quote_column_name) \
|
|
129
|
+
.any_number_of_times \
|
|
130
|
+
.with(an_instance_of(String)) \
|
|
131
|
+
.and_return { |column_name| "'#{column_name}'" }
|
|
132
|
+
|
|
133
|
+
session.should_receive(:quote_table_name) \
|
|
134
|
+
.any_number_of_times \
|
|
135
|
+
.with(an_instance_of(String)) \
|
|
136
|
+
.and_return { |table_name| "'#{table_name}'" }
|
|
137
|
+
|
|
138
|
+
session
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Turns an SQL query into a regular expression:
|
|
142
|
+
# * Handles quotes (differing depending on DBMS).
|
|
143
|
+
# * Handles round brackets (escaping with backslash to make them literals).
|
|
144
|
+
# * Removes line breaks and double spaces
|
|
145
|
+
# (allowing use of intendation and line continuation)
|
|
146
|
+
# Returns the regular expression created from the provided +sql+ string.
|
|
147
|
+
def sql_to_regexp(sql)
|
|
148
|
+
Regexp.new(sql.strip.squeeze(" ") \
|
|
149
|
+
.gsub("(", "\\(").gsub(")", "\\)") \
|
|
150
|
+
.gsub("'", 'E?.') \
|
|
151
|
+
.gsub('"', 'E?.'))
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Returns a deep copy of the provided object. Works also for Proc objects or
|
|
155
|
+
# objects referencing Proc objects.
|
|
156
|
+
def deep_copy(object)
|
|
157
|
+
Proc.send :define_method, :_dump, lambda { |depth|
|
|
158
|
+
@@proc_store ||= {}
|
|
159
|
+
@@proc_key ||= "000000000"
|
|
160
|
+
@@proc_key.succ!
|
|
161
|
+
@@proc_store[@@proc_key] = self
|
|
162
|
+
@@proc_key
|
|
163
|
+
}
|
|
164
|
+
Proc.class.send :define_method, :_load, lambda { |key|
|
|
165
|
+
proc = @@proc_store[key]
|
|
166
|
+
@@proc_store.delete key
|
|
167
|
+
proc
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
Marshal.restore(Marshal.dump(object))
|
|
171
|
+
ensure
|
|
172
|
+
Proc.send :remove_method, :_dump if Proc.method_defined? :_dump
|
|
173
|
+
Proc.class.send :remove_method, :_load if Proc.class.method_defined? :_load
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Allows the temporary faking of RUBY_PLATFORM to the given value
|
|
177
|
+
# Needs to be called with a block. While the block is executed, RUBY_PLATFORM
|
|
178
|
+
# is set to the given fake value
|
|
179
|
+
def fake_ruby_platform(fake_ruby_platform)
|
|
180
|
+
old_ruby_platform = RUBY_PLATFORM
|
|
181
|
+
old_verbose, $VERBOSE = $VERBOSE, nil
|
|
182
|
+
Object.const_set 'RUBY_PLATFORM', fake_ruby_platform
|
|
183
|
+
$VERBOSE = old_verbose
|
|
184
|
+
yield
|
|
185
|
+
ensure
|
|
186
|
+
$VERBOSE = nil
|
|
187
|
+
Object.const_set 'RUBY_PLATFORM', old_ruby_platform
|
|
188
|
+
$VERBOSE = old_verbose
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Reads the database configuration from the config folder for the specified config key
|
|
192
|
+
# E.g. if config is :postgres, tries to read the config from 'postgres_config.rb'
|
|
193
|
+
def read_config(config)
|
|
194
|
+
$config_cache ||= {}
|
|
195
|
+
cache_key = "#{config.to_s}_#{ENV['RR_TEST_DB']}"
|
|
196
|
+
unless $config_cache[cache_key]
|
|
197
|
+
# load the proxied config but ensure that the original configuration is restored
|
|
198
|
+
old_config = RR::Initializer.configuration
|
|
199
|
+
RR::Initializer.reset
|
|
200
|
+
begin
|
|
201
|
+
load File.dirname(__FILE__) + "/../config/#{config}_config.rb"
|
|
202
|
+
$config_cache[cache_key] = RR::Initializer.configuration
|
|
203
|
+
ensure
|
|
204
|
+
RR::Initializer.configuration = old_config
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
$config_cache[cache_key]
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Removes all cached database configurations
|
|
211
|
+
def clear_config_cache
|
|
212
|
+
$config_cache = {}
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# Retrieves the proxied database config as specified in config/proxied_test_config.rb
|
|
216
|
+
def proxied_config
|
|
217
|
+
read_config :proxied_test
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Retrieves the standard (non-proxied) database config as specified in config/test_config.rb
|
|
221
|
+
def standard_config
|
|
222
|
+
read_config :test
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Inserts two records into 'sequence_test' and returns the generated id values
|
|
226
|
+
# * session: the active Session
|
|
227
|
+
# * table: name of the table which is to be tested
|
|
228
|
+
def get_example_sequence_values(session, table = 'sequence_test')
|
|
229
|
+
session.left.insert_record table, { 'name' => 'bla' }
|
|
230
|
+
id1 = session.left.select_one("select max(id) as id from #{table}")['id'].to_i
|
|
231
|
+
session.left.insert_record table, { 'name' => 'blub' }
|
|
232
|
+
id2 = session.left.select_one("select max(id) as id from #{table}")['id'].to_i
|
|
233
|
+
return id1, id2
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# If true, start proxy as external process (more realistic test but also slower).
|
|
237
|
+
# Otherwise start in the current process as thread.
|
|
238
|
+
$start_proxy_as_external_process ||= false
|
|
239
|
+
|
|
240
|
+
# Starts a proxy under the given host and post
|
|
241
|
+
def start_proxy(host, port)
|
|
242
|
+
if $start_proxy_as_external_process
|
|
243
|
+
bin_path = File.join(File.dirname(__FILE__), "..", "bin", "rubyrep")
|
|
244
|
+
ruby = RUBY_PLATFORM =~ /java/ ? 'jruby' : 'ruby'
|
|
245
|
+
cmd = "#{ruby} #{bin_path} proxy -h #{host} -p #{port}"
|
|
246
|
+
Thread.new {system cmd}
|
|
247
|
+
else
|
|
248
|
+
url = "druby://#{host}:#{port}"
|
|
249
|
+
DRb.start_service(url, DatabaseProxy.new)
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# Set to true if the proxy as per SPEC_PROXY_CONFIG is running
|
|
254
|
+
$proxy_confirmed_running = false
|
|
255
|
+
|
|
256
|
+
# Starts a proxy as per left proxy settings defined in config/proxied_test_config.rb.
|
|
257
|
+
# Only starts the proxy though if none is running yet at the according host / port.
|
|
258
|
+
# If it starts a proxy child process, it also prepares automatic termination
|
|
259
|
+
# after the spec run is finished.
|
|
260
|
+
def ensure_proxy
|
|
261
|
+
# only execute the network verification once per spec run
|
|
262
|
+
unless $proxy_confirmed_running
|
|
263
|
+
drb_url = "druby://#{proxied_config.left[:proxy_host]}:#{proxied_config.left[:proxy_port]}"
|
|
264
|
+
# try to connect to the proxy
|
|
265
|
+
begin
|
|
266
|
+
proxy = DRbObject.new nil, drb_url
|
|
267
|
+
proxy.ping
|
|
268
|
+
$proxy_confirmed_running = true
|
|
269
|
+
rescue DRb::DRbConnError => e
|
|
270
|
+
# Proxy not yet running ==> start it
|
|
271
|
+
start_proxy proxied_config.left[:proxy_host], proxied_config.left[:proxy_port]
|
|
272
|
+
|
|
273
|
+
maximum_startup_time = 5 # maximum time in seconds for the proxy to start
|
|
274
|
+
waiting_time = 0.1 # time to wait between connection attempts
|
|
275
|
+
|
|
276
|
+
time = 0.0
|
|
277
|
+
ping_response = ''
|
|
278
|
+
# wait for the proxy to start up and become operational
|
|
279
|
+
while ping_response != 'pong' and time < maximum_startup_time
|
|
280
|
+
begin
|
|
281
|
+
proxy = DRbObject.new nil, drb_url
|
|
282
|
+
ping_response = proxy.ping
|
|
283
|
+
break
|
|
284
|
+
rescue DRb::DRbConnError => e
|
|
285
|
+
# do nothing (just try again)
|
|
286
|
+
end
|
|
287
|
+
sleep waiting_time
|
|
288
|
+
time += waiting_time
|
|
289
|
+
end
|
|
290
|
+
if ping_response == 'pong'
|
|
291
|
+
#puts "Proxy started (took #{time} seconds)"
|
|
292
|
+
# Ensure that the started proxy is terminated with the completion of the spec run.
|
|
293
|
+
at_exit do
|
|
294
|
+
proxy = DRbObject.new nil, drb_url
|
|
295
|
+
proxy.terminate! rescue DRb::DRbConnError
|
|
296
|
+
end if $start_proxy_as_external_process
|
|
297
|
+
else
|
|
298
|
+
raise "Could not start proxy"
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# if we got till here, then a proxy is running or was successfully started
|
|
303
|
+
$proxy_confirmed_running = true
|
|
304
|
+
end
|
|
305
|
+
end
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
include RR
|
|
4
|
+
|
|
5
|
+
describe "Unusual table and column name support" do
|
|
6
|
+
before(:each) do
|
|
7
|
+
Initializer.configuration = standard_config
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "should be able to insert, update and delete records" do
|
|
11
|
+
session = Session.new
|
|
12
|
+
session.left.begin_db_transaction
|
|
13
|
+
begin
|
|
14
|
+
select_row = Proc.new {
|
|
15
|
+
session.left.
|
|
16
|
+
select_one(
|
|
17
|
+
"select #{session.left.quote_column_name(STRANGE_COLUMN)}
|
|
18
|
+
from #{session.left.quote_table_name(STRANGE_TABLE)}")
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
session.left.insert_record STRANGE_TABLE, 'id' => 1, STRANGE_COLUMN => 'bla'
|
|
22
|
+
select_row.call[STRANGE_COLUMN].should == 'bla'
|
|
23
|
+
|
|
24
|
+
session.left.update_record STRANGE_TABLE, 'id' => 1, STRANGE_COLUMN => 'blub'
|
|
25
|
+
select_row.call[STRANGE_COLUMN].should == 'blub'
|
|
26
|
+
|
|
27
|
+
session.left.delete_record STRANGE_TABLE, 'id' => 1
|
|
28
|
+
select_row.call.should be_nil
|
|
29
|
+
ensure
|
|
30
|
+
session.left.rollback_db_transaction
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "should be able to identify primary keys" do
|
|
35
|
+
session = Session.new
|
|
36
|
+
session.left.primary_key_names(STRANGE_TABLE).should == ['id']
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "should be able to identify referenced tables" do
|
|
40
|
+
session = Session.new
|
|
41
|
+
referenced_tables = session.left.referenced_tables([STRANGE_TABLE])
|
|
42
|
+
referenced_tables.size.should == 1
|
|
43
|
+
referenced_tables[STRANGE_TABLE].sort.
|
|
44
|
+
should == ["referenced_table"]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should support sequence operations" do
|
|
48
|
+
session = Session.new
|
|
49
|
+
begin
|
|
50
|
+
left_sequence_values = session.left.sequence_values('rr', STRANGE_TABLE)
|
|
51
|
+
right_sequence_values = session.right.sequence_values('rr', STRANGE_TABLE)
|
|
52
|
+
session.left.update_sequences(
|
|
53
|
+
'rr', STRANGE_TABLE, 10, 7, left_sequence_values, right_sequence_values, 1)
|
|
54
|
+
|
|
55
|
+
sequence_props = session.left.sequence_values('rr', STRANGE_TABLE).values.first
|
|
56
|
+
sequence_props[:increment].should == 10
|
|
57
|
+
(sequence_props[:value] % 10).should == 7
|
|
58
|
+
|
|
59
|
+
session.left.clear_sequence_setup 'rr', STRANGE_TABLE
|
|
60
|
+
|
|
61
|
+
session.left.sequence_values('rr', STRANGE_TABLE).values.first[:increment].should == 1
|
|
62
|
+
ensure
|
|
63
|
+
session.left.clear_sequence_setup 'rr', STRANGE_TABLE
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "should support trigger operations for strange tables" do
|
|
68
|
+
trigger_name = 'rr_' + STRANGE_TABLE
|
|
69
|
+
session = Session.new
|
|
70
|
+
begin
|
|
71
|
+
session.left.replication_trigger_exists?(trigger_name, STRANGE_TABLE).should be_false
|
|
72
|
+
session.left.create_replication_trigger({
|
|
73
|
+
:trigger_name => trigger_name,
|
|
74
|
+
:table => STRANGE_TABLE,
|
|
75
|
+
:keys => ['id'],
|
|
76
|
+
:log_table => 'rr_pending_changes',
|
|
77
|
+
:activity_table => 'rr_running_flags',
|
|
78
|
+
:key_sep => '|',
|
|
79
|
+
:exclude_rr_activity => true
|
|
80
|
+
})
|
|
81
|
+
session.left.replication_trigger_exists?(trigger_name, STRANGE_TABLE).should be_true
|
|
82
|
+
session.left.insert_record STRANGE_TABLE, {
|
|
83
|
+
'id' => 11,
|
|
84
|
+
STRANGE_COLUMN => 'blub'
|
|
85
|
+
}
|
|
86
|
+
log_record = session.left.select_one(
|
|
87
|
+
"select * from rr_pending_changes where change_table = '#{STRANGE_TABLE}'")
|
|
88
|
+
log_record['change_key'].should == 'id|11'
|
|
89
|
+
log_record['change_type'].should == 'I'
|
|
90
|
+
|
|
91
|
+
session.left.drop_replication_trigger trigger_name, STRANGE_TABLE
|
|
92
|
+
session.left.replication_trigger_exists?(trigger_name, STRANGE_TABLE).should be_false
|
|
93
|
+
ensure
|
|
94
|
+
if session.left.replication_trigger_exists?(trigger_name, STRANGE_TABLE)
|
|
95
|
+
session.left.drop_replication_trigger trigger_name, STRANGE_TABLE
|
|
96
|
+
end
|
|
97
|
+
session.left.execute "delete from #{session.left.quote_table_name(STRANGE_TABLE)}"
|
|
98
|
+
session.left.execute "delete from rr_pending_changes"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "should support trigger operations for table with strange primary keys" do
|
|
103
|
+
trigger_name = 'rr_table_with_strange_key'
|
|
104
|
+
session = Session.new
|
|
105
|
+
begin
|
|
106
|
+
session.left.replication_trigger_exists?(trigger_name, :table_with_strange_key).should be_false
|
|
107
|
+
session.left.create_replication_trigger({
|
|
108
|
+
:trigger_name => trigger_name,
|
|
109
|
+
:table => :table_with_strange_key,
|
|
110
|
+
:keys => [STRANGE_COLUMN],
|
|
111
|
+
:log_table => 'rr_pending_changes',
|
|
112
|
+
:activity_table => 'rr_running_flags',
|
|
113
|
+
:key_sep => '|',
|
|
114
|
+
:exclude_rr_activity => true
|
|
115
|
+
})
|
|
116
|
+
session.left.replication_trigger_exists?(trigger_name, :table_with_strange_key).should be_true
|
|
117
|
+
session.left.insert_record 'table_with_strange_key', {
|
|
118
|
+
STRANGE_COLUMN => '11'
|
|
119
|
+
}
|
|
120
|
+
log_record = session.left.select_one(
|
|
121
|
+
"select * from rr_pending_changes where change_table = 'table_with_strange_key'")
|
|
122
|
+
log_record['change_key'].should == "#{STRANGE_COLUMN}|11"
|
|
123
|
+
log_record['change_type'].should == 'I'
|
|
124
|
+
|
|
125
|
+
session.left.drop_replication_trigger trigger_name, :table_with_strange_key
|
|
126
|
+
session.left.replication_trigger_exists?(trigger_name, :table_with_strange_key).should be_false
|
|
127
|
+
ensure
|
|
128
|
+
if session.left.replication_trigger_exists?(trigger_name, :table_with_strange_key)
|
|
129
|
+
session.left.drop_replication_trigger trigger_name, :table_with_strange_key
|
|
130
|
+
end
|
|
131
|
+
session.left.execute "delete from #{session.left.quote_table_name(:table_with_strange_key)}"
|
|
132
|
+
session.left.execute "delete from rr_pending_changes"
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
include RR
|
|
4
|
+
|
|
5
|
+
describe SyncHelper do
|
|
6
|
+
before(:each) do
|
|
7
|
+
Initializer.configuration = standard_config
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "initialize should initialize the correct committer" do
|
|
11
|
+
sync = TableSync.new(Session.new, 'scanner_records')
|
|
12
|
+
helper = SyncHelper.new(sync)
|
|
13
|
+
c = helper.instance_eval {committer}
|
|
14
|
+
c.should be_an_instance_of(Committers::DefaultCommitter)
|
|
15
|
+
c.session.should == helper.session
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "session should return the session" do
|
|
19
|
+
sync = TableSync.new(Session.new, 'scanner_records')
|
|
20
|
+
helper = SyncHelper.new(sync)
|
|
21
|
+
helper.session.should == sync.session
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "extract_key should extract the primary key column_name => value pairs" do
|
|
25
|
+
sync = TableSync.new(Session.new, 'extender_combined_key')
|
|
26
|
+
helper = SyncHelper.new(sync)
|
|
27
|
+
helper.extract_key('first_id' => 1, 'second_id' => 2, 'name' => 'bla').
|
|
28
|
+
should == {'first_id' => 1, 'second_id' => 2}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "ensure_event_log should ask the replication_initializer to ensure the event log" do
|
|
32
|
+
sync = TableSync.new(Session.new, 'scanner_records')
|
|
33
|
+
helper = SyncHelper.new(sync)
|
|
34
|
+
ReplicationInitializer.any_instance_should_receive(:ensure_event_log) do
|
|
35
|
+
helper.ensure_event_log
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "log_sync_outcome should log the replication outcome correctly" do
|
|
40
|
+
session = Session.new
|
|
41
|
+
session.left.begin_db_transaction
|
|
42
|
+
begin
|
|
43
|
+
sync = TableSync.new(Session.new, 'scanner_records')
|
|
44
|
+
helper = SyncHelper.new(sync)
|
|
45
|
+
|
|
46
|
+
# Verify that the log information are made fitting
|
|
47
|
+
helper.should_receive(:fit_description_columns).
|
|
48
|
+
with('my_outcome', 'my_long_description').
|
|
49
|
+
and_return(['my_outcomeX', 'my_long_descriptionY'])
|
|
50
|
+
|
|
51
|
+
helper.log_sync_outcome(
|
|
52
|
+
{'bla' => 'blub', 'id' => 1},
|
|
53
|
+
'my_sync_type',
|
|
54
|
+
'my_outcome',
|
|
55
|
+
'my_long_description'
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
row = session.left.select_one("select * from rr_logged_events order by id desc")
|
|
59
|
+
row['activity'].should == 'sync'
|
|
60
|
+
row['change_table'].should == 'scanner_records'
|
|
61
|
+
row['diff_type'].should == 'my_sync_type'
|
|
62
|
+
row['change_key'].should == '1'
|
|
63
|
+
row['left_change_type'].should be_nil
|
|
64
|
+
row['right_change_type'].should be_nil
|
|
65
|
+
row['description'].should == 'my_outcomeX'
|
|
66
|
+
row['long_description'].should == 'my_long_descriptionY'
|
|
67
|
+
Time.parse(row['event_time']).should >= 10.seconds.ago
|
|
68
|
+
row['diff_dump'].should == nil
|
|
69
|
+
ensure
|
|
70
|
+
session.left.rollback_db_transaction if session
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "log_sync_outcome should log events for combined primary key tables correctly" do
|
|
75
|
+
session = Session.new
|
|
76
|
+
session.left.begin_db_transaction
|
|
77
|
+
begin
|
|
78
|
+
sync = TableSync.new(Session.new, 'extender_combined_key')
|
|
79
|
+
helper = SyncHelper.new(sync)
|
|
80
|
+
|
|
81
|
+
helper.log_sync_outcome(
|
|
82
|
+
{'bla' => 'blub', 'first_id' => 1, 'second_id' => 2},
|
|
83
|
+
'my_sync_type',
|
|
84
|
+
'my_outcome',
|
|
85
|
+
'my_long_description'
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
row = session.left.select_one("select * from rr_logged_events order by id desc")
|
|
89
|
+
row['change_key'].should == '"first_id"=>"1", "second_id"=>"2"'
|
|
90
|
+
ensure
|
|
91
|
+
session.left.rollback_db_transaction if session
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "left_table and right_table should return the correct table names" do
|
|
96
|
+
sync = TableSync.new(Session.new, 'scanner_records')
|
|
97
|
+
helper = SyncHelper.new(sync)
|
|
98
|
+
helper.left_table.should == 'scanner_records'
|
|
99
|
+
helper.right_table.should == 'scanner_records'
|
|
100
|
+
|
|
101
|
+
sync = TableSync.new(Session.new, 'scanner_records', 'right_table')
|
|
102
|
+
helper = SyncHelper.new(sync)
|
|
103
|
+
helper.left_table.should == 'scanner_records'
|
|
104
|
+
helper.right_table.should == 'right_table'
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "tables should return the correct table name hash" do
|
|
108
|
+
sync = TableSync.new(Session.new, 'scanner_records', 'right_table')
|
|
109
|
+
helper = SyncHelper.new(sync)
|
|
110
|
+
helper.tables.should == {:left => 'scanner_records', :right => 'right_table'}
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "table_sync should return the current table sync instance" do
|
|
114
|
+
sync = TableSync.new(Session.new, 'scanner_records')
|
|
115
|
+
helper = SyncHelper.new(sync)
|
|
116
|
+
helper.table_sync.should == sync
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "sync_options should return the correct sync options" do
|
|
120
|
+
sync = TableSync.new(Session.new, 'scanner_records')
|
|
121
|
+
helper = SyncHelper.new(sync)
|
|
122
|
+
helper.sync_options.should == sync.sync_options
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it "insert_record should insert the given record" do
|
|
126
|
+
sync = TableSync.new(Session.new, 'scanner_records')
|
|
127
|
+
helper = SyncHelper.new(sync)
|
|
128
|
+
c = helper.instance_eval {committer}
|
|
129
|
+
c.should_receive(:insert_record).with(:right, 'scanner_records', :dummy_record)
|
|
130
|
+
helper.insert_record :right, 'scanner_records', :dummy_record
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it "update_record should update the given record" do
|
|
134
|
+
sync = TableSync.new(Session.new, 'scanner_records')
|
|
135
|
+
helper = SyncHelper.new(sync)
|
|
136
|
+
c = helper.instance_eval {committer}
|
|
137
|
+
c.should_receive(:update_record).with(:right, 'scanner_records', :dummy_record, nil)
|
|
138
|
+
helper.update_record :right, 'scaner_records', :dummy_record
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it "update_record should update the given record with the provided old key" do
|
|
142
|
+
sync = TableSync.new(Session.new, 'scanner_records')
|
|
143
|
+
helper = SyncHelper.new(sync)
|
|
144
|
+
c = helper.instance_eval {committer}
|
|
145
|
+
c.should_receive(:update_record).with(:right, 'scanner_records', :dummy_record, :old_key)
|
|
146
|
+
helper.update_record :right, 'scanner_records', :dummy_record, :old_key
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it "delete_record should delete the given record" do
|
|
150
|
+
sync = TableSync.new(Session.new, 'scanner_records')
|
|
151
|
+
helper = SyncHelper.new(sync)
|
|
152
|
+
c = helper.instance_eval {committer}
|
|
153
|
+
c.should_receive(:delete_record).with(:right, 'scanner_records', :dummy_record)
|
|
154
|
+
helper.delete_record :right, 'scanner_records', :dummy_record
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it "finalize should be delegated to the committer" do
|
|
158
|
+
sync = TableSync.new(Session.new, 'scanner_records')
|
|
159
|
+
helper = SyncHelper.new(sync)
|
|
160
|
+
|
|
161
|
+
# finalize itself should not lead to creation of committer
|
|
162
|
+
helper.finalize
|
|
163
|
+
helper.instance_eval {@committer}.should be_nil
|
|
164
|
+
|
|
165
|
+
c = helper.instance_eval {committer}
|
|
166
|
+
c.should_receive(:finalize).with(false)
|
|
167
|
+
helper.finalize(false)
|
|
168
|
+
end
|
|
169
|
+
end
|