stepper_motor 0.1.6 → 0.1.8
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 +4 -4
- data/.github/dependabot.yml +12 -0
- data/.github/workflows/ci.yml +51 -0
- data/CHANGELOG.md +77 -2
- data/Gemfile +11 -0
- data/README.md +13 -374
- data/Rakefile +21 -3
- data/bin/test +5 -0
- data/lib/generators/install_generator.rb +6 -1
- data/lib/generators/stepper_motor_migration_003.rb.erb +6 -0
- data/lib/generators/stepper_motor_migration_004.rb.erb +26 -0
- data/lib/stepper_motor/forward_scheduler.rb +8 -4
- data/lib/stepper_motor/journey/flow_control.rb +58 -0
- data/lib/stepper_motor/journey/recovery.rb +34 -0
- data/lib/stepper_motor/journey.rb +85 -84
- data/lib/stepper_motor/perform_step_job_v2.rb +2 -2
- data/lib/stepper_motor/railtie.rb +1 -1
- data/lib/stepper_motor/recover_stuck_journeys_job_v1.rb +3 -1
- data/lib/stepper_motor/step.rb +70 -5
- data/lib/stepper_motor/version.rb +1 -1
- data/lib/stepper_motor.rb +1 -2
- data/lib/tasks/stepper_motor_tasks.rake +8 -0
- data/manual/MANUAL.md +538 -0
- data/rbi/stepper_motor.rbi +459 -0
- data/sig/stepper_motor.rbs +406 -3
- data/stepper_motor.gemspec +49 -0
- data/test/dummy/Rakefile +8 -0
- data/test/dummy/app/assets/stylesheets/application.css +1 -0
- data/test/dummy/app/controllers/application_controller.rb +6 -0
- data/test/dummy/app/helpers/application_helper.rb +4 -0
- data/test/dummy/app/jobs/application_job.rb +9 -0
- data/test/dummy/app/mailers/application_mailer.rb +6 -0
- data/test/dummy/app/models/application_record.rb +5 -0
- data/test/dummy/app/views/layouts/application.html.erb +27 -0
- data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/test/dummy/app/views/pwa/manifest.json.erb +22 -0
- data/test/dummy/app/views/pwa/service-worker.js +26 -0
- data/test/dummy/bin/dev +2 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +34 -0
- data/test/dummy/config/application.rb +28 -0
- data/test/dummy/config/boot.rb +7 -0
- data/test/dummy/config/cable.yml +10 -0
- data/test/dummy/config/database.yml +32 -0
- data/test/dummy/config/environment.rb +7 -0
- data/test/dummy/config/environments/development.rb +71 -0
- data/test/dummy/config/environments/production.rb +91 -0
- data/test/dummy/config/environments/test.rb +55 -0
- data/test/dummy/config/initializers/content_security_policy.rb +27 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +10 -0
- data/test/dummy/config/initializers/inflections.rb +18 -0
- data/test/dummy/config/initializers/stepper_motor.rb +3 -0
- data/test/dummy/config/locales/en.yml +31 -0
- data/test/dummy/config/puma.rb +40 -0
- data/test/dummy/config/routes.rb +16 -0
- data/test/dummy/config/storage.yml +34 -0
- data/test/dummy/config.ru +8 -0
- data/test/dummy/db/migrate/20250520094921_stepper_motor_migration_001.rb +38 -0
- data/test/dummy/db/migrate/20250520094922_stepper_motor_migration_002.rb +8 -0
- data/test/dummy/db/migrate/20250522212312_stepper_motor_migration_003.rb +7 -0
- data/test/dummy/db/migrate/20250525110812_stepper_motor_migration_004.rb +28 -0
- data/test/dummy/db/schema.rb +37 -0
- data/test/dummy/public/400.html +114 -0
- data/test/dummy/public/404.html +114 -0
- data/test/dummy/public/406-unsupported-browser.html +114 -0
- data/test/dummy/public/422.html +114 -0
- data/test/dummy/public/500.html +114 -0
- data/test/dummy/public/icon.png +0 -0
- data/test/dummy/public/icon.svg +3 -0
- data/test/side_effects_helper.rb +67 -0
- data/test/stepper_motor/cyclic_scheduler_test.rb +77 -0
- data/{spec/stepper_motor/forward_scheduler_spec.rb → test/stepper_motor/forward_scheduler_test.rb} +9 -10
- data/test/stepper_motor/journey/exception_handling_test.rb +89 -0
- data/test/stepper_motor/journey/flow_control_test.rb +78 -0
- data/test/stepper_motor/journey/idempotency_test.rb +65 -0
- data/test/stepper_motor/journey/step_definition_test.rb +187 -0
- data/test/stepper_motor/journey/uniqueness_test.rb +48 -0
- data/test/stepper_motor/journey_test.rb +352 -0
- data/{spec/stepper_motor/recover_stuck_journeys_job_spec.rb → test/stepper_motor/recover_stuck_journeys_job_test.rb} +14 -14
- data/{spec/stepper_motor/recovery_spec.rb → test/stepper_motor/recovery_test.rb} +27 -27
- data/test/stepper_motor/test_helper_test.rb +44 -0
- data/test/stepper_motor_test.rb +9 -0
- data/test/test_helper.rb +46 -0
- metadata +120 -24
- data/.rspec +0 -3
- data/.ruby-version +0 -1
- data/.standard.yml +0 -4
- data/.yardopts +0 -1
- data/spec/helpers/side_effects.rb +0 -85
- data/spec/spec_helper.rb +0 -90
- data/spec/stepper_motor/cyclic_scheduler_spec.rb +0 -68
- data/spec/stepper_motor/generator_spec.rb +0 -16
- data/spec/stepper_motor/journey_spec.rb +0 -401
- data/spec/stepper_motor/test_helper_spec.rb +0 -48
- data/spec/stepper_motor_spec.rb +0 -7
@@ -1,401 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "../spec_helper"
|
4
|
-
|
5
|
-
# rubocop:disable Lint/ConstantDefinitionInBlock
|
6
|
-
RSpec.describe "StepperMotor::Journey" do
|
7
|
-
include ActiveJob::TestHelper
|
8
|
-
|
9
|
-
it "allows an empty journey to be defined and performed to completion" do
|
10
|
-
pointless_class = create_journey_subclass
|
11
|
-
journey = pointless_class.create!
|
12
|
-
journey.perform_next_step!
|
13
|
-
expect(journey).to be_finished
|
14
|
-
end
|
15
|
-
|
16
|
-
it "allows a journey consisting of one step to be defined and performed to completion" do
|
17
|
-
single_step_class = create_journey_subclass do
|
18
|
-
step :do_thing do
|
19
|
-
SideEffects.touch!("do_thing")
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
journey = single_step_class.create!
|
24
|
-
expect(journey.next_step_to_be_performed_at).not_to be_nil
|
25
|
-
journey.perform_next_step!
|
26
|
-
expect(journey).to be_finished
|
27
|
-
expect(SideEffects).to be_produced("do_thing")
|
28
|
-
end
|
29
|
-
|
30
|
-
it "allows a journey consisting of multiple named steps to be defined and performed to completion" do
|
31
|
-
multi_step_journey_class = create_journey_subclass do
|
32
|
-
[:step1, :step2, :step3].each do |step_name|
|
33
|
-
step step_name do
|
34
|
-
SideEffects.touch!("from_#{step_name}")
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
journey = multi_step_journey_class.create!
|
40
|
-
expect(journey.next_step_name).to eq("step1")
|
41
|
-
|
42
|
-
journey.perform_next_step!
|
43
|
-
expect(journey.next_step_name).to eq("step2")
|
44
|
-
expect(journey.previous_step_name).to eq("step1")
|
45
|
-
|
46
|
-
journey.perform_next_step!
|
47
|
-
expect(journey.next_step_name).to eq("step3")
|
48
|
-
expect(journey.previous_step_name).to eq("step2")
|
49
|
-
|
50
|
-
journey.perform_next_step!
|
51
|
-
expect(journey).to be_finished
|
52
|
-
expect(journey.next_step_name).to be_nil
|
53
|
-
expect(journey.previous_step_name).to eq("step3")
|
54
|
-
|
55
|
-
expect(SideEffects).to be_produced("from_step1")
|
56
|
-
expect(SideEffects).to be_produced("from_step2")
|
57
|
-
expect(SideEffects).to be_produced("from_step3")
|
58
|
-
end
|
59
|
-
|
60
|
-
it "allows a journey consisting of multiple anonymous steps to be defined and performed to completion" do
|
61
|
-
anonymous_steps_class = create_journey_subclass do
|
62
|
-
3.times do |n|
|
63
|
-
step do
|
64
|
-
SideEffects.touch!("sidefx_#{n}")
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
journey = anonymous_steps_class.create!
|
70
|
-
expect(journey.next_step_name).to eq("step_1")
|
71
|
-
|
72
|
-
journey.perform_next_step!
|
73
|
-
expect(journey.next_step_name).to eq("step_2")
|
74
|
-
expect(journey.previous_step_name).to eq("step_1")
|
75
|
-
|
76
|
-
journey.perform_next_step!
|
77
|
-
expect(journey.next_step_name).to eq("step_3")
|
78
|
-
expect(journey.previous_step_name).to eq("step_2")
|
79
|
-
|
80
|
-
journey.perform_next_step!
|
81
|
-
expect(journey).to be_finished
|
82
|
-
expect(journey.next_step_name).to be_nil
|
83
|
-
expect(journey.previous_step_name).to eq("step_3")
|
84
|
-
|
85
|
-
expect(SideEffects).to be_produced("sidefx_0")
|
86
|
-
expect(SideEffects).to be_produced("sidefx_1")
|
87
|
-
expect(SideEffects).to be_produced("sidefx_2")
|
88
|
-
end
|
89
|
-
|
90
|
-
it "allows an arbitrary ActiveRecord to be attached as the hero" do
|
91
|
-
carried_journey_class = create_journey_subclass
|
92
|
-
carrier_journey_class = create_journey_subclass do
|
93
|
-
step :only do
|
94
|
-
raise "Incorrect" unless hero.instance_of?(carried_journey_class)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
hero = carried_journey_class.create!
|
99
|
-
journey = carrier_journey_class.create!(hero: hero)
|
100
|
-
expect {
|
101
|
-
journey.perform_next_step!
|
102
|
-
}.not_to raise_error
|
103
|
-
end
|
104
|
-
|
105
|
-
it "allows a journey where steps are delayed in time using wait:" do
|
106
|
-
timely_journey_class = create_journey_subclass do
|
107
|
-
step wait: 10.hours do
|
108
|
-
SideEffects.touch! "after_10_hours.txt"
|
109
|
-
end
|
110
|
-
|
111
|
-
step wait: 5.minutes do
|
112
|
-
SideEffects.touch! "after_5_minutes.txt"
|
113
|
-
end
|
114
|
-
|
115
|
-
step do
|
116
|
-
SideEffects.touch! "final_nowait.txt"
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
freeze_time
|
121
|
-
timely_journey_class.create!
|
122
|
-
|
123
|
-
expect {
|
124
|
-
perform_enqueued_jobs
|
125
|
-
}.to not_have_produced_any_side_effects
|
126
|
-
|
127
|
-
travel 10.hours
|
128
|
-
expect {
|
129
|
-
perform_enqueued_jobs
|
130
|
-
}.to have_produced_side_effects_named("after_10_hours.txt")
|
131
|
-
|
132
|
-
travel 4.minutes
|
133
|
-
expect {
|
134
|
-
perform_enqueued_jobs
|
135
|
-
}.to not_have_produced_any_side_effects
|
136
|
-
|
137
|
-
travel 1.minutes
|
138
|
-
expect {
|
139
|
-
perform_enqueued_jobs
|
140
|
-
}.to have_produced_side_effects_named("after_5_minutes.txt")
|
141
|
-
|
142
|
-
expect {
|
143
|
-
perform_enqueued_jobs
|
144
|
-
}.to have_produced_side_effects_named("final_nowait.txt")
|
145
|
-
end
|
146
|
-
|
147
|
-
it "allows a journey where steps are delayed in time using after:" do
|
148
|
-
journey_class = create_journey_subclass do
|
149
|
-
step after: 10.hours do
|
150
|
-
SideEffects.touch! "step1"
|
151
|
-
end
|
152
|
-
|
153
|
-
step after: 605.minutes do
|
154
|
-
SideEffects.touch! "step2"
|
155
|
-
end
|
156
|
-
|
157
|
-
step do
|
158
|
-
SideEffects.touch! "step3"
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
timely_journey = journey_class.create!
|
163
|
-
freeze_time
|
164
|
-
|
165
|
-
# Note that the "perform_enqueued_jobs" helper method performs the job even if
|
166
|
-
# its "scheduled_at" lies in the future. Presumably this is done so that testing is
|
167
|
-
# easier to do, but we check the time the journey was set to perform the next step at
|
168
|
-
# - and therefore a job which runs too early will produce another job that replaces it.
|
169
|
-
expect { perform_enqueued_jobs }.to not_have_produced_any_side_effects
|
170
|
-
|
171
|
-
travel_to(timely_journey.next_step_to_be_performed_at + 1.second)
|
172
|
-
expect { perform_enqueued_jobs }.to have_produced_side_effects_named("step1")
|
173
|
-
|
174
|
-
travel(4.minutes)
|
175
|
-
expect { perform_enqueued_jobs }.to not_have_produced_any_side_effects
|
176
|
-
|
177
|
-
travel(1.minutes + 1.second)
|
178
|
-
expect { perform_enqueued_jobs }.to have_produced_side_effects_named("step2")
|
179
|
-
expect { perform_enqueued_jobs }.to have_produced_side_effects_named("step3")
|
180
|
-
expect(enqueued_jobs).to be_empty # Journey ended
|
181
|
-
end
|
182
|
-
|
183
|
-
it "tracks steps entered and completed using counters" do
|
184
|
-
failing = create_journey_subclass do
|
185
|
-
step do
|
186
|
-
raise "oops"
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
not_failing = create_journey_subclass do
|
191
|
-
step do
|
192
|
-
true # no-op
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
failing_journey = failing.create!
|
197
|
-
expect { failing_journey.perform_next_step! }.to raise_error(/oops/)
|
198
|
-
expect(failing_journey.steps_entered).to eq(1)
|
199
|
-
expect(failing_journey.steps_completed).to eq(0)
|
200
|
-
|
201
|
-
failing_journey.ready!
|
202
|
-
expect { failing_journey.perform_next_step! }.to raise_error(/oops/)
|
203
|
-
expect(failing_journey.steps_entered).to eq(2)
|
204
|
-
expect(failing_journey.steps_completed).to eq(0)
|
205
|
-
|
206
|
-
non_failing_journey = not_failing.create!
|
207
|
-
non_failing_journey.perform_next_step!
|
208
|
-
expect(non_failing_journey.steps_entered).to eq(1)
|
209
|
-
expect(non_failing_journey.steps_completed).to eq(1)
|
210
|
-
end
|
211
|
-
|
212
|
-
it "does not allow invalid values for after: and wait:" do
|
213
|
-
expect {
|
214
|
-
create_journey_subclass do
|
215
|
-
step after: 10.hours do
|
216
|
-
# pass
|
217
|
-
end
|
218
|
-
|
219
|
-
step after: 5.hours do
|
220
|
-
# pass
|
221
|
-
end
|
222
|
-
end
|
223
|
-
}.to raise_error(ArgumentError)
|
224
|
-
|
225
|
-
expect {
|
226
|
-
create_journey_subclass do
|
227
|
-
step wait: -5.hours do
|
228
|
-
# pass
|
229
|
-
end
|
230
|
-
end
|
231
|
-
}.to raise_error(ArgumentError)
|
232
|
-
|
233
|
-
expect {
|
234
|
-
create_journey_subclass do
|
235
|
-
step after: 5.hours, wait: 2.seconds do
|
236
|
-
# pass
|
237
|
-
end
|
238
|
-
end
|
239
|
-
}.to raise_error(ArgumentError)
|
240
|
-
end
|
241
|
-
|
242
|
-
it "allows a step to reattempt itself" do
|
243
|
-
deferring = create_journey_subclass do
|
244
|
-
step do
|
245
|
-
reattempt! wait: 5.minutes
|
246
|
-
raise "Should never be reached"
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
journey = deferring.create!
|
251
|
-
perform_enqueued_jobs
|
252
|
-
|
253
|
-
journey.reload
|
254
|
-
expect(journey.previous_step_name).to eq("step_1")
|
255
|
-
expect(journey.next_step_name).to eq("step_1")
|
256
|
-
expect(journey.next_step_to_be_performed_at).to be_within(1.second).of(Time.current + 5.minutes)
|
257
|
-
|
258
|
-
travel 5.minutes + 1.second
|
259
|
-
perform_enqueued_jobs
|
260
|
-
|
261
|
-
journey.reload
|
262
|
-
expect(journey.previous_step_name).to eq("step_1")
|
263
|
-
expect(journey.next_step_name).to eq("step_1")
|
264
|
-
expect(journey.next_step_to_be_performed_at).to be_within(1.second).of(Time.current + 5.minutes)
|
265
|
-
end
|
266
|
-
|
267
|
-
it "allows a journey consisting of multiple steps where the first step bails out to be defined and performed to the point of cancellation" do
|
268
|
-
interrupting = create_journey_subclass do
|
269
|
-
step :step1 do
|
270
|
-
SideEffects.touch!("step1_before_cancel")
|
271
|
-
cancel!
|
272
|
-
SideEffects.touch!("step1_after_cancel")
|
273
|
-
end
|
274
|
-
|
275
|
-
step :step2 do
|
276
|
-
raise "Should never be reached"
|
277
|
-
end
|
278
|
-
end
|
279
|
-
|
280
|
-
journey = interrupting.create!
|
281
|
-
expect(journey.next_step_name).to eq("step1")
|
282
|
-
|
283
|
-
perform_enqueued_jobs
|
284
|
-
expect(SideEffects).to be_produced("step1_before_cancel")
|
285
|
-
expect(SideEffects).not_to be_produced("step1_after_cancel")
|
286
|
-
assert_canceled_or_finished(journey)
|
287
|
-
end
|
288
|
-
|
289
|
-
it "forbids multiple similar journeys for the same hero at the same time unless allow_multiple is set" do
|
290
|
-
actor_class = create_journey_subclass
|
291
|
-
hero = actor_class.create!
|
292
|
-
|
293
|
-
exclusive_journey_class = create_journey_subclass do
|
294
|
-
step do
|
295
|
-
raise "The step should never be entered as we are not testing the step itself here"
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
expect {
|
300
|
-
2.times { exclusive_journey_class.create! }
|
301
|
-
}.not_to raise_error
|
302
|
-
|
303
|
-
expect {
|
304
|
-
2.times { exclusive_journey_class.create!(hero: hero) }
|
305
|
-
}.to raise_error(ActiveRecord::RecordNotUnique)
|
306
|
-
|
307
|
-
expect {
|
308
|
-
2.times { exclusive_journey_class.create!(hero: hero, allow_multiple: true) }
|
309
|
-
}.not_to raise_error
|
310
|
-
end
|
311
|
-
|
312
|
-
it "forbids multiple steps with the same name within a journey" do
|
313
|
-
expect {
|
314
|
-
create_journey_subclass do
|
315
|
-
step :foo do
|
316
|
-
true
|
317
|
-
end
|
318
|
-
|
319
|
-
step "foo" do
|
320
|
-
true
|
321
|
-
end
|
322
|
-
end
|
323
|
-
}.to raise_error(ArgumentError)
|
324
|
-
end
|
325
|
-
|
326
|
-
it "finishes the journey after perform_next_step" do
|
327
|
-
rapid = create_journey_subclass do
|
328
|
-
step :one do
|
329
|
-
true # no-op
|
330
|
-
end
|
331
|
-
step :two do
|
332
|
-
true # no-op
|
333
|
-
end
|
334
|
-
end
|
335
|
-
|
336
|
-
journey = rapid.create!
|
337
|
-
expect(journey).to be_ready
|
338
|
-
journey.perform_next_step!
|
339
|
-
expect(journey).to be_ready
|
340
|
-
journey.perform_next_step!
|
341
|
-
expect(journey).to be_finished
|
342
|
-
end
|
343
|
-
|
344
|
-
it "does not enter next step on a finished journey" do
|
345
|
-
near_instant = create_journey_subclass do
|
346
|
-
step :one do
|
347
|
-
finished!
|
348
|
-
end
|
349
|
-
|
350
|
-
step :two do
|
351
|
-
raise "Should never be reache"
|
352
|
-
end
|
353
|
-
end
|
354
|
-
|
355
|
-
journey = near_instant.create!
|
356
|
-
expect(journey).to be_ready
|
357
|
-
journey.perform_next_step!
|
358
|
-
expect(journey).to be_finished
|
359
|
-
|
360
|
-
expect { journey.perform_next_step! }.not_to raise_error
|
361
|
-
end
|
362
|
-
|
363
|
-
it "raises an exception if a step changes the journey but does not save it" do
|
364
|
-
mutating = create_journey_subclass do
|
365
|
-
step :one do
|
366
|
-
self.state = "canceled"
|
367
|
-
end
|
368
|
-
end
|
369
|
-
|
370
|
-
journey = mutating.create!
|
371
|
-
expect {
|
372
|
-
journey.perform_next_step!
|
373
|
-
}.to raise_error(StepperMotor::JourneyNotPersisted)
|
374
|
-
end
|
375
|
-
|
376
|
-
it "resets the instance variables after performing a step" do
|
377
|
-
self_resetting = create_journey_subclass do
|
378
|
-
step :one do
|
379
|
-
raise unless @current_step_definition
|
380
|
-
end
|
381
|
-
|
382
|
-
step :two do
|
383
|
-
@reattempt_after = 2.minutes
|
384
|
-
end
|
385
|
-
end
|
386
|
-
|
387
|
-
journey = self_resetting.create!
|
388
|
-
expect { journey.perform_next_step! }.not_to raise_error
|
389
|
-
expect(journey.instance_variable_get(:@current_step_definition)).to be_nil
|
390
|
-
|
391
|
-
expect { journey.perform_next_step! }.not_to raise_error
|
392
|
-
expect(journey.instance_variable_get(:@current_step_definition)).to be_nil
|
393
|
-
expect(journey.instance_variable_get(:@reattempt_after)).to be_nil
|
394
|
-
end
|
395
|
-
|
396
|
-
def assert_canceled_or_finished(model)
|
397
|
-
model.reload
|
398
|
-
expect(model.state).to be_in(["canceled", "finished"])
|
399
|
-
end
|
400
|
-
end
|
401
|
-
# rubocop:enable Lint/ConstantDefinitionInBlock
|
@@ -1,48 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "../spec_helper"
|
4
|
-
|
5
|
-
RSpec.describe "StepperMotor::TestHelper" do
|
6
|
-
include SideEffects::SpecHelper
|
7
|
-
include StepperMotor::TestHelper
|
8
|
-
|
9
|
-
before do
|
10
|
-
establish_test_connection
|
11
|
-
run_generator
|
12
|
-
run_migrations
|
13
|
-
end
|
14
|
-
|
15
|
-
def speedy_journey_class
|
16
|
-
create_journey_subclass do
|
17
|
-
step :step_1, wait: 40.minutes do
|
18
|
-
SideEffects.touch!("step_1")
|
19
|
-
end
|
20
|
-
|
21
|
-
step :step_2, wait: 2.days do
|
22
|
-
SideEffects.touch!("step_2")
|
23
|
-
end
|
24
|
-
|
25
|
-
step do
|
26
|
-
SideEffects.touch!("step_3")
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
it "speedruns the journey despite waits being configured" do
|
32
|
-
journey = speedy_journey_class.create!
|
33
|
-
expect(journey).to be_ready
|
34
|
-
|
35
|
-
expect {
|
36
|
-
speedrun_journey(journey)
|
37
|
-
}.to have_produced_side_effects_named("step_1", "step_2", "step_3")
|
38
|
-
end
|
39
|
-
|
40
|
-
it "is able to perform a single step forcibly" do
|
41
|
-
journey = speedy_journey_class.create!
|
42
|
-
expect(journey).to be_ready
|
43
|
-
|
44
|
-
expect {
|
45
|
-
immediately_perform_single_step(journey, :step_2)
|
46
|
-
}.to have_produced_side_effects_named("step_2")
|
47
|
-
end
|
48
|
-
end
|