massive 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +22 -0
- data/.rspec +3 -0
- data/.rvmrc +1 -0
- data/.travis.yml +7 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +141 -0
- data/Guardfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +196 -0
- data/Rakefile +8 -0
- data/lib/massive.rb +63 -0
- data/lib/massive/cancelling.rb +20 -0
- data/lib/massive/file.rb +80 -0
- data/lib/massive/file_job.rb +9 -0
- data/lib/massive/file_process.rb +7 -0
- data/lib/massive/file_step.rb +7 -0
- data/lib/massive/job.rb +115 -0
- data/lib/massive/locking.rb +27 -0
- data/lib/massive/memory_consumption.rb +15 -0
- data/lib/massive/notifications.rb +40 -0
- data/lib/massive/notifiers.rb +6 -0
- data/lib/massive/notifiers/base.rb +32 -0
- data/lib/massive/notifiers/pusher.rb +17 -0
- data/lib/massive/process.rb +69 -0
- data/lib/massive/process_serializer.rb +12 -0
- data/lib/massive/retry.rb +49 -0
- data/lib/massive/status.rb +59 -0
- data/lib/massive/step.rb +143 -0
- data/lib/massive/step_serializer.rb +12 -0
- data/lib/massive/timing_support.rb +10 -0
- data/lib/massive/version.rb +3 -0
- data/massive.gemspec +23 -0
- data/spec/fixtures/custom_job.rb +4 -0
- data/spec/fixtures/custom_step.rb +19 -0
- data/spec/models/massive/cancelling_spec.rb +83 -0
- data/spec/models/massive/file_job_spec.rb +24 -0
- data/spec/models/massive/file_spec.rb +209 -0
- data/spec/models/massive/file_step_spec.rb +22 -0
- data/spec/models/massive/job_spec.rb +319 -0
- data/spec/models/massive/locking_spec.rb +52 -0
- data/spec/models/massive/memory_consumption_spec.rb +24 -0
- data/spec/models/massive/notifications_spec.rb +107 -0
- data/spec/models/massive/notifiers/base_spec.rb +48 -0
- data/spec/models/massive/notifiers/pusher_spec.rb +49 -0
- data/spec/models/massive/process_serializer_spec.rb +38 -0
- data/spec/models/massive/process_spec.rb +235 -0
- data/spec/models/massive/status_spec.rb +104 -0
- data/spec/models/massive/step_serializer_spec.rb +40 -0
- data/spec/models/massive/step_spec.rb +490 -0
- data/spec/models/massive/timing_support_spec.rb +55 -0
- data/spec/shared/step_context.rb +25 -0
- data/spec/spec_helper.rb +42 -0
- data/spec/support/mongoid.yml +78 -0
- metadata +175 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
shared_examples_for Massive::Locking do
|
4
|
+
let(:redis) { Resque.redis }
|
5
|
+
let(:key) { :some_key }
|
6
|
+
|
7
|
+
context "when there is a lock for the given key" do
|
8
|
+
let(:lock_key) { subject.send(:lock_key_for, key) }
|
9
|
+
before { redis.set(lock_key, 60) }
|
10
|
+
|
11
|
+
it { should be_locked(key) }
|
12
|
+
|
13
|
+
it "does not sets the an expiration for the key" do
|
14
|
+
redis.should_not_receive(:pexpire)
|
15
|
+
subject.locked?(key)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "when there is no lock for the given key" do
|
20
|
+
let(:lock_key) { subject.send(:lock_key_for, key) }
|
21
|
+
|
22
|
+
it { should_not be_locked(key) }
|
23
|
+
|
24
|
+
context "and an expiration is not given for the locked key" do
|
25
|
+
it "sets the expiration to 60 seconds, specifying in miliseconds" do
|
26
|
+
redis.should_receive(:pexpire).with(lock_key, 60 * 1000)
|
27
|
+
subject.locked?(key)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "and an expiration is given for the locked key" do
|
32
|
+
it "sets the expiration to this value" do
|
33
|
+
redis.should_receive(:pexpire).with(lock_key, 10)
|
34
|
+
subject.locked?(key, 10)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "when pexpire command is not supported" do
|
39
|
+
let(:error) { Redis::CommandError.new('not supported') }
|
40
|
+
before { redis.stub(:pexpire).and_raise(error) }
|
41
|
+
|
42
|
+
it "should set expiration using expire command, dividing expiration per 1000 and rounding" do
|
43
|
+
redis.should_receive(:expire).with(lock_key, (1500/1000).to_i)
|
44
|
+
subject.locked?(key, 1500)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe Massive::Step do
|
51
|
+
it_should_behave_like Massive::Locking
|
52
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
shared_examples_for Massive::MemoryConsumption do
|
4
|
+
let(:memory) { 123456 }
|
5
|
+
let(:io) { double(IO, gets: " #{memory} ") }
|
6
|
+
before { IO.stub(:popen).with("ps -o rss= -p #{Process.pid}").and_yield(io) }
|
7
|
+
|
8
|
+
its(:current_memory_consumption) { should eq(memory) }
|
9
|
+
|
10
|
+
context "and an error is raised" do
|
11
|
+
let(:error) { StandardError.new('some error') }
|
12
|
+
before { IO.stub(:popen).with("ps -o rss= -p #{Process.pid}").and_raise(error) }
|
13
|
+
|
14
|
+
its(:current_memory_consumption) { should be_zero }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe Massive::Step do
|
19
|
+
it_should_behave_like Massive::MemoryConsumption
|
20
|
+
end
|
21
|
+
|
22
|
+
describe Massive::Job do
|
23
|
+
it_should_behave_like Massive::MemoryConsumption
|
24
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
shared_examples_for Massive::Notifications do
|
4
|
+
its(:notifier_id) { should eq("#{described_class.name.underscore.gsub('/', '-')}-#{notifyable.id}") }
|
5
|
+
|
6
|
+
after do
|
7
|
+
described_class.notifier(:base, {}) # resetting notifier
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "#notify(message)" do
|
11
|
+
let(:message) { :some_message }
|
12
|
+
let(:serializer) { notifyable.active_model_serializer.new(notifyable) }
|
13
|
+
|
14
|
+
it "notifies the message" do
|
15
|
+
notifyable.notifier.should_receive(:notify).with(message)
|
16
|
+
notifyable.notify(message)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "sends a serialized version of itself, after reloading itself, as data" do
|
20
|
+
notifyable.save
|
21
|
+
notifyable.notify(message)
|
22
|
+
notifyable.notifier.last[:data].as_json.should eq(serializer.as_json)
|
23
|
+
end
|
24
|
+
|
25
|
+
context "when there is no serializer for the step" do
|
26
|
+
before { notifyable.stub(:active_model_serializer).and_return(nil) }
|
27
|
+
|
28
|
+
it "does not sends the notification" do
|
29
|
+
notifyable.notifier.should_not_receive(:notify)
|
30
|
+
notifyable.notify(message)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#notifier" do
|
36
|
+
let(:options) { { expiration: 200, foo: 'bar', other: 'yup' } }
|
37
|
+
|
38
|
+
it "returns an instance of the notifier" do
|
39
|
+
notifyable.notifier.should be_a(Massive::Notifiers::Base)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "passes notifier options when creating the notifier" do
|
43
|
+
described_class.notifier :base, options
|
44
|
+
notifyable.notifier.options.should eq(options)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "creates it with the notifier_id" do
|
48
|
+
notifyable.notifier.id.should eq(notifyable.notifier_id)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe ".notifier" do
|
53
|
+
context "when a parameter is given" do
|
54
|
+
context "as a symbol" do
|
55
|
+
it "returns a notifier class from Massive::Notifiers::<given_symbol.camelized>" do
|
56
|
+
described_class.notifier :pusher
|
57
|
+
described_class.notifier_class.should eq(Massive::Notifiers::Pusher)
|
58
|
+
end
|
59
|
+
|
60
|
+
context "and others parameters are given" do
|
61
|
+
let(:options) { { expiration: 200, foo: 'bar', other: 'yup' } }
|
62
|
+
|
63
|
+
it "store these parameters to be used when creating the notifier" do
|
64
|
+
described_class.notifier :pusher, options
|
65
|
+
described_class.notifier_options.should eq(options)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "as a Class" do
|
71
|
+
it "configures the notifier, using the symbol to get the class" do
|
72
|
+
described_class.notifier(Massive::Notifiers::Pusher)
|
73
|
+
described_class.notifier_class.should eq(Massive::Notifiers::Pusher)
|
74
|
+
end
|
75
|
+
|
76
|
+
context "and others parameters are given" do
|
77
|
+
let(:options) { { expiration: 200, foo: 'bar', other: 'yup' } }
|
78
|
+
|
79
|
+
it "passes these parameters when creating the notifier" do
|
80
|
+
described_class.notifier(Massive::Notifiers::Pusher, options)
|
81
|
+
described_class.notifier_options.should eq(options)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe Massive::Step do
|
90
|
+
let(:process) { Massive::Process.new }
|
91
|
+
subject(:notifyable) { process.steps.build }
|
92
|
+
|
93
|
+
it_should_behave_like Massive::Notifications
|
94
|
+
end
|
95
|
+
|
96
|
+
describe Massive::Job do
|
97
|
+
let(:process) { Massive::Process.new }
|
98
|
+
let(:step) { process.steps.build }
|
99
|
+
subject(:job) { step.jobs.build }
|
100
|
+
|
101
|
+
let(:message) { 'some message' }
|
102
|
+
|
103
|
+
it "delegates #notify to the step" do
|
104
|
+
step.should_receive(:notify).with(message)
|
105
|
+
job.notify(message)
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Massive::Notifiers::Base do
|
4
|
+
let(:id) { 'some-id' }
|
5
|
+
subject(:notifier) { Massive::Notifiers::Base.new(id) }
|
6
|
+
|
7
|
+
it_should_behave_like Massive::Locking
|
8
|
+
|
9
|
+
describe "#notify(message, data)" do
|
10
|
+
let(:redis) { Resque.redis }
|
11
|
+
|
12
|
+
let(:message) { :some_message }
|
13
|
+
let(:data) { { some: 'data' } }
|
14
|
+
|
15
|
+
context "when a notification for this message is not locked" do
|
16
|
+
it "sends a notification" do
|
17
|
+
notifier.notify(message, data)
|
18
|
+
notifier.last[:message].should eq(message)
|
19
|
+
notifier.last[:data].should eq(data)
|
20
|
+
end
|
21
|
+
|
22
|
+
context "when a block is given" do
|
23
|
+
it "sends a notification with the data being the return from the block" do
|
24
|
+
notifier.notify(message) { data }
|
25
|
+
notifier.last[:message].should eq(message)
|
26
|
+
notifier.last[:data].should eq(data)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "when a notification for this message is locked" do
|
32
|
+
let(:lock_key) { subject.send(:lock_key_for, message) }
|
33
|
+
before { redis.set(lock_key, 60) }
|
34
|
+
|
35
|
+
it "does not send a notification" do
|
36
|
+
notifier.notify(message, data)
|
37
|
+
notifier.last[:message].should be_nil
|
38
|
+
notifier.last[:data].should be_nil
|
39
|
+
end
|
40
|
+
|
41
|
+
context "when a block is given" do
|
42
|
+
it "does not execute the block" do
|
43
|
+
notifier.notify(message) { fail('should not execute this block') }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Massive::Notifiers::Pusher do
|
4
|
+
let(:id) { 'pusher_notifier' }
|
5
|
+
|
6
|
+
let(:client) { double('Pusher') }
|
7
|
+
subject(:notifier) { Massive::Notifiers::Pusher.new(id, client: client) }
|
8
|
+
|
9
|
+
it_should_behave_like Massive::Locking
|
10
|
+
|
11
|
+
it { should be_a(Massive::Notifiers::Base) }
|
12
|
+
|
13
|
+
describe "#notify(message, data)" do
|
14
|
+
let(:redis) { Resque.redis }
|
15
|
+
|
16
|
+
let(:message) { :some_message }
|
17
|
+
let(:data) { { some: 'data' } }
|
18
|
+
|
19
|
+
context "when a notification for this message is not locked" do
|
20
|
+
it "sends a notification" do
|
21
|
+
client.should_receive(:trigger).with(id, message, data)
|
22
|
+
notifier.notify(message, data)
|
23
|
+
end
|
24
|
+
|
25
|
+
context "when a block is given" do
|
26
|
+
it "sends a notification with the data being the return from the block" do
|
27
|
+
client.should_receive(:trigger).with(id, message, data)
|
28
|
+
notifier.notify(message) { data }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "when a notification for this message is locked" do
|
34
|
+
let(:lock_key) { subject.send(:lock_key_for, message) }
|
35
|
+
before { redis.set(lock_key, 60) }
|
36
|
+
|
37
|
+
it "does not send a notification" do
|
38
|
+
client.should_not_receive(:trigger)
|
39
|
+
notifier.notify(message, data)
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when a block is given" do
|
43
|
+
it "does not execute the block" do
|
44
|
+
notifier.notify(message) { fail('should not execute this block') }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Massive::ProcessSerializer do
|
4
|
+
let(:process) { Massive::Process.new }
|
5
|
+
subject(:serialized) { described_class.new(process).as_json(root: false) }
|
6
|
+
|
7
|
+
it "serializes process id as string" do
|
8
|
+
serialized[:id].should eq(process.id.to_s)
|
9
|
+
end
|
10
|
+
|
11
|
+
[:created_at, :updated_at].each do |field|
|
12
|
+
it "serializes the #{field}" do
|
13
|
+
process[field] = 1.minute.ago
|
14
|
+
serialized[field].should eq(process[field])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it "serializes the processed percentage" do
|
19
|
+
process.stub(:processed_percentage).and_return(12)
|
20
|
+
serialized[:processed_percentage].should eq(process.processed_percentage)
|
21
|
+
end
|
22
|
+
|
23
|
+
context "when it is completed" do
|
24
|
+
before { process.stub(:completed?).and_return(true) }
|
25
|
+
|
26
|
+
it "serializes completed" do
|
27
|
+
serialized[:completed].should be_true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "when it is not completed" do
|
32
|
+
before { process.stub(:completed?).and_return(false) }
|
33
|
+
|
34
|
+
it "serializes completed" do
|
35
|
+
serialized[:completed].should be_false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,235 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Massive::Process do
|
4
|
+
subject(:process) { Massive::Process.new }
|
5
|
+
|
6
|
+
describe "#enqueue_next" do
|
7
|
+
context "when there are steps" do
|
8
|
+
let!(:first_step) { process.steps.build }
|
9
|
+
let!(:second_step) { process.steps.build }
|
10
|
+
let!(:third_step) { process.steps.build }
|
11
|
+
|
12
|
+
context "and none of them are completed" do
|
13
|
+
it "enqueues the first step" do
|
14
|
+
first_step.should_receive(:enqueue)
|
15
|
+
process.enqueue_next
|
16
|
+
end
|
17
|
+
|
18
|
+
it "does not enqueue the other steps" do
|
19
|
+
second_step.should_not_receive(:enqueue)
|
20
|
+
third_step.should_not_receive(:enqueue)
|
21
|
+
process.enqueue_next
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "and the first one is completed, but the second one is not" do
|
26
|
+
before { first_step.finished_at = Time.now }
|
27
|
+
|
28
|
+
it "does not enqueue the first step" do
|
29
|
+
first_step.should_not_receive(:enqueue)
|
30
|
+
process.enqueue_next
|
31
|
+
end
|
32
|
+
|
33
|
+
it "enqueues the second step" do
|
34
|
+
second_step.should_receive(:enqueue)
|
35
|
+
process.enqueue_next
|
36
|
+
end
|
37
|
+
|
38
|
+
it "does not enqueue the third step" do
|
39
|
+
third_step.should_not_receive(:enqueue)
|
40
|
+
process.enqueue_next
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "and the first one is enqueued" do
|
45
|
+
before { first_step.stub(:enqueued?).and_return(true) }
|
46
|
+
|
47
|
+
it "does not enqueue the next step" do
|
48
|
+
second_step.should_not_receive(:enqueue)
|
49
|
+
process.enqueue_next
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "but all of them are completed" do
|
54
|
+
before do
|
55
|
+
process.steps.each do |step|
|
56
|
+
step.finished_at = Time.now
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
it "does not enqueue any of the steps" do
|
61
|
+
process.steps.each do |step|
|
62
|
+
step.should_not_receive(:enqueue)
|
63
|
+
end
|
64
|
+
|
65
|
+
process.enqueue_next
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#next_step" do
|
72
|
+
let!(:step) { process.steps.build }
|
73
|
+
|
74
|
+
context "when the step is enqueued" do
|
75
|
+
before { step.stub(:enqueued?).and_return(true) }
|
76
|
+
|
77
|
+
its(:next_step) { should be_nil }
|
78
|
+
end
|
79
|
+
|
80
|
+
context "when the step is not enqueued" do
|
81
|
+
its(:next_step) { should eq step }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe ".find_step" do
|
86
|
+
let!(:step) { process.steps.build }
|
87
|
+
|
88
|
+
before { process.save }
|
89
|
+
|
90
|
+
it "returns the step with id within the process" do
|
91
|
+
Massive::Process.find_step(process.id, step.id).should eq(step)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe ".find_job" do
|
96
|
+
let!(:step) { process.steps.build }
|
97
|
+
let!(:job) { step.jobs.build }
|
98
|
+
|
99
|
+
before { process.save }
|
100
|
+
|
101
|
+
it "returns the job with id within the step of the process" do
|
102
|
+
Massive::Process.find_job(process.id, step.id, job.id).should eq(job)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "#processed_percentage" do
|
107
|
+
let(:step_1) { process.steps.build(weight: 9) }
|
108
|
+
let(:step_2) { process.steps.build }
|
109
|
+
|
110
|
+
context "when the process have not started" do
|
111
|
+
before do
|
112
|
+
step_1.stub(:processed_percentage).and_return(0)
|
113
|
+
step_2.stub(:processed_percentage).and_return(0)
|
114
|
+
end
|
115
|
+
|
116
|
+
its(:processed_percentage) { should eq 0 }
|
117
|
+
end
|
118
|
+
|
119
|
+
context "when the process have finished" do
|
120
|
+
before do
|
121
|
+
step_1.stub(:processed_percentage).and_return(1)
|
122
|
+
step_2.stub(:processed_percentage).and_return(1)
|
123
|
+
end
|
124
|
+
|
125
|
+
its(:processed_percentage) { should eq 1 }
|
126
|
+
end
|
127
|
+
|
128
|
+
context "when the file export step is finished" do
|
129
|
+
before { step_1.stub(:processed_percentage).and_return(1) }
|
130
|
+
|
131
|
+
context "and the file upload step is not finished" do
|
132
|
+
before { step_2.stub(:processed_percentage).and_return(0) }
|
133
|
+
|
134
|
+
its(:processed_percentage) { should eq 0.9 }
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context "when the file export step is finished" do
|
139
|
+
before { step_1.stub(:processed_percentage).and_return(1) }
|
140
|
+
|
141
|
+
context "and the file upload step is finished" do
|
142
|
+
before { step_2.stub(:processed_percentage).and_return(1) }
|
143
|
+
|
144
|
+
its(:processed_percentage) { should eq 1 }
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context "when the file export step is finished" do
|
149
|
+
before { step_1.stub(:processed_percentage).and_return(1) }
|
150
|
+
|
151
|
+
context "and the file upload step is half way to be finished" do
|
152
|
+
before { step_2.stub(:processed_percentage).and_return(0.5) }
|
153
|
+
|
154
|
+
its(:processed_percentage) { should eq 0.95 }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context "when the total weight of the steps is zero" do
|
159
|
+
let(:step_1) { process.steps.build(weight: 0) }
|
160
|
+
let(:step_2) { process.steps.build(weight: 0) }
|
161
|
+
|
162
|
+
its(:processed_percentage) { should eq 0 }
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe "#completed?" do
|
167
|
+
let!(:step_1) { process.steps.build }
|
168
|
+
let!(:step_2) { process.steps.build }
|
169
|
+
|
170
|
+
before { process.save }
|
171
|
+
|
172
|
+
context "when the steps are incompleted steps" do
|
173
|
+
its(:completed?) { should be_false }
|
174
|
+
end
|
175
|
+
|
176
|
+
context "when therere are no incompleted steps" do
|
177
|
+
before do
|
178
|
+
step_1.update_attributes(finished_at: Time.now, failed_at: nil)
|
179
|
+
step_2.update_attributes(finished_at: Time.now, failed_at: nil)
|
180
|
+
end
|
181
|
+
|
182
|
+
its(:completed?) { should be_true }
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe "#cancel" do
|
187
|
+
let!(:now) do
|
188
|
+
Time.now.tap do |now|
|
189
|
+
Time.stub(:now).and_return(now)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
it "sets cancelled_at to the current time, persisting it" do
|
194
|
+
process.cancel
|
195
|
+
process.reload.cancelled_at.to_i.should eq(now.to_i)
|
196
|
+
end
|
197
|
+
|
198
|
+
it "sets a cancelled key in redis with the process id" do
|
199
|
+
process.cancel
|
200
|
+
Massive.redis.exists("#{process.class.name.underscore}:#{process.id}:cancelled").should be_true
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe "#canceled?" do
|
205
|
+
context "when it has a cancelled_at" do
|
206
|
+
before { process.cancelled_at = Time.now }
|
207
|
+
|
208
|
+
it { should be_cancelled }
|
209
|
+
end
|
210
|
+
|
211
|
+
context "when it doesn't have a cancelled_at" do
|
212
|
+
it { should_not be_cancelled }
|
213
|
+
|
214
|
+
context "but there is a cancelled key for this process in redis" do
|
215
|
+
before { Massive.redis.set("#{process.class.name.underscore}:#{process.id}:cancelled", true) }
|
216
|
+
|
217
|
+
it { should be_cancelled }
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
describe "#active_model_serializer" do
|
223
|
+
its(:active_model_serializer) { should eq Massive::ProcessSerializer }
|
224
|
+
|
225
|
+
context "when class inherits from Massive::Process and does not have a serializer" do
|
226
|
+
class TestProcess < Massive::Process
|
227
|
+
end
|
228
|
+
|
229
|
+
it "returns Massive::ProcessSerializer" do
|
230
|
+
process = TestProcess.new
|
231
|
+
process.active_model_serializer.should eq Massive::ProcessSerializer
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|