perfectsched 0.8.10 → 0.8.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +11 -4
- data/ChangeLog +6 -0
- data/Gemfile +2 -0
- data/README.md +2 -1
- data/circle.yml +3 -0
- data/lib/perfectsched.rb +4 -23
- data/lib/perfectsched/application/decider.rb +1 -1
- data/lib/perfectsched/backend/rdb_compat.rb +5 -4
- data/lib/perfectsched/schedule_metadata.rb +0 -20
- data/lib/perfectsched/version.rb +1 -1
- data/lib/perfectsched/worker.rb +1 -4
- data/perfectsched.gemspec +6 -4
- data/spec/application/base_spec.rb +81 -0
- data/spec/application/decider_spec.rb +54 -0
- data/spec/client_spec.rb +122 -0
- data/spec/engine_spec.rb +82 -0
- data/spec/model_spec.rb +24 -0
- data/spec/perfect_sched_spec.rb +105 -0
- data/spec/rdb_compat_backend_spec.rb +287 -27
- data/spec/runner_spec.rb +23 -0
- data/spec/schedule_collection_spec.rb +63 -63
- data/spec/schedule_spec.rb +66 -0
- data/spec/spec_helper.rb +7 -2
- data/spec/task_spec.rb +51 -0
- data/spec/worker_spec.rb +182 -39
- metadata +52 -19
data/spec/engine_spec.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PerfectSched::Engine do
|
4
|
+
let (:logger){ double('logger').as_null_object }
|
5
|
+
let (:runner){ double('runner') }
|
6
|
+
let (:scheds){ double('scheds') }
|
7
|
+
let (:config){ {logger: logger} }
|
8
|
+
let (:engine) do
|
9
|
+
Engine.new(runner, config)
|
10
|
+
end
|
11
|
+
before do
|
12
|
+
expect(PerfectSched).to receive(:open).with(config).and_return(scheds)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '.new' do
|
16
|
+
it 'returns an Engine' do
|
17
|
+
engine = Engine.new(runner, config)
|
18
|
+
expect(engine).to be_an_instance_of(Engine)
|
19
|
+
expect(engine.instance_variable_get(:@runner)).to eq(runner)
|
20
|
+
expect(engine.instance_variable_get(:@poll_interval)).to eq(1.0)
|
21
|
+
expect(engine.instance_variable_get(:@log)).to eq(logger)
|
22
|
+
expect(engine.instance_variable_get(:@running_flag)).to be_a(BlockingFlag)
|
23
|
+
expect(engine.instance_variable_get(:@finish_flag)).to be_a(BlockingFlag)
|
24
|
+
expect(engine.instance_variable_get(:@scheds)).to be_a(PerfectSched)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#run' do
|
29
|
+
it 'runs until stopped' do
|
30
|
+
rflag = engine.instance_variable_get(:@running_flag)
|
31
|
+
fflag = engine.instance_variable_get(:@finish_flag)
|
32
|
+
task1 = double('task1')
|
33
|
+
task2 = double('task2')
|
34
|
+
allow(scheds).to receive(:poll).and_return(task1, nil, task2)
|
35
|
+
expect(runner).to receive(:new).exactly(:twice) do |task|
|
36
|
+
expect(rflag.set?).to be true
|
37
|
+
r = double('r')
|
38
|
+
case task
|
39
|
+
when task1
|
40
|
+
expect(r).to receive(:run)
|
41
|
+
when task2
|
42
|
+
expect(r).to receive(:run){ fflag.set! }
|
43
|
+
else
|
44
|
+
raise ArgumentError
|
45
|
+
end
|
46
|
+
r
|
47
|
+
end
|
48
|
+
expect(engine.run).to eq(engine)
|
49
|
+
expect(rflag.set?).to be false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '#stop' do
|
54
|
+
it 'sets finish_flag' do
|
55
|
+
expect(engine.stop).to eq(engine)
|
56
|
+
expect(engine.instance_variable_get(:@finish_flag).set?).to eq true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#join' do
|
61
|
+
it 'waits running flag is set' do
|
62
|
+
expect(engine.join).to eq(engine)
|
63
|
+
expect(engine.instance_variable_get(:@running_flag).set?).to eq false
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '#close' do
|
68
|
+
it 'closes scheds' do
|
69
|
+
expect(scheds).to receive(:close)
|
70
|
+
expect(engine.close).to eq(engine)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe '#shutdown' do
|
75
|
+
it 'calls stop, join, and close' do
|
76
|
+
expect(engine).to receive(:stop)
|
77
|
+
expect(engine).to receive(:join)
|
78
|
+
expect(engine).to receive(:close).and_return(engine)
|
79
|
+
expect(engine.shutdown).to eq(engine)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/spec/model_spec.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PerfectSched::Model do
|
4
|
+
let (:config){ double('config') }
|
5
|
+
let (:client){ double('client', config: config) }
|
6
|
+
let (:klass){ Class.new{include PerfectSched::Model} }
|
7
|
+
let (:model){ klass.new(client) }
|
8
|
+
describe '.new' do
|
9
|
+
it 'creates an instance' do
|
10
|
+
expect(model).to be_a(PerfectSched::Model)
|
11
|
+
expect(model.instance_variable_get(:@client)).to eq(client)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
describe '#client' do
|
15
|
+
it 'returns its client' do
|
16
|
+
expect(model.client).to eq(client)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
describe '#config' do
|
20
|
+
it 'returns its client.config' do
|
21
|
+
expect(model.config).to eq(client.config)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PerfectSched do
|
4
|
+
describe '.open' do
|
5
|
+
let (:config){ double('config') }
|
6
|
+
let (:client){ double('client') }
|
7
|
+
let (:schedule_collection){ double('schedule_collection') }
|
8
|
+
before do
|
9
|
+
expect(Client).to receive(:new).with(config).and_return(client)
|
10
|
+
expect(ScheduleCollection).to receive(:new).with(client).and_return(schedule_collection)
|
11
|
+
end
|
12
|
+
it 'returns an instance without block' do
|
13
|
+
expect(client).not_to receive(:close)
|
14
|
+
expect(PerfectSched.open(config)).to eq(schedule_collection)
|
15
|
+
end
|
16
|
+
it 'yields block if given' do
|
17
|
+
ret = double('ret')
|
18
|
+
expect(client).to receive(:close)
|
19
|
+
r = PerfectSched.open(config) do |sc|
|
20
|
+
expect(sc).to eq(schedule_collection)
|
21
|
+
ret
|
22
|
+
end
|
23
|
+
expect(r).to eq(ret)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '.cron_time' do
|
28
|
+
it do
|
29
|
+
ts = PerfectSched.cron_time('0 * * * *', 1, nil)
|
30
|
+
expect(ts).to eq 3600
|
31
|
+
end
|
32
|
+
it do
|
33
|
+
expect{PerfectSched.cron_time('0 * * * *', 0, 'JST-9')}.to raise_error(ArgumentError)
|
34
|
+
end
|
35
|
+
it do
|
36
|
+
ts = PerfectSched.cron_time('0,30 * * * *', 1, nil)
|
37
|
+
expect(ts).to eq 1800
|
38
|
+
end
|
39
|
+
it do
|
40
|
+
ts = PerfectSched.cron_time('*/7 * * * *', 1, nil)
|
41
|
+
expect(ts).to eq 420
|
42
|
+
end
|
43
|
+
it 'supports @hourly' do
|
44
|
+
ts = PerfectSched.cron_time('@hourly', 1, nil)
|
45
|
+
expect(ts).to eq 3600
|
46
|
+
end
|
47
|
+
it 'supports @daily' do
|
48
|
+
ts = PerfectSched.cron_time('@daily', 1, nil)
|
49
|
+
expect(ts).to eq 86400
|
50
|
+
end
|
51
|
+
it 'supports @monthly' do
|
52
|
+
ts = PerfectSched.cron_time('@monthly', 1, nil)
|
53
|
+
expect(ts).to eq 2678400
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '.next_time' do
|
58
|
+
it 'can run hourly cron' do
|
59
|
+
ts = PerfectSched.next_time(' 0 * * * * ', 0, nil)
|
60
|
+
expect(ts).to eq 3600
|
61
|
+
end
|
62
|
+
it 'calculates 4 years quickly' do
|
63
|
+
t = Time.utc(2012,2,29)
|
64
|
+
ts = PerfectSched.next_time('0 0 29 2 *', t.to_i, nil)
|
65
|
+
expect(ts).to eq(Time.utc(2016,2,29).to_i)
|
66
|
+
end
|
67
|
+
it 'raises error on unsupported timezone' do
|
68
|
+
expect{PerfectSched.next_time('0 * * * *', 0, 'JST-9')}.to raise_error(ArgumentError)
|
69
|
+
end
|
70
|
+
it 'returns next run time of given time' do
|
71
|
+
t0 = Time.new(2015, 12, 3, 1, 0, 0, 9*3600)
|
72
|
+
t1 = Time.new(2015, 12, 3, 2, 0, 0, 9*3600)
|
73
|
+
ts = PerfectSched.next_time('0 * * * *', t0.to_i, 'Asia/Tokyo')
|
74
|
+
expect(ts).to eq(t1.to_i)
|
75
|
+
end
|
76
|
+
it 'returns next run time of given time' do
|
77
|
+
t0 = Time.new(2015, 12, 3, 1, 59, 59, 9*3600)
|
78
|
+
t1 = Time.new(2015, 12, 3, 2, 0, 0, 9*3600)
|
79
|
+
ts = PerfectSched.next_time('0 * * * *', t0.to_i, 'Asia/Tokyo')
|
80
|
+
expect(ts).to eq(t1.to_i)
|
81
|
+
end
|
82
|
+
it 'returns next run time with day of week (0=Sun)' do
|
83
|
+
t0 = Time.new(2015, 12, 3, 0, 0, 0, 9*3600)
|
84
|
+
t1 = Time.new(2015, 12, 6, 0, 0, 0, 9*3600)
|
85
|
+
ts = PerfectSched.next_time('0 0 * * 0', t0.to_i, 'Asia/Tokyo')
|
86
|
+
expect(ts).to eq(t1.to_i)
|
87
|
+
end
|
88
|
+
context 'DST 2015' do
|
89
|
+
it 'skips task on 8 Mar because 1:59:59 PST -> 3:00:00 PDT' do
|
90
|
+
t0 = Time.new(2015, 3, 7, 2, 0, 0, -8*3600)
|
91
|
+
# 2015-03-08T02:00:00 doesn't exist
|
92
|
+
t1 = Time.new(2015, 3, 9, 2, 0, 0, -7*3600)
|
93
|
+
ts = PerfectSched.next_time('0 2 * * *', t0.to_i, 'America/Los_Angeles')
|
94
|
+
expect(ts).to eq(t1.to_i)
|
95
|
+
end
|
96
|
+
it 'runs twice on Nov 11 because 1:59:59 PDT -> 1:00:00 PST' do
|
97
|
+
# 2015-11-01T01:00:00 exists twice
|
98
|
+
t0 = Time.new(2015, 11, 1, 1, 0, 0, -7*3600)
|
99
|
+
t1 = Time.new(2015, 11, 1, 1, 0, 0, -8*3600)
|
100
|
+
ts = PerfectSched.next_time('0 1 * * *', t0.to_i, 'America/Los_Angeles')
|
101
|
+
expect(ts).to eq(t1.to_i)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -2,40 +2,300 @@ require 'spec_helper'
|
|
2
2
|
require 'perfectsched/backend/rdb_compat'
|
3
3
|
|
4
4
|
describe Backend::RDBCompatBackend do
|
5
|
-
let :
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
let (:now){ Time.now.to_i }
|
6
|
+
let (:client){ double('client') }
|
7
|
+
let (:config){ {url: 'sqlite://spec/test.db', table: 'test_scheds'} }
|
8
|
+
let (:db) do
|
9
|
+
d = Backend::RDBCompatBackend.new(client, config)
|
10
|
+
s = d.db
|
11
|
+
s.tables.each{|t| s.drop_table(t) }
|
12
|
+
d.init_database(nil)
|
13
|
+
d
|
10
14
|
end
|
11
15
|
|
12
|
-
|
13
|
-
sc
|
16
|
+
context 'compatibility' do
|
17
|
+
let :sc do
|
18
|
+
FileUtils.rm_f 'spec/test.db'
|
19
|
+
sc = PerfectSched.open({:type=>'rdb_compat', :url=>'sqlite://spec/test.db', :table=>'test_scheds'})
|
20
|
+
sc.client.init_database
|
21
|
+
sc
|
22
|
+
end
|
23
|
+
|
24
|
+
let :client do
|
25
|
+
sc.client
|
26
|
+
end
|
27
|
+
|
28
|
+
let :backend do
|
29
|
+
client.backend
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'backward compatibility 1' do
|
33
|
+
backend.db["INSERT INTO test_scheds (id, timeout, next_time, cron, delay, data, timezone) VALUES (?, ?, ?, ?, ?, ?, ?)", "maint_sched.1.do_hourly", 1339812000, 1339812000, "0 * * * *", 0, {"account_id"=>1}.to_json, "UTC"].insert
|
34
|
+
ts = backend.acquire(60, 1, {:now=>1339812003})
|
35
|
+
expect(ts).not_to eq(nil)
|
36
|
+
t = ts[0]
|
37
|
+
expect(t.data).to eq({'account_id'=>1})
|
38
|
+
expect(t.type).to eq('maint_sched')
|
39
|
+
expect(t.key).to eq('maint_sched.1.do_hourly')
|
40
|
+
expect(t.next_time).to eq(1339812000)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'backward compatibility 2' do
|
44
|
+
backend.db["INSERT INTO test_scheds (id, timeout, next_time, cron, delay, data, timezone) VALUES (?, ?, ?, ?, ?, ?, ?)", "merge", 1339812060, 1339812000, "@hourly", 60, '', "Asia/Tokyo"].insert
|
45
|
+
ts = backend.acquire(60, 1, {:now=>1339812060})
|
46
|
+
t = ts[0]
|
47
|
+
expect(t.data).to eq({})
|
48
|
+
expect(t.type).to eq('merge')
|
49
|
+
expect(t.key).to eq('merge')
|
50
|
+
expect(t.next_time).to eq(1339812000)
|
51
|
+
end
|
14
52
|
end
|
15
53
|
|
16
|
-
|
17
|
-
client
|
54
|
+
context '.new' do
|
55
|
+
let (:client){ double('client') }
|
56
|
+
let (:table){ double('table') }
|
57
|
+
it 'raises error unless url' do
|
58
|
+
expect{Backend::RDBCompatBackend.new(client, {})}.to raise_error(ConfigError)
|
59
|
+
end
|
60
|
+
it 'raises error unless table' do
|
61
|
+
expect{Backend::RDBCompatBackend.new(client, {url: ''})}.to raise_error(ConfigError)
|
62
|
+
end
|
63
|
+
it 'supports sqlite' do
|
64
|
+
config = {url: 'sqlite://localhost', table: table}
|
65
|
+
expect(Backend::RDBCompatBackend.new(client, config)).to be_an_instance_of(Backend::RDBCompatBackend)
|
66
|
+
end
|
67
|
+
it 'supports mysql' do
|
68
|
+
config = {url: 'mysql://root:@localhost/perfectsched_test', table: table}
|
69
|
+
expect(Backend::RDBCompatBackend.new(client, config)).to be_an_instance_of(Backend::RDBCompatBackend)
|
70
|
+
end
|
71
|
+
it 'doesn\'t support postgres' do
|
72
|
+
config = {url: 'postgres://localhost', table: table}
|
73
|
+
expect{Backend::RDBCompatBackend.new(client, config)}.to raise_error(ConfigError)
|
74
|
+
end
|
18
75
|
end
|
19
76
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
t = ts[0]
|
25
|
-
t.data.should == {'account_id'=>1}
|
26
|
-
t.type.should == 'maint_sched'
|
27
|
-
t.key.should == 'maint_sched.1.do_hourly'
|
28
|
-
t.next_time.should == 1339812000
|
77
|
+
context '#init_database' do
|
78
|
+
it 'creates the table' do
|
79
|
+
db.add('key', 'test', '* * * * *', 0, 'Asia/Tokyo', {}, now, now, {})
|
80
|
+
end
|
29
81
|
end
|
30
82
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
83
|
+
context '#get_schedule_metadata' do
|
84
|
+
before do
|
85
|
+
db.add('key', 'test', '* * * * *', 0, 'Asia/Tokyo', {}, now, now, {})
|
86
|
+
end
|
87
|
+
it 'fetches a metadata' do
|
88
|
+
expect(db.get_schedule_metadata('key')).to be_an_instance_of(ScheduleWithMetadata)
|
89
|
+
end
|
90
|
+
it 'raises error if non exist key' do
|
91
|
+
expect{db.get_schedule_metadata('nonexistent')}.to raise_error(NotFoundError)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context '#list' do
|
96
|
+
before do
|
97
|
+
db.add('key', 'test', '* * * * *', 0, 'Asia/Tokyo', {}, now, now, {})
|
98
|
+
end
|
99
|
+
it 'lists a metadata' do
|
100
|
+
db.list(nil) do |x|
|
101
|
+
expect(x).to be_an_instance_of(PerfectSched::ScheduleWithMetadata)
|
102
|
+
expect(x.key).to eq('key')
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context '#add' do
|
108
|
+
it 'adds schedules' do
|
109
|
+
db.add('key', 'test', '* * * * *', 0, 'Asia/Tokyo', {}, now, now, {})
|
110
|
+
expect{db.add('key', 'test', '* * * * *', 0, 'Asia/Tokyo', {}, now, now, {})}.to raise_error(IdempotentAlreadyExistsError)
|
111
|
+
end
|
39
112
|
end
|
40
|
-
end
|
41
113
|
|
114
|
+
context '#delete' do
|
115
|
+
before do
|
116
|
+
db.add('key', 'test', '* * * * *', 0, 'Asia/Tokyo', {}, now, now, {})
|
117
|
+
end
|
118
|
+
it 'deletes schedules' do
|
119
|
+
db.delete('key', nil)
|
120
|
+
expect{db.delete('key', nil)}.to raise_error(IdempotentNotFoundError)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context '#modify' do
|
125
|
+
before do
|
126
|
+
db.add('key', 'test', '* * * * *', 0, 'Asia/Tokyo', {}, now, now, {})
|
127
|
+
end
|
128
|
+
it 'returns nil if no keys' do
|
129
|
+
expect(db.modify('key', {})).to be_nil
|
130
|
+
end
|
131
|
+
it 'modifies schedules' do
|
132
|
+
db.modify('key', {delay: 1})
|
133
|
+
end
|
134
|
+
it 'raises if nonexistent' do
|
135
|
+
expect{db.modify('nonexistent', {delay: 0})}.to raise_error(NotFoundError)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context '#acquire' do
|
140
|
+
context 'no tasks' do
|
141
|
+
it 'returns nil' do
|
142
|
+
expect(db.acquire(0, nil, {})).to be_nil
|
143
|
+
end
|
144
|
+
end
|
145
|
+
context 'some tasks' do
|
146
|
+
before do
|
147
|
+
db.add('key1', 'test', '* * * * *', 0, 'Asia/Tokyo', {}, now, now, {})
|
148
|
+
end
|
149
|
+
it 'returns a task' do
|
150
|
+
ary = db.acquire(0, nil, {})
|
151
|
+
expect(ary).to be_an_instance_of(Array)
|
152
|
+
expect(ary[0]).to be_an_instance_of(Task)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
context 'some tasks but conflict with another process' do
|
156
|
+
before do
|
157
|
+
db.add('key1', 'test', '* * * * *', 0, 'Asia/Tokyo', {}, now, now, {})
|
158
|
+
db.add('key2', 'test', '* * * * *', 0, 'Asia/Tokyo', {}, now, now, {})
|
159
|
+
db.add('key3', 'test', '* * * * *', 0, 'Asia/Tokyo', {}, now, now, {})
|
160
|
+
end
|
161
|
+
it 'returns nil' do
|
162
|
+
data_set = double('data_set', update: 0)
|
163
|
+
allow(db.db).to receive(:[]).and_return(data_set)
|
164
|
+
expect(db.acquire(0, nil, {})).to be_nil
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context '#heartbeat' do
|
170
|
+
let (:next_time){ now }
|
171
|
+
let (:task_token){ Backend::RDBCompatBackend::Token.new('key', next_time, '* * * * *', 0, 'Asia/Tokyo') }
|
172
|
+
context 'have a scheduled task' do
|
173
|
+
before do
|
174
|
+
db.add('key', 'test', '* * * * *', 0, 'Asia/Tokyo', {}, next_time, next_time, {})
|
175
|
+
end
|
176
|
+
it 'returns nil if next_run_time is not updated' do
|
177
|
+
expect(db.heartbeat(task_token, 0, {now: next_time})).to be_nil
|
178
|
+
end
|
179
|
+
it 'returns nil even if next_run_time is updated' do
|
180
|
+
expect(db.heartbeat(task_token, 1, {})).to be_nil
|
181
|
+
end
|
182
|
+
end
|
183
|
+
context 'no tasks' do
|
184
|
+
it 'raises PreemptedError' do
|
185
|
+
expect{db.heartbeat(task_token, 0, {})}.to raise_error(PreemptedError)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
context '#finish' do
|
191
|
+
let (:next_time){ now }
|
192
|
+
let (:task_token){ Backend::RDBCompatBackend::Token.new('key', next_time, '* * * * *', 0, 'Asia/Tokyo') }
|
193
|
+
context 'have the task' do
|
194
|
+
before do
|
195
|
+
db.add('key', 'test', '* * * * *', 0, 'Asia/Tokyo', {}, next_time, next_time, {})
|
196
|
+
end
|
197
|
+
it 'returns nil' do
|
198
|
+
expect(db.finish(task_token, nil)).to be_nil
|
199
|
+
end
|
200
|
+
end
|
201
|
+
context 'already finished' do
|
202
|
+
it 'raises IdempotentAlreadyFinishedError' do
|
203
|
+
expect{db.finish(task_token, nil)}.to raise_error(IdempotentAlreadyFinishedError)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
context '#connect' do
|
209
|
+
context 'normal' do
|
210
|
+
let (:ret){ double('ret') }
|
211
|
+
it 'returns block result' do
|
212
|
+
expect(db.__send__(:connect){ ret }).to eq(ret)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
context 'error' do
|
216
|
+
it 'returns block result' do
|
217
|
+
expect(RuntimeError).to receive(:new).exactly(Backend::RDBCompatBackend::MAX_RETRY).and_call_original
|
218
|
+
allow(STDERR).to receive(:puts)
|
219
|
+
allow(db).to receive(:sleep)
|
220
|
+
expect do
|
221
|
+
db.__send__(:connect) do
|
222
|
+
raise RuntimeError.new('try restarting transaction')
|
223
|
+
end
|
224
|
+
end.to raise_error(RuntimeError)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
context '#create_attributes' do
|
230
|
+
let (:data){ Hash.new }
|
231
|
+
let (:row) do
|
232
|
+
r = double('row')
|
233
|
+
allow(r).to receive(:[]){|k| data[k] }
|
234
|
+
r
|
235
|
+
end
|
236
|
+
it 'returns a hash consisting the data of the row' do
|
237
|
+
data[:timezone] = timezone = double('timezone')
|
238
|
+
data[:delay] = delay = double('delay')
|
239
|
+
data[:cron] = cron = double('cron')
|
240
|
+
data[:next_time] = next_time = double('next_time')
|
241
|
+
data[:timeout] = timeout = double('timeout')
|
242
|
+
data[:data] = '{"type":"foo.bar","a":"b"}'
|
243
|
+
data[:id] = 'hoge'
|
244
|
+
expect(db.__send__(:create_attributes, row)).to eq(
|
245
|
+
timezone: timezone,
|
246
|
+
delay: delay,
|
247
|
+
cron: cron,
|
248
|
+
data: {"a"=>"b"},
|
249
|
+
next_time: next_time,
|
250
|
+
next_run_time: timeout,
|
251
|
+
type: 'foo.bar',
|
252
|
+
message: nil,
|
253
|
+
node: nil,
|
254
|
+
)
|
255
|
+
end
|
256
|
+
it 'returns {} if data\'s JSON is broken' do
|
257
|
+
data[:data] = '}{'
|
258
|
+
data[:id] = 'foo.bar.baz'
|
259
|
+
expect(db.__send__(:create_attributes, row)).to eq(
|
260
|
+
timezone: 'UTC',
|
261
|
+
delay: 0,
|
262
|
+
cron: nil,
|
263
|
+
data: {},
|
264
|
+
next_time: nil,
|
265
|
+
next_run_time: nil,
|
266
|
+
type: 'foo',
|
267
|
+
message: nil,
|
268
|
+
node: nil,
|
269
|
+
)
|
270
|
+
end
|
271
|
+
it 'uses id[/\A[^.]*/] if type is empty string' do
|
272
|
+
data[:data] = '{"type":""}'
|
273
|
+
data[:id] = 'foo.bar.baz'
|
274
|
+
expect(db.__send__(:create_attributes, row)).to eq(
|
275
|
+
timezone: 'UTC',
|
276
|
+
delay: 0,
|
277
|
+
cron: nil,
|
278
|
+
data: {},
|
279
|
+
next_time: nil,
|
280
|
+
next_run_time: nil,
|
281
|
+
type: 'foo',
|
282
|
+
message: nil,
|
283
|
+
node: nil,
|
284
|
+
)
|
285
|
+
end
|
286
|
+
it 'uses id[/\A[^.]*/] if type is nil' do
|
287
|
+
data[:id] = 'foo.bar.baz'
|
288
|
+
expect(db.__send__(:create_attributes, row)).to eq(
|
289
|
+
timezone: 'UTC',
|
290
|
+
delay: 0,
|
291
|
+
cron: nil,
|
292
|
+
data: {},
|
293
|
+
next_time: nil,
|
294
|
+
next_run_time: nil,
|
295
|
+
type: 'foo',
|
296
|
+
message: nil,
|
297
|
+
node: nil,
|
298
|
+
)
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|