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,399 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe ProxyConnection do
6
+ before(:each) do
7
+ Initializer.configuration = proxied_config
8
+ @connection = ProxyConnection.new Initializer.configuration.left
9
+ end
10
+
11
+ it "initialize should connect to the database" do
12
+ @connection.connection.active?.should == true
13
+ end
14
+
15
+ it "initialize should store the configuratin" do
16
+ @connection.config.should == Initializer.configuration.left
17
+ end
18
+
19
+ it "destroy should disconnect from the database" do
20
+ @connection.destroy
21
+
22
+ @connection.connection.active?.should == false
23
+ end
24
+
25
+ it "refresh should not do anything if the connection is still active" do
26
+ ConnectionExtenders.should_not_receive(:db_connect)
27
+ @connection.refresh
28
+ end
29
+
30
+ it "refresh should reestablish the connection if it is no more active" do
31
+ @connection.destroy
32
+ @connection.refresh
33
+ @connection.connection.should be_active
34
+ end
35
+
36
+ it "cursors should return the current cursor hash or an empty hash if nil" do
37
+ @connection.cursors.should == {}
38
+ @connection.cursors[:dummy_cursor] = :dummy_cursor
39
+ @connection.cursors.should == {:dummy_cursor => :dummy_cursor}
40
+ end
41
+
42
+ it "save_cursor should register the provided cursor" do
43
+ @connection.save_cursor :dummy_cursor
44
+
45
+ @connection.cursors[:dummy_cursor].should == :dummy_cursor
46
+ end
47
+
48
+ it "destroy should destroy and unregister any stored cursors" do
49
+ cursor = mock("Cursor")
50
+ cursor.should_receive(:destroy)
51
+
52
+ @connection.save_cursor cursor
53
+ @connection.destroy
54
+
55
+ @connection.cursors.should == {}
56
+ end
57
+
58
+ it "destroy_cursor should destroy and unregister the provided cursor" do
59
+ cursor = mock("Cursor")
60
+ cursor.should_receive(:destroy)
61
+
62
+ @connection.save_cursor cursor
63
+ @connection.destroy_cursor cursor
64
+
65
+ @connection.cursors.should == {}
66
+ end
67
+
68
+ it "create_cursor should create and register the cursor and initiate row fetching" do
69
+ cursor = @connection.create_cursor(
70
+ ProxyRowCursor,
71
+ 'scanner_records',
72
+ :from => {'id' => 2},
73
+ :to => {'id' => 2}
74
+ )
75
+
76
+ cursor.should be_an_instance_of(ProxyRowCursor)
77
+ cursor.next_row_keys_and_checksum[0].should == {'id' => 2} # verify that 'from' range was used
78
+ cursor.next?.should be_false # verify that 'to' range was used
79
+ end
80
+
81
+ it "column_names should return the column names of the specified table" do
82
+ @connection.column_names('scanner_records').should == ['id', 'name']
83
+ end
84
+
85
+ it "column_names should cache the column names" do
86
+ @connection.column_names('scanner_records')
87
+ @connection.column_names('scanner_text_key')
88
+ @connection.connection.should_not_receive(:columns)
89
+ @connection.column_names('scanner_records').should == ['id', 'name']
90
+ end
91
+
92
+ it "primary_key_names should return the correct primary keys" do
93
+ @connection.primary_key_names('scanner_records').should == ['id']
94
+ end
95
+
96
+ it "primary_key_names should return the manual primary keys if they exist" do
97
+ @connection.stub!(:manual_primary_keys).
98
+ and_return({'scanner_records' => ['manual_key']})
99
+ @connection.primary_key_names('scanner_records').should == ['manual_key']
100
+ end
101
+
102
+ it "primary_key_names should not cache or manually overwrite if :raw option is given" do
103
+ @connection.stub!(:manual_primary_keys).
104
+ and_return({'scanner_records' => ['manual_key']})
105
+ key1 = @connection.primary_key_names('scanner_records', :raw => true)
106
+ key1.should == ['id']
107
+
108
+ key2 = @connection.primary_key_names('scanner_records', :raw => true)
109
+ key1.__id__.should_not == key2.__id__
110
+ end
111
+
112
+ it "primary_key_names should cache the primary primary keys" do
113
+ @connection.connection.should_receive(:primary_key_names) \
114
+ .with('dummy_table').once.and_return(['dummy_key'])
115
+ @connection.connection.should_receive(:primary_key_names) \
116
+ .with('dummy_table2').once.and_return(['dummy_key2'])
117
+
118
+ @connection.primary_key_names('dummy_table').should == ['dummy_key']
119
+ @connection.primary_key_names('dummy_table2').should == ['dummy_key2']
120
+ @connection.primary_key_names('dummy_table').should == ['dummy_key']
121
+ end
122
+
123
+ it "select_cursor should forwarded the handed over query" do
124
+ @connection.connection.should_receive(:select_cursor).with('bla', 123)
125
+ @connection.select_cursor(:query => 'bla', :row_buffer_size => 123)
126
+ end
127
+
128
+ it "select_cursor should use the default row buffer size if no explicit value is specified" do
129
+ @connection.connection.should_receive(:select_cursor) \
130
+ .with('bla', ProxyConnection::DEFAULT_ROW_BUFFER_SIZE)
131
+ @connection.select_cursor(:query => 'bla')
132
+ end
133
+
134
+ it "select_cursor should construct the query and execute it" do
135
+ @connection.connection.should_receive(:select_cursor) \
136
+ .with(sql_to_regexp('select "id", "name" from "scanner_records" order by "id"'), ProxyConnection::DEFAULT_ROW_BUFFER_SIZE)
137
+ @connection.select_cursor(:table => 'scanner_records')
138
+ end
139
+
140
+ it "select_cursor should return a type casting cursor if :type_cast option is specified" do
141
+ @connection.select_cursor(:table => 'scanner_records').
142
+ should_not be_an_instance_of(TypeCastingCursor)
143
+ @connection.select_cursor(:table => 'scanner_records', :type_cast => true).
144
+ should be_an_instance_of(TypeCastingCursor)
145
+ end
146
+
147
+ it "table_select_query should handle queries without any conditions" do
148
+ @connection.table_select_query('scanner_records') \
149
+ .should =~ sql_to_regexp("\
150
+ select 'id', 'name' from 'scanner_records'\
151
+ order by 'id'")
152
+ end
153
+
154
+ it "table_select_query should handle queries with only a from condition" do
155
+ @connection.table_select_query('scanner_records', :from => {'id' => 1}) \
156
+ .should =~ sql_to_regexp("\
157
+ select 'id', 'name' from 'scanner_records' \
158
+ where ('id') >= (1) order by 'id'")
159
+ end
160
+
161
+ it "table_select_query should handle queries with an exclusive from condition" do
162
+ @connection.table_select_query(
163
+ 'scanner_records',
164
+ :from => {'id' => 1},
165
+ :exclude_starting_row => true
166
+ ).should =~ sql_to_regexp("\
167
+ select 'id', 'name' from 'scanner_records' \
168
+ where ('id') > (1) order by 'id'")
169
+ end
170
+
171
+ it "table_select_query should handle queries with only a to condition" do
172
+ @connection.table_select_query('scanner_text_key', :to => {'text_id' => 'k1'}) \
173
+ .should =~ sql_to_regexp("\
174
+ select 'text_id', 'name' from 'scanner_text_key' \
175
+ where ('text_id') <= ('k1') order by 'text_id'")
176
+ end
177
+
178
+ it "table_select_query should handle queries with both from and to conditions" do
179
+ @connection.table_select_query('scanner_records',
180
+ :from => {'id' => 0}, :to => {'id' => 1}) \
181
+ .should =~ sql_to_regexp("\
182
+ select 'id', 'name' from 'scanner_records' \
183
+ where ('id') >= (0) and ('id') <= (1) order by 'id'")
184
+ end
185
+
186
+ it "table_select_query should handle queries for specific rows" do
187
+ @connection.table_select_query('scanner_records',
188
+ :row_keys => [{'id' => 0}, {'id' => 1}]) \
189
+ .should =~ sql_to_regexp("\
190
+ select 'id', 'name' from 'scanner_records' \
191
+ where ('id') in ((0), (1)) order by 'id'")
192
+ end
193
+
194
+ it "table_select_query should handle queries for specific rows with the row array actually being empty" do
195
+ @connection.table_select_query('scanner_records', :row_keys => []) \
196
+ .should =~ sql_to_regexp("\
197
+ select 'id', 'name' from 'scanner_records' \
198
+ where false order by 'id'")
199
+ end
200
+
201
+ it "table_select_query should handle queries for specific rows in combination with other conditions" do
202
+ @connection.table_select_query('scanner_records',
203
+ :from => {'id' => 0},
204
+ :row_keys => [{'id' => 1}, {'id' => 2}]) \
205
+ .should =~ sql_to_regexp("\
206
+ select 'id', 'name' from 'scanner_records' \
207
+ where ('id') >= (0) and ('id') in ((1), (2)) order by 'id'")
208
+ end
209
+
210
+ it "table_select_query should handle tables with combined primary keys" do
211
+ @connection.table_select_query('extender_combined_key',
212
+ :from => {'first_id' => 0, 'second_id' => 1},
213
+ :to => {'first_id' => 2, 'second_id' => 3}) \
214
+ .should =~ sql_to_regexp("\
215
+ select 'first_id', 'second_id', 'name' from 'extender_combined_key' \
216
+ where ('first_id', 'second_id') >= (0, 1) \
217
+ and ('first_id', 'second_id') <= (2, 3) \
218
+ order by 'first_id', 'second_id'")
219
+ end
220
+
221
+ it "table_select_query should quote column values" do
222
+ select_options = {:from => {'text_id' => 'a'}, :to => {'text_id' => 'b'}}
223
+
224
+ @connection.table_select_query('scanner_text_key', select_options) \
225
+ .should match(/'a'.*'b'/)
226
+
227
+ # additional check that the quoted query actually works
228
+ cursor = ProxyCursor.new(@connection, 'scanner_text_key')
229
+ results = cursor.prepare_fetch(select_options)
230
+ results.next_row.should == {'text_id' => 'a', 'name' => 'Alice'}
231
+ results.next_row.should == {'text_id' => 'b', 'name' => 'Bob'}
232
+ results.next?.should be_false
233
+ end
234
+
235
+ it "table_insert_query should return the correct SQL query" do
236
+ @connection.table_insert_query('scanner_records', 'name' => 'bla') \
237
+ .should =~ sql_to_regexp(%q!insert into "scanner_records"("name") values("bla")!)
238
+ end
239
+
240
+ it "insert_record should insert the specified record" do
241
+ @connection.begin_db_transaction
242
+ begin
243
+ @connection.insert_record('scanner_records', 'id' => 9, 'name' => 'bla')
244
+ @connection.select_one("select * from scanner_records where id = 9") \
245
+ .should == {'id' => '9', 'name' => 'bla'}
246
+ ensure
247
+ @connection.rollback_db_transaction
248
+ end
249
+ end
250
+
251
+ it "insert_record should handle combined primary keys" do
252
+ @connection.begin_db_transaction
253
+ begin
254
+ @connection.insert_record('extender_combined_key', 'first_id' => 8, 'second_id' => '9')
255
+ @connection.select_one(
256
+ "select first_id, second_id
257
+ from extender_combined_key where (first_id, second_id) = (8, 9)") \
258
+ .should == {'first_id' => '8', 'second_id' => '9'}
259
+ ensure
260
+ @connection.rollback_db_transaction
261
+ end
262
+ end
263
+
264
+ it "insert_record should write nil values correctly" do
265
+ @connection.begin_db_transaction
266
+ begin
267
+ @connection.insert_record('extender_combined_key', 'first_id' => 8, 'second_id' => '9', 'name' => nil)
268
+ @connection.select_one(
269
+ "select first_id, second_id, name
270
+ from extender_combined_key where (first_id, second_id) = (8, 9)") \
271
+ .should == {'first_id' => '8', 'second_id' => '9', "name" => nil}
272
+ ensure
273
+ @connection.rollback_db_transaction
274
+ end
275
+ end
276
+
277
+ it "insert_record should also insert uncommon data types correctly" do
278
+ @connection.begin_db_transaction
279
+ begin
280
+ test_data = {
281
+ 'id' => 2,
282
+ 'decimal_test' => 1.234,
283
+ 'timestamp' => Time.local(2008,"jun",9,20,15,1),
284
+ 'multi_byte' => "よろしくお願(ねが)いします yoroshiku onegai shimasu: I humbly ask for your favor.",
285
+ 'binary_test' => Marshal.dump(['bla',:dummy,1,2,3]),
286
+ 'text_test' => 'dummy text'
287
+ }
288
+ @connection.insert_record('extender_type_check', test_data)
289
+
290
+ cursor = @connection.select_cursor(
291
+ :table => 'extender_type_check',
292
+ :row_keys => [{'id' => 2}],
293
+ :type_cast => true
294
+ )
295
+ result_data = cursor.next_row
296
+ result_data.should == test_data
297
+ ensure
298
+ @connection.rollback_db_transaction
299
+ end
300
+ end
301
+
302
+ it "table_update_query should return the correct SQL query" do
303
+ @connection.table_update_query('scanner_records', 'id' => 1) \
304
+ .should =~ sql_to_regexp(%q!update "scanner_records" set "id" = 1 where ("id") = (1)!)
305
+ end
306
+
307
+ it "update_record should update the specified record" do
308
+ @connection.begin_db_transaction
309
+ begin
310
+ @connection.update_record('scanner_records', 'id' => 1, 'name' => 'update_test')
311
+ @connection.select_one("select * from scanner_records where id = 1") \
312
+ .should == {'id' => '1', 'name' => 'update_test'}
313
+ ensure
314
+ @connection.rollback_db_transaction
315
+ end
316
+ end
317
+
318
+ it "update_record should handle combined primary keys" do
319
+ @connection.begin_db_transaction
320
+ begin
321
+ @connection.update_record('extender_combined_key', 'first_id' => 1, 'second_id' => '1', 'name' => 'xy')
322
+ @connection.select_one(
323
+ "select first_id, second_id, name
324
+ from extender_combined_key where (first_id, second_id) = (1, 1)") \
325
+ .should == {'first_id' => '1', 'second_id' => '1', 'name' => 'xy'}
326
+ ensure
327
+ @connection.rollback_db_transaction
328
+ end
329
+ end
330
+
331
+ it "update_record should handle key changes" do
332
+ @connection.begin_db_transaction
333
+ begin
334
+ @connection.update_record 'extender_combined_key',
335
+ {'first_id' => '8', 'second_id' => '9', 'name' => 'xy'},
336
+ {'first_id' => '1', 'second_id' => '1'}
337
+ @connection.select_one(
338
+ "select first_id, second_id, name
339
+ from extender_combined_key where (first_id, second_id) = (8, 9)") \
340
+ .should == {'first_id' => '8', 'second_id' => '9', 'name' => 'xy'}
341
+ ensure
342
+ @connection.rollback_db_transaction
343
+ end
344
+ end
345
+
346
+ it "update_record should write nil values correctly" do
347
+ @connection.begin_db_transaction
348
+ begin
349
+ @connection.update_record('extender_combined_key', 'first_id' => 1, 'second_id' => '1', 'name' => nil)
350
+ @connection.select_one(
351
+ "select first_id, second_id, name
352
+ from extender_combined_key where (first_id, second_id) = (1, 1)") \
353
+ .should == {'first_id' => '1', 'second_id' => '1', 'name' => nil}
354
+ ensure
355
+ @connection.rollback_db_transaction
356
+ end
357
+ end
358
+
359
+ it "update_record should also update uncommon data types correctly" do
360
+ @connection.begin_db_transaction
361
+ begin
362
+ test_data = {
363
+ 'id' => 1,
364
+ 'decimal_test' => 0.234,
365
+ 'timestamp' => Time.local(2009,"jun",9,20,15,1),
366
+ 'multi_byte' => "よろしくお願(ねが)いします yoroshiku onegai shimasu: I humbly ask for your favor. bla",
367
+ 'binary_test' => Marshal.dump(['bla',:dummy,1,2,3,4]),
368
+ 'text_test' => 'dummy text bla'
369
+ }
370
+ @connection.update_record('extender_type_check', test_data)
371
+
372
+ org_cursor = @connection.select_cursor(:query => "select * from extender_type_check where id = 1")
373
+ cursor = TypeCastingCursor.new @connection, 'extender_type_check', org_cursor
374
+ result_data = cursor.next_row
375
+ result_data.should == test_data
376
+ ensure
377
+ @connection.rollback_db_transaction
378
+ end
379
+ end
380
+
381
+ it "table_delete_query should return the correct SQL query" do
382
+ @connection.table_delete_query('scanner_records', 'id' => 1) \
383
+ .should =~ sql_to_regexp(%q!delete from "scanner_records" where ("id") = (1)!)
384
+ end
385
+
386
+ it "delete_record should delete the specified record" do
387
+ @connection.begin_db_transaction
388
+ begin
389
+ @connection.delete_record('extender_combined_key', 'first_id' => 1, 'second_id' => '1', 'name' => 'xy')
390
+ @connection.select_one(
391
+ "select first_id, second_id, name
392
+ from extender_combined_key where (first_id, second_id) = (1, 1)") \
393
+ .should be_nil
394
+ ensure
395
+ @connection.rollback_db_transaction
396
+ end
397
+ end
398
+
399
+ end
@@ -0,0 +1,56 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe ProxyCursor do
6
+ before(:each) do
7
+ Initializer.configuration = proxied_config
8
+ end
9
+
10
+ it "initialize should store session and table and cache the primary keys of table" do
11
+ connection = create_mock_proxy_connection 'dummy_table', ['dummy_key']
12
+
13
+ cursor = ProxyCursor.new connection, 'dummy_table'
14
+
15
+ cursor.connection.should == connection
16
+ cursor.table.should == 'dummy_table'
17
+ cursor.primary_key_names.should == ['dummy_key']
18
+ end
19
+
20
+ it "prepare_fetch should initiate the query and wrap it for type casting" do
21
+ connection = ProxyConnection.new Initializer.configuration.left
22
+
23
+ cursor = ProxyCursor.new(connection, 'scanner_records')
24
+ cursor.prepare_fetch
25
+ cursor.cursor.should be_an_instance_of(TypeCastingCursor)
26
+ cursor.cursor.next_row.should == {'id' => 1, 'name' => 'Alice - exists in both databases'}
27
+ end
28
+
29
+ it "prepare_fetch called with option :row_keys should initiate the correct query" do
30
+ # Note: I am testing row_keys exclusively to make sure that this type of
31
+ # sub query will work correctly on all supported databases
32
+ connection = ProxyConnection.new Initializer.configuration.left
33
+
34
+ cursor = ProxyCursor.new(connection, 'extender_combined_key')
35
+ cursor.prepare_fetch :row_keys => [
36
+ {'first_id' => 1, 'second_id' => 1},
37
+ {'first_id' => 1, 'second_id' => 2}
38
+ ]
39
+ cursor.cursor.next_row.should == {'first_id' => 1, 'second_id' => 1, 'name' => 'aa'}
40
+ cursor.cursor.next_row.should == {'first_id' => 1, 'second_id' => 2, 'name' => 'ab'}
41
+ cursor.cursor.next?.should == false
42
+ end
43
+
44
+
45
+ it "destroy should clear and nil the cursor" do
46
+ connection = create_mock_proxy_connection 'dummy_table', ['dummy_key']
47
+ cursor = ProxyCursor.new connection, 'dummy_table'
48
+
49
+ table_cursor = mock("DBCursor")
50
+ table_cursor.should_receive(:clear)
51
+ cursor.cursor = table_cursor
52
+
53
+ cursor.destroy
54
+ cursor.cursor.should be_nil
55
+ end
56
+ end
@@ -0,0 +1,66 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe ProxyRowCursor do
6
+ before(:each) do
7
+ Initializer.configuration = standard_config
8
+ end
9
+
10
+ it "initialize should super to ProxyCursor" do
11
+ session = create_mock_proxy_connection 'dummy_table', ['dummy_id']
12
+ cursor = ProxyRowCursor.new session, 'dummy_table'
13
+ cursor.table.should == 'dummy_table'
14
+ end
15
+
16
+ it "next? should delegate to the DB cursor" do
17
+ session = create_mock_proxy_connection 'dummy_table', ['dummy_id']
18
+ cursor = ProxyRowCursor.new session, 'dummy_table'
19
+
20
+ table_cursor = mock("DBCursor")
21
+ table_cursor.should_receive(:next?).and_return(true)
22
+ cursor.cursor = table_cursor
23
+
24
+ cursor.next?.should == true
25
+ end
26
+
27
+ it "next_row should return the next row in the cursor" do
28
+ session = create_mock_proxy_connection 'dummy_table', ['dummy_id']
29
+ cursor = ProxyRowCursor.new session, 'dummy_table'
30
+
31
+ table_cursor = mock("DBCursor")
32
+ table_cursor.should_receive(:next_row).and_return(:dummy_row)
33
+ cursor.cursor = table_cursor
34
+
35
+ cursor.next_row.should == :dummy_row
36
+ end
37
+
38
+ it "next_row_keys_and_checksum should store the found row under current_row" do
39
+ session = create_mock_proxy_connection 'dummy_table', ['dummy_id']
40
+ cursor = ProxyRowCursor.new session, 'dummy_table'
41
+
42
+ table_cursor = mock("DBCursor")
43
+ table_cursor.should_receive(:next_row).and_return('dummy_id' => 'dummy_value')
44
+
45
+ cursor.cursor = table_cursor
46
+ cursor.next_row_keys_and_checksum
47
+ cursor.current_row.should == {'dummy_id' => 'dummy_value'}
48
+ end
49
+
50
+ it "next_row_keys_and_checksum should returns the primary_keys and checksum of the found row" do
51
+ session = ProxyConnection.new proxied_config.left
52
+
53
+ cursor = ProxyRowCursor.new session, 'scanner_records'
54
+ cursor.prepare_fetch
55
+
56
+ keys, checksum = cursor.next_row_keys_and_checksum
57
+
58
+ expected_checksum = Digest::SHA1.hexdigest(
59
+ Marshal.dump('id' => 1, 'name' => 'Alice - exists in both databases')
60
+ )
61
+
62
+ keys.should == {'id' => 1}
63
+ checksum.should == expected_checksum
64
+ end
65
+
66
+ end
@@ -0,0 +1,70 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe ProxyRunner do
6
+ before(:each) do
7
+ DRb.stub!(:start_service)
8
+ DRb.thread.stub!(:join)
9
+ $stderr.stub!(:puts)
10
+ end
11
+
12
+ it "get_options should return options as nil and status as 1 if command line parameters are unknown" do
13
+ # also verify that an error message is printed
14
+ $stderr.should_receive(:puts).any_number_of_times
15
+ options, status = ProxyRunner.new.get_options ["--nonsense"]
16
+ options.should == nil
17
+ status.should == 1
18
+ end
19
+
20
+ it "get_options should return options as nil and status as 0 if command line includes '--help'" do
21
+ # also verify that the help message is printed
22
+ $stderr.should_receive(:puts)
23
+ options, status = ProxyRunner.new.get_options ["--help"]
24
+ options.should == nil
25
+ status.should == 0
26
+ end
27
+
28
+ it "get_options should return the default options if none were given on the command line" do
29
+ options, status = ProxyRunner.new.get_options []
30
+ options.should == ProxyRunner::DEFAULT_OPTIONS
31
+ status.should == 0
32
+ end
33
+
34
+ it "get_options should return :host and :port options as per given command line" do
35
+ options, status = ProxyRunner.new.get_options ["--host", "127.0.0.1", "--port", "1234"]
36
+ options.should == {:host => '127.0.0.1', :port => 1234}
37
+ status.should == 0
38
+ end
39
+
40
+ it "construct_url should create the correct druby URL" do
41
+ ProxyRunner.new.build_url(:host => '127.0.0.1', :port => '1234').should == "druby://127.0.0.1:1234"
42
+ end
43
+
44
+ it "start_server should create a DatabaseProxy and start the DRB server" do
45
+ DatabaseProxy.should_receive(:new)
46
+ DRb.should_receive(:start_service,"druby://127.0.0.1:1234")
47
+ DRb.stub!(:thread).and_return(Object.new)
48
+ DRb.thread.should_receive(:join)
49
+ ProxyRunner.new.start_server("druby://127.0.0.1:1234")
50
+ end
51
+
52
+ it "run should not start a server if the command line is invalid" do
53
+ DRb.should_not_receive(:start_service)
54
+ DRb.stub!(:thread).and_return(Object.new)
55
+ DRb.thread.should_not_receive(:join)
56
+ ProxyRunner.run("--nonsense")
57
+ end
58
+
59
+ it "run should start a server if the command line is correct" do
60
+ DRb.should_receive(:start_service)
61
+ DRb.stub!(:thread).and_return(Object.new)
62
+ DRb.thread.should_receive(:join)
63
+ ProxyRunner.run(["--port=1234"])
64
+ end
65
+
66
+ it "should register itself with CommandRunner" do
67
+ CommandRunner.commands['proxy'][:command].should == ProxyRunner
68
+ CommandRunner.commands['proxy'][:description].should be_an_instance_of(String)
69
+ end
70
+ end