public
Description: Database based asynchronously priority queue system -- Extracted from Shopify
Homepage: http://www.shopify.com
Clone URL: git://github.com/tobi/delayed_job.git
delayed_job / spec / job_spec.rb
100644 135 lines (90 sloc) 4.57 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
require File.dirname(__FILE__) + '/database'
 
class SimpleJob
  cattr_accessor :runs; self.runs = 0
  def perform; @@runs += 1; end
end
 
class ErrorJob
  cattr_accessor :runs; self.runs = 0
  def perform; raise 'did not work'; end
end
 
describe Delayed::Job do
  
  before :each do
    reset_db
  end
 
  it "should set run_at automatically" do
    Delayed::Job.create(:payload_object => ErrorJob.new ).run_at.should_not == nil
  end
 
  it "should raise ArgumentError when handler doesn't respond_to :perform" do
    lambda { Delayed::Job.enqueue(Object.new) }.should raise_error(ArgumentError)
  end
      
  it "should increase count after enqueuing items" do
    Delayed::Job.enqueue SimpleJob.new
    Delayed::Job.count.should == 1
  end
  
  it "should call perform on jobs when running work_off" do
    SimpleJob.runs.should == 0
        
    Delayed::Job.enqueue SimpleJob.new
    Delayed::Job.work_off
    
    SimpleJob.runs.should == 1
  end
  
  it "should re-schedule by about 1 second at first and increment this more and more minutes when it fails to execute properly" do
    Delayed::Job.enqueue ErrorJob.new
    runner = Delayed::Job.work_off(1)
 
    job = Delayed::Job.find(:first)
    job.last_error.should == 'did not work'
    job.attempts.should == 1
    job.run_at.should > Time.now
    job.run_at.should < Time.now + 6.minutes
  end
  
  it "should raise an DeserializationError when the job class is totally unknown" do
 
    job = Delayed::Job.new
    job['handler'] = "--- !ruby/object:JobThatDoesNotExist {}"
 
    lambda { job.payload_object.perform }.should raise_error(Delayed::DeserializationError)
  end
 
  it "should try to load the class when it is unknown at the time of the deserialization" do
    job = Delayed::Job.new
    job['handler'] = "--- !ruby/object:JobThatDoesNotExist {}"
 
    job.should_receive(:attempt_to_load).with('JobThatDoesNotExist').and_return(true)
     
    lambda { job.payload_object.perform }.should raise_error(Delayed::DeserializationError)
  end
  
  it "should try include the namespace when loading unknown objects" do
    job = Delayed::Job.new
    job['handler'] = "--- !ruby/object:Delayed::JobThatDoesNotExist {}"
    job.should_receive(:attempt_to_load).with('Delayed::JobThatDoesNotExist').and_return(true)
    lambda { job.payload_object.perform }.should raise_error(Delayed::DeserializationError)
  end
  
  
  it "should also try to load structs when they are unknown (raises TypeError)" do
    job = Delayed::Job.new
    job['handler'] = "--- !ruby/struct:JobThatDoesNotExist {}"
 
    job.should_receive(:attempt_to_load).with('JobThatDoesNotExist').and_return(true)
     
    lambda { job.payload_object.perform }.should raise_error(Delayed::DeserializationError)
  end
  
  it "should try include the namespace when loading unknown structs" do
    job = Delayed::Job.new
    job['handler'] = "--- !ruby/struct:Delayed::JobThatDoesNotExist {}"
    job.should_receive(:attempt_to_load).with('Delayed::JobThatDoesNotExist').and_return(true)
    lambda { job.payload_object.perform }.should raise_error(Delayed::DeserializationError)
  end
             
  
  describe "when another worker is already performing an task, it" do
    
    before :each do
      Delayed::Job.worker_name = 'worker1'
      @job = Delayed::Job.create :payload_object => SimpleJob.new, :locked_by => 'worker1', :locked_at => Time.now.utc
    end
    
    it "should not allow a second worker to get exclusive access" do
      lambda { @job.lock_exclusively! 4.hours, 'worker2' }.should raise_error(Delayed::Job::LockError)
    end
    
    it "should be able to get access to the task if it was started more then max_age ago" do
      @job.locked_at = 5.hours.ago
      @job.save
 
      @job.lock_exclusively! 4.hours, 'worker2'
      @job.reload
      @job.locked_by.should == 'worker2'
      @job.locked_at.should > 1.minute.ago
    end
 
    it "should be able to get exclusive access again when the worker name is the same" do
      @job.lock_exclusively! Time.now + 20, 'worker1'
      @job.lock_exclusively! Time.now + 21, 'worker1'
      @job.lock_exclusively! Time.now + 22, 'worker1'
    end
  end
  
end