rubyrep 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. data/History.txt +4 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +137 -0
  4. data/README.txt +37 -0
  5. data/Rakefile +30 -0
  6. data/bin/rubyrep +8 -0
  7. data/config/hoe.rb +72 -0
  8. data/config/mysql_config.rb +25 -0
  9. data/config/postgres_config.rb +21 -0
  10. data/config/proxied_test_config.rb +14 -0
  11. data/config/redmine_config.rb +17 -0
  12. data/config/rep_config.rb +20 -0
  13. data/config/requirements.rb +32 -0
  14. data/config/test_config.rb +20 -0
  15. data/lib/rubyrep/base_runner.rb +195 -0
  16. data/lib/rubyrep/command_runner.rb +144 -0
  17. data/lib/rubyrep/committers/buffered_committer.rb +140 -0
  18. data/lib/rubyrep/committers/committers.rb +146 -0
  19. data/lib/rubyrep/configuration.rb +240 -0
  20. data/lib/rubyrep/connection_extenders/connection_extenders.rb +133 -0
  21. data/lib/rubyrep/connection_extenders/jdbc_extender.rb +284 -0
  22. data/lib/rubyrep/connection_extenders/mysql_extender.rb +168 -0
  23. data/lib/rubyrep/connection_extenders/postgresql_extender.rb +261 -0
  24. data/lib/rubyrep/database_proxy.rb +52 -0
  25. data/lib/rubyrep/direct_table_scan.rb +75 -0
  26. data/lib/rubyrep/generate_runner.rb +105 -0
  27. data/lib/rubyrep/initializer.rb +39 -0
  28. data/lib/rubyrep/logged_change.rb +326 -0
  29. data/lib/rubyrep/proxied_table_scan.rb +171 -0
  30. data/lib/rubyrep/proxy_block_cursor.rb +145 -0
  31. data/lib/rubyrep/proxy_connection.rb +318 -0
  32. data/lib/rubyrep/proxy_cursor.rb +44 -0
  33. data/lib/rubyrep/proxy_row_cursor.rb +43 -0
  34. data/lib/rubyrep/proxy_runner.rb +89 -0
  35. data/lib/rubyrep/replication_difference.rb +91 -0
  36. data/lib/rubyrep/replication_extenders/mysql_replication.rb +271 -0
  37. data/lib/rubyrep/replication_extenders/postgresql_replication.rb +204 -0
  38. data/lib/rubyrep/replication_extenders/replication_extenders.rb +26 -0
  39. data/lib/rubyrep/replication_helper.rb +104 -0
  40. data/lib/rubyrep/replication_initializer.rb +307 -0
  41. data/lib/rubyrep/replication_run.rb +48 -0
  42. data/lib/rubyrep/replication_runner.rb +138 -0
  43. data/lib/rubyrep/replicators/replicators.rb +37 -0
  44. data/lib/rubyrep/replicators/two_way_replicator.rb +334 -0
  45. data/lib/rubyrep/scan_progress_printers/progress_bar.rb +65 -0
  46. data/lib/rubyrep/scan_progress_printers/scan_progress_printers.rb +65 -0
  47. data/lib/rubyrep/scan_report_printers/scan_detail_reporter.rb +111 -0
  48. data/lib/rubyrep/scan_report_printers/scan_report_printers.rb +67 -0
  49. data/lib/rubyrep/scan_report_printers/scan_summary_reporter.rb +75 -0
  50. data/lib/rubyrep/scan_runner.rb +25 -0
  51. data/lib/rubyrep/session.rb +177 -0
  52. data/lib/rubyrep/sync_helper.rb +111 -0
  53. data/lib/rubyrep/sync_runner.rb +31 -0
  54. data/lib/rubyrep/syncers/syncers.rb +112 -0
  55. data/lib/rubyrep/syncers/two_way_syncer.rb +174 -0
  56. data/lib/rubyrep/table_scan.rb +54 -0
  57. data/lib/rubyrep/table_scan_helper.rb +38 -0
  58. data/lib/rubyrep/table_sorter.rb +70 -0
  59. data/lib/rubyrep/table_spec_resolver.rb +136 -0
  60. data/lib/rubyrep/table_sync.rb +68 -0
  61. data/lib/rubyrep/trigger_mode_switcher.rb +63 -0
  62. data/lib/rubyrep/type_casting_cursor.rb +31 -0
  63. data/lib/rubyrep/uninstall_runner.rb +92 -0
  64. data/lib/rubyrep/version.rb +9 -0
  65. data/lib/rubyrep.rb +68 -0
  66. data/script/destroy +14 -0
  67. data/script/generate +14 -0
  68. data/script/txt2html +74 -0
  69. data/setup.rb +1585 -0
  70. data/sims/performance/big_rep_spec.rb +100 -0
  71. data/sims/performance/big_scan_spec.rb +57 -0
  72. data/sims/performance/big_sync_spec.rb +141 -0
  73. data/sims/performance/performance.rake +228 -0
  74. data/sims/sim_helper.rb +24 -0
  75. data/spec/base_runner_spec.rb +218 -0
  76. data/spec/buffered_committer_spec.rb +271 -0
  77. data/spec/command_runner_spec.rb +145 -0
  78. data/spec/committers_spec.rb +174 -0
  79. data/spec/configuration_spec.rb +198 -0
  80. data/spec/connection_extender_interface_spec.rb +138 -0
  81. data/spec/connection_extenders_registration_spec.rb +129 -0
  82. data/spec/database_proxy_spec.rb +48 -0
  83. data/spec/database_rake_spec.rb +40 -0
  84. data/spec/db_specific_connection_extenders_spec.rb +34 -0
  85. data/spec/db_specific_replication_extenders_spec.rb +38 -0
  86. data/spec/direct_table_scan_spec.rb +61 -0
  87. data/spec/generate_runner_spec.rb +84 -0
  88. data/spec/initializer_spec.rb +46 -0
  89. data/spec/logged_change_spec.rb +480 -0
  90. data/spec/postgresql_replication_spec.rb +48 -0
  91. data/spec/postgresql_support_spec.rb +57 -0
  92. data/spec/progress_bar_spec.rb +77 -0
  93. data/spec/proxied_table_scan_spec.rb +151 -0
  94. data/spec/proxy_block_cursor_spec.rb +197 -0
  95. data/spec/proxy_connection_spec.rb +399 -0
  96. data/spec/proxy_cursor_spec.rb +56 -0
  97. data/spec/proxy_row_cursor_spec.rb +66 -0
  98. data/spec/proxy_runner_spec.rb +70 -0
  99. data/spec/replication_difference_spec.rb +160 -0
  100. data/spec/replication_extender_interface_spec.rb +365 -0
  101. data/spec/replication_extenders_spec.rb +32 -0
  102. data/spec/replication_helper_spec.rb +121 -0
  103. data/spec/replication_initializer_spec.rb +477 -0
  104. data/spec/replication_run_spec.rb +166 -0
  105. data/spec/replication_runner_spec.rb +213 -0
  106. data/spec/replicators_spec.rb +31 -0
  107. data/spec/rubyrep_spec.rb +8 -0
  108. data/spec/scan_detail_reporter_spec.rb +119 -0
  109. data/spec/scan_progress_printers_spec.rb +68 -0
  110. data/spec/scan_report_printers_spec.rb +67 -0
  111. data/spec/scan_runner_spec.rb +50 -0
  112. data/spec/scan_summary_reporter_spec.rb +61 -0
  113. data/spec/session_spec.rb +212 -0
  114. data/spec/spec.opts +1 -0
  115. data/spec/spec_helper.rb +295 -0
  116. data/spec/sync_helper_spec.rb +157 -0
  117. data/spec/sync_runner_spec.rb +78 -0
  118. data/spec/syncers_spec.rb +171 -0
  119. data/spec/table_scan_helper_spec.rb +29 -0
  120. data/spec/table_scan_spec.rb +49 -0
  121. data/spec/table_sorter_spec.rb +31 -0
  122. data/spec/table_spec_resolver_spec.rb +102 -0
  123. data/spec/table_sync_spec.rb +84 -0
  124. data/spec/trigger_mode_switcher_spec.rb +83 -0
  125. data/spec/two_way_replicator_spec.rb +551 -0
  126. data/spec/two_way_syncer_spec.rb +256 -0
  127. data/spec/type_casting_cursor_spec.rb +50 -0
  128. data/spec/uninstall_runner_spec.rb +86 -0
  129. data/tasks/database.rake +439 -0
  130. data/tasks/deployment.rake +29 -0
  131. data/tasks/environment.rake +9 -0
  132. data/tasks/java.rake +37 -0
  133. data/tasks/redmine_test.rake +47 -0
  134. data/tasks/rspec.rake +68 -0
  135. data/tasks/rubyrep.tailor +18 -0
  136. data/tasks/stats.rake +19 -0
  137. data/tasks/task_helper.rb +20 -0
  138. data.tar.gz.sig +0 -0
  139. metadata +243 -0
  140. metadata.gz.sig +0 -0
@@ -0,0 +1,78 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe SyncRunner do
6
+ before(:each) do
7
+ end
8
+
9
+ it "should register itself with CommandRunner" do
10
+ CommandRunner.commands['sync'][:command].should == SyncRunner
11
+ CommandRunner.commands['sync'][:description].should be_an_instance_of(String)
12
+ end
13
+
14
+ it "prepare_table_pairs should sort the tables" do
15
+ session = Session.new standard_config
16
+ session.should_receive(:sort_table_pairs).
17
+ with(:dummy_table_pairs).
18
+ and_return(:sorted_dummy_table_pairs)
19
+
20
+ sync_runner = SyncRunner.new
21
+ sync_runner.stub!(:session).and_return(session)
22
+
23
+ sync_runner.prepare_table_pairs(:dummy_table_pairs).should == :sorted_dummy_table_pairs
24
+ end
25
+
26
+ it "execute should sync the specified tables" do
27
+ org_stdout = $stdout
28
+ session = nil
29
+
30
+ # This is necessary to avoid the cached RubyRep configurations from getting
31
+ # overwritten by the sync run
32
+ old_config, Initializer.configuration = Initializer.configuration, Configuration.new
33
+
34
+ session = Session.new(standard_config)
35
+ session.left.begin_db_transaction
36
+ session.right.begin_db_transaction
37
+
38
+ $stdout = StringIO.new
39
+ begin
40
+ sync_runner = SyncRunner.new
41
+ sync_runner.options = {
42
+ :config_file => "#{File.dirname(__FILE__)}/../config/test_config.rb",
43
+ :table_specs => ["scanner_records"]
44
+ }
45
+
46
+ sync_runner.execute
47
+
48
+ $stdout.string.should =~
49
+ /scanner_records .* 5\n/
50
+
51
+ left_records = session.left.connection.select_all("select * from scanner_records order by id")
52
+ right_records = session.right.connection.select_all("select * from scanner_records order by id")
53
+ left_records.should == right_records
54
+ ensure
55
+ $stdout = org_stdout
56
+ Initializer.configuration = old_config if old_config
57
+ if session
58
+ session.left.rollback_db_transaction
59
+ session.right.rollback_db_transaction
60
+ end
61
+ end
62
+ end
63
+
64
+ it "create_processor should create the TableSync instance" do
65
+ TableSync.should_receive(:new).
66
+ with(:dummy_session, "left_table", "right_table").
67
+ and_return(:dummy_table_sync)
68
+ sync_runner = SyncRunner.new
69
+ sync_runner.should_receive(:session).and_return(:dummy_session)
70
+ sync_runner.create_processor("left_table", "right_table").
71
+ should == :dummy_table_sync
72
+ end
73
+
74
+ it "summary_description should return a description" do
75
+ SyncRunner.new.summary_description.should be_an_instance_of(String)
76
+ end
77
+
78
+ end
@@ -0,0 +1,171 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe Syncers do
6
+ before(:each) do
7
+ @old_syncers = Syncers.syncers
8
+ end
9
+
10
+ after(:each) do
11
+ Syncers.instance_variable_set :@syncers, @old_syncers
12
+ end
13
+
14
+ it "syncers should return empty hash if nil" do
15
+ Syncers.instance_variable_set :@syncers, nil
16
+ Syncers.syncers.should == {}
17
+ end
18
+
19
+ it "syncers should return the registered syncers" do
20
+ Syncers.instance_variable_set :@syncers, :dummy_data
21
+ Syncers.syncers.should == :dummy_data
22
+ end
23
+
24
+ it "configured_syncer should return the correct syncer as per :syncer option, if both :syncer and :replicator is configured" do
25
+ options = {
26
+ :syncer => :two_way,
27
+ :replicator => :key2
28
+ }
29
+ Syncers.configured_syncer(options).should == Syncers::TwoWaySyncer
30
+ end
31
+
32
+ it "configured_syncer should return the correct syncer as per :replicator option if no :syncer option is provided" do
33
+ options = {:replicator => :two_way}
34
+ Syncers.configured_syncer(options).should == Syncers::TwoWaySyncer
35
+ end
36
+
37
+ it "register should register the provided commiter" do
38
+ Syncers.instance_variable_set :@syncers, nil
39
+ Syncers.register :a_key => :a
40
+ Syncers.register :b_key => :b
41
+ Syncers.syncers[:a_key].should == :a
42
+ Syncers.syncers[:b_key].should == :b
43
+ end
44
+ end
45
+
46
+ describe Syncers::OneWaySyncer do
47
+ before(:each) do
48
+ Initializer.configuration = standard_config
49
+ end
50
+
51
+ it "should register itself" do
52
+ Syncers::syncers[:one_way].should == Syncers::OneWaySyncer
53
+ end
54
+
55
+ it "initialize should store sync_helper" do
56
+ sync = TableSync.new(Session.new, 'scanner_records')
57
+ helper = SyncHelper.new(sync)
58
+ syncer = Syncers::OneWaySyncer.new(helper)
59
+ syncer.sync_helper.should == helper
60
+ end
61
+
62
+ it "initialize should calculate course source, target and source_record_index" do
63
+ sync = TableSync.new(Session.new, 'scanner_records')
64
+ helper = SyncHelper.new(sync)
65
+
66
+ # verify correct behaviour if syncing to the left
67
+ helper.stub!(:sync_options).and_return({:direction => :left})
68
+ syncer = Syncers::OneWaySyncer.new(helper)
69
+ syncer.source.should == :right
70
+ syncer.target.should == :left
71
+ syncer.source_record_index.should == 1
72
+
73
+ # verify correct behaviour if syncing to the right
74
+ helper.stub!(:sync_options).and_return({:direction => :right})
75
+ syncer = Syncers::OneWaySyncer.new(helper)
76
+ syncer.source.should == :left
77
+ syncer.target.should == :right
78
+ syncer.source_record_index.should == 0
79
+ end
80
+
81
+ it "default_option should return the correct default options" do
82
+ Syncers::OneWaySyncer.default_options.should == {
83
+ :direction => :right,
84
+ :delete => false, :update => true, :insert => true
85
+ }
86
+ end
87
+
88
+ it "sync_difference should only delete if :delete option is given" do
89
+ sync = TableSync.new(Session.new, 'scanner_records')
90
+ helper = SyncHelper.new(sync)
91
+ helper.stub!(:sync_options).and_return({:direction => :left})
92
+ syncer = Syncers::OneWaySyncer.new(helper)
93
+ helper.should_not_receive(:delete_record)
94
+ helper.should_not_receive(:update_record)
95
+ helper.should_not_receive(:insert_record)
96
+ syncer.sync_difference(:left, :dummy_record)
97
+ end
98
+
99
+ it "sync_difference should delete in the right database" do
100
+ sync = TableSync.new(Session.new, 'scanner_records')
101
+ helper = SyncHelper.new(sync)
102
+ helper.stub!(:sync_options).and_return({:direction => :left, :delete => true})
103
+ syncer = Syncers::OneWaySyncer.new(helper)
104
+ helper.should_receive(:delete_record).with(:left, :dummy_record)
105
+ helper.should_not_receive(:update_record)
106
+ helper.should_not_receive(:insert_record)
107
+ syncer.sync_difference(:left, :dummy_record)
108
+
109
+ helper = SyncHelper.new(sync)
110
+ helper.stub!(:sync_options).and_return({:direction => :right, :delete => true})
111
+ syncer = Syncers::OneWaySyncer.new(helper)
112
+ helper.should_receive(:delete_record).with(:right, :dummy_record)
113
+ syncer.sync_difference(:right, :dummy_record)
114
+ end
115
+
116
+ it "sync_difference should not insert if :insert option is not true" do
117
+ sync = TableSync.new(Session.new, 'scanner_records')
118
+ helper = SyncHelper.new(sync)
119
+ helper.stub!(:sync_options).and_return({:direction => :left, :insert => false})
120
+ syncer = Syncers::OneWaySyncer.new(helper)
121
+ helper.should_not_receive(:delete_record)
122
+ helper.should_not_receive(:update_record)
123
+ helper.should_not_receive(:insert_record)
124
+ syncer.sync_difference(:right, :dummy_record)
125
+ end
126
+
127
+ it "sync_difference should insert in the right database" do
128
+ sync = TableSync.new(Session.new, 'scanner_records')
129
+ helper = SyncHelper.new(sync)
130
+ helper.stub!(:sync_options).and_return({:direction => :left, :insert => true})
131
+ syncer = Syncers::OneWaySyncer.new(helper)
132
+ helper.should_not_receive(:delete_record)
133
+ helper.should_not_receive(:update_record)
134
+ helper.should_receive(:insert_record).with(:left, :dummy_record)
135
+ syncer.sync_difference(:right, :dummy_record)
136
+
137
+ helper = SyncHelper.new(sync)
138
+ helper.stub!(:sync_options).and_return({:direction => :right, :insert => true})
139
+ syncer = Syncers::OneWaySyncer.new(helper)
140
+ helper.should_receive(:insert_record).with(:right, :dummy_record)
141
+ syncer.sync_difference(:left, :dummy_record)
142
+ end
143
+
144
+ it "sync_difference should not update if :update option is not true" do
145
+ sync = TableSync.new(Session.new, 'scanner_records')
146
+ helper = SyncHelper.new(sync)
147
+ helper.stub!(:sync_options).and_return({:direction => :left, :update => false})
148
+ syncer = Syncers::OneWaySyncer.new(helper)
149
+ helper.should_not_receive(:delete_record)
150
+ helper.should_not_receive(:update_record)
151
+ helper.should_not_receive(:insert_record)
152
+ syncer.sync_difference(:conflict, :dummy_records)
153
+ end
154
+
155
+ it "sync_difference should update the right values in the right database" do
156
+ sync = TableSync.new(Session.new, 'scanner_records')
157
+ helper = SyncHelper.new(sync)
158
+ helper.stub!(:sync_options).and_return({:direction => :left, :update => true})
159
+ syncer = Syncers::OneWaySyncer.new(helper)
160
+ helper.should_not_receive(:delete_record)
161
+ helper.should_receive(:update_record).with(:left, :right_record)
162
+ helper.should_not_receive(:insert_record)
163
+ syncer.sync_difference(:conflict, [:left_record, :right_record])
164
+
165
+ helper = SyncHelper.new(sync)
166
+ helper.stub!(:sync_options).and_return({:direction => :right, :update => true})
167
+ syncer = Syncers::OneWaySyncer.new(helper)
168
+ helper.should_receive(:update_record).with(:right, :left_record)
169
+ syncer.sync_difference(:conflict, [:left_record, :right_record])
170
+ end
171
+ end
@@ -0,0 +1,29 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe TableScanHelper do
6
+ before(:each) do
7
+ @scan = Object.new
8
+ @scan.extend TableScanHelper
9
+ end
10
+
11
+ it "rank_rows should calculate the correct rank of rows based on their primary keys" do
12
+ @scan.stub!(:primary_key_names).and_return(['first_id', 'second_id'])
13
+ @scan.rank_rows({'first_id' => 1, 'second_id' => 1}, {'first_id' => 1, 'second_id' => 1}).should == 0
14
+ @scan.rank_rows({'first_id' => 1, 'second_id' => 1}, {'first_id' => 1, 'second_id' => 2}).should == -1
15
+ @scan.rank_rows({'first_id' => 2, 'second_id' => 1}, {'first_id' => 1, 'second_id' => 1}).should == 1
16
+ lambda {@scan.rank_rows(nil,nil)}.should raise_error(RuntimeError, 'At least one of left_row and right_row must not be nil!')
17
+ @scan.rank_rows(nil, {'first_id' => 1, 'second_id' => 1}).should == 1
18
+ @scan.rank_rows({'first_id' => 1, 'second_id' => 1}, nil).should == -1
19
+ end
20
+
21
+ it "table_scan_class should return TableScan for non-proxied sessions" do
22
+ TableScanHelper.scan_class(Session.new(standard_config)).should == DirectTableScan
23
+ end
24
+
25
+ it "table_scan_class should return ProxiedTableScan for proxied sessions" do
26
+ ensure_proxy
27
+ TableScanHelper.scan_class(Session.new(proxied_config)).should == ProxiedTableScan
28
+ end
29
+ end
@@ -0,0 +1,49 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe TableScan do
6
+ before(:each) do
7
+ Initializer.configuration = standard_config
8
+ end
9
+
10
+ it "initialize should raise exception if table doesn't have primary keys" do
11
+ session = Session.new
12
+ lambda {TableScan.new session, 'extender_without_key'} \
13
+ .should raise_error(RuntimeError, /.*extender_without_key.*primary key/)
14
+ end
15
+
16
+ it "initialize should cache the primary keys of the given table" do
17
+ session = Session.new
18
+ scann = TableScan.new session, 'scanner_records'
19
+ scann.primary_key_names.should == ['id']
20
+ end
21
+
22
+ it "initialize should use the name of the left table as overwritable default for right table" do
23
+ session = Session.new
24
+ TableScan.new(session, 'scanner_records').right_table.should == 'scanner_records'
25
+ TableScan.new(session, 'scanner_records', 'dummy').right_table.should == 'dummy'
26
+ end
27
+
28
+ it "progress_printer= should store the progress printer class" do
29
+ session = Session.new
30
+ TableScan.new(session, 'scanner_records').progress_printer = :dummy_printer_class
31
+ end
32
+
33
+ it "update_progres should create the progress printer and report the progress" do
34
+ session = Session.new
35
+ scan = TableScan.new(session, 'extender_one_record', 'extender_no_record')
36
+
37
+ progress_printer_class = mock("progress printer class")
38
+ progress_printer = mock("progress printer")
39
+ progress_printer_class.should_receive(:new).
40
+ with(1, scan.session, 'extender_one_record', 'extender_no_record').
41
+ and_return(progress_printer)
42
+ progress_printer.should_receive(:step).with(2)
43
+ progress_printer.should_receive(:step).with(3)
44
+
45
+ scan.progress_printer = progress_printer_class
46
+ scan.update_progress 2
47
+ scan.update_progress 3
48
+ end
49
+ end
@@ -0,0 +1,31 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe TableSorter do
6
+ before(:each) do
7
+ end
8
+
9
+ it "sort should order the tables correctly" do
10
+ tables = [
11
+ 'scanner_records',
12
+ 'referencing_table',
13
+ 'referenced_table',
14
+ 'scanner_text_key',
15
+ ]
16
+
17
+ sorted_tables = [
18
+ 'scanner_records',
19
+ 'referenced_table',
20
+ 'referencing_table',
21
+ 'scanner_text_key',
22
+ ]
23
+
24
+ sorter = TableSorter.new Session.new(standard_config), tables
25
+ sorter.sort.should == sorted_tables
26
+
27
+ # verify that we are using TSort#tsort to get that result
28
+ sorter.should_not_receive(:tsort)
29
+ sorter.sort
30
+ end
31
+ end
@@ -0,0 +1,102 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe TableSpecResolver do
6
+ before(:each) do
7
+ Initializer.configuration = standard_config
8
+ @session = Session.new
9
+ @resolver = TableSpecResolver.new @session
10
+ end
11
+
12
+ it "initialize should store the session and cache the tables of the session" do
13
+ @resolver.session.should == @session
14
+ end
15
+
16
+ it "tables should return the tables of the specified database" do
17
+ @resolver.tables(:left).should == @session.left.tables
18
+ @resolver.tables(:right).should == @session.right.tables
19
+ end
20
+
21
+ it "resolve should resolve direct table names correctly" do
22
+ @resolver.resolve(['scanner_records', 'referenced_table']).should == [
23
+ {:left => 'scanner_records', :right => 'scanner_records'},
24
+ {:left => 'referenced_table', :right => 'referenced_table'}
25
+ ]
26
+ end
27
+
28
+ it "resolve should resolve table name pairs correctly" do
29
+ @resolver.resolve(['left_table , right_table']).should == [
30
+ {:left => 'left_table', :right => 'right_table'}
31
+ ]
32
+ end
33
+
34
+ it "resolve should complain about non-existing tables" do
35
+ lambda {@resolver.resolve(['dummy, scanner_records'])}.
36
+ should raise_error(/non-existing.*dummy/)
37
+ end
38
+
39
+ it "resolve should not check for non-existing tables if that is disabled" do
40
+ lambda {@resolver.resolve(['dummy, scanner_records'], [], false)}.
41
+ should_not raise_error
42
+ end
43
+
44
+ it "resolve should resolve string in form of regular expression correctly" do
45
+ @resolver.resolve(['/SCANNER_RECORDS|scanner_text_key/']).sort { |a,b|
46
+ a[:left] <=> b[:left]
47
+ }.should == [
48
+ {:left => 'scanner_records', :right => 'scanner_records'},
49
+ {:left => 'scanner_text_key', :right => 'scanner_text_key'}
50
+ ]
51
+ end
52
+
53
+ it "resolve should resolve regular expressions correctly" do
54
+ @resolver.resolve([/SCANNER_RECORDS|scanner_text_key/]).sort { |a,b|
55
+ a[:left] <=> b[:left]
56
+ }.should == [
57
+ {:left => 'scanner_records', :right => 'scanner_records'},
58
+ {:left => 'scanner_text_key', :right => 'scanner_text_key'}
59
+ ]
60
+ end
61
+
62
+ it "resolve should should not return the same table multiple times" do
63
+ @resolver.resolve([
64
+ 'scanner_records',
65
+ 'scanner_records',
66
+ 'scanner_records, bla',
67
+ '/scanner_records/'
68
+ ]
69
+ ).should == [
70
+ {:left => 'scanner_records', :right => 'scanner_records'}
71
+ ]
72
+ end
73
+
74
+ it "resolve should not return tables that are excluded" do
75
+ @resolver.resolve(
76
+ [/SCANNER_RECORDS|scanner_text_key/],
77
+ [/scanner_text/]
78
+ ).should == [
79
+ {:left => 'scanner_records', :right => 'scanner_records'},
80
+ ]
81
+ end
82
+
83
+ it "non_existing_tables should return an empty hash if all tables exist" do
84
+ table_pairs = [{:left => 'scanner_records', :right => 'referenced_table'}]
85
+ @resolver.non_existing_tables(table_pairs).should == {}
86
+ end
87
+
88
+ it "non_existing_tables should return an empty hash if all tables exist" do
89
+ table_pairs = [{:left => 'scanner_records', :right => 'bla'}]
90
+ @resolver.non_existing_tables(table_pairs).should == {:right => ['bla']}
91
+
92
+ table_pairs = [
93
+ {:left => 'blub', :right => 'bla'},
94
+ {:left => 'scanner_records', :right => 'xyz'}
95
+ ]
96
+ @resolver.non_existing_tables(table_pairs).should == {
97
+ :left => ['blub'],
98
+ :right => ['bla', 'xyz']
99
+ }
100
+ end
101
+
102
+ end
@@ -0,0 +1,84 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe TableSync do
6
+ it "sync_options should return the correct table specific sync options" do
7
+ config = deep_copy(standard_config)
8
+ old_table_specific_options = config.tables_with_options
9
+ begin
10
+ config.options = {:syncer => :bla}
11
+ config.include_tables 'scanner_records', {:syncer => :blub}
12
+ TableSync.new(Session.new(config), 'scanner_records').sync_options[:syncer] \
13
+ .should == :blub
14
+ ensure
15
+ config.instance_eval {@tables_with_options = old_table_specific_options}
16
+ end
17
+ end
18
+
19
+ it "execute_sync_hook should work if the hook is not configured" do
20
+ session = Session.new standard_config
21
+ sync = TableSync.new(session, 'scanner_records')
22
+ sync.execute_sync_hook(:before_table_sync)
23
+ end
24
+
25
+ it "execute_sync_look should execute the given SQL command" do
26
+ config = deep_copy(standard_config)
27
+ config.add_table_options 'scanner_records', :before_table_sync => 'dummy_command'
28
+ session = Session.new config
29
+ sync = TableSync.new(session, 'scanner_records')
30
+
31
+ session.left.should_receive(:execute).with('dummy_command')
32
+ session.right.should_receive(:execute).with('dummy_command')
33
+
34
+ sync.execute_sync_hook(:before_table_sync)
35
+ end
36
+
37
+ it "execute_sync_look should execute the given Proc" do
38
+ config = deep_copy(standard_config)
39
+ received_handler = nil
40
+ config.add_table_options 'scanner_records',
41
+ :before_table_sync => lambda {|helper| received_handler = helper}
42
+ session = Session.new config
43
+ sync = TableSync.new(session, 'scanner_records')
44
+ sync.helper = :dummy_helper
45
+
46
+ sync.execute_sync_hook(:before_table_sync)
47
+
48
+ received_handler.should == :dummy_helper
49
+ end
50
+
51
+ it "run should synchronize the databases" do
52
+ config = deep_copy(standard_config)
53
+ config.options[:committer] = :never_commit
54
+ config.options[:logged_sync_events] = [:all_conflicts]
55
+ before_hook_called = false
56
+ after_hook_called = false
57
+ config.options[:before_table_sync] = lambda {|helper| before_hook_called = true}
58
+ config.options[:after_table_sync] = lambda { |helper| after_hook_called = true}
59
+ session = Session.new(config)
60
+ begin
61
+ sync = TableSync.new(session, 'scanner_records')
62
+ sync.run
63
+
64
+ # Verify that sync events are logged
65
+ row = session.left.select_one("select * from rr_logged_events where change_key = '2' order by id")
66
+ row['change_table'].should == 'scanner_records'
67
+ row['diff_type'].should == 'conflict'
68
+ row['description'].should == 'left_wins'
69
+
70
+ # verify that the table was synchronized
71
+ left_records = session.left.select_all("select * from scanner_records order by id")
72
+ right_records = session.right.select_all("select * from scanner_records order by id")
73
+ left_records.should == right_records
74
+
75
+ # verify that hooks where called
76
+ before_hook_called.should be_true
77
+ after_hook_called.should be_true
78
+ ensure
79
+ Committers::NeverCommitter.rollback_current_session
80
+ session.left.execute "delete from rr_logged_events"
81
+ end
82
+ end
83
+
84
+ end
@@ -0,0 +1,83 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe TriggerModeSwitcher do
6
+ before(:each) do
7
+ Initializer.configuration = standard_config
8
+ end
9
+
10
+ it "initialize should save the session and initialize triggers hash" do
11
+ session = Session.new
12
+ switcher = TriggerModeSwitcher.new(session)
13
+ switcher.session.should == session
14
+ end
15
+
16
+ it "exclude_rr_activity should switch the trigger for the given table" do
17
+ switcher = TriggerModeSwitcher.new(Session.new)
18
+
19
+ switcher.should_receive(:switch_trigger_mode).with(:right, 'right1', true).once
20
+ switcher.exclude_rr_activity(:right, 'right1')
21
+
22
+ # Verify that for a given table, the trigger is not modified multiple times
23
+ switcher.exclude_rr_activity(:right, 'right1')
24
+ end
25
+
26
+ it "restore_triggers should restore the triggers" do
27
+ switcher = TriggerModeSwitcher.new(Session.new)
28
+
29
+ switcher.stub!(:switch_trigger_mode)
30
+ switcher.exclude_rr_activity :left, 'left1'
31
+ switcher.should_receive(:switch_trigger_mode).with(:left, 'left1', false).once
32
+ switcher.restore_triggers
33
+ switcher.restore_triggers # ensure the restore is only done once
34
+ end
35
+
36
+ it "switch_trigger_mode should switch the exclude_rr_activity mode as specified" do
37
+ session = nil
38
+ initializer = nil
39
+ begin
40
+ session = Session.new
41
+ initializer = ReplicationInitializer.new(session)
42
+ initializer.create_trigger(:left, 'trigger_test')
43
+
44
+ switcher = TriggerModeSwitcher.new session
45
+ switcher.switch_trigger_mode :left, 'trigger_test', true
46
+
47
+ session.left.insert_record 'trigger_test', {
48
+ 'first_id' => 1,
49
+ 'second_id' => 2,
50
+ 'name' => 'blub'
51
+ }
52
+ session.left.execute "insert into rr_running_flags values(1)"
53
+ session.left.insert_record 'trigger_test', {
54
+ 'first_id' => 1,
55
+ 'second_id' => 3,
56
+ 'name' => 'bla'
57
+ }
58
+
59
+ rows = session.left.select_all("select * from rr_pending_changes order by id")
60
+ rows.each {|row| row.delete 'id'; row.delete 'change_time'}
61
+ rows.should == [{
62
+ 'change_table' => 'trigger_test',
63
+ 'change_key' => 'first_id|1|second_id|2',
64
+ 'change_new_key' => nil,
65
+ 'change_type' => 'I'
66
+ }]
67
+ ensure
68
+ initializer.drop_trigger :left, 'trigger_test' if initializer
69
+ if session
70
+ session.left.execute 'delete from rr_running_flags'
71
+ session.left.execute 'delete from trigger_test'
72
+ session.left.execute 'delete from rr_pending_changes'
73
+ end
74
+ end
75
+ end
76
+
77
+ it "switch_trigger_mode should not switch the trigger mode if the table has no trigger" do
78
+ session = Session.new
79
+ switcher = TriggerModeSwitcher.new session
80
+ session.left.should_not_receive(:execute)
81
+ switcher.switch_trigger_mode(:left, 'scanner_records', true)
82
+ end
83
+ end