andyjeffries-rubyrep 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|