andyjeffries-rubyrep 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. data/History.txt +83 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +151 -0
  4. data/README.txt +37 -0
  5. data/bin/rubyrep +8 -0
  6. data/lib/rubyrep.rb +72 -0
  7. data/lib/rubyrep/base_runner.rb +195 -0
  8. data/lib/rubyrep/command_runner.rb +144 -0
  9. data/lib/rubyrep/committers/buffered_committer.rb +151 -0
  10. data/lib/rubyrep/committers/committers.rb +152 -0
  11. data/lib/rubyrep/configuration.rb +275 -0
  12. data/lib/rubyrep/connection_extenders/connection_extenders.rb +165 -0
  13. data/lib/rubyrep/connection_extenders/jdbc_extender.rb +65 -0
  14. data/lib/rubyrep/connection_extenders/mysql_extender.rb +59 -0
  15. data/lib/rubyrep/connection_extenders/postgresql_extender.rb +277 -0
  16. data/lib/rubyrep/database_proxy.rb +52 -0
  17. data/lib/rubyrep/direct_table_scan.rb +75 -0
  18. data/lib/rubyrep/generate_runner.rb +105 -0
  19. data/lib/rubyrep/initializer.rb +39 -0
  20. data/lib/rubyrep/log_helper.rb +30 -0
  21. data/lib/rubyrep/logged_change.rb +160 -0
  22. data/lib/rubyrep/logged_change_loader.rb +197 -0
  23. data/lib/rubyrep/noisy_connection.rb +80 -0
  24. data/lib/rubyrep/proxied_table_scan.rb +171 -0
  25. data/lib/rubyrep/proxy_block_cursor.rb +145 -0
  26. data/lib/rubyrep/proxy_connection.rb +431 -0
  27. data/lib/rubyrep/proxy_cursor.rb +44 -0
  28. data/lib/rubyrep/proxy_row_cursor.rb +43 -0
  29. data/lib/rubyrep/proxy_runner.rb +89 -0
  30. data/lib/rubyrep/replication_difference.rb +100 -0
  31. data/lib/rubyrep/replication_extenders/mysql_replication.rb +271 -0
  32. data/lib/rubyrep/replication_extenders/postgresql_replication.rb +236 -0
  33. data/lib/rubyrep/replication_extenders/replication_extenders.rb +26 -0
  34. data/lib/rubyrep/replication_helper.rb +142 -0
  35. data/lib/rubyrep/replication_initializer.rb +327 -0
  36. data/lib/rubyrep/replication_run.rb +142 -0
  37. data/lib/rubyrep/replication_runner.rb +166 -0
  38. data/lib/rubyrep/replicators/replicators.rb +42 -0
  39. data/lib/rubyrep/replicators/two_way_replicator.rb +361 -0
  40. data/lib/rubyrep/scan_progress_printers/progress_bar.rb +65 -0
  41. data/lib/rubyrep/scan_progress_printers/scan_progress_printers.rb +65 -0
  42. data/lib/rubyrep/scan_report_printers/scan_detail_reporter.rb +111 -0
  43. data/lib/rubyrep/scan_report_printers/scan_report_printers.rb +67 -0
  44. data/lib/rubyrep/scan_report_printers/scan_summary_reporter.rb +75 -0
  45. data/lib/rubyrep/scan_runner.rb +25 -0
  46. data/lib/rubyrep/session.rb +230 -0
  47. data/lib/rubyrep/sync_helper.rb +121 -0
  48. data/lib/rubyrep/sync_runner.rb +31 -0
  49. data/lib/rubyrep/syncers/syncers.rb +112 -0
  50. data/lib/rubyrep/syncers/two_way_syncer.rb +174 -0
  51. data/lib/rubyrep/table_scan.rb +54 -0
  52. data/lib/rubyrep/table_scan_helper.rb +46 -0
  53. data/lib/rubyrep/table_sorter.rb +70 -0
  54. data/lib/rubyrep/table_spec_resolver.rb +142 -0
  55. data/lib/rubyrep/table_sync.rb +90 -0
  56. data/lib/rubyrep/task_sweeper.rb +77 -0
  57. data/lib/rubyrep/trigger_mode_switcher.rb +63 -0
  58. data/lib/rubyrep/type_casting_cursor.rb +31 -0
  59. data/lib/rubyrep/uninstall_runner.rb +93 -0
  60. data/lib/rubyrep/version.rb +9 -0
  61. data/rubyrep +8 -0
  62. data/rubyrep.bat +4 -0
  63. data/setup.rb +1585 -0
  64. data/spec/base_runner_spec.rb +218 -0
  65. data/spec/buffered_committer_spec.rb +274 -0
  66. data/spec/command_runner_spec.rb +145 -0
  67. data/spec/committers_spec.rb +178 -0
  68. data/spec/configuration_spec.rb +203 -0
  69. data/spec/connection_extender_interface_spec.rb +141 -0
  70. data/spec/connection_extenders_registration_spec.rb +164 -0
  71. data/spec/database_proxy_spec.rb +48 -0
  72. data/spec/database_rake_spec.rb +40 -0
  73. data/spec/db_specific_connection_extenders_spec.rb +34 -0
  74. data/spec/db_specific_replication_extenders_spec.rb +38 -0
  75. data/spec/direct_table_scan_spec.rb +61 -0
  76. data/spec/dolphins.jpg +0 -0
  77. data/spec/generate_runner_spec.rb +84 -0
  78. data/spec/initializer_spec.rb +46 -0
  79. data/spec/log_helper_spec.rb +39 -0
  80. data/spec/logged_change_loader_spec.rb +68 -0
  81. data/spec/logged_change_spec.rb +470 -0
  82. data/spec/noisy_connection_spec.rb +78 -0
  83. data/spec/postgresql_replication_spec.rb +48 -0
  84. data/spec/postgresql_schema_support_spec.rb +212 -0
  85. data/spec/postgresql_support_spec.rb +63 -0
  86. data/spec/progress_bar_spec.rb +77 -0
  87. data/spec/proxied_table_scan_spec.rb +151 -0
  88. data/spec/proxy_block_cursor_spec.rb +197 -0
  89. data/spec/proxy_connection_spec.rb +423 -0
  90. data/spec/proxy_cursor_spec.rb +56 -0
  91. data/spec/proxy_row_cursor_spec.rb +66 -0
  92. data/spec/proxy_runner_spec.rb +70 -0
  93. data/spec/replication_difference_spec.rb +161 -0
  94. data/spec/replication_extender_interface_spec.rb +367 -0
  95. data/spec/replication_extenders_spec.rb +32 -0
  96. data/spec/replication_helper_spec.rb +178 -0
  97. data/spec/replication_initializer_spec.rb +509 -0
  98. data/spec/replication_run_spec.rb +443 -0
  99. data/spec/replication_runner_spec.rb +254 -0
  100. data/spec/replicators_spec.rb +36 -0
  101. data/spec/rubyrep_spec.rb +8 -0
  102. data/spec/scan_detail_reporter_spec.rb +119 -0
  103. data/spec/scan_progress_printers_spec.rb +68 -0
  104. data/spec/scan_report_printers_spec.rb +67 -0
  105. data/spec/scan_runner_spec.rb +50 -0
  106. data/spec/scan_summary_reporter_spec.rb +61 -0
  107. data/spec/session_spec.rb +253 -0
  108. data/spec/spec.opts +1 -0
  109. data/spec/spec_helper.rb +305 -0
  110. data/spec/strange_name_support_spec.rb +135 -0
  111. data/spec/sync_helper_spec.rb +169 -0
  112. data/spec/sync_runner_spec.rb +78 -0
  113. data/spec/syncers_spec.rb +171 -0
  114. data/spec/table_scan_helper_spec.rb +36 -0
  115. data/spec/table_scan_spec.rb +49 -0
  116. data/spec/table_sorter_spec.rb +30 -0
  117. data/spec/table_spec_resolver_spec.rb +111 -0
  118. data/spec/table_sync_spec.rb +140 -0
  119. data/spec/task_sweeper_spec.rb +47 -0
  120. data/spec/trigger_mode_switcher_spec.rb +83 -0
  121. data/spec/two_way_replicator_spec.rb +721 -0
  122. data/spec/two_way_syncer_spec.rb +256 -0
  123. data/spec/type_casting_cursor_spec.rb +50 -0
  124. data/spec/uninstall_runner_spec.rb +93 -0
  125. metadata +190 -0
@@ -0,0 +1,61 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe DirectTableScan do
6
+ before(:each) do
7
+ Initializer.configuration = standard_config
8
+ end
9
+
10
+ it "run should compare all the records in the table" do
11
+ session = Session.new
12
+ scan = DirectTableScan.new session, 'scanner_records'
13
+ diff = []
14
+ scan.run do |type, row|
15
+ diff.push [type, row]
16
+ end
17
+ # in this scenario the right table has the 'highest' data,
18
+ # so 'right-sided' data are already implicitely tested here
19
+ diff.should == [
20
+ [:conflict, [
21
+ {'id' => 2, 'name' => 'Bob - left database version'},
22
+ {'id' => 2, 'name' => 'Bob - right database version'}]],
23
+ [:left, {'id' => 3, 'name' => 'Charlie - exists in left database only'}],
24
+ [:right, {'id' => 4, 'name' => 'Dave - exists in right database only'}],
25
+ [:left, {'id' => 5, 'name' => 'Eve - exists in left database only'}],
26
+ [:right, {'id' => 6, 'name' => 'Fred - exists in right database only'}]
27
+ ]
28
+ end
29
+
30
+ it "run should handle one-sided data" do
31
+ # separate test case for left-sided data; right-sided data are already covered in the general test
32
+ session = Session.new
33
+ scan = DirectTableScan.new session, 'scanner_left_records_only'
34
+ diff = []
35
+ scan.run do |type, row|
36
+ diff.push [type, row]
37
+ end
38
+ diff.should == [
39
+ [:left, {'id' => 1, 'name' => 'Alice'}],
40
+ [:left, {'id' => 2, 'name' => 'Bob'}]
41
+ ]
42
+ end
43
+
44
+ it "run should update the progress" do
45
+ session = Session.new
46
+ scan = DirectTableScan.new session, 'scanner_records'
47
+ number_steps = 0
48
+ scan.should_receive(:update_progress).any_number_of_times do |steps|
49
+ number_steps += steps
50
+ end
51
+ scan.run {|_, _|}
52
+ number_steps.should == 8
53
+ end
54
+
55
+ it "run should update the progress even if there are no records" do
56
+ # it should do that to ensure the progress bar is printed
57
+ scan = DirectTableScan.new Session.new, 'extender_no_record'
58
+ scan.should_receive(:update_progress).at_least(:once)
59
+ scan.run {|_, _|}
60
+ end
61
+ end
Binary file
@@ -0,0 +1,84 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe GenerateRunner do
6
+ before(:each) do
7
+ end
8
+
9
+ it "should register itself with CommandRunner" do
10
+ CommandRunner.commands['generate'][:command].should == GenerateRunner
11
+ CommandRunner.commands['generate'][:description].should be_an_instance_of(String)
12
+ end
13
+
14
+ it "process_options should make options as nil and teturn status as 1 if command line parameters are unknown" do
15
+ # also verify that an error message is printed
16
+ $stderr.should_receive(:puts).any_number_of_times
17
+ runner = GenerateRunner.new
18
+ status = runner.process_options ["--nonsense"]
19
+ runner.options.should == nil
20
+ status.should == 1
21
+ end
22
+
23
+ it "process_options should make options as nil and return status as 1 if file name is not given" do
24
+ # also verify that an error message is printed
25
+ $stderr.should_receive(:puts).any_number_of_times
26
+ runner = GenerateRunner.new
27
+ status = runner.process_options []
28
+ runner.options.should == nil
29
+ status.should == 1
30
+ end
31
+
32
+ it "process_options should make options as nil and return status as 0 if command line includes '--help'" do
33
+ # also verify that the help message is printed
34
+ $stderr.should_receive(:puts)
35
+ runner = GenerateRunner.new
36
+ status = runner.process_options ["--help"]
37
+ runner.options.should == nil
38
+ status.should == 0
39
+ end
40
+
41
+ it "process_options should set the correct options" do
42
+ runner = GenerateRunner.new
43
+ runner.process_options ["my_file_name"]
44
+ runner.options[:file_name].should == 'my_file_name'
45
+ end
46
+
47
+ it "run should not start the generate command if the command line is invalid" do
48
+ $stderr.should_receive(:puts).any_number_of_times
49
+ GenerateRunner.any_instance_should_not_receive(:execute) {
50
+ GenerateRunner.run(["--nonsense"])
51
+ }
52
+ end
53
+
54
+ it "run should start an uninstall if the command line is correct" do
55
+ GenerateRunner.any_instance_should_receive(:execute) {
56
+ GenerateRunner.run(["my_file_name"])
57
+ }
58
+ end
59
+
60
+ it "execute should refuse to overwrite an existing file" do
61
+ begin
62
+ File.open("my_config_template", 'w') do |f|
63
+ f.write 'bla'
64
+ end
65
+ runner = GenerateRunner.new
66
+ runner.options = {:file_name => 'my_config_template'}
67
+ lambda {runner.execute}.should raise_error(/refuse/)
68
+ ensure
69
+ File.delete('my_config_template') rescue nil
70
+ end
71
+ end
72
+
73
+ it "execute should create the configuration template under the specified name" do
74
+ begin
75
+ runner = GenerateRunner.new
76
+ runner.options = {:file_name => 'my_config_template'}
77
+ runner.execute
78
+ File.exists?('my_config_template').should be_true
79
+ ensure
80
+ File.delete 'my_config_template' rescue nil
81
+ end
82
+ end
83
+
84
+ end
@@ -0,0 +1,46 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe Initializer do
6
+ it "should have an empty configuration" do
7
+ Initializer::configuration.should be_an_instance_of(Configuration)
8
+ end
9
+ end
10
+
11
+ describe Initializer do
12
+ before(:each) do
13
+ Initializer::reset
14
+ end
15
+
16
+ it "run should yield the configuration object" do
17
+ Initializer::run do |config|
18
+ config.should be_an_instance_of(Configuration)
19
+ end
20
+ end
21
+
22
+ def make_dummy_configuration_change
23
+ Initializer::run do |config|
24
+ config.left = :dummy
25
+ end
26
+ end
27
+
28
+ it "configuration should return the current configuration" do
29
+ make_dummy_configuration_change
30
+ Initializer::configuration.should be_an_instance_of(Configuration)
31
+ Initializer::configuration.left.should == :dummy
32
+ end
33
+
34
+ it "configuration= should set a new configuration" do
35
+ make_dummy_configuration_change
36
+ Initializer::configuration = :dummy_config
37
+ Initializer::configuration.should == :dummy_config
38
+ end
39
+
40
+ it "reset should clear the configuration" do
41
+ make_dummy_configuration_change
42
+ Initializer::reset
43
+ Initializer::configuration.left.should {}
44
+ end
45
+ end
46
+
@@ -0,0 +1,39 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ class MyLogHelper
6
+ include LogHelper
7
+ end
8
+
9
+ describe LogHelper do
10
+ before(:each) do
11
+ end
12
+
13
+ it "should do nothing if the description fields are small enough" do
14
+ MyLogHelper.new.fit_description_columns("bla", "blub").
15
+ should == %w(bla blub)
16
+ end
17
+
18
+ it "should cut details to fit into the 'long_description' column" do
19
+ MyLogHelper.new.fit_description_columns(
20
+ "bla",
21
+ "x" * (ReplicationInitializer::LONG_DESCRIPTION_SIZE - 1) + "yz").
22
+ should == ["bla", "x" * (ReplicationInitializer::LONG_DESCRIPTION_SIZE - 1) + "y"]
23
+ end
24
+
25
+ it "should cut outcome to fit into the 'description' column" do
26
+ MyLogHelper.new.fit_description_columns(
27
+ "x" * (ReplicationInitializer::DESCRIPTION_SIZE - 1) + "yz",
28
+ "blub")[0].
29
+ should == "x" * (ReplicationInitializer::DESCRIPTION_SIZE - 1) + "y"
30
+ end
31
+
32
+ it "should carry over a long outcome into the 'long_description' column" do
33
+ MyLogHelper.new.fit_description_columns(
34
+ "x" * (ReplicationInitializer::DESCRIPTION_SIZE - 1) + "yz",
35
+ "blub")[1].
36
+ should == "x" * (ReplicationInitializer::DESCRIPTION_SIZE - 1) + "yz\nblub"
37
+ end
38
+
39
+ end
@@ -0,0 +1,68 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe LoggedChangeLoaders do
6
+ before(:each) do
7
+ Initializer.configuration = standard_config
8
+ end
9
+
10
+ it "initializers should create both logged change loaders" do
11
+ session = Session.new
12
+ loaders = LoggedChangeLoaders.new(session)
13
+ loaders[:left].session.should == session
14
+ loaders[:left].database.should == :left
15
+ loaders[:right].database.should == :right
16
+ end
17
+
18
+ it "update should execute a forced update of both logged change loaders" do
19
+ session = Session.new
20
+ loaders = LoggedChangeLoaders.new(session)
21
+ loaders[:left].should_receive(:update).with(:forced => true)
22
+ loaders[:right].should_receive(:update).with(:forced => true)
23
+ loaders.update
24
+ end
25
+
26
+ end
27
+
28
+ describe LoggedChangeLoader do
29
+ before(:each) do
30
+ Initializer.configuration = standard_config
31
+ end
32
+
33
+ # Note:
34
+ # LoggedChangeLoader is a helper for LoggedChange.
35
+ # It is tested through the specs for LoggedChange.
36
+
37
+ it "oldest_change_time should return nil if there are no changes" do
38
+ session = Session.new
39
+ session.left.execute "delete from rr_pending_changes"
40
+ loader = LoggedChangeLoader.new session, :left
41
+ loader.oldest_change_time.should be_nil
42
+ end
43
+
44
+ it "oldest_change_time should return the time of the oldest change" do
45
+ session = Session.new
46
+ session.left.begin_db_transaction
47
+ begin
48
+ time = Time.now
49
+ session.left.insert_record 'rr_pending_changes', {
50
+ 'change_table' => 'left_table',
51
+ 'change_key' => 'id|1',
52
+ 'change_type' => 'I',
53
+ 'change_time' => time
54
+ }
55
+ session.left.insert_record 'rr_pending_changes', {
56
+ 'change_table' => 'left_table',
57
+ 'change_key' => 'id|2',
58
+ 'change_type' => 'I',
59
+ 'change_time' => 100.seconds.from_now
60
+ }
61
+ loader = LoggedChangeLoader.new session, :left
62
+ loader.oldest_change_time.should.to_s == time.to_s
63
+ ensure
64
+ session.left.rollback_db_transaction
65
+ end
66
+ end
67
+
68
+ end
@@ -0,0 +1,470 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe LoggedChange do
6
+ before(:each) do
7
+ Initializer.configuration = standard_config
8
+ end
9
+
10
+ it "initialize should store session and database" do
11
+ session = Session.new
12
+ loader = LoggedChangeLoader.new session, :left
13
+ change = LoggedChange.new loader
14
+ change.session.should == session
15
+ change.database.should == :left
16
+ end
17
+
18
+ it "load_specified should load the specified change" do
19
+ session = Session.new
20
+ session.left.begin_db_transaction
21
+ begin
22
+ session.left.insert_record 'rr_pending_changes', {
23
+ 'change_table' => 'left_table',
24
+ 'change_key' => 'id|1',
25
+ 'change_type' => 'I',
26
+ 'change_time' => Time.now
27
+ }
28
+ session.left.insert_record 'rr_pending_changes', {
29
+ 'change_table' => 'right_table',
30
+ 'change_key' => 'id|2',
31
+ 'change_new_key' => 'id|2',
32
+ 'change_type' => 'U',
33
+ 'change_time' => Time.now
34
+ }
35
+ session.left.insert_record 'rr_pending_changes', {
36
+ 'change_table' => 'left_table',
37
+ 'change_key' => 'id|2',
38
+ 'change_type' => 'I',
39
+ 'change_time' => Time.now
40
+ }
41
+ loader = LoggedChangeLoader.new session, :left
42
+ change = LoggedChange.new loader
43
+ change.load_specified 'left_table', {'id' => '2'}
44
+
45
+ change.table.should == 'left_table'
46
+ change.type.should == :insert
47
+ change.key.should == {'id' => '2'}
48
+ ensure
49
+ session.left.rollback_db_transaction
50
+ end
51
+ end
52
+
53
+ it "load_specified should accept a column_name => value hash as key" do
54
+ config = deep_copy(standard_config)
55
+ config.included_table_specs.clear
56
+ config.include_tables "scanner_records", :key => ['id1', 'id2']
57
+
58
+ session = Session.new config
59
+ session.left.begin_db_transaction
60
+ begin
61
+ session.left.insert_record 'rr_pending_changes', {
62
+ 'change_table' => 'scanner_records',
63
+ 'change_key' => 'id1|1|id2|2',
64
+ 'change_type' => 'I',
65
+ 'change_time' => Time.now
66
+ }
67
+ loader = LoggedChangeLoader.new session, :left
68
+ change = LoggedChange.new loader
69
+ change.load_specified 'scanner_records', {'id1' => 1, 'id2' => 2}
70
+
71
+ change.table.should == 'scanner_records'
72
+ change.type.should == :insert
73
+ change.key.should == {'id1' => '1', 'id2' => '2'}
74
+ ensure
75
+ session.left.rollback_db_transaction
76
+ end
77
+ end
78
+
79
+ it "load_specified should delete loaded changes from the database" do
80
+ session = Session.new
81
+ session.left.begin_db_transaction
82
+ begin
83
+ session.left.insert_record 'rr_pending_changes', {
84
+ 'change_table' => 'left_table',
85
+ 'change_key' => 'id|1',
86
+ 'change_type' => 'I',
87
+ 'change_time' => Time.now
88
+ }
89
+ loader = LoggedChangeLoader.new session, :left
90
+ change = LoggedChange.new loader
91
+ change.load_specified 'left_table', {'id' => 1}
92
+
93
+ session.left.
94
+ select_one("select * from rr_pending_changes where change_key = 'id|1'").
95
+ should be_nil
96
+ ensure
97
+ session.left.rollback_db_transaction
98
+ end
99
+ end
100
+
101
+ it "load_specified should set first_change_at and last_changed_at correctly" do
102
+ session = Session.new
103
+ session.left.begin_db_transaction
104
+ begin
105
+ t1 = 5.seconds.ago
106
+ t2 = 5.seconds.from_now
107
+ session.left.insert_record 'rr_pending_changes', {
108
+ 'change_table' => 'left_table',
109
+ 'change_key' => 'id|1',
110
+ 'change_type' => 'I',
111
+ 'change_time' => t1
112
+ }
113
+ session.left.insert_record 'rr_pending_changes', {
114
+ 'change_table' => 'left_table',
115
+ 'change_key' => 'id|1',
116
+ 'change_new_key' => 'id|1',
117
+ 'change_type' => 'U',
118
+ 'change_time' => t2
119
+ }
120
+ loader = LoggedChangeLoader.new session, :left
121
+ change = LoggedChange.new loader
122
+ change.load_specified 'left_table', {'id' => 1}
123
+
124
+ (change.first_changed_at - t1).abs.should < 1
125
+ (change.last_changed_at - t2).abs.should < 1
126
+ ensure
127
+ session.left.rollback_db_transaction
128
+ end
129
+ end
130
+
131
+ it "load_specified should follow primary key updates correctly" do
132
+ session = Session.new
133
+ session.left.begin_db_transaction
134
+ begin
135
+ session.left.insert_record 'rr_pending_changes', {
136
+ 'change_table' => 'left_table',
137
+ 'change_key' => 'id|1',
138
+ 'change_new_key' => 'id|2',
139
+ 'change_type' => 'U',
140
+ 'change_time' => Time.now
141
+ }
142
+ session.left.insert_record 'rr_pending_changes', {
143
+ 'change_table' => 'left_table',
144
+ 'change_key' => 'id|2',
145
+ 'change_new_key' => 'id|3',
146
+ 'change_type' => 'U',
147
+ 'change_time' => Time.now
148
+ }
149
+ loader = LoggedChangeLoader.new session, :left
150
+ change = LoggedChange.new loader
151
+ change.load_specified 'left_table', {'id' => 1}
152
+
153
+ change.type.should == :update
154
+ change.key.should == {'id' => 1}
155
+ change.new_key.should == {'id' => '3'}
156
+ ensure
157
+ session.left.rollback_db_transaction
158
+ end
159
+ end
160
+
161
+ it "load_specified should recognize if changes cancel each other out" do
162
+ session = Session.new
163
+ session.left.begin_db_transaction
164
+ begin
165
+ session.left.insert_record 'rr_pending_changes', {
166
+ 'change_table' => 'left_table',
167
+ 'change_key' => 'id|1',
168
+ 'change_type' => 'I',
169
+ 'change_time' => Time.now
170
+ }
171
+ session.left.insert_record 'rr_pending_changes', {
172
+ 'change_table' => 'left_table',
173
+ 'change_key' => 'id|1',
174
+ 'change_new_key' => 'id|2',
175
+ 'change_type' => 'U',
176
+ 'change_time' => Time.now
177
+ }
178
+ session.left.insert_record 'rr_pending_changes', {
179
+ 'change_table' => 'left_table',
180
+ 'change_key' => 'id|2',
181
+ 'change_type' => 'D',
182
+ 'change_time' => Time.now
183
+ }
184
+ loader = LoggedChangeLoader.new session, :left
185
+ change = LoggedChange.new loader
186
+ change.load_specified 'left_table', {'id' => '1'}
187
+
188
+ change.type.should == :no_change
189
+ ensure
190
+ session.left.rollback_db_transaction
191
+ end
192
+ end
193
+
194
+ it "load_specified should transist states correctly" do
195
+ session = Session.new
196
+ session.left.begin_db_transaction
197
+ begin
198
+
199
+ # first test case
200
+ session.left.insert_record 'rr_pending_changes', {
201
+ 'change_table' => 'left_table',
202
+ 'change_key' => 'id|1',
203
+ 'change_type' => 'I',
204
+ 'change_time' => Time.now
205
+ }
206
+ session.left.insert_record 'rr_pending_changes', {
207
+ 'change_table' => 'left_table',
208
+ 'change_key' => 'id|1',
209
+ 'change_type' => 'D',
210
+ 'change_time' => Time.now
211
+ }
212
+ session.left.insert_record 'rr_pending_changes', {
213
+ 'change_table' => 'left_table',
214
+ 'change_key' => 'id|1',
215
+ 'change_type' => 'I',
216
+ 'change_time' => Time.now
217
+ }
218
+ session.left.insert_record 'rr_pending_changes', {
219
+ 'change_table' => 'left_table',
220
+ 'change_key' => 'id|1',
221
+ 'change_new_key' => 'id|2',
222
+ 'change_type' => 'U',
223
+ 'change_time' => Time.now
224
+ }
225
+ loader = LoggedChangeLoader.new session, :left
226
+ change = LoggedChange.new loader
227
+ change.load_specified 'left_table', {'id' => '1'}
228
+ change.type.should == :insert
229
+ change.key.should == {'id' => '2'}
230
+
231
+ # second test case
232
+ session.left.insert_record 'rr_pending_changes', {
233
+ 'change_table' => 'left_table',
234
+ 'change_key' => 'id|5',
235
+ 'change_type' => 'D',
236
+ 'change_time' => Time.now
237
+ }
238
+ session.left.insert_record 'rr_pending_changes', {
239
+ 'change_table' => 'left_table',
240
+ 'change_key' => 'id|5',
241
+ 'change_type' => 'I',
242
+ 'change_time' => Time.now
243
+ }
244
+ loader.update :forced => true
245
+ change = LoggedChange.new loader
246
+ change.load_specified 'left_table', {'id' => '5'}
247
+ change.type.should == :update
248
+ change.key.should == {'id' => '5'}
249
+ change.new_key.should == {'id' => '5'}
250
+ ensure
251
+ session.left.rollback_db_transaction
252
+ end
253
+ end
254
+
255
+ it "amend should work if there were no changes" do
256
+ session = Session.new
257
+ session.left.begin_db_transaction
258
+ begin
259
+ session.left.insert_record 'rr_pending_changes', {
260
+ 'change_table' => 'scanner_records',
261
+ 'change_key' => 'id|1',
262
+ 'change_type' => 'I',
263
+ 'change_time' => Time.now
264
+ }
265
+ loader = LoggedChangeLoader.new session, :left
266
+ change = LoggedChange.new loader
267
+ change.load_specified 'scanner_records', {'id' => '1'}
268
+
269
+ change.table.should == 'scanner_records'
270
+ change.type.should == :insert
271
+ change.key.should == {'id' => '1'}
272
+
273
+ change.load
274
+
275
+ change.table.should == 'scanner_records'
276
+ change.type.should == :insert
277
+ change.key.should == {'id' => '1'}
278
+ ensure
279
+ session.left.rollback_db_transaction
280
+ end
281
+ end
282
+
283
+ it "amend should work if the current type is :no_change" do
284
+ session = Session.new
285
+ session.left.begin_db_transaction
286
+ begin
287
+ loader = LoggedChangeLoader.new session, :left
288
+ change = LoggedChange.new loader
289
+ change.load_specified 'scanner_records', {'id' => '1'}
290
+
291
+ change.table.should == 'scanner_records'
292
+ change.type.should == :no_change
293
+ change.key.should == {'id' => '1'}
294
+
295
+ change.load
296
+
297
+ change.table.should == 'scanner_records'
298
+ change.type.should == :no_change
299
+ change.key.should == {'id' => '1'}
300
+ ensure
301
+ session.left.rollback_db_transaction
302
+ end
303
+ end
304
+
305
+ it "amend should amend the change correctly" do
306
+ session = Session.new
307
+ session.left.begin_db_transaction
308
+ begin
309
+ session.left.insert_record 'left_table', {
310
+ :id => '1',
311
+ :name => 'bla'
312
+ }
313
+ session.left.insert_record 'rr_pending_changes', {
314
+ 'change_table' => 'left_table',
315
+ 'change_key' => 'id|1',
316
+ 'change_new_key' => 'id|1',
317
+ 'change_type' => 'U',
318
+ 'change_time' => Time.now
319
+ }
320
+ loader = LoggedChangeLoader.new session, :left
321
+ change = LoggedChange.new loader
322
+ change.load_specified 'left_table', {'id' => '1'}
323
+ session.left.insert_record 'rr_pending_changes', {
324
+ 'change_table' => 'left_table',
325
+ 'change_key' => 'id|1',
326
+ 'change_type' => 'D',
327
+ 'change_time' => Time.now
328
+ }
329
+ loader.update :forced => true
330
+ change.load
331
+
332
+ change.table.should == 'left_table'
333
+ change.type.should == :delete
334
+ change.key.should == {'id' => '1'}
335
+ ensure
336
+ session.left.rollback_db_transaction
337
+ end
338
+ end
339
+
340
+ it "amend should support primary key updates" do
341
+ session = Session.new
342
+ session.left.begin_db_transaction
343
+ begin
344
+ session.left.insert_record 'left_table', {
345
+ :id => '1',
346
+ :name => 'bla'
347
+ }
348
+ session.left.insert_record 'rr_pending_changes', {
349
+ 'change_table' => 'left_table',
350
+ 'change_key' => 'id|1',
351
+ 'change_new_key' => 'id|2',
352
+ 'change_type' => 'U',
353
+ 'change_time' => Time.now
354
+ }
355
+ loader = LoggedChangeLoader.new session, :left
356
+ change = LoggedChange.new loader
357
+ change.load_specified 'left_table', {'id' => '1'}
358
+ session.left.insert_record 'rr_pending_changes', {
359
+ 'change_table' => 'left_table',
360
+ 'change_key' => 'id|2',
361
+ 'change_new_key' => 'id|3',
362
+ 'change_type' => 'U',
363
+ 'change_time' => Time.now
364
+ }
365
+ loader.update :forced => true
366
+ change.load
367
+
368
+ change.table.should == 'left_table'
369
+ change.type.should == :update
370
+ change.key.should == {'id' => '1'}
371
+ change.new_key.should == {'id' => '3'}
372
+ ensure
373
+ session.left.rollback_db_transaction
374
+ end
375
+ end
376
+
377
+ it "key_from_raw_key should return the correct column_name => value hash for the given key" do
378
+ loader = LoggedChangeLoader.new Session.new, :left
379
+ change = LoggedChange.new loader
380
+ change.key_to_hash("a|1|b|2").should == {
381
+ 'a' => '1',
382
+ 'b' => '2'
383
+ }
384
+ end
385
+
386
+ it "key_from_raw_key should work with multi character key_sep strings" do
387
+ loader = LoggedChangeLoader.new Session.new, :left
388
+ change = LoggedChange.new loader
389
+ change.stub!(:key_sep).and_return('BLA')
390
+ change.key_to_hash("aBLA1BLAbBLA2").should == {
391
+ 'a' => '1',
392
+ 'b' => '2'
393
+ }
394
+ end
395
+
396
+ it "load_oldest should not load a change if none available" do
397
+ loader = LoggedChangeLoader.new Session.new, :left
398
+ change = LoggedChange.new loader
399
+ change.should_not_receive :load_specified
400
+ change.load_oldest
401
+ end
402
+
403
+ it "load_oldest should load the oldest available change" do
404
+ session = Session.new
405
+ session.left.begin_db_transaction
406
+ begin
407
+ session.left.insert_record 'rr_pending_changes', {
408
+ 'change_table' => 'left_table',
409
+ 'change_key' => 'id|1',
410
+ 'change_type' => 'I',
411
+ 'change_time' => Time.now
412
+ }
413
+ session.left.insert_record 'rr_pending_changes', {
414
+ 'change_table' => 'left_table',
415
+ 'change_key' => 'id|2',
416
+ 'change_type' => 'I',
417
+ 'change_time' => Time.now
418
+ }
419
+ loader = LoggedChangeLoader.new session, :left
420
+ change = LoggedChange.new loader
421
+ change.load_oldest
422
+
423
+ change.key.should == {'id' => '1'}
424
+ ensure
425
+ session.left.rollback_db_transaction
426
+ end
427
+ end
428
+
429
+ it "load_oldest should skip irrelevant changes" do
430
+ session = Session.new
431
+ session.left.begin_db_transaction
432
+ begin
433
+ session.left.insert_record 'rr_pending_changes', {
434
+ 'change_table' => 'left_table',
435
+ 'change_key' => 'id|1',
436
+ 'change_type' => 'I',
437
+ 'change_time' => Time.now
438
+ }
439
+ session.left.insert_record 'rr_pending_changes', {
440
+ 'change_table' => 'left_table',
441
+ 'change_key' => 'id|1',
442
+ 'change_type' => 'D',
443
+ 'change_time' => Time.now
444
+ }
445
+ session.left.insert_record 'rr_pending_changes', {
446
+ 'change_table' => 'left_table',
447
+ 'change_key' => 'id|2',
448
+ 'change_type' => 'I',
449
+ 'change_time' => Time.now
450
+ }
451
+ loader = LoggedChangeLoader.new session, :left
452
+ change = LoggedChange.new loader
453
+ change.load_oldest
454
+
455
+ change.type.should == :insert
456
+ change.key.should == {'id' => '2'}
457
+ ensure
458
+ session.left.rollback_db_transaction
459
+ end
460
+ end
461
+
462
+ it "to_yaml should blank out session and loader" do
463
+ session = Session.new
464
+ loader = LoggedChangeLoader.new session, :left
465
+ change = LoggedChange.new loader
466
+ yaml = change.to_yaml
467
+ yaml.should_not =~ /session/
468
+ yaml.should_not =~ /loader/
469
+ end
470
+ end