andyjeffries-rubyrep 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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