sidekiq-status 0.5.0 → 3.0.3
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.
- checksums.yaml +5 -5
- data/.github/workflows/ci.yaml +53 -0
- data/.gitignore +4 -1
- data/.gitlab-ci.yml +17 -0
- data/Appraisals +11 -0
- data/CHANGELOG.md +41 -0
- data/README.md +148 -41
- data/Rakefile +2 -0
- data/gemfiles/sidekiq_6.1.gemfile +7 -0
- data/gemfiles/sidekiq_6.x.gemfile +7 -0
- data/gemfiles/sidekiq_7.x.gemfile +7 -0
- data/lib/sidekiq-status/client_middleware.rb +54 -1
- data/lib/sidekiq-status/redis_adapter.rb +18 -0
- data/lib/sidekiq-status/redis_client_adapter.rb +14 -0
- data/lib/sidekiq-status/server_middleware.rb +76 -17
- data/lib/sidekiq-status/sidekiq_extensions.rb +7 -0
- data/lib/sidekiq-status/storage.rb +26 -13
- data/lib/sidekiq-status/testing/inline.rb +10 -0
- data/lib/sidekiq-status/version.rb +1 -1
- data/lib/sidekiq-status/web.rb +158 -10
- data/lib/sidekiq-status/worker.rb +12 -5
- data/lib/sidekiq-status.rb +43 -10
- data/sidekiq-status.gemspec +9 -4
- data/spec/environment.rb +1 -0
- data/spec/lib/sidekiq-status/client_middleware_spec.rb +30 -13
- data/spec/lib/sidekiq-status/server_middleware_spec.rb +102 -30
- data/spec/lib/sidekiq-status/web_spec.rb +84 -0
- data/spec/lib/sidekiq-status/worker_spec.rb +21 -2
- data/spec/lib/sidekiq-status_spec.rb +158 -77
- data/spec/spec_helper.rb +104 -24
- data/spec/support/test_jobs.rb +84 -7
- data/web/sidekiq-status-single-web.png +0 -0
- data/web/sidekiq-status-web.png +0 -0
- data/web/views/status.erb +118 -0
- data/web/views/status_not_found.erb +6 -0
- data/web/views/statuses.erb +135 -17
- metadata +102 -16
- data/.travis.yml +0 -2
- data/CHANGELOG +0 -2
@@ -5,62 +5,134 @@ describe Sidekiq::Status::ServerMiddleware do
|
|
5
5
|
let!(:redis) { Sidekiq.redis { |conn| conn } }
|
6
6
|
let!(:job_id) { SecureRandom.hex(12) }
|
7
7
|
|
8
|
-
|
9
|
-
# Seems like flushall has no effect on recently published messages,
|
10
|
-
# so we should wait till they expire
|
11
|
-
before { redis.flushall; sleep 0.1 }
|
12
|
-
|
13
|
-
describe "#call" do
|
8
|
+
describe "without :expiration parameter" do
|
14
9
|
it "sets working/complete status" do
|
15
|
-
|
16
|
-
SecureRandom.should_receive(:hex).once.and_return(job_id)
|
10
|
+
allow(SecureRandom).to receive(:hex).once.and_return(job_id)
|
17
11
|
start_server do
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
12
|
+
thread = branched_redis_thread 4, "status_updates", "job_messages_#{job_id}" do
|
13
|
+
expect(ConfirmationJob.perform_async 'arg1' => 'val1').to eq(job_id)
|
14
|
+
end
|
15
|
+
expect(thread.value).to eq([
|
16
|
+
job_id,
|
17
|
+
job_id,
|
18
|
+
"while in #perform, status = working",
|
19
|
+
job_id
|
20
|
+
])
|
22
21
|
end
|
23
|
-
redis.hget(job_id, :status).
|
24
|
-
Sidekiq::Status::complete?(job_id).
|
22
|
+
expect(redis.hget("sidekiq:status:#{job_id}", :status)).to eq('complete')
|
23
|
+
expect(Sidekiq::Status::complete?(job_id)).to be_truthy
|
25
24
|
end
|
26
25
|
|
27
26
|
it "sets failed status" do
|
28
|
-
SecureRandom.
|
27
|
+
allow(SecureRandom).to receive(:hex).once.and_return(job_id)
|
28
|
+
start_server do
|
29
|
+
expect(capture_status_updates(3) {
|
30
|
+
expect(FailingJob.perform_async).to eq(job_id)
|
31
|
+
}).to eq([job_id]*3)
|
32
|
+
end
|
33
|
+
expect(redis.hget("sidekiq:status:#{job_id}", :status)).to eq('failed')
|
34
|
+
expect(Sidekiq::Status::failed?(job_id)).to be_truthy
|
35
|
+
end
|
36
|
+
|
37
|
+
it "sets failed status when Exception raised" do
|
38
|
+
allow(SecureRandom).to receive(:hex).once.and_return(job_id)
|
29
39
|
start_server do
|
30
|
-
|
31
|
-
|
32
|
-
|
40
|
+
expect(capture_status_updates(3) {
|
41
|
+
expect(FailingHardJob.perform_async).to eq(job_id)
|
42
|
+
}).to eq([job_id]*3)
|
33
43
|
end
|
34
|
-
redis.hget(job_id, :status).
|
35
|
-
Sidekiq::Status::failed?(job_id).
|
44
|
+
expect(redis.hget("sidekiq:status:#{job_id}", :status)).to eq('failed')
|
45
|
+
expect(Sidekiq::Status::failed?(job_id)).to be_truthy
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when Sidekiq::Status::Worker is not included in the job" do
|
49
|
+
it "should not set a failed status" do
|
50
|
+
allow(SecureRandom).to receive(:hex).once.and_return(job_id)
|
51
|
+
start_server do
|
52
|
+
expect(FailingNoStatusJob.perform_async).to eq(job_id)
|
53
|
+
end
|
54
|
+
expect(redis.hget("sidekiq:status:#{job_id}", :status)).to be_nil
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should not set any status when Exception raised" do
|
58
|
+
allow(SecureRandom).to receive(:hex).once.and_return(job_id)
|
59
|
+
start_server do
|
60
|
+
expect(FailingHardNoStatusJob.perform_async).to eq(job_id)
|
61
|
+
end
|
62
|
+
expect(redis.hget("sidekiq:status:#{job_id}", :status)).to be_nil
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should not set any status on system exit signal" do
|
66
|
+
allow(SecureRandom).to receive(:hex).once.and_return(job_id)
|
67
|
+
start_server do
|
68
|
+
expect(ExitedNoStatusJob.perform_async).to eq(job_id)
|
69
|
+
end
|
70
|
+
expect(redis.hget("sidekiq:status:#{job_id}", :status)).to be_nil
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should not set any status on interrupt signal" do
|
74
|
+
allow(SecureRandom).to receive(:hex).once.and_return(job_id)
|
75
|
+
start_server do
|
76
|
+
expect(InterruptedNoStatusJob.perform_async).to eq(job_id)
|
77
|
+
end
|
78
|
+
expect(redis.hget("sidekiq:status:#{job_id}", :status)).to be_nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "sets interrupted status" do
|
83
|
+
it "on system exit signal" do
|
84
|
+
allow(SecureRandom).to receive(:hex).once.and_return(job_id)
|
85
|
+
start_server do
|
86
|
+
expect(capture_status_updates(3) {
|
87
|
+
expect(ExitedJob.perform_async).to eq(job_id)
|
88
|
+
}).to eq([job_id]*3)
|
89
|
+
end
|
90
|
+
expect(redis.hget("sidekiq:status:#{job_id}", :status)).to eq('interrupted')
|
91
|
+
expect(Sidekiq::Status::interrupted?(job_id)).to be_truthy
|
92
|
+
end
|
93
|
+
|
94
|
+
it "on interrupt signal" do
|
95
|
+
allow(SecureRandom).to receive(:hex).once.and_return(job_id)
|
96
|
+
start_server do
|
97
|
+
expect(capture_status_updates(3) {
|
98
|
+
expect(InterruptedJob.perform_async).to eq(job_id)
|
99
|
+
}).to eq([job_id]*3)
|
100
|
+
end
|
101
|
+
expect(redis.hget("sidekiq:status:#{job_id}", :status)).to eq('interrupted')
|
102
|
+
expect(Sidekiq::Status::interrupted?(job_id)).to be_truthy
|
103
|
+
end
|
104
|
+
|
36
105
|
end
|
37
106
|
|
38
107
|
it "sets status hash ttl" do
|
39
|
-
SecureRandom.
|
40
|
-
|
41
|
-
|
108
|
+
allow(SecureRandom).to receive(:hex).once.and_return(job_id)
|
109
|
+
start_server do
|
110
|
+
expect(StubJob.perform_async 'arg1' => 'val1').to eq(job_id)
|
111
|
+
end
|
112
|
+
expect(1..Sidekiq::Status::DEFAULT_EXPIRY).to cover redis.ttl("sidekiq:status:#{job_id}")
|
42
113
|
end
|
43
114
|
end
|
44
115
|
|
45
|
-
describe ":expiration parameter" do
|
116
|
+
describe "with :expiration parameter" do
|
46
117
|
let(:huge_expiration) { Sidekiq::Status::DEFAULT_EXPIRY * 100 }
|
47
118
|
before do
|
48
|
-
SecureRandom.
|
119
|
+
allow(SecureRandom).to receive(:hex).once.and_return(job_id)
|
49
120
|
end
|
121
|
+
|
50
122
|
it "overwrites default expiry value" do
|
51
123
|
start_server(:expiration => huge_expiration) do
|
52
|
-
StubJob.perform_async
|
124
|
+
StubJob.perform_async 'arg1' => 'val1'
|
53
125
|
end
|
54
|
-
((Sidekiq::Status::DEFAULT_EXPIRY
|
126
|
+
expect((Sidekiq::Status::DEFAULT_EXPIRY-1)..huge_expiration).to cover redis.ttl("sidekiq:status:#{job_id}")
|
55
127
|
end
|
56
128
|
|
57
129
|
it "can be overwritten by worker expiration method" do
|
58
130
|
overwritten_expiration = huge_expiration * 100
|
59
|
-
StubJob.
|
131
|
+
allow_any_instance_of(StubJob).to receive(:expiration).and_return(overwritten_expiration)
|
60
132
|
start_server(:expiration => huge_expiration) do
|
61
|
-
StubJob.perform_async
|
133
|
+
StubJob.perform_async 'arg1' => 'val1'
|
62
134
|
end
|
63
|
-
((huge_expiration+1)..overwritten_expiration).
|
135
|
+
expect((huge_expiration+1)..overwritten_expiration).to cover redis.ttl("sidekiq:status:#{job_id}")
|
64
136
|
end
|
65
137
|
end
|
66
138
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'sidekiq-status/web'
|
3
|
+
require 'rack/test'
|
4
|
+
|
5
|
+
describe 'sidekiq status web' do
|
6
|
+
include Rack::Test::Methods
|
7
|
+
|
8
|
+
let!(:redis) { Sidekiq.redis { |conn| conn } }
|
9
|
+
let!(:job_id) { SecureRandom.hex(12) }
|
10
|
+
|
11
|
+
def app
|
12
|
+
Sidekiq::Web
|
13
|
+
end
|
14
|
+
|
15
|
+
before do
|
16
|
+
env 'rack.session', csrf: Base64.urlsafe_encode64('token')
|
17
|
+
client_middleware
|
18
|
+
allow(SecureRandom).to receive(:hex).and_return(job_id)
|
19
|
+
end
|
20
|
+
|
21
|
+
around { |example| start_server(&example) }
|
22
|
+
|
23
|
+
it 'shows the list of jobs in progress' do
|
24
|
+
capture_status_updates(2) do
|
25
|
+
expect(LongJob.perform_async(0.5)).to eq(job_id)
|
26
|
+
end
|
27
|
+
|
28
|
+
get '/statuses'
|
29
|
+
expect(last_response).to be_ok
|
30
|
+
expect(last_response.body).to match(/#{job_id}/)
|
31
|
+
expect(last_response.body).to match(/LongJob/)
|
32
|
+
expect(last_response.body).to match(/working/)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'allows filtering the list of jobs by status' do
|
36
|
+
capture_status_updates(2) do
|
37
|
+
LongJob.perform_async(0.5)
|
38
|
+
end
|
39
|
+
|
40
|
+
get '/statuses?status=working'
|
41
|
+
expect(last_response).to be_ok
|
42
|
+
expect(last_response.body).to match(/#{job_id}/)
|
43
|
+
expect(last_response.body).to match(/LongJob/)
|
44
|
+
expect(last_response.body).to match(/working/)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'allows filtering the list of jobs by completed status' do
|
48
|
+
capture_status_updates(2) do
|
49
|
+
LongJob.perform_async(0.5)
|
50
|
+
end
|
51
|
+
get '/statuses?status=completed'
|
52
|
+
expect(last_response).to be_ok
|
53
|
+
expect(last_response.body).to_not match(/LongJob/)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'shows a single job in progress' do
|
57
|
+
capture_status_updates(2) do
|
58
|
+
LongJob.perform_async(1, 'another argument')
|
59
|
+
end
|
60
|
+
|
61
|
+
get "/statuses/#{job_id}"
|
62
|
+
expect(last_response).to be_ok
|
63
|
+
expect(last_response.body).to match(/#{job_id}/)
|
64
|
+
expect(last_response.body).to match(/1,"another argument"/)
|
65
|
+
expect(last_response.body).to match(/working/)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'shows custom data for a single job' do
|
69
|
+
capture_status_updates(3) do
|
70
|
+
CustomDataJob.perform_async
|
71
|
+
end
|
72
|
+
|
73
|
+
get "/statuses/#{job_id}"
|
74
|
+
expect(last_response).to be_ok
|
75
|
+
expect(last_response.body).to match(/mister_cat/)
|
76
|
+
expect(last_response.body).to match(/meow/)
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'show an error when the requested job ID is not found' do
|
80
|
+
get '/statuses/12345'
|
81
|
+
expect(last_response).to be_not_found
|
82
|
+
expect(last_response.body).to match(/That job can't be found/)
|
83
|
+
end
|
84
|
+
end
|
@@ -6,17 +6,36 @@ describe Sidekiq::Status::Worker do
|
|
6
6
|
|
7
7
|
describe ".perform_async" do
|
8
8
|
it "generates and returns job id" do
|
9
|
-
SecureRandom.
|
10
|
-
StubJob.perform_async().
|
9
|
+
allow(SecureRandom).to receive(:hex).once.and_return(job_id)
|
10
|
+
expect(StubJob.perform_async()).to eq(job_id)
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
14
|
describe ".expiration" do
|
15
15
|
subject { StubJob.new }
|
16
|
+
|
16
17
|
it "allows to set/get expiration" do
|
17
18
|
expect(subject.expiration).to be_nil
|
18
19
|
subject.expiration = :val
|
19
20
|
expect(subject.expiration).to eq(:val)
|
20
21
|
end
|
21
22
|
end
|
23
|
+
|
24
|
+
describe ".at" do
|
25
|
+
subject { StubJob.new }
|
26
|
+
|
27
|
+
it "records when the worker has started" do
|
28
|
+
expect { subject.at(0) }.to(change { subject.retrieve('working_at') })
|
29
|
+
end
|
30
|
+
|
31
|
+
context "when setting the total for the worker" do
|
32
|
+
it "records when the worker has started" do
|
33
|
+
expect { subject.total(100) }.to(change { subject.retrieve('working_at') })
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "records when the worker last worked" do
|
38
|
+
expect { subject.at(0) }.to(change { subject.retrieve('update_time') })
|
39
|
+
end
|
40
|
+
end
|
22
41
|
end
|
@@ -8,113 +8,132 @@ describe Sidekiq::Status do
|
|
8
8
|
let!(:unused_id) { SecureRandom.hex(12) }
|
9
9
|
let!(:plain_sidekiq_job_id) { SecureRandom.hex(12) }
|
10
10
|
let!(:retried_job_id) { SecureRandom.hex(12) }
|
11
|
-
|
12
|
-
# Clean Redis before each test
|
13
|
-
# Seems like flushall has no effect on recently published messages,
|
14
|
-
# so we should wait till they expire
|
15
|
-
before { redis.flushall; sleep 0.1 }
|
11
|
+
let!(:retry_and_fail_job_id) { SecureRandom.hex(12) }
|
16
12
|
|
17
13
|
describe ".status, .working?, .complete?" do
|
18
14
|
it "gets job status by id as symbol" do
|
19
|
-
SecureRandom.
|
15
|
+
allow(SecureRandom).to receive(:hex).once.and_return(job_id)
|
20
16
|
|
21
17
|
start_server do
|
22
|
-
capture_status_updates(2) {
|
23
|
-
LongJob.perform_async(
|
24
|
-
}.
|
25
|
-
Sidekiq::Status.status(job_id).
|
26
|
-
Sidekiq::Status.working?(job_id).
|
27
|
-
Sidekiq::Status::queued?(job_id).
|
28
|
-
Sidekiq::Status::
|
29
|
-
Sidekiq::Status::
|
30
|
-
Sidekiq::Status::
|
31
|
-
|
32
|
-
|
33
|
-
|
18
|
+
expect(capture_status_updates(2) {
|
19
|
+
expect(LongJob.perform_async(0.5)).to eq(job_id)
|
20
|
+
}).to eq([job_id]*2)
|
21
|
+
expect(Sidekiq::Status.status(job_id)).to eq(:working)
|
22
|
+
expect(Sidekiq::Status.working?(job_id)).to be_truthy
|
23
|
+
expect(Sidekiq::Status::queued?(job_id)).to be_falsey
|
24
|
+
expect(Sidekiq::Status::retrying?(job_id)).to be_falsey
|
25
|
+
expect(Sidekiq::Status::failed?(job_id)).to be_falsey
|
26
|
+
expect(Sidekiq::Status::complete?(job_id)).to be_falsey
|
27
|
+
expect(Sidekiq::Status::stopped?(job_id)).to be_falsey
|
28
|
+
expect(Sidekiq::Status::interrupted?(job_id)).to be_falsey
|
29
|
+
end
|
30
|
+
expect(Sidekiq::Status.status(job_id)).to eq(:complete)
|
31
|
+
expect(Sidekiq::Status.complete?(job_id)).to be_truthy
|
34
32
|
end
|
35
33
|
end
|
36
34
|
|
37
35
|
describe ".get" do
|
38
36
|
it "gets a single value from data hash as string" do
|
39
|
-
SecureRandom.
|
37
|
+
allow(SecureRandom).to receive(:hex).once.and_return(job_id)
|
40
38
|
|
41
39
|
start_server do
|
42
|
-
capture_status_updates(3) {
|
43
|
-
DataJob.perform_async.
|
44
|
-
}.
|
45
|
-
Sidekiq::Status.get(job_id, :status).
|
40
|
+
expect(capture_status_updates(3) {
|
41
|
+
expect(DataJob.perform_async).to eq(job_id)
|
42
|
+
}).to eq([job_id]*3)
|
43
|
+
expect(Sidekiq::Status.get(job_id, :status)).to eq('working')
|
46
44
|
end
|
47
|
-
Sidekiq::Status.get(job_id, :data).
|
45
|
+
expect(Sidekiq::Status.get(job_id, :data)).to eq('meow')
|
48
46
|
end
|
49
47
|
end
|
50
48
|
|
51
49
|
describe ".at, .total, .pct_complete, .message" do
|
52
50
|
it "should return job progress with correct type to it" do
|
53
|
-
SecureRandom.
|
51
|
+
allow(SecureRandom).to receive(:hex).once.and_return(job_id)
|
54
52
|
|
55
53
|
start_server do
|
56
|
-
capture_status_updates(
|
57
|
-
ProgressJob.perform_async.
|
58
|
-
}.
|
54
|
+
expect(capture_status_updates(4) {
|
55
|
+
expect(ProgressJob.perform_async).to eq(job_id)
|
56
|
+
}).to eq([job_id]*4)
|
59
57
|
end
|
60
|
-
Sidekiq::Status.at(job_id).
|
61
|
-
Sidekiq::Status.total(job_id).
|
62
|
-
|
63
|
-
Sidekiq::Status.
|
58
|
+
expect(Sidekiq::Status.at(job_id)).to be(100)
|
59
|
+
expect(Sidekiq::Status.total(job_id)).to be(500)
|
60
|
+
# It returns a float therefor we need eq()
|
61
|
+
expect(Sidekiq::Status.pct_complete(job_id)).to eq(20)
|
62
|
+
expect(Sidekiq::Status.message(job_id)).to eq('howdy, partner?')
|
64
63
|
end
|
65
64
|
end
|
66
65
|
|
67
66
|
describe ".get_all" do
|
68
67
|
it "gets the job hash by id" do
|
69
|
-
SecureRandom.
|
68
|
+
allow(SecureRandom).to receive(:hex).once.and_return(job_id)
|
69
|
+
|
70
|
+
start_server do
|
71
|
+
expect(capture_status_updates(2) {
|
72
|
+
expect(LongJob.perform_async(0.5)).to eq(job_id)
|
73
|
+
}).to eq([job_id]*2)
|
74
|
+
expect(hash = Sidekiq::Status.get_all(job_id)).to include 'status' => 'working'
|
75
|
+
expect(hash).to include 'update_time'
|
76
|
+
end
|
77
|
+
expect(hash = Sidekiq::Status.get_all(job_id)).to include 'status' => 'complete'
|
78
|
+
expect(hash).to include 'update_time'
|
79
|
+
end
|
80
|
+
end
|
70
81
|
|
82
|
+
describe '.delete' do
|
83
|
+
it 'deletes the status hash for given job id' do
|
84
|
+
allow(SecureRandom).to receive(:hex).once.and_return(job_id)
|
71
85
|
start_server do
|
72
|
-
capture_status_updates(2) {
|
73
|
-
LongJob.perform_async(
|
74
|
-
}.
|
75
|
-
(hash = Sidekiq::Status.get_all(job_id)).should include 'status' => 'working'
|
76
|
-
hash.should include 'update_time'
|
86
|
+
expect(capture_status_updates(2) {
|
87
|
+
expect(LongJob.perform_async(0.5)).to eq(job_id)
|
88
|
+
}).to eq([job_id]*2)
|
77
89
|
end
|
78
|
-
(
|
79
|
-
|
90
|
+
expect(Sidekiq::Status.delete(job_id)).to eq(1)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should not raise error while deleting status hash if invalid job id' do
|
94
|
+
allow(SecureRandom).to receive(:hex).once.and_return(job_id)
|
95
|
+
expect(Sidekiq::Status.delete(job_id)).to eq(0)
|
80
96
|
end
|
81
97
|
end
|
82
98
|
|
83
99
|
describe ".cancel" do
|
84
100
|
it "cancels a job by id" do
|
85
|
-
SecureRandom.
|
101
|
+
allow(SecureRandom).to receive(:hex).twice.and_return(job_id, job_id_1)
|
86
102
|
start_server do
|
87
103
|
job = LongJob.perform_in(3600)
|
88
|
-
job.
|
104
|
+
expect(job).to eq(job_id)
|
89
105
|
second_job = LongJob.perform_in(3600)
|
90
|
-
second_job.
|
106
|
+
expect(second_job).to eq(job_id_1)
|
91
107
|
|
92
|
-
initial_schedule = redis.zrange "schedule", 0, -1,
|
93
|
-
initial_schedule.size.
|
94
|
-
initial_schedule.select {|scheduled_job| JSON.parse(scheduled_job[0])["jid"] == job_id }.size.
|
108
|
+
initial_schedule = redis.zrange "schedule", 0, -1, withscores: true
|
109
|
+
expect(initial_schedule.size).to be(2)
|
110
|
+
expect(initial_schedule.select {|scheduled_job| JSON.parse(scheduled_job[0])["jid"] == job_id }.size).to be(1)
|
95
111
|
|
96
|
-
Sidekiq::Status.unschedule(job_id).
|
97
|
-
|
112
|
+
expect(Sidekiq::Status.unschedule(job_id)).to be_truthy
|
113
|
+
# Unused, therefore unfound => false
|
114
|
+
expect(Sidekiq::Status.cancel(unused_id)).to be_falsey
|
98
115
|
|
99
|
-
remaining_schedule = redis.zrange "schedule", 0, -1,
|
100
|
-
remaining_schedule.size.
|
101
|
-
remaining_schedule.select {|scheduled_job| JSON.parse(scheduled_job[0])["jid"] == job_id }.size.
|
116
|
+
remaining_schedule = redis.zrange "schedule", 0, -1, withscores: true
|
117
|
+
expect(remaining_schedule.size).to be(initial_schedule.size - 1)
|
118
|
+
expect(remaining_schedule.select {|scheduled_job| JSON.parse(scheduled_job[0])["jid"] == job_id }.size).to be(0)
|
102
119
|
end
|
103
120
|
end
|
104
121
|
|
105
122
|
it "does not cancel a job with correct id but wrong time" do
|
106
|
-
SecureRandom.
|
123
|
+
allow(SecureRandom).to receive(:hex).once.and_return(job_id)
|
107
124
|
start_server do
|
108
125
|
scheduled_time = Time.now.to_i + 3600
|
109
126
|
returned_job_id = LongJob.perform_at(scheduled_time)
|
110
|
-
returned_job_id.
|
127
|
+
expect(returned_job_id).to eq(job_id)
|
111
128
|
|
112
|
-
initial_schedule = redis.zrange "schedule", 0, -1,
|
113
|
-
initial_schedule.size.
|
114
|
-
|
115
|
-
(
|
116
|
-
|
117
|
-
|
129
|
+
initial_schedule = redis.zrange "schedule", 0, -1, withscores: true
|
130
|
+
expect(initial_schedule.size).to be(1)
|
131
|
+
# wrong time, therefore unfound => false
|
132
|
+
expect(Sidekiq::Status.cancel(returned_job_id, (scheduled_time + 1))).to be_falsey
|
133
|
+
expect((redis.zrange "schedule", 0, -1, withscores: true).size).to be(1)
|
134
|
+
# same id, same time, deletes
|
135
|
+
expect(Sidekiq::Status.cancel(returned_job_id, (scheduled_time))).to be_truthy
|
136
|
+
expect(redis.zrange "schedule", 0, -1, withscores: true).to be_empty
|
118
137
|
end
|
119
138
|
end
|
120
139
|
end
|
@@ -129,14 +148,63 @@ describe Sidekiq::Status do
|
|
129
148
|
expect_2_jobs_ttl_covers 1..Sidekiq::Status::DEFAULT_EXPIRY
|
130
149
|
end
|
131
150
|
|
151
|
+
it "does jobs without a known class" do
|
152
|
+
seed_secure_random_with_job_ids
|
153
|
+
start_server(:expiration => expiration_param) do
|
154
|
+
expect {
|
155
|
+
Sidekiq::Client.new(Sidekiq.redis_pool).
|
156
|
+
push("class" => "NotAKnownClass", "args" => [])
|
157
|
+
}.to_not raise_error
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
132
161
|
it "retries failed jobs" do
|
133
|
-
SecureRandom.
|
162
|
+
allow(SecureRandom).to receive(:hex).exactly(3).times.and_return(retried_job_id)
|
134
163
|
start_server do
|
135
|
-
capture_status_updates(
|
136
|
-
RetriedJob.perform_async().
|
137
|
-
}.
|
164
|
+
expect(capture_status_updates(3) {
|
165
|
+
expect(RetriedJob.perform_async()).to eq(retried_job_id)
|
166
|
+
}).to eq([retried_job_id] * 3)
|
167
|
+
expect(Sidekiq::Status.status(retried_job_id)).to eq(:retrying)
|
168
|
+
expect(Sidekiq::Status.working?(retried_job_id)).to be_falsey
|
169
|
+
expect(Sidekiq::Status::queued?(retried_job_id)).to be_falsey
|
170
|
+
expect(Sidekiq::Status::retrying?(retried_job_id)).to be_truthy
|
171
|
+
expect(Sidekiq::Status::failed?(retried_job_id)).to be_falsey
|
172
|
+
expect(Sidekiq::Status::complete?(retried_job_id)).to be_falsey
|
173
|
+
expect(Sidekiq::Status::stopped?(retried_job_id)).to be_falsey
|
174
|
+
expect(Sidekiq::Status::interrupted?(retried_job_id)).to be_falsey
|
175
|
+
end
|
176
|
+
expect(Sidekiq::Status.status(retried_job_id)).to eq(:retrying)
|
177
|
+
expect(Sidekiq::Status::retrying?(retried_job_id)).to be_truthy
|
178
|
+
|
179
|
+
# restarting and waiting for the job to complete
|
180
|
+
start_server do
|
181
|
+
expect(capture_status_updates(3) {}).to eq([retried_job_id] * 3)
|
182
|
+
expect(Sidekiq::Status.status(retried_job_id)).to eq(:complete)
|
183
|
+
expect(Sidekiq::Status.complete?(retried_job_id)).to be_truthy
|
184
|
+
expect(Sidekiq::Status::retrying?(retried_job_id)).to be_falsey
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
it "marks retried jobs as failed once they do eventually fail" do
|
189
|
+
allow(SecureRandom).to receive(:hex).and_return(retry_and_fail_job_id)
|
190
|
+
start_server do
|
191
|
+
expect(
|
192
|
+
capture_status_updates(3) {
|
193
|
+
expect(RetryAndFailJob.perform_async).to eq(retry_and_fail_job_id)
|
194
|
+
}
|
195
|
+
).to eq([retry_and_fail_job_id] * 3)
|
196
|
+
|
197
|
+
expect(Sidekiq::Status.status(retry_and_fail_job_id)).to eq(:retrying)
|
198
|
+
end
|
199
|
+
|
200
|
+
# restarting and waiting for the job to fail
|
201
|
+
start_server do
|
202
|
+
expect(capture_status_updates(3) {}).to eq([retry_and_fail_job_id] * 3)
|
203
|
+
|
204
|
+
expect(Sidekiq::Status.status(retry_and_fail_job_id)).to eq(:failed)
|
205
|
+
expect(Sidekiq::Status.failed?(retry_and_fail_job_id)).to be_truthy
|
206
|
+
expect(Sidekiq::Status::retrying?(retry_and_fail_job_id)).to be_falsey
|
138
207
|
end
|
139
|
-
Sidekiq::Status.status(retried_job_id).should == :complete
|
140
208
|
end
|
141
209
|
|
142
210
|
context ":expiration param" do
|
@@ -149,40 +217,53 @@ describe Sidekiq::Status do
|
|
149
217
|
expect_2_jobs_ttl_covers (Sidekiq::Status::DEFAULT_EXPIRY+1)..expiration_param
|
150
218
|
end
|
151
219
|
|
152
|
-
it "allow to overwrite :expiration parameter by
|
220
|
+
it "allow to overwrite :expiration parameter by #expiration method from worker" do
|
153
221
|
overwritten_expiration = expiration_param * 100
|
154
|
-
NoStatusConfirmationJob.
|
155
|
-
|
222
|
+
allow_any_instance_of(NoStatusConfirmationJob).to receive(:expiration).
|
223
|
+
and_return(overwritten_expiration)
|
224
|
+
allow_any_instance_of(StubJob).to receive(:expiration).
|
225
|
+
and_return(overwritten_expiration)
|
156
226
|
run_2_jobs!
|
157
227
|
expect_2_jobs_are_done_and_status_eq :complete
|
158
228
|
expect_2_jobs_ttl_covers (expiration_param+1)..overwritten_expiration
|
159
229
|
end
|
230
|
+
|
231
|
+
it "reads #expiration from a method when defined" do
|
232
|
+
allow(SecureRandom).to receive(:hex).once.and_return(job_id, job_id_1)
|
233
|
+
start_server do
|
234
|
+
expect(StubJob.perform_async).to eq(job_id)
|
235
|
+
expect(ExpiryJob.perform_async).to eq(job_id_1)
|
236
|
+
expect(redis.ttl("sidekiq:status:#{job_id}")).to eq(30 * 60)
|
237
|
+
expect(redis.ttl("sidekiq:status:#{job_id_1}")).to eq(15)
|
238
|
+
end
|
239
|
+
end
|
160
240
|
end
|
161
241
|
|
162
242
|
def seed_secure_random_with_job_ids
|
163
|
-
SecureRandom.
|
243
|
+
allow(SecureRandom).to receive(:hex).exactly(4).times.
|
244
|
+
and_return(plain_sidekiq_job_id, plain_sidekiq_job_id, job_id_1, job_id_1)
|
164
245
|
end
|
165
246
|
|
166
247
|
def run_2_jobs!
|
167
248
|
start_server(:expiration => expiration_param) do
|
168
|
-
capture_status_updates(
|
169
|
-
StubJob.perform_async.
|
249
|
+
expect(capture_status_updates(6) {
|
250
|
+
expect(StubJob.perform_async).to eq(plain_sidekiq_job_id)
|
170
251
|
NoStatusConfirmationJob.perform_async(1)
|
171
|
-
StubJob.perform_async.
|
252
|
+
expect(StubJob.perform_async).to eq(job_id_1)
|
172
253
|
NoStatusConfirmationJob.perform_async(2)
|
173
|
-
}.
|
254
|
+
}).to match_array([plain_sidekiq_job_id, job_id_1] * 3)
|
174
255
|
end
|
175
256
|
end
|
176
257
|
|
177
258
|
def expect_2_jobs_ttl_covers(range)
|
178
|
-
range.
|
179
|
-
range.
|
259
|
+
expect(range).to cover redis.ttl("sidekiq:status:#{plain_sidekiq_job_id}")
|
260
|
+
expect(range).to cover redis.ttl("sidekiq:status:#{job_id_1}")
|
180
261
|
end
|
181
262
|
|
182
263
|
def expect_2_jobs_are_done_and_status_eq(status)
|
183
|
-
redis.mget('NoStatusConfirmationJob_1', 'NoStatusConfirmationJob_2').
|
184
|
-
Sidekiq::Status.status(plain_sidekiq_job_id).
|
185
|
-
Sidekiq::Status.status(job_id_1).
|
264
|
+
expect(redis.mget('NoStatusConfirmationJob_1', 'NoStatusConfirmationJob_2')).to eq(%w(done)*2)
|
265
|
+
expect(Sidekiq::Status.status(plain_sidekiq_job_id)).to eq(status)
|
266
|
+
expect(Sidekiq::Status.status(job_id_1)).to eq(status)
|
186
267
|
end
|
187
268
|
end
|
188
269
|
|