rubyrep 1.0.5 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,80 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe NoisyConnection do
6
+ before(:each) do
7
+ Initializer.configuration = proxied_config
8
+ @connection = ProxyConnection.new Initializer.configuration.left
9
+ @connection.send(:extend, NoisyConnection)
10
+ @connection.sweeper = TaskSweeper.new(1)
11
+ end
12
+
13
+ it "select_cursor should return correct results" do
14
+ @connection.sweeper.should_receive(:ping).exactly(4).times
15
+ fetcher = @connection.select_cursor(:table => 'scanner_records')
16
+ row = fetcher.next_row
17
+ row.should == {
18
+ 'id' => '1',
19
+ 'name' => 'Alice - exists in both databases'
20
+ }
21
+ end
22
+
23
+ it "insert_record should write nil values correctly" do
24
+ @connection.sweeper.should_receive(:ping).exactly(2).times
25
+ @connection.begin_db_transaction
26
+ begin
27
+ @connection.insert_record('extender_combined_key', 'first_id' => 8, 'second_id' => '9', 'name' => nil)
28
+ @connection.select_one(
29
+ "select first_id, second_id, name
30
+ from extender_combined_key where (first_id, second_id) = (8, 9)") \
31
+ .should == {'first_id' => '8', 'second_id' => '9', "name" => nil}
32
+ ensure
33
+ @connection.rollback_db_transaction
34
+ end
35
+ end
36
+
37
+ it "update_record should update the specified record" do
38
+ @connection.sweeper.should_receive(:ping).exactly(2).times
39
+ @connection.begin_db_transaction
40
+ begin
41
+ @connection.update_record('scanner_records', 'id' => 1, 'name' => 'update_test')
42
+ @connection.select_one("select * from scanner_records where id = 1") \
43
+ .should == {'id' => '1', 'name' => 'update_test'}
44
+ ensure
45
+ @connection.rollback_db_transaction
46
+ end
47
+ end
48
+
49
+ it "delete_record should delete the specified record" do
50
+ @connection.sweeper.should_receive(:ping).exactly(2).times
51
+ @connection.begin_db_transaction
52
+ begin
53
+ @connection.delete_record('extender_combined_key', 'first_id' => 1, 'second_id' => '1', 'name' => 'xy')
54
+ @connection.select_one(
55
+ "select first_id, second_id, name
56
+ from extender_combined_key where (first_id, second_id) = (1, 1)") \
57
+ .should be_nil
58
+ ensure
59
+ @connection.rollback_db_transaction
60
+ end
61
+ end
62
+
63
+ it "commit_db_transaction should update TaskSweeper" do
64
+ @connection.begin_db_transaction
65
+ initializer = ReplicationInitializer.new Session.new(standard_config)
66
+ begin
67
+ @connection.execute "insert into scanner_records(id,name) values(99, 'bla')"
68
+ @connection.sweeper.should_receive(:ping).exactly(2).times
69
+ @connection.commit_db_transaction
70
+ initializer.silence_ddl_notices(:left) do # avoid PostgreSQL warning that no transaction is open
71
+ @connection.rollback_db_transaction
72
+ end
73
+ @connection.select_one("select name from scanner_records where id = 99")['name'].
74
+ should == 'bla'
75
+ ensure
76
+ @connection.execute "delete from scanner_records where id = 99"
77
+ end
78
+ end
79
+
80
+ end
@@ -22,17 +22,6 @@ describe ProxyConnection do
22
22
  @connection.connection.active?.should == false
23
23
  end
24
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
25
  it "cursors should return the current cursor hash or an empty hash if nil" do
37
26
  @connection.cursors.should == {}
38
27
  @connection.cursors[:dummy_cursor] = :dummy_cursor
@@ -7,21 +7,22 @@ describe ReplicationDifference do
7
7
  Initializer.configuration = standard_config
8
8
  end
9
9
 
10
- it "initialize should store the session" do
10
+ it "initialize should store the loaders" do
11
11
  session = Session.new
12
- diff = ReplicationDifference.new session
13
- diff.session.should == session
12
+ loaders = LoggedChangeLoaders.new session
13
+ diff = ReplicationDifference.new loaders
14
+ diff.loaders.should == loaders
14
15
  end
15
16
 
16
17
  it "loaded? should return true if a difference was loaded" do
17
- diff = ReplicationDifference.new Session.new
18
+ diff = ReplicationDifference.new LoggedChangeLoaders.new(Session.new)
18
19
  diff.should_not be_loaded
19
20
  diff.loaded = true
20
21
  diff.should be_loaded
21
22
  end
22
23
 
23
24
  it "load should leave the instance unloaded if no changes are available" do
24
- diff = ReplicationDifference.new Session.new
25
+ diff = ReplicationDifference.new LoggedChangeLoaders.new(Session.new)
25
26
  diff.load
26
27
  diff.should_not be_loaded
27
28
  end
@@ -36,7 +37,7 @@ describe ReplicationDifference do
36
37
  'change_type' => 'I',
37
38
  'change_time' => Time.now
38
39
  }
39
- diff = ReplicationDifference.new session
40
+ diff = ReplicationDifference.new LoggedChangeLoaders.new(session)
40
41
  diff.load
41
42
 
42
43
  diff.should be_loaded
@@ -57,7 +58,7 @@ describe ReplicationDifference do
57
58
  'change_type' => 'D',
58
59
  'change_time' => Time.now
59
60
  }
60
- diff = ReplicationDifference.new session
61
+ diff = ReplicationDifference.new LoggedChangeLoaders.new(session)
61
62
  diff.load
62
63
 
63
64
  diff.should be_loaded
@@ -96,7 +97,7 @@ describe ReplicationDifference do
96
97
  'change_type' => 'D',
97
98
  'change_time' => 5.seconds.ago
98
99
  }
99
- diff = ReplicationDifference.new session
100
+ diff = ReplicationDifference.new LoggedChangeLoaders.new(session)
100
101
  diff.load
101
102
 
102
103
  diff.should be_loaded
@@ -124,7 +125,7 @@ describe ReplicationDifference do
124
125
  'change_type' => 'I',
125
126
  'change_time' => Time.now
126
127
  }
127
- diff = ReplicationDifference.new session
128
+ diff = ReplicationDifference.new LoggedChangeLoaders.new(session)
128
129
  diff.load
129
130
 
130
131
  diff.should be_loaded
@@ -9,7 +9,7 @@ describe ReplicationHelper do
9
9
 
10
10
  it "initialize should initialize the correct committer" do
11
11
  session = Session.new
12
- rep_run = ReplicationRun.new(session)
12
+ rep_run = ReplicationRun.new(session, TaskSweeper.new(1))
13
13
  helper = ReplicationHelper.new(rep_run)
14
14
  c = helper.instance_eval {@committer}
15
15
  c.should be_an_instance_of(Committers::DefaultCommitter)
@@ -17,26 +17,26 @@ describe ReplicationHelper do
17
17
  end
18
18
 
19
19
  it "session should return the session" do
20
- rep_run = ReplicationRun.new(Session.new)
20
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
21
21
  helper = ReplicationHelper.new(rep_run)
22
22
  helper.session.should == rep_run.session
23
23
  end
24
24
 
25
25
  it "replication_run should return the current ReplicationRun instance" do
26
- rep_run = ReplicationRun.new(Session.new)
26
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
27
27
  helper = ReplicationHelper.new(rep_run)
28
28
  helper.replication_run.should == rep_run
29
29
  end
30
30
 
31
31
  it "options should return the correct options" do
32
32
  session = Session.new
33
- rep_run = ReplicationRun.new(session)
33
+ rep_run = ReplicationRun.new(session, TaskSweeper.new(1))
34
34
  helper = ReplicationHelper.new(rep_run)
35
35
  helper.options.should == session.configuration.options
36
36
  end
37
37
 
38
38
  it "insert_record should insert the given record" do
39
- rep_run = ReplicationRun.new(Session.new)
39
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
40
40
  helper = ReplicationHelper.new(rep_run)
41
41
  c = helper.instance_eval {committer}
42
42
  c.should_receive(:insert_record).with(:right, 'scanner_records', :dummy_record)
@@ -44,7 +44,7 @@ describe ReplicationHelper do
44
44
  end
45
45
 
46
46
  it "update_record should update the given record" do
47
- rep_run = ReplicationRun.new(Session.new)
47
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
48
48
  helper = ReplicationHelper.new(rep_run)
49
49
  c = helper.instance_eval {committer}
50
50
  c.should_receive(:update_record).with(:right, 'scanner_records', :dummy_record, nil)
@@ -52,7 +52,7 @@ describe ReplicationHelper do
52
52
  end
53
53
 
54
54
  it "update_record should update the given record with the provided old key" do
55
- rep_run = ReplicationRun.new(Session.new)
55
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
56
56
  helper = ReplicationHelper.new(rep_run)
57
57
  c = helper.instance_eval {committer}
58
58
  c.should_receive(:update_record).with(:right, 'scanner_records', :dummy_record, :old_key)
@@ -60,7 +60,7 @@ describe ReplicationHelper do
60
60
  end
61
61
 
62
62
  it "delete_record should delete the given record" do
63
- rep_run = ReplicationRun.new(Session.new)
63
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
64
64
  helper = ReplicationHelper.new(rep_run)
65
65
  c = helper.instance_eval {committer}
66
66
  c.should_receive(:delete_record).with(:right, 'scanner_records', :dummy_record)
@@ -68,7 +68,7 @@ describe ReplicationHelper do
68
68
  end
69
69
 
70
70
  it "load_record should load the specified record (values converted to original data types)" do
71
- rep_run = ReplicationRun.new(Session.new)
71
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
72
72
  helper = ReplicationHelper.new(rep_run)
73
73
  helper.load_record(:right, 'scanner_records', 'id' => '2').should == {
74
74
  'id' => 2, # Note: it's a number, not a string...
@@ -80,11 +80,14 @@ describe ReplicationHelper do
80
80
  session = Session.new
81
81
  session.left.begin_db_transaction
82
82
  begin
83
- rep_run = ReplicationRun.new(session)
83
+ rep_run = ReplicationRun.new(session, TaskSweeper.new(1))
84
84
  helper = ReplicationHelper.new(rep_run)
85
- left_change = LoggedChange.new session, :left
86
- right_change = LoggedChange.new session, :right
87
- diff = ReplicationDifference.new session
85
+
86
+ loaders = LoggedChangeLoaders.new(session)
87
+
88
+ left_change = LoggedChange.new loaders[:left]
89
+ right_change = LoggedChange.new loaders[:right]
90
+ diff = ReplicationDifference.new loaders
88
91
  diff.changes.replace :left => left_change, :right => right_change
89
92
  diff.type = :conflict
90
93
 
@@ -116,7 +119,7 @@ describe ReplicationHelper do
116
119
  end
117
120
 
118
121
  it "finalize should be delegated to the committer" do
119
- rep_run = ReplicationRun.new(Session.new)
122
+ rep_run = ReplicationRun.new(Session.new, TaskSweeper.new(1))
120
123
  helper = ReplicationHelper.new(rep_run)
121
124
 
122
125
  c = helper.instance_eval {@committer}
@@ -363,6 +363,19 @@ describe ReplicationInitializer do
363
363
  initializer.ensure_infrastructure
364
364
  end
365
365
 
366
+ it "call_after_init_handler should call the according handler" do
367
+ config = deep_copy(standard_config)
368
+ received_session = nil
369
+ config.options[:after_infrastructure_setup] = lambda do |session|
370
+ received_session = session
371
+ end
372
+ session = Session.new config
373
+ initializer = ReplicationInitializer.new session
374
+ initializer.call_after_infrastructure_setup_handler
375
+
376
+ received_session.should == session
377
+ end
378
+
366
379
  it "exclude_ruby_rep_tables should exclude the correct system tables" do
367
380
  config = deep_copy(standard_config)
368
381
  initializer = ReplicationInitializer.new(Session.new(config))
@@ -416,6 +429,12 @@ describe ReplicationInitializer do
416
429
  config = deep_copy(standard_config)
417
430
  config.options[:committer] = :buffered_commit
418
431
  config.options[:use_ansi] = false
432
+
433
+ received_session = nil
434
+ config.options[:after_infrastructure_setup] = lambda do |session|
435
+ received_session = session
436
+ end
437
+
419
438
  config.include_tables 'rr_pending_changes' # added to verify that it is ignored
420
439
 
421
440
  session = Session.new(config)
@@ -426,6 +445,9 @@ describe ReplicationInitializer do
426
445
  initializer.should_receive(:ensure_infrastructure).any_number_of_times
427
446
  initializer.should_receive(:restore_unconfigured_tables).any_number_of_times
428
447
  initializer.prepare_replication
448
+
449
+ received_session.should == session
450
+
429
451
  # verify sequences have been setup
430
452
  session.left.sequence_values('rr','scanner_left_records_only').values[0][:increment].should == 2
431
453
  session.right.sequence_values('rr','scanner_left_records_only').values[0][:increment].should == 2
@@ -9,12 +9,23 @@ describe ReplicationRun do
9
9
 
10
10
  it "initialize should store the provided session" do
11
11
  session = Session.new
12
- run = ReplicationRun.new session
12
+ sweeper = TaskSweeper.new 1
13
+ run = ReplicationRun.new session, sweeper
13
14
  run.session.should == session
14
15
  end
15
16
 
17
+ it "install_sweeper should install a task sweeper into the database connections" do
18
+ session = Session.new
19
+ sweeper = TaskSweeper.new 1
20
+ run = ReplicationRun.new session, sweeper
21
+ run.install_sweeper
22
+
23
+ session.left.sweeper.should == sweeper
24
+ session.right.sweeper.should == sweeper
25
+ end
26
+
16
27
  it "helper should return the correctly initialized replication helper" do
17
- run = ReplicationRun.new Session.new
28
+ run = ReplicationRun.new Session.new, TaskSweeper.new(1)
18
29
  run.helper.should be_an_instance_of(ReplicationHelper)
19
30
  run.helper.replication_run.should == run
20
31
  run.helper.should == run.helper # ensure the helper is created only once
@@ -22,7 +33,7 @@ describe ReplicationRun do
22
33
 
23
34
  it "replicator should return the configured replicator" do
24
35
  session = Session.new
25
- run = ReplicationRun.new session
36
+ run = ReplicationRun.new session, TaskSweeper.new(1)
26
37
  run.replicator.
27
38
  should be_an_instance_of(Replicators.replicators[session.configuration.options[:replicator]])
28
39
  run.replicator.should == run.replicator # should only create the replicator once
@@ -47,7 +58,7 @@ describe ReplicationRun do
47
58
  'change_time' => Time.now
48
59
  }
49
60
 
50
- run = ReplicationRun.new session
61
+ run = ReplicationRun.new session, TaskSweeper.new(1)
51
62
  run.run
52
63
 
53
64
  session.right.select_one("select * from extender_no_record").should == {
@@ -66,7 +77,7 @@ describe ReplicationRun do
66
77
 
67
78
  it "run should not create the replicator if there are no pending changes" do
68
79
  session = Session.new
69
- run = ReplicationRun.new session
80
+ run = ReplicationRun.new session, TaskSweeper.new(1)
70
81
  run.should_not_receive(:replicator)
71
82
  run.run
72
83
  end
@@ -90,7 +101,7 @@ describe ReplicationRun do
90
101
  'change_time' => Time.now
91
102
  }
92
103
 
93
- run = ReplicationRun.new session
104
+ run = ReplicationRun.new session, TaskSweeper.new(1)
94
105
  run.replicator.should_not_receive(:replicate)
95
106
  run.run
96
107
 
@@ -113,7 +124,7 @@ describe ReplicationRun do
113
124
  'change_type' => 'D',
114
125
  'change_time' => Time.now
115
126
  }
116
- run = ReplicationRun.new session
127
+ run = ReplicationRun.new session, TaskSweeper.new(1)
117
128
  run.replicator.stub!(:replicate_difference).and_return {raise Exception, 'dummy message'}
118
129
  run.run
119
130
 
@@ -140,7 +151,7 @@ describe ReplicationRun do
140
151
  'change_time' => Time.now
141
152
  }
142
153
 
143
- run = ReplicationRun.new session
154
+ run = ReplicationRun.new session, TaskSweeper.new(1)
144
155
  lambda {run.run}.should raise_error(ArgumentError)
145
156
  ensure
146
157
  session.left.rollback_db_transaction
@@ -163,7 +174,7 @@ describe ReplicationRun do
163
174
  'name' => 'bla'
164
175
  }
165
176
 
166
- run = ReplicationRun.new session
177
+ run = ReplicationRun.new session, TaskSweeper.new(1)
167
178
  run.run
168
179
 
169
180
  session.right.select_one("select * from extender_no_record").should == {
data/spec/session_spec.rb CHANGED
@@ -67,6 +67,7 @@ describe Session do # here database connection caching is _not_ disabled
67
67
  dummy_connection = mock("dummy connection")
68
68
  dummy_connection.stub!(:tables).and_return([])
69
69
  dummy_connection.stub!(:manual_primary_keys=)
70
+ dummy_connection.stub!(:select_one).and_return({'x' => '2'})
70
71
  dummy_proxy.should_receive(:create_session).and_return(dummy_connection)
71
72
  DRbObject.should_receive(:new).with(nil,"druby://localhost:9876").and_return(dummy_proxy)
72
73
 
@@ -88,12 +89,33 @@ describe Session do # here database connection caching is _not_ disabled
88
89
  session.right.manual_primary_keys.should == {'extender_without_key'=>['id']}
89
90
  end
90
91
 
91
- it "refresh should reestablsih the database connections if no more active" do
92
+ it "refresh should reestablish the database connections if not active anymore" do
92
93
  session = Session.new
93
94
  session.right.destroy
94
95
  session.right.connection.should_not be_active
96
+ lambda {session.right.select_one("select 1+1 as x")}.should raise_error
95
97
  session.refresh
96
98
  session.right.connection.should be_active
99
+ session.right.select_one("select 1+1 as x")['x'].to_i.should == 2
100
+ end
101
+
102
+ it "refresh should raise error even if database connect fails silently" do
103
+ session = Session.new
104
+ session.right.destroy
105
+ session.right.connection.should_not be_active
106
+ session.should_receive(:connect_database)
107
+ lambda {session.refresh}.should raise_error(/no connection to.*right.*database/)
108
+ end
109
+
110
+ it "refresh should work with proxied database connections" do
111
+ ensure_proxy
112
+ session = Session.new(proxied_config)
113
+ session.right.destroy
114
+ session.right.connection.should_not be_active
115
+ lambda {session.right.select_one("select 1+1 as x")}.should raise_error
116
+ session.refresh
117
+ session.right.connection.should be_active
118
+ session.right.select_one("select 1+1 as x")['x'].to_i.should == 2
97
119
  end
98
120
 
99
121
  it "refresh should not do anyting if the connection is still active" do
data/spec/spec_helper.rb CHANGED
@@ -94,6 +94,7 @@ def mock_active_record(number_of_calls)
94
94
  dummy_connection.stub!(:extend)
95
95
  dummy_connection.stub!(:tables).and_return([])
96
96
  dummy_connection.stub!(:initialize_search_path)
97
+ dummy_connection.stub!(:select_one).and_return({'x' => '2'})
97
98
 
98
99
  ConnectionExtenders::DummyActiveRecord.should_receive(:connection).send(number_of_calls) \
99
100
  .and_return {dummy_connection}
@@ -0,0 +1,47 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ include RR
4
+
5
+ describe TaskSweeper do
6
+ before(:each) do
7
+ end
8
+
9
+ it "should execute the given task" do
10
+ x = nil
11
+ TaskSweeper.timeout(1) {|sweeper| x = 1}
12
+ x.should == 1
13
+ end
14
+
15
+ it "should raise exceptions thrown by the task" do
16
+ lambda {
17
+ TaskSweeper.timeout(1) {raise "bla"}
18
+ }.should raise_error("bla")
19
+ end
20
+
21
+ it "should return if task stalls" do
22
+ start = Time.now
23
+ TaskSweeper.timeout(0.01) {sleep 10}.should be_terminated
24
+ (Time.now - start < 5).should be_true
25
+ end
26
+
27
+ it "should not return if task is active" do
28
+ start = Time.now
29
+ TaskSweeper.timeout(0.1) do |sweeper|
30
+ 10.times do
31
+ sleep 0.05
32
+ sweeper.ping
33
+ end
34
+ end.should_not be_terminated
35
+ (Time.now - start > 0.4).should be_true
36
+
37
+ end
38
+
39
+ it "should notify a stalled task about it's termination" do
40
+ terminated = false
41
+ TaskSweeper.timeout(0.01) do |sweeper|
42
+ sleep 0.05
43
+ terminated = sweeper.terminated?
44
+ end.join
45
+ terminated.should be_true
46
+ end
47
+ end