sidejob 3.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.
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ # fibonacci calculation by recursively spawning jobs
4
+ class TestFib
5
+ include SideJob::Worker
6
+ register(
7
+ inports: {
8
+ n: {mode: :memory}
9
+ },
10
+ outports: {
11
+ num: {}
12
+ }
13
+ )
14
+ def perform
15
+ suspend unless input(:n).data?
16
+ n = input(:n).read
17
+
18
+ if n <= 2
19
+ output(:num).write 1
20
+ else
21
+ job1 = child(:job1)
22
+ if ! job1
23
+ job1 = queue('testq', 'TestFib', name: :job1)
24
+ job1.input(:n).write n-1
25
+ end
26
+
27
+ job2 = child(:job2)
28
+ if ! job2
29
+ job2 = queue('testq', 'TestFib', name: :job2)
30
+ job2.input(:n).write n-2
31
+ end
32
+
33
+ if job1.status != 'completed' || job2.status != 'completed'
34
+ suspend
35
+ else
36
+ output(:num).write (job1.output(:num).read + job2.output(:num).read)
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ describe TestFib do
43
+ it 'calculates fibonacci correctly' do
44
+ job = SideJob.queue('testq', 'TestFib')
45
+ job.input(:n).write 6 # 1, 1, 2, 3, 5, 8
46
+ SideJob::Worker.drain_queue
47
+ job.reload
48
+ expect(job.status).to eq 'completed'
49
+ expect(job.output(:num).read).to eq(8)
50
+ end
51
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ class TestSumFlow
4
+ include SideJob::Worker
5
+ register(
6
+ inports: {},
7
+ outports: {
8
+ out: {},
9
+ }
10
+ )
11
+ def perform
12
+ sum = child(:sum)
13
+ if ! sum
14
+ queue('testq', 'TestSum', name: :sum)
15
+ suspend
16
+ else
17
+ if get(:sent)
18
+ output(:out).write sum.output(:sum).read
19
+ else
20
+ sum.input(:in).write 5
21
+ sum.input(:in).write 6
22
+ sum.input(:ready).write 1
23
+ set(sent: 1)
24
+ sum.run
25
+ suspend
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ describe TestSumFlow do
32
+ it 'calls child job to sum numbers' do
33
+ job = SideJob.queue('testq', 'TestSumFlow')
34
+ SideJob::Worker.drain_queue
35
+ job.reload
36
+ expect(job.status).to eq 'completed'
37
+ expect(job.output(:out).read).to eq(11)
38
+ end
39
+ end
@@ -0,0 +1,543 @@
1
+ require 'spec_helper'
2
+
3
+ describe SideJob::Job do
4
+ describe '#==, #eql?' do
5
+ it 'two jobs with the same id are eq' do
6
+ expect(SideJob::Job.new('123')).to eq(SideJob::Job.new('123'))
7
+ expect(SideJob::Job.new('123')).to eql(SideJob::Job.new('123'))
8
+ end
9
+
10
+ it 'two jobs with different id are not eq' do
11
+ expect(SideJob::Job.new('123')).not_to eq(SideJob::Job.new('456'))
12
+ expect(SideJob::Job.new('123')).not_to eql(SideJob::Job.new('456'))
13
+ end
14
+ end
15
+
16
+ describe '#hash' do
17
+ it 'uses hash of the job id and can be used as hash keys' do
18
+ job = SideJob::Job.new('abc')
19
+ expect(job.hash).to eq('abc'.hash)
20
+ h = {}
21
+ h[job] = 1
22
+ job2 = SideJob::Job.new('abc')
23
+ expect(job.hash).to eq(job2.hash)
24
+ h[job2] = 3
25
+ expect(h.keys.length).to be(1)
26
+ expect(h[job]).to be(3)
27
+ end
28
+ end
29
+
30
+ describe '#to_s' do
31
+ it 'returns the redis key' do
32
+ job = SideJob::Job.new('abc')
33
+ expect(job.to_s).to eq 'job:abc'
34
+ end
35
+ end
36
+
37
+ describe '#exists?' do
38
+ it 'returns true if job exists' do
39
+ @job = SideJob.queue('testq', 'TestWorker')
40
+ expect(@job.exists?).to be true
41
+ end
42
+ it 'returns false if job does not exist' do
43
+ expect(SideJob::Job.new('job').exists?).to be false
44
+ end
45
+ end
46
+
47
+ describe '#log' do
48
+ before do
49
+ @job = SideJob.queue('testq', 'TestWorker')
50
+ @entry = {'abc' => [1,2]}
51
+ @entry_job = @entry.merge({job: @job.id})
52
+ end
53
+
54
+ it 'logs to SideJob.log if logger not set' do
55
+ expect(SideJob).to receive(:log).with @entry_job
56
+ @job.log(@entry)
57
+ end
58
+
59
+ it 'logs to job logger if set' do
60
+ expect(SideJob).not_to receive(:log).with @entry_job
61
+ @job.logger = Class.new
62
+ expect(@job.logger).to receive(:log).with @entry_job
63
+ @job.log(@entry)
64
+ end
65
+ end
66
+
67
+ describe '#group_port_logs' do
68
+ before do
69
+ @job = SideJob.queue('testq', 'TestWorker', inports: {port1: {}})
70
+ @port = @job.input(:port1)
71
+ now = Time.now
72
+ allow(Time).to receive(:now) { now }
73
+ end
74
+
75
+ it 'does not generate a log if no logs occur' do
76
+ @job.group_port_logs {}
77
+ expect(SideJob.logs.length).to eq 0
78
+ end
79
+
80
+ it 'passes through logs not generated by port read/write' do
81
+ @job.group_port_logs do
82
+ @job.log({'test' => 123})
83
+ expect(SideJob.logs).to eq [{'timestamp' => SideJob.timestamp, 'test' => 123, 'job' => @job.id}]
84
+ end
85
+ end
86
+
87
+ it 'merges logs from port reads and writes' do
88
+ @job.group_port_logs do
89
+ @port.write 'hello'
90
+ @port.write 2
91
+ end
92
+ expect(SideJob.logs).to eq [{'timestamp' => SideJob.timestamp, 'job' => @job.id,
93
+ 'read' => [],
94
+ 'write' => [{'job' => @job.id, 'inport' => 'port1', 'data' => ['hello', 2]}]}]
95
+ end
96
+
97
+ it 'does not write out a log entry until the end of outermost group' do
98
+ @job.group_port_logs do
99
+ @port.write 'hello'
100
+ @job.group_port_logs do
101
+ @port.write 2
102
+ end
103
+ expect(SideJob.logs.length).to eq 0
104
+ end
105
+ expect(SideJob.logs).to eq [{'timestamp' => SideJob.timestamp, 'job' => @job.id,
106
+ 'read' => [],
107
+ 'write' => [{'job' => @job.id, 'inport' => 'port1', 'data' => ['hello', 2]}]}]
108
+ end
109
+
110
+ it 'can overwrite job for log attribution' do
111
+ @job.group_port_logs(job: 1234) do
112
+ @port.write 'hello'
113
+ @port.read
114
+ end
115
+ expect(SideJob.logs).to eq [{'timestamp' => SideJob.timestamp, 'job' => 1234,
116
+ 'read' => [{'job' => @job.id, 'inport' => 'port1', 'data' => ['hello']}],
117
+ 'write' => [{'job' => @job.id, 'inport' => 'port1', 'data' => ['hello']}]}]
118
+ end
119
+
120
+ it 'can include arbitrary metadata' do
121
+ @job.group_port_logs(user: 'test') do
122
+ @port.write 'hello'
123
+ @port.read
124
+ end
125
+ expect(SideJob.logs).to eq [{'timestamp' => SideJob.timestamp, 'job' => @job.id, 'user' => 'test',
126
+ 'read' => [{'job' => @job.id, 'inport' => 'port1', 'data' => ['hello']}],
127
+ 'write' => [{'job' => @job.id, 'inport' => 'port1', 'data' => ['hello']}]}]
128
+ end
129
+ end
130
+
131
+ describe '#status' do
132
+ it 'retrieves status' do
133
+ @job = SideJob.queue('testq', 'TestWorker')
134
+ expect(@job.status).to eq 'queued'
135
+ end
136
+ end
137
+
138
+ describe '#status=' do
139
+ it 'sets status' do
140
+ @job = SideJob.queue('testq', 'TestWorker')
141
+ @job.status = 'newstatus'
142
+ expect(@job.status).to eq 'newstatus'
143
+ end
144
+ end
145
+
146
+ describe '#terminate' do
147
+ before do
148
+ @job = SideJob.queue('testq', 'TestWorker')
149
+ end
150
+
151
+ it 'sets the status to terminating' do
152
+ @job.terminate
153
+ expect(@job.status).to eq 'terminating'
154
+ end
155
+
156
+ it 'does nothing if status is terminated' do
157
+ @job.status = 'terminated'
158
+ @job.terminate
159
+ expect(@job.status).to eq 'terminated'
160
+ end
161
+
162
+ it 'throws error and immediately sets status to terminated if job class is unregistered' do
163
+ SideJob.redis.del 'workers:testq'
164
+ expect { @job.terminate }.to raise_error
165
+ expect(@job.status).to eq 'terminated'
166
+ end
167
+
168
+ it 'queues the job for termination run' do
169
+ expect {
170
+ @job.terminate
171
+ }.to change {Sidekiq::Stats.new.enqueued}.by(1)
172
+ end
173
+
174
+ it 'by default does not terminate children' do
175
+ child = SideJob.queue('testq', 'TestWorker', parent: @job, name: 'child')
176
+ expect(child.status).to eq 'queued'
177
+ @job.terminate
178
+ expect(child.status).to eq 'queued'
179
+ end
180
+
181
+ it 'can recursively terminate' do
182
+ 5.times {|i| SideJob.queue('testq', 'TestWorker', parent: @job, name: "child#{i}") }
183
+ @job.terminate(recursive: true)
184
+ @job.children.each_value do |child|
185
+ expect(child.status).to eq 'terminating'
186
+ end
187
+ end
188
+ end
189
+
190
+ describe '#run' do
191
+ before do
192
+ @job = SideJob.queue('testq', 'TestWorker')
193
+ end
194
+
195
+ %w{queued running suspended completed failed}.each do |status|
196
+ it "queues the job if status is #{status}" do
197
+ expect {
198
+ @job.status = status
199
+ @job.run
200
+ expect(@job.status).to eq 'queued'
201
+ }.to change {Sidekiq::Stats.new.enqueued}.by(1)
202
+ end
203
+ end
204
+
205
+ %w{terminating terminated}.each do |status|
206
+ it "does not queue the job if status is #{status}" do
207
+ expect {
208
+ @job.status = status
209
+ @job.run
210
+ expect(@job.status).to eq status
211
+ }.to change {Sidekiq::Stats.new.enqueued}.by(0)
212
+ end
213
+
214
+ it "queues the job if status is #{status} and force=true" do
215
+ expect {
216
+ @job.status = status
217
+ @job.run(force: true)
218
+ expect(@job.status).to eq 'queued'
219
+ }.to change {Sidekiq::Stats.new.enqueued}.by(1)
220
+ end
221
+ end
222
+
223
+ it 'throws error and immediately sets status to terminated if job class is unregistered' do
224
+ SideJob.redis.del "workers:#{@job.get(:queue)}"
225
+ expect { @job.run }.to raise_error
226
+ expect(@job.status).to eq 'terminated'
227
+ end
228
+
229
+ it 'can schedule a job to run at a specific time using a float' do
230
+ time = Time.now.to_f + 10000
231
+ expect { @job.run(at: time) }.to change {Sidekiq::Stats.new.scheduled_size}.by(1)
232
+ expect(Sidekiq::ScheduledSet.new.find_job(@job.id).at).to eq(Time.at(time))
233
+ expect(@job.status).to eq 'queued'
234
+ end
235
+
236
+ it 'can schedule a job to run at a specific time using a Time' do
237
+ time = Time.now + 1000
238
+ expect { @job.run(at: time) }.to change {Sidekiq::Stats.new.scheduled_size}.by(1)
239
+ expect(Sidekiq::ScheduledSet.new.find_job(@job.id).at).to eq(Time.at(time.to_f))
240
+ expect(@job.status).to eq 'queued'
241
+ end
242
+
243
+ it 'can schedule a job to run in a specific time' do
244
+ now = Time.now
245
+ allow(Time).to receive(:now) { now }
246
+ expect { @job.run(wait: 100) }.to change {Sidekiq::Stats.new.scheduled_size}.by(1)
247
+ expect(Sidekiq::ScheduledSet.new.find_job(@job.id).at).to eq(Time.at(now.to_f + 100))
248
+ expect(@job.status).to eq 'queued'
249
+ end
250
+
251
+ it 'raises error if job no longer exists' do
252
+ job2 = SideJob.find(@job.id)
253
+ job2.status = 'terminated'
254
+ expect(job2.delete).to be true
255
+ expect { @job.run }.to raise_error
256
+ end
257
+ end
258
+
259
+ describe '#child' do
260
+ it 'returns nil for missing child' do
261
+ job = SideJob.queue('testq', 'TestWorker')
262
+ expect(job.child('child')).to be nil
263
+ end
264
+
265
+ it 'returns child by name' do
266
+ job = SideJob.queue('testq', 'TestWorker')
267
+ child = SideJob.queue('testq', 'TestWorker', parent: job, name: 'child')
268
+ expect(job.child('child')).to eq child
269
+ end
270
+ end
271
+
272
+ describe '#children, #parent' do
273
+ it 'can get children and parent jobs' do
274
+ parent = SideJob.queue('testq', 'TestWorker')
275
+ child = SideJob.queue('testq', 'TestWorker', parent: parent, name: 'child')
276
+ expect(parent.children).to eq('child' => child)
277
+ expect(child.parent).to eq(parent)
278
+ end
279
+ end
280
+
281
+ describe '#ancestors' do
282
+ it 'returns empty array if no parent' do
283
+ job = SideJob.queue('testq', 'TestWorker')
284
+ expect(job.ancestors).to eq([])
285
+ end
286
+
287
+ it 'returns entire job tree' do
288
+ j1 = SideJob.queue('testq', 'TestWorker')
289
+ j2 = SideJob.queue('testq', 'TestWorker', parent: j1, name: 'child')
290
+ j3 = SideJob.queue('testq', 'TestWorker', parent: j2, name: 'child')
291
+ j4 = SideJob.queue('testq', 'TestWorker', parent: j3, name: 'child')
292
+ expect(j4.ancestors).to eq([j3, j2, j1])
293
+ end
294
+ end
295
+
296
+ describe '#terminated?' do
297
+ before do
298
+ @job = SideJob.queue('testq', 'TestWorker')
299
+ end
300
+
301
+ it 'returns false if job status is not terminated' do
302
+ expect(@job.terminated?).to be false
303
+ end
304
+
305
+ it 'returns true if job status is terminated' do
306
+ @job.status = 'terminated'
307
+ expect(@job.terminated?).to be true
308
+ end
309
+
310
+ it 'returns false if child job is not terminated' do
311
+ @job.status = 'terminated'
312
+ SideJob.queue('testq', 'TestWorker', parent: @job, name: 'child')
313
+ expect(@job.terminated?).to be false
314
+ end
315
+
316
+ it 'returns true if child job is terminated' do
317
+ @job.status = 'terminated'
318
+ child = SideJob.queue('testq', 'TestWorker', parent: @job, name: 'child')
319
+ child.status = 'terminated'
320
+ expect(@job.terminated?).to be true
321
+ end
322
+ end
323
+
324
+ describe '#delete' do
325
+ before do
326
+ @job = SideJob.queue('testq', 'TestWorker', inports: {in1: {}}, outports: {out1: {}})
327
+ end
328
+
329
+ it 'does not delete non-terminated jobs' do
330
+ expect(@job.delete).to be false
331
+ expect(@job.exists?).to be true
332
+ end
333
+
334
+ it 'deletes terminated jobs' do
335
+ @job.status = 'terminated'
336
+ expect(@job.delete).to be true
337
+ expect(@job.exists?).to be false
338
+ end
339
+
340
+ it 'recursively deletes jobs' do
341
+ child = SideJob.queue('testq', 'TestWorker', parent: @job, name: 'child')
342
+ expect(@job.status).to eq('queued')
343
+ expect(child.status).to eq('queued')
344
+ expect(SideJob.redis {|redis| redis.keys('job:*').length}).to be > 0
345
+ @job.status = 'terminated'
346
+ child.status = 'terminated'
347
+ @job.delete
348
+ expect(SideJob.redis {|redis| redis.keys('job:*').length}).to be(0)
349
+ expect(@job.exists?).to be false
350
+ expect(child.exists?).to be false
351
+ end
352
+
353
+ it 'deletes data on input and output ports' do
354
+ @job.input(:in1).write 'data'
355
+ @job.output(:out1).write 'data'
356
+ expect(@job.input(:in1).size).to be 1
357
+ expect(@job.output(:out1).size).to be 1
358
+ @job.status = 'terminated'
359
+ @job.delete
360
+ expect(SideJob.redis {|redis| redis.keys('job:*').length}).to be(0)
361
+ end
362
+ end
363
+
364
+ # Tests are identical for input and output port methods
365
+ %i{in out}.each do |type|
366
+ describe "##{type}put" do
367
+ it "returns a cached #{type}put port" do
368
+ spec = {}
369
+ spec[:"#{type}ports"] = {port: {}}
370
+ @job = SideJob.queue('testq', 'TestWorker', **spec)
371
+ expect(@job.send("#{type}put", :port)).to eq(SideJob::Port.new(@job, type, :port))
372
+ expect(@job.send("#{type}put", :port)).to be(@job.send("#{type}put", :port))
373
+ end
374
+
375
+ it 'raises error on unknown port' do
376
+ @job = SideJob.queue('testq', 'TestWorker')
377
+ expect { @job.send("#{type}put", :unknown) }.to raise_error
378
+ end
379
+
380
+ it 'can dynamically create ports' do
381
+ spec = {}
382
+ spec[:"#{type}ports"] = {'*' => {mode: :memory, default: 123}}
383
+ @job = SideJob.queue('testq', 'TestWorker', **spec)
384
+ expect(@job.send("#{type}ports").size).to eq 0
385
+ port = @job.send("#{type}put", :newport)
386
+ expect(@job.send("#{type}ports").size).to eq 1
387
+ expect(port.mode).to eq :memory
388
+ expect(port.default).to eq 123
389
+ end
390
+ end
391
+
392
+ describe "##{type}ports" do
393
+ it "returns all #{type}put ports" do
394
+ @job = SideJob.queue('testq', 'TestWorker', inports: { port1: {} }, outports: { port1: {} })
395
+ expect(@job.send("#{type}ports")).to eq([SideJob::Port.new(@job, type, :port1)])
396
+ end
397
+ end
398
+
399
+ describe "##{type}ports=" do
400
+ before do
401
+ @job = SideJob.queue('testq', 'TestWorker')
402
+ end
403
+
404
+ it 'can specify ports with options' do
405
+ expect(@job.send("#{type}ports").size).to eq 0
406
+ @job.send("#{type}ports=", {myport: {mode: :memory, default: 'def'}})
407
+ expect(@job.send("#{type}ports").size).to eq 1
408
+ expect(@job.send("#{type}ports").map(&:name)).to include(:myport)
409
+ expect(@job.send("#{type}put", :myport).mode).to eq :memory
410
+ expect(@job.send("#{type}put", :myport).default).to eq 'def'
411
+ end
412
+
413
+ it 'merges ports with the worker configuration' do
414
+ allow(@job).to receive(:config) { {"#{type}ports" => {'port1' => {}, 'port2' => {'mode' => 'memory'}}}}
415
+ @job.send("#{type}ports=", {port2: {mode: :queue}, port3: {}})
416
+ expect(@job.send("#{type}ports").size).to eq 3
417
+ expect(@job.send("#{type}ports").all? {|port| port.options == {mode: :queue}}).to be true
418
+ end
419
+
420
+ it 'can change existing port mode while keeping data intact' do
421
+ @job.send("#{type}ports=", {myport: {}})
422
+ @job.send("#{type}put", :myport).write 'data'
423
+ @job.send("#{type}ports=", {myport: {mode: :memory, default: 'def'}})
424
+ expect(@job.send("#{type}ports").size).to eq 1
425
+ expect(@job.send("#{type}put", :myport).mode).to eq :memory
426
+ expect(@job.send("#{type}put", :myport).default).to eq 'def'
427
+ expect(@job.send("#{type}put", :myport).read).to eq 'data'
428
+ expect(@job.send("#{type}put", :myport).read).to eq 'def'
429
+ end
430
+
431
+ it 'deletes no longer used ports' do
432
+ @job.send("#{type}ports=", {myport: {}})
433
+ @job.send("#{type}put", :myport).write 123
434
+ @job.send("#{type}ports=", {})
435
+ expect(@job.send("#{type}ports").map(&:name)).not_to include(:myport)
436
+ expect { @job.send("#{type}put", :myport) }.to raise_error
437
+ end
438
+
439
+ it 'can specify port data' do
440
+ @job.send("#{type}ports=", {myport: {data: [1,'abc']}})
441
+ expect(@job.send("#{type}put", :myport).read).to eq 1
442
+ expect(@job.send("#{type}put", :myport).read).to eq 'abc'
443
+ end
444
+
445
+ it 'can clear port data' do
446
+ @job.send("#{type}ports=", {myport: {}})
447
+ @job.send("#{type}put", :myport).write 'data'
448
+ expect(@job.send("#{type}put", :myport).size).to eq 1
449
+ @job.send("#{type}ports=", {myport: {data: []}})
450
+ expect(@job.send("#{type}put", :myport).size).to eq 0
451
+ end
452
+
453
+ it 'overwrites existing data' do
454
+ @job.send("#{type}ports=", {myport: {}})
455
+ @job.send("#{type}put", :myport).write 'data'
456
+ expect(@job.send("#{type}put", :myport).size).to eq 1
457
+ @job.send("#{type}ports=", {myport: {data: [1,'abc']}})
458
+ expect(@job.send("#{type}put", :myport).size).to eq 2
459
+ expect(@job.send("#{type}put", :myport).read).to eq 1
460
+ expect(@job.send("#{type}put", :myport).read).to eq 'abc'
461
+ end
462
+
463
+ it 'groups logs' do
464
+ now = Time.now
465
+ allow(Time).to receive(:now) { now }
466
+ @job.send("#{type}ports=", {myport: {data: [1,'abc']}})
467
+ expect(SideJob.logs).to eq [{'timestamp' => SideJob.timestamp, 'job' => @job.id, 'read' => [], 'write' => [{'job' => @job.id, "#{type}port" => 'myport', 'data' => [1,'abc']}]}]
468
+ end
469
+ end
470
+ end
471
+
472
+ describe '#get' do
473
+ before do
474
+ @job = SideJob.queue('testq', 'TestWorker')
475
+ SideJob.redis.hset 'jobs', @job.id, { queue: 'testq', class: 'TestWorker', field1: 'value1', field2: [1,2], field3: 123 }.to_json
476
+ @job.reload
477
+ end
478
+
479
+ it 'returns a value from job state using symbol key' do
480
+ expect(@job.get(:field3)).to eq 123
481
+ end
482
+
483
+ it 'returns a value from job state using string key' do
484
+ expect(@job.get('field1')).to eq 'value1'
485
+ end
486
+
487
+ it 'returns nil for missing value' do
488
+ expect(@job.get(:missing)).to be nil
489
+ end
490
+
491
+ it 'can retrieve complex objects in job state' do
492
+ expect(@job.get(:field2)).to eq [1, 2]
493
+ end
494
+
495
+ it 'caches the state' do
496
+ expect(@job.get(:field3)).to eq 123
497
+ SideJob.redis.hmset @job.redis_key, :field3, '789'
498
+ expect(@job.get(:field3)).to eq 123
499
+ end
500
+
501
+ it 'raises error if job no longer exists and state is not cached' do
502
+ @job.reload
503
+ job2 = SideJob.find(@job.id)
504
+ job2.status = 'terminated'
505
+ job2.delete
506
+ expect { @job.get(:key) }.to raise_error
507
+ end
508
+
509
+ it 'does not raise error if job no longer exists but state is cached' do
510
+ @job.get(:foo)
511
+ job2 = SideJob.find(@job.id)
512
+ job2.status = 'terminated'
513
+ job2.delete
514
+ expect { @job.get(:key) }.not_to raise_error
515
+ end
516
+ end
517
+
518
+ describe '#reload' do
519
+ before do
520
+ @job = SideJob.queue('testq', 'TestWorker')
521
+ end
522
+
523
+ it 'clears the job state cache' do
524
+ expect(@job.get(:field1)).to be nil
525
+ SideJob.redis.hset 'jobs', @job.id, {queue: 'testq', class: 'TestWorker', field1: 789}.to_json
526
+ @job.reload
527
+ expect(@job.get(:field1)).to eq 789
528
+ end
529
+ end
530
+
531
+ describe '#config' do
532
+ before do
533
+ @job = SideJob.queue('testq', 'TestWorker')
534
+ end
535
+
536
+ it 'returns and caches worker configuration' do
537
+ expect(SideJob::Worker).to receive(:config).with('testq', 'TestWorker').once.and_call_original
538
+ @job.reload
539
+ @job.config
540
+ @job.config # test cached
541
+ end
542
+ end
543
+ end