rubyrep 1.0.0

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.
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