be_taskable 0.5.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.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +178 -0
- data/Guardfile +5 -0
- data/LICENSE.txt +20 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/be_taskable.gemspec +100 -0
- data/docs/task_refresh_activity_diagram.delineato +0 -0
- data/docs/taskable_complete_flow.delineato +0 -0
- data/lib/be_taskable/task.rb +173 -0
- data/lib/be_taskable/task_assignment.rb +60 -0
- data/lib/be_taskable/task_resolver.rb +47 -0
- data/lib/be_taskable/task_runner.rb +87 -0
- data/lib/be_taskable/taskable.rb +157 -0
- data/lib/be_taskable/tasker.rb +21 -0
- data/lib/be_taskable.rb +28 -0
- data/lib/generators/be_taskable/migration_generator.rb +44 -0
- data/lib/generators/be_taskable/resolver_generator.rb +28 -0
- data/lib/generators/be_taskable/templates/active_record/migration.rb +35 -0
- data/lib/generators/be_taskable/templates/resolver.rb.tpl +67 -0
- data/readme.md +292 -0
- data/spec/be_taskable/integration/end_to_end_spec.rb +79 -0
- data/spec/be_taskable/integration/irrelevance_spec.rb +70 -0
- data/spec/be_taskable/unit/be_taskable_spec.rb +17 -0
- data/spec/be_taskable/unit/task_assignment_spec.rb +185 -0
- data/spec/be_taskable/unit/task_runner_spec.rb +200 -0
- data/spec/be_taskable/unit/task_spec.rb +490 -0
- data/spec/be_taskable/unit/taskable_spec.rb +309 -0
- data/spec/be_taskable/unit/tasker_spec.rb +24 -0
- data/spec/database.yml +19 -0
- data/spec/models.rb +38 -0
- data/spec/schema.rb +37 -0
- data/spec/spec_helper.rb +96 -0
- metadata +245 -0
@@ -0,0 +1,200 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe 'BeTaskable::TaskRunner' do
|
4
|
+
|
5
|
+
let(:resolver) { double.as_null_object }
|
6
|
+
let(:taskable) { stub_model(Taskable) }
|
7
|
+
let(:task) { BeTaskable::Task.new(taskable: taskable, action: 'review') }
|
8
|
+
let(:runner) { BeTaskable::TaskRunner.new(task) }
|
9
|
+
let(:assignee1) { stub_model(User) }
|
10
|
+
let(:assignee2) { stub_model(User) }
|
11
|
+
let(:assignee3) { stub_model(User) }
|
12
|
+
let(:assignees) { [assignee1, assignee2] }
|
13
|
+
let(:label_for_assigment) { 'Label for assignment' }
|
14
|
+
let(:url_for_assignment) { '/url/for/assignment' }
|
15
|
+
|
16
|
+
before do
|
17
|
+
resolver.stub(:due_date_for_assignment).and_return(DateTime.now)
|
18
|
+
resolver.stub(:visible_date_for_assignment).and_return(DateTime.now)
|
19
|
+
resolver.stub(:label_for_task).and_return('Label for task')
|
20
|
+
resolver.stub(:label_for_assignment).and_return(label_for_assigment)
|
21
|
+
resolver.stub(:url_for_assignment).and_return(url_for_assignment)
|
22
|
+
runner.stub(:resolver).and_return(resolver)
|
23
|
+
runner.stub(:_tally)
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "initialize" do
|
27
|
+
it "requires a task" do
|
28
|
+
expect{ BeTaskable::TaskRunner.new }.to raise_error(ArgumentError)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "requires a task" do
|
32
|
+
expect{ BeTaskable::TaskRunner.new(2) }.to raise_error(ArgumentError)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "requires a task" do
|
36
|
+
expect{ BeTaskable::TaskRunner.new(task) }.not_to raise_error
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
describe ".resolver" do
|
42
|
+
it "asks the task for the resolver" do
|
43
|
+
runner.unstub(:resolver)
|
44
|
+
task.should_receive(:resolver)
|
45
|
+
runner.resolver
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "._assignees" do
|
50
|
+
it "asks the resolver for the assignees" do
|
51
|
+
# runner.stub(:resolver).and_return(resolver)
|
52
|
+
resolver.should_receive(:assignees_for_task)
|
53
|
+
runner._assignees
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe ".refresh" do
|
58
|
+
|
59
|
+
before do
|
60
|
+
task.save
|
61
|
+
runner.stub(:_assignees).and_return(assignees)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "asks the resolver for relevant status of the task" do
|
65
|
+
resolver.should_receive(:is_task_relevant?)
|
66
|
+
runner.refresh
|
67
|
+
end
|
68
|
+
|
69
|
+
it "sets the label on the task" do
|
70
|
+
l = "Label"
|
71
|
+
resolver.stub(:label_for_task).and_return(l)
|
72
|
+
runner.refresh
|
73
|
+
expect(task.label).to eq(l)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "creates an assignment for all assignees" do
|
77
|
+
runner.refresh
|
78
|
+
expect(BeTaskable::TaskAssignment.count).to eq(2)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "asks the resolver for the due date" do
|
82
|
+
resolver.should_receive(:due_date_for_assignment)
|
83
|
+
runner.refresh
|
84
|
+
end
|
85
|
+
|
86
|
+
it "sets the complete_by as given by the resolver" do
|
87
|
+
d = DateTime.now + 3.days
|
88
|
+
resolver.stub(:due_date_for_assignment).and_return(d)
|
89
|
+
runner.refresh
|
90
|
+
assignment = BeTaskable::TaskAssignment.first
|
91
|
+
expect(assignment.complete_by).to be_within(1.minute).of(d)
|
92
|
+
end
|
93
|
+
|
94
|
+
it "asks the resolver for the visible at date" do
|
95
|
+
resolver.should_receive(:visible_date_for_assignment)
|
96
|
+
runner.refresh
|
97
|
+
end
|
98
|
+
|
99
|
+
it "sets the visible_at as given by the resolver" do
|
100
|
+
d = DateTime.now + 3.days
|
101
|
+
resolver.stub(:visible_date_for_assignment).and_return(d)
|
102
|
+
runner.refresh
|
103
|
+
assignment = BeTaskable::TaskAssignment.first
|
104
|
+
expect(assignment.visible_at).to be_within(1.minute).of(d)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "sets the assignment label as given by the resolver" do
|
108
|
+
runner.refresh
|
109
|
+
assignment = BeTaskable::TaskAssignment.first
|
110
|
+
expect(assignment.label).to eq(label_for_assigment)
|
111
|
+
end
|
112
|
+
|
113
|
+
it "sets the assignment url as given by the resolver" do
|
114
|
+
runner.refresh
|
115
|
+
assignment = BeTaskable::TaskAssignment.first
|
116
|
+
expect(assignment.url).to eq(url_for_assignment)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "doesn't create duplicate assignments" do
|
120
|
+
runner.refresh
|
121
|
+
expect(BeTaskable::TaskAssignment.count).to eq(2)
|
122
|
+
runner.refresh
|
123
|
+
expect(BeTaskable::TaskAssignment.count).to eq(2)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "deletes an assignment when not relevant anymore" do
|
127
|
+
runner.refresh
|
128
|
+
expect(BeTaskable::TaskAssignment.count).to eq(2)
|
129
|
+
assignees.pop
|
130
|
+
runner.refresh
|
131
|
+
expect(BeTaskable::TaskAssignment.count).to eq(1)
|
132
|
+
end
|
133
|
+
|
134
|
+
it "creates new assignment if needed" do
|
135
|
+
runner.refresh
|
136
|
+
expect(BeTaskable::TaskAssignment.count).to eq(2)
|
137
|
+
assignees.push(assignee3)
|
138
|
+
runner.refresh
|
139
|
+
expect(BeTaskable::TaskAssignment.count).to eq(3)
|
140
|
+
end
|
141
|
+
|
142
|
+
it "doesn't delete completed assignments" do
|
143
|
+
runner.refresh
|
144
|
+
expect(BeTaskable::TaskAssignment.count).to eq(2)
|
145
|
+
BeTaskable::TaskAssignment.last.update_attribute(:completed_at, DateTime.now)
|
146
|
+
assignees.pop
|
147
|
+
runner.refresh
|
148
|
+
expect(BeTaskable::TaskAssignment.count).to eq(2)
|
149
|
+
end
|
150
|
+
|
151
|
+
it "marks the task as irrelevant if taskable cannot be found" do
|
152
|
+
task.stub(:taskable){ nil }
|
153
|
+
runner.refresh
|
154
|
+
expect(task).to be_irrelevant
|
155
|
+
end
|
156
|
+
|
157
|
+
context 'irrelevant task' do
|
158
|
+
before do
|
159
|
+
# task.save
|
160
|
+
puts task.errors.full_messages
|
161
|
+
resolver.stub(:is_task_relevant?).and_return(false)
|
162
|
+
end
|
163
|
+
|
164
|
+
# it "is persisted" do
|
165
|
+
# expect(task).to be_persisted
|
166
|
+
# end
|
167
|
+
|
168
|
+
it "marks the task as irrelevant" do
|
169
|
+
# task.should_receive(:make_irrelevant)
|
170
|
+
|
171
|
+
runner.refresh
|
172
|
+
expect(task).to be_irrelevant
|
173
|
+
end
|
174
|
+
|
175
|
+
it "deletes the existing assignments" do
|
176
|
+
task.assignments.create
|
177
|
+
expect(task.assignments.size).to eq(1)
|
178
|
+
runner.refresh
|
179
|
+
expect(task.assignments.size).to eq(0)
|
180
|
+
end
|
181
|
+
|
182
|
+
it "doesn't change the state of an already completed task" do
|
183
|
+
task.update_attribute(:state, 'completed')
|
184
|
+
task.should_not_receive(:make_irrelevant)
|
185
|
+
runner.refresh
|
186
|
+
end
|
187
|
+
|
188
|
+
it "doesn't delete assignments on an already completed task" do
|
189
|
+
task.update_attribute(:state, 'completed')
|
190
|
+
task.assignments.create
|
191
|
+
expect(task.assignments.size).to eq(1)
|
192
|
+
runner.refresh
|
193
|
+
expect(task.assignments.size).to eq(1)
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
@@ -0,0 +1,490 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe 'BeTaskable::Task' do
|
4
|
+
|
5
|
+
let(:assignee) { stub_model(User) }
|
6
|
+
let(:resolver) { double.as_null_object }
|
7
|
+
let(:assignment) { double.as_null_object }
|
8
|
+
let(:taskable) { Taskable.create }
|
9
|
+
let(:action) { 'Publish' }
|
10
|
+
let(:task) { BeTaskable::Task.new(taskable: taskable, action: action) }
|
11
|
+
let(:runner) { double.as_null_object }
|
12
|
+
|
13
|
+
before do
|
14
|
+
task.stub(:resolver).and_return(resolver)
|
15
|
+
resolver.stub(:label_for_task).and_return('Label for task')
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#completed" do
|
19
|
+
it "finds completed tasks" do
|
20
|
+
task1 = BeTaskable::Task.create(taskable: taskable, action: 'update', completed_at: DateTime.now)
|
21
|
+
task2 = BeTaskable::Task.create(taskable: taskable, action: 'review')
|
22
|
+
res = BeTaskable::Task.completed.all
|
23
|
+
expect(res).to eq([task1])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#uncompleted" do
|
28
|
+
it "finds uncompleted tasks" do
|
29
|
+
task1 = BeTaskable::Task.create(taskable: taskable, action: 'update', completed_at: DateTime.now)
|
30
|
+
task2 = BeTaskable::Task.create(taskable: taskable, action: 'review')
|
31
|
+
res = BeTaskable::Task.uncompleted.all
|
32
|
+
expect(res).to eq([task2])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#expired" do
|
37
|
+
it "finds expired tasks" do
|
38
|
+
task1 = BeTaskable::Task.create(taskable: taskable, action: 'update', expired_at: DateTime.now)
|
39
|
+
task2 = BeTaskable::Task.create(taskable: taskable, action: 'review')
|
40
|
+
res = BeTaskable::Task.expired.all
|
41
|
+
expect(res).to eq([task1])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#unexpired" do
|
46
|
+
it "finds unexpired tasks" do
|
47
|
+
task1 = BeTaskable::Task.create(taskable: taskable, action: 'update', expired_at: DateTime.now)
|
48
|
+
task2 = BeTaskable::Task.create(taskable: taskable, action: 'review')
|
49
|
+
# expect(task1).to be_persisted
|
50
|
+
# expect(task2).to be_persisted
|
51
|
+
res = BeTaskable::Task.unexpired.all
|
52
|
+
expect(res).to eq([task2])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "#current" do
|
57
|
+
it "finds current tasks" do
|
58
|
+
task1 = BeTaskable::Task.create(taskable: taskable, action: 'update', expired_at: DateTime.now)
|
59
|
+
task2 = BeTaskable::Task.create(taskable: taskable, action: 'update', completed_at: DateTime.now)
|
60
|
+
task3 = BeTaskable::Task.create(taskable: taskable, action: 'review')
|
61
|
+
res = BeTaskable::Task.current.all
|
62
|
+
expect(res).to eq([task3])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "validations" do
|
67
|
+
it "can create two task with the same taskable and action" do
|
68
|
+
taskable = Taskable.create
|
69
|
+
action = 'publish'
|
70
|
+
task1 = BeTaskable::Task.create(taskable: taskable, action: action)
|
71
|
+
task2 = BeTaskable::Task.create(taskable: taskable, action: action)
|
72
|
+
|
73
|
+
expect(task2.errors).to be_empty
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
it "can save the task" do
|
78
|
+
task.save
|
79
|
+
expect(task).to be_persisted
|
80
|
+
end
|
81
|
+
|
82
|
+
describe ".taskable" do
|
83
|
+
it "responds to taskable" do
|
84
|
+
expect(task).to respond_to('taskable')
|
85
|
+
end
|
86
|
+
|
87
|
+
it "finds taskable" do
|
88
|
+
taskable = Taskable.create
|
89
|
+
task = taskable.tasks.create
|
90
|
+
expect(task.taskable).to eq(taskable)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe ".assignments" do
|
95
|
+
it 'responds to assignments' do
|
96
|
+
expect(task).to respond_to('assignments')
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe ".assignees" do
|
101
|
+
it "responds to assignees" do
|
102
|
+
expect(task).to respond_to('assignees')
|
103
|
+
end
|
104
|
+
|
105
|
+
it "finds the assignees" do
|
106
|
+
user = User.create
|
107
|
+
task.save
|
108
|
+
task.assignments.create(assignee: user)
|
109
|
+
|
110
|
+
expect(task.assignees).to eq([user])
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe ".resolver" do
|
115
|
+
|
116
|
+
before do
|
117
|
+
task.unstub(:resolver)
|
118
|
+
end
|
119
|
+
|
120
|
+
it "asks the taskable for the resolver" do
|
121
|
+
taskable.should_receive(:task_resolver_for_action)
|
122
|
+
task.resolver
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "._runner" do
|
127
|
+
it "returns a runner" do
|
128
|
+
res = task._runner
|
129
|
+
expect(res).to be_instance_of(BeTaskable::TaskRunner)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe ".refresh" do
|
134
|
+
|
135
|
+
before do
|
136
|
+
task.stub(:_runner).and_return(runner)
|
137
|
+
end
|
138
|
+
|
139
|
+
it "calls the runner" do
|
140
|
+
runner.should_receive(:refresh)
|
141
|
+
task.refresh
|
142
|
+
end
|
143
|
+
|
144
|
+
it "doesnt run for completed task" do
|
145
|
+
task.stub(:state).and_return('completed')
|
146
|
+
runner.should_not_receive(:refresh)
|
147
|
+
task.refresh
|
148
|
+
end
|
149
|
+
|
150
|
+
it "runs for open task" do
|
151
|
+
task.stub(:state).and_return('open')
|
152
|
+
runner.should_receive(:refresh)
|
153
|
+
task.refresh
|
154
|
+
end
|
155
|
+
|
156
|
+
it "runs for irrelevant task" do
|
157
|
+
task.stub(:state).and_return('irrelevant')
|
158
|
+
runner.should_receive(:refresh)
|
159
|
+
task.refresh
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe ".audit" do
|
164
|
+
|
165
|
+
before do
|
166
|
+
task.save
|
167
|
+
end
|
168
|
+
|
169
|
+
it "calls refresh" do
|
170
|
+
task.should_receive(:refresh)
|
171
|
+
task.audit
|
172
|
+
end
|
173
|
+
|
174
|
+
it "calls tally" do
|
175
|
+
# puts task.inspect
|
176
|
+
task.should_receive(:tally)
|
177
|
+
task.audit
|
178
|
+
end
|
179
|
+
|
180
|
+
it "doesnt run for completed task" do
|
181
|
+
task.stub(:state) { 'completed' }
|
182
|
+
task.should_not_receive(:refresh)
|
183
|
+
task.audit
|
184
|
+
end
|
185
|
+
|
186
|
+
it "runs for open task" do
|
187
|
+
task.stub(:state) { 'open' }
|
188
|
+
task.should_receive(:refresh)
|
189
|
+
task.audit
|
190
|
+
end
|
191
|
+
|
192
|
+
it "runs for irrelevant task" do
|
193
|
+
task.stub(:state) { 'irrelevant' }
|
194
|
+
task.should_receive(:refresh)
|
195
|
+
task.audit
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
describe ".consensus?" do
|
200
|
+
before do
|
201
|
+
task.stub(:resolver).and_return(resolver)
|
202
|
+
end
|
203
|
+
|
204
|
+
it "ask the resolver for consensus?" do
|
205
|
+
resolver.should_receive(:consensus?)
|
206
|
+
task.consensus?
|
207
|
+
end
|
208
|
+
|
209
|
+
it "pases the task to the resolver" do
|
210
|
+
# assignments = [1,2]
|
211
|
+
# task.stub(:assignments).and_return(assignments)
|
212
|
+
resolver.should_receive(:consensus?).with(task)
|
213
|
+
task.consensus?
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
describe ".tally" do
|
218
|
+
it "doesnt do anything is already completed" do
|
219
|
+
task.update_attribute(:state, 'completed')
|
220
|
+
task.stub(:consensus?).and_return(true)
|
221
|
+
task.should_not_receive(:_on_completion)
|
222
|
+
task.tally
|
223
|
+
end
|
224
|
+
|
225
|
+
it "calls on_completion when completed" do
|
226
|
+
task.stub(:consensus?).and_return(true)
|
227
|
+
task.should_receive(:_on_completion)
|
228
|
+
task.tally
|
229
|
+
end
|
230
|
+
|
231
|
+
it "doesn't call _on_completion if not completed" do
|
232
|
+
task.stub(:consensus?).and_return(false)
|
233
|
+
task.should_not_receive(:_on_completion)
|
234
|
+
task.tally
|
235
|
+
end
|
236
|
+
|
237
|
+
it "doesnt run for completed task" do
|
238
|
+
task.stub(:state) { 'completed' }
|
239
|
+
task.should_not_receive(:complete)
|
240
|
+
task.tally
|
241
|
+
end
|
242
|
+
|
243
|
+
it "runs for open task" do
|
244
|
+
task.stub(:state) { 'open' }
|
245
|
+
task.should_receive(:complete)
|
246
|
+
task.tally
|
247
|
+
end
|
248
|
+
|
249
|
+
it "runs for irrelevant task" do
|
250
|
+
task.stub(:state) { 'irrelevant' }
|
251
|
+
task.should_receive(:complete)
|
252
|
+
task.tally
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
describe ".on_creation" do
|
257
|
+
it "calls the resolver" do
|
258
|
+
resolver.should_receive(:on_creation).with(task)
|
259
|
+
task.on_creation
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
describe ".complete" do
|
264
|
+
it "changes the state" do
|
265
|
+
task.complete
|
266
|
+
expect(task).to be_completed
|
267
|
+
end
|
268
|
+
|
269
|
+
it "calls _on_completion" do
|
270
|
+
task.should_receive(:_on_completion)
|
271
|
+
task.complete
|
272
|
+
end
|
273
|
+
|
274
|
+
it "returns true" do
|
275
|
+
res = task.complete
|
276
|
+
expect(res).to be_true
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
describe "._on_completion" do
|
281
|
+
before do
|
282
|
+
task.stub(:resolver).and_return(resolver)
|
283
|
+
end
|
284
|
+
|
285
|
+
it "changes completed_at on the tasks" do
|
286
|
+
task._on_completion
|
287
|
+
expect(task.completed_at).to be_within(1.minute).of(DateTime.now)
|
288
|
+
end
|
289
|
+
|
290
|
+
it "calls on_completion on the resolver" do
|
291
|
+
resolver.should_receive(:on_completion)
|
292
|
+
task._on_completion
|
293
|
+
end
|
294
|
+
|
295
|
+
it "marks all assignments as completed" do
|
296
|
+
task.save
|
297
|
+
a1 = task.assignments.create()
|
298
|
+
a2 = task.assignments.create()
|
299
|
+
task._on_completion
|
300
|
+
a1.reload
|
301
|
+
a2.reload
|
302
|
+
expect(a1).to be_completed
|
303
|
+
expect(a2).to be_completed
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
describe "assignment_for" do
|
308
|
+
it "find the assignment for the assignee" do
|
309
|
+
task.save
|
310
|
+
assignment = task.assignments.create(assignee: assignee)
|
311
|
+
res = task.assignment_for(assignee)
|
312
|
+
expect(res).to eq(assignment)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
describe ".complete_by" do
|
317
|
+
|
318
|
+
let(:assignment) { double.as_null_object }
|
319
|
+
|
320
|
+
before do
|
321
|
+
task.stub(:assignment_for).and_return(assignment)
|
322
|
+
end
|
323
|
+
|
324
|
+
it "requires an assignee" do
|
325
|
+
expect{ task.complete_by }.to raise_error(ArgumentError)
|
326
|
+
end
|
327
|
+
|
328
|
+
it "sends complete to the assignment" do
|
329
|
+
assignment.should_receive(:complete)
|
330
|
+
task.complete_by(assignee)
|
331
|
+
end
|
332
|
+
|
333
|
+
it "returns true" do
|
334
|
+
res = task.complete_by(assignee)
|
335
|
+
expect(res).to be_true
|
336
|
+
end
|
337
|
+
|
338
|
+
it "returns false if it cannot find the assignment" do
|
339
|
+
task.stub(:assignment_for).and_return(nil)
|
340
|
+
res = task.complete_by(assignee)
|
341
|
+
expect(res).to be_false
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
describe ".expire" do
|
346
|
+
it "marks the task as expired" do
|
347
|
+
# expect(task).to be_open
|
348
|
+
res = task.expire
|
349
|
+
# expect(res).to be_true
|
350
|
+
# puts task.state
|
351
|
+
# task.reload
|
352
|
+
expect(task).to be_expired
|
353
|
+
end
|
354
|
+
|
355
|
+
it "calls _on_expiration" do
|
356
|
+
task.should_receive(:_on_expiration)
|
357
|
+
task.expire
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
describe "._on_expiration" do
|
362
|
+
|
363
|
+
it "sets the expired date" do
|
364
|
+
task.expire
|
365
|
+
expect(task.expired_at).to be_within(1.minute).of(DateTime.now)
|
366
|
+
end
|
367
|
+
|
368
|
+
it "marks the assignments as expired" do
|
369
|
+
task.stub(:assignments).and_return([assignment])
|
370
|
+
assignment.should_receive(:expire)
|
371
|
+
task._on_expiration
|
372
|
+
end
|
373
|
+
|
374
|
+
it "calls on_expiration on the resolver" do
|
375
|
+
resolver.should_receive(:on_expiration)
|
376
|
+
task._on_expiration
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
describe ".on_assignment_completed" do
|
381
|
+
it "triggers a tally" do
|
382
|
+
task.should_receive(:tally)
|
383
|
+
task.on_assignment_completed(assignment)
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
describe ".label!" do
|
388
|
+
it "asks the resolver" do
|
389
|
+
resolver.should_receive(:label_for_task)
|
390
|
+
task.label!
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
describe "consensus helpers" do
|
395
|
+
|
396
|
+
let(:assignment1) { stub_model(BeTaskable::TaskAssignment) }
|
397
|
+
let(:assignment2) { stub_model(BeTaskable::TaskAssignment) }
|
398
|
+
let(:assignment3) { stub_model(BeTaskable::TaskAssignment) }
|
399
|
+
let(:assignment4) { stub_model(BeTaskable::TaskAssignment) }
|
400
|
+
let(:assignments) { [assignment1, assignment2, assignment3, assignment4] }
|
401
|
+
|
402
|
+
before do
|
403
|
+
task.stub(:assignments).and_return(assignments)
|
404
|
+
end
|
405
|
+
|
406
|
+
describe ".majority_of_assignments_done?" do
|
407
|
+
|
408
|
+
it "is not true if minority done" do
|
409
|
+
assignment1.stub(:completed?).and_return(true)
|
410
|
+
res = task.majority_of_assignments_done?
|
411
|
+
expect(res).to be_false
|
412
|
+
end
|
413
|
+
|
414
|
+
it "is not true if half done" do
|
415
|
+
assignment1.stub(:completed?).and_return(true)
|
416
|
+
assignment2.stub(:completed?).and_return(true)
|
417
|
+
res = task.majority_of_assignments_done?
|
418
|
+
expect(res).to be_false
|
419
|
+
end
|
420
|
+
|
421
|
+
it "is true if majority done" do
|
422
|
+
assignment1.stub(:completed?).and_return(true)
|
423
|
+
assignment2.stub(:completed?).and_return(true)
|
424
|
+
assignment3.stub(:completed?).and_return(true)
|
425
|
+
res = task.majority_of_assignments_done?
|
426
|
+
expect(res).to be_true
|
427
|
+
end
|
428
|
+
|
429
|
+
end
|
430
|
+
|
431
|
+
describe ".any_assignment_done?" do
|
432
|
+
|
433
|
+
it "is not false if none done" do
|
434
|
+
res = task.any_assignment_done?
|
435
|
+
expect(res).to be_false
|
436
|
+
end
|
437
|
+
|
438
|
+
it "is not true if one is done" do
|
439
|
+
assignment1.stub(:completed?).and_return(true)
|
440
|
+
res = task.any_assignment_done?
|
441
|
+
expect(res).to be_true
|
442
|
+
end
|
443
|
+
|
444
|
+
it "is not true if more than one is done" do
|
445
|
+
assignment1.stub(:completed?).and_return(true)
|
446
|
+
assignment2.stub(:completed?).and_return(true)
|
447
|
+
res = task.any_assignment_done?
|
448
|
+
expect(res).to be_true
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
describe ".all_assignments_done?" do
|
453
|
+
|
454
|
+
it "is not false if none done" do
|
455
|
+
res = task.all_assignments_done?
|
456
|
+
expect(res).to be_false
|
457
|
+
end
|
458
|
+
|
459
|
+
it "is not false if majority done" do
|
460
|
+
assignment1.stub(:completed?).and_return(true)
|
461
|
+
assignment2.stub(:completed?).and_return(true)
|
462
|
+
assignment3.stub(:completed?).and_return(true)
|
463
|
+
res = task.all_assignments_done?
|
464
|
+
expect(res).to be_false
|
465
|
+
end
|
466
|
+
|
467
|
+
it "is not true if all done" do
|
468
|
+
assignment1.stub(:completed?).and_return(true)
|
469
|
+
assignment2.stub(:completed?).and_return(true)
|
470
|
+
assignment3.stub(:completed?).and_return(true)
|
471
|
+
assignment4.stub(:completed?).and_return(true)
|
472
|
+
res = task.all_assignments_done?
|
473
|
+
expect(res).to be_true
|
474
|
+
end
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
describe ".enacted_assignments" do
|
479
|
+
it "returns the assignees that actually did the task" do
|
480
|
+
user1 = User.create
|
481
|
+
user2 = User.create
|
482
|
+
task.save
|
483
|
+
as1 = task.assignments.create(assignee: user1)
|
484
|
+
as2 = task.assignments.create(assignee: user2, enacted: true)
|
485
|
+
res = task.enacted_assignments
|
486
|
+
expect(res).to eq([as2])
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
end
|