massive 0.1.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.
- 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
|