solidus_subscriptions 1.0.0 → 1.0.1
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/.circleci/config.yml +57 -9
- data/.github/dependabot.yml +7 -0
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +201 -169
- data/Gemfile +14 -1
- data/README.md +17 -7
- data/app/controllers/solidus_subscriptions/api/v1/subscriptions_controller.rb +24 -0
- data/app/controllers/spree/admin/subscriptions_controller.rb +32 -4
- data/app/decorators/models/solidus_subscriptions/spree/wallet_payment_source/report_default_change_to_subscriptions.rb +2 -2
- data/app/jobs/solidus_subscriptions/create_subscription_job.rb +11 -0
- data/app/jobs/solidus_subscriptions/process_installment_job.rb +1 -1
- data/app/models/solidus_subscriptions/subscription.rb +75 -31
- data/app/subscribers/solidus_subscriptions/churn_buster_subscriber.rb +1 -0
- data/app/subscribers/solidus_subscriptions/event_storage_subscriber.rb +1 -0
- data/app/subscribers/solidus_subscriptions/order_subscriber.rb +16 -0
- data/app/views/spree/admin/shared/_subscription_actions.html.erb +27 -8
- data/app/views/spree/admin/shared/_subscription_sidebar.html.erb +2 -0
- data/app/views/spree/admin/subscriptions/_state_pill.html.erb +3 -2
- data/app/views/spree/admin/subscriptions/index.html.erb +50 -13
- data/bin/sandbox +1 -6
- data/config/locales/en.yml +16 -0
- data/config/routes.rb +9 -3
- data/db/migrate/20210905145955_add_paused_to_subscriptions.rb +5 -0
- data/lib/decorators/api/controllers/solidus_subscriptions/spree/api/line_items_controller/create_subscription_line_items.rb +14 -0
- data/lib/generators/solidus_subscriptions/install/install_generator.rb +16 -0
- data/lib/generators/solidus_subscriptions/install/templates/app/controllers/concerns/create_subscription.rb +17 -0
- data/lib/generators/solidus_subscriptions/install/templates/app/views/cart_line_items/_subscription_fields.html.erb +30 -0
- data/lib/generators/solidus_subscriptions/install/templates/initializer.rb +3 -1
- data/lib/solidus_subscriptions/configuration.rb +29 -6
- data/lib/solidus_subscriptions/dispatcher/payment_failed_dispatcher.rb +2 -2
- data/lib/solidus_subscriptions/dispatcher/success_dispatcher.rb +2 -2
- data/lib/solidus_subscriptions/engine.rb +28 -0
- data/lib/solidus_subscriptions/permission_sets/default_customer.rb +1 -1
- data/lib/solidus_subscriptions/processing_error_handlers/rails_logger.rb +25 -0
- data/lib/solidus_subscriptions/subscription_generator.rb +6 -0
- data/lib/solidus_subscriptions/version.rb +1 -1
- data/solidus_subscriptions.gemspec +6 -6
- data/spec/controllers/spree/api/line_items_controller_spec.rb +63 -28
- data/spec/controllers/spree/api/users_controller_spec.rb +3 -0
- data/spec/decorators/controllers/solidus_subscriptions/spree/orders_controller/create_subscription_line_items_spec.rb +4 -4
- data/spec/decorators/models/solidus_subscriptions/spree/variant/auto_delete_from_subscriptions_spec.rb +2 -2
- data/spec/features/admin/subscriptions_spec.rb +13 -3
- data/spec/jobs/solidus_subscriptions/create_subscription_job_spec.rb +20 -0
- data/spec/jobs/solidus_subscriptions/process_installment_job_spec.rb +17 -0
- data/spec/lib/solidus_subscriptions/dispatcher/payment_failed_dispatcher_spec.rb +3 -3
- data/spec/lib/solidus_subscriptions/dispatcher/success_dispatcher_spec.rb +3 -3
- data/spec/lib/solidus_subscriptions/subscription_generator_spec.rb +1 -1
- data/spec/models/solidus_subscriptions/installment_spec.rb +1 -1
- data/spec/models/solidus_subscriptions/subscription_spec.rb +893 -16
- data/spec/models/spree/wallet_payment_source_spec.rb +3 -3
- data/spec/requests/api/v1/subscriptions_spec.rb +229 -0
- data/spec/subscribers/solidus_subscriptions/churn_buster_subscriber_spec.rb +8 -8
- data/spec/subscribers/solidus_subscriptions/order_subscriber_spec.rb +13 -0
- metadata +24 -17
- data/app/decorators/models/solidus_subscriptions/spree/order/finalize_creates_subscriptions.rb +0 -25
- data/config/initializers/subscribers.rb +0 -9
- data/spec/decorators/models/solidus_subscriptions/spree/order/finalize_creates_subscriptions_spec.rb +0 -32
- /data/{app → lib}/views/spree/frontend/products/_subscription_line_item_fields.html.erb +0 -0
@@ -28,12 +28,12 @@ RSpec.describe SolidusSubscriptions::Subscription, type: :model do
|
|
28
28
|
|
29
29
|
describe 'creating a subscription' do
|
30
30
|
it 'tracks the creation' do
|
31
|
-
stub_const('
|
31
|
+
stub_const('SolidusSupport::LegacyEventCompat::Bus', class_spy(SolidusSupport::LegacyEventCompat::Bus))
|
32
32
|
|
33
33
|
subscription = create(:subscription)
|
34
34
|
|
35
|
-
expect(
|
36
|
-
'solidus_subscriptions.subscription_created',
|
35
|
+
expect(SolidusSupport::LegacyEventCompat::Bus).to have_received(:publish).with(
|
36
|
+
:'solidus_subscriptions.subscription_created',
|
37
37
|
subscription: subscription,
|
38
38
|
)
|
39
39
|
end
|
@@ -53,49 +53,49 @@ RSpec.describe SolidusSubscriptions::Subscription, type: :model do
|
|
53
53
|
|
54
54
|
describe 'updating a subscription' do
|
55
55
|
it 'tracks interval changes' do
|
56
|
-
stub_const('
|
56
|
+
stub_const('SolidusSupport::LegacyEventCompat::Bus', class_spy(SolidusSupport::LegacyEventCompat::Bus))
|
57
57
|
subscription = create(:subscription)
|
58
58
|
|
59
59
|
subscription.update!(interval_length: subscription.interval_length + 1)
|
60
60
|
|
61
|
-
expect(
|
62
|
-
'solidus_subscriptions.subscription_frequency_changed',
|
61
|
+
expect(SolidusSupport::LegacyEventCompat::Bus).to have_received(:publish).with(
|
62
|
+
:'solidus_subscriptions.subscription_frequency_changed',
|
63
63
|
subscription: subscription,
|
64
64
|
)
|
65
65
|
end
|
66
66
|
|
67
67
|
it 'tracks shipping address changes' do
|
68
|
-
stub_const('
|
68
|
+
stub_const('SolidusSupport::LegacyEventCompat::Bus', class_spy(SolidusSupport::LegacyEventCompat::Bus))
|
69
69
|
subscription = create(:subscription)
|
70
70
|
|
71
71
|
subscription.update!(shipping_address: create(:address))
|
72
72
|
|
73
|
-
expect(
|
74
|
-
'solidus_subscriptions.subscription_shipping_address_changed',
|
73
|
+
expect(SolidusSupport::LegacyEventCompat::Bus).to have_received(:publish).with(
|
74
|
+
:'solidus_subscriptions.subscription_shipping_address_changed',
|
75
75
|
subscription: subscription,
|
76
76
|
)
|
77
77
|
end
|
78
78
|
|
79
79
|
it 'tracks billing address changes' do
|
80
|
-
stub_const('
|
80
|
+
stub_const('SolidusSupport::LegacyEventCompat::Bus', class_spy(SolidusSupport::LegacyEventCompat::Bus))
|
81
81
|
subscription = create(:subscription)
|
82
82
|
|
83
83
|
subscription.update!(billing_address: create(:address))
|
84
84
|
|
85
|
-
expect(
|
86
|
-
'solidus_subscriptions.subscription_billing_address_changed',
|
85
|
+
expect(SolidusSupport::LegacyEventCompat::Bus).to have_received(:publish).with(
|
86
|
+
:'solidus_subscriptions.subscription_billing_address_changed',
|
87
87
|
subscription: subscription,
|
88
88
|
)
|
89
89
|
end
|
90
90
|
|
91
91
|
it 'tracks payment method changes' do
|
92
|
-
stub_const('
|
92
|
+
stub_const('SolidusSupport::LegacyEventCompat::Bus', class_spy(SolidusSupport::LegacyEventCompat::Bus))
|
93
93
|
|
94
94
|
subscription = create(:subscription)
|
95
95
|
subscription.update!(payment_source: create(:credit_card, user: subscription.user))
|
96
96
|
|
97
|
-
expect(
|
98
|
-
'solidus_subscriptions.subscription_payment_method_changed',
|
97
|
+
expect(SolidusSupport::LegacyEventCompat::Bus).to have_received(:publish).with(
|
98
|
+
:'solidus_subscriptions.subscription_payment_method_changed',
|
99
99
|
subscription: subscription,
|
100
100
|
)
|
101
101
|
end
|
@@ -155,6 +155,866 @@ RSpec.describe SolidusSubscriptions::Subscription, type: :model do
|
|
155
155
|
end
|
156
156
|
end
|
157
157
|
|
158
|
+
describe '#pause' do
|
159
|
+
context 'when an active subscription is paused' do
|
160
|
+
it 'sets the paused column to true' do
|
161
|
+
subscription = create(
|
162
|
+
:subscription,
|
163
|
+
:actionable,
|
164
|
+
:with_shipping_address,
|
165
|
+
state: 'active',
|
166
|
+
paused: false
|
167
|
+
)
|
168
|
+
|
169
|
+
subscription.pause
|
170
|
+
|
171
|
+
expect(subscription.reload.paused).to be_truthy
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'does not change the state' do
|
175
|
+
subscription = create(
|
176
|
+
:subscription,
|
177
|
+
:actionable,
|
178
|
+
:with_shipping_address,
|
179
|
+
state: 'active',
|
180
|
+
paused: false
|
181
|
+
)
|
182
|
+
|
183
|
+
subscription.pause
|
184
|
+
|
185
|
+
expect(subscription.reload.state).to eq('active')
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'creates a paused event' do
|
189
|
+
subscription = create(
|
190
|
+
:subscription,
|
191
|
+
:actionable,
|
192
|
+
:with_shipping_address,
|
193
|
+
state: 'active',
|
194
|
+
paused: false
|
195
|
+
)
|
196
|
+
|
197
|
+
subscription.pause
|
198
|
+
|
199
|
+
expect(subscription.events.last).to have_attributes(event_type: 'subscription_paused')
|
200
|
+
end
|
201
|
+
|
202
|
+
context 'when today is used as the actionable date' do
|
203
|
+
it 'sets actionable_date to the next day' do
|
204
|
+
subscription = create(
|
205
|
+
:subscription,
|
206
|
+
:actionable,
|
207
|
+
:with_shipping_address,
|
208
|
+
state: 'active',
|
209
|
+
paused: false
|
210
|
+
)
|
211
|
+
|
212
|
+
subscription.pause(actionable_date: Time.zone.today)
|
213
|
+
|
214
|
+
expect(subscription.reload.actionable_date).to eq(Time.zone.tomorrow)
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'is not actionable' do
|
218
|
+
subscription = create(
|
219
|
+
:subscription,
|
220
|
+
:actionable,
|
221
|
+
:with_shipping_address,
|
222
|
+
state: 'active',
|
223
|
+
paused: false
|
224
|
+
)
|
225
|
+
|
226
|
+
subscription.pause(actionable_date: Time.zone.today)
|
227
|
+
|
228
|
+
expect(described_class.actionable).not_to include subscription
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'pauses correctly' do
|
232
|
+
subscription = create(
|
233
|
+
:subscription,
|
234
|
+
:actionable,
|
235
|
+
:with_shipping_address,
|
236
|
+
state: 'active',
|
237
|
+
paused: false
|
238
|
+
)
|
239
|
+
|
240
|
+
subscription.pause(actionable_date: Time.zone.today)
|
241
|
+
|
242
|
+
aggregate_failures do
|
243
|
+
expect(subscription.reload.paused).to be_truthy
|
244
|
+
expect(subscription.state).to eq('active')
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
it 'creates a paused event' do
|
249
|
+
subscription = create(
|
250
|
+
:subscription,
|
251
|
+
:actionable,
|
252
|
+
:with_shipping_address,
|
253
|
+
state: 'active',
|
254
|
+
paused: false
|
255
|
+
)
|
256
|
+
|
257
|
+
subscription.pause(actionable_date: Time.zone.today)
|
258
|
+
|
259
|
+
expect(subscription.events.last).to have_attributes(event_type: 'subscription_paused')
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
context 'when a past date is used as the actionable date' do
|
264
|
+
it 'sets actionable_date to the next day' do
|
265
|
+
subscription = create(
|
266
|
+
:subscription,
|
267
|
+
:actionable,
|
268
|
+
:with_shipping_address,
|
269
|
+
state: 'active',
|
270
|
+
paused: false
|
271
|
+
)
|
272
|
+
|
273
|
+
subscription.pause(actionable_date: Time.zone.yesterday)
|
274
|
+
|
275
|
+
expect(subscription.reload.actionable_date).to eq(Time.zone.tomorrow)
|
276
|
+
end
|
277
|
+
|
278
|
+
it 'is not actionable' do
|
279
|
+
subscription = create(
|
280
|
+
:subscription,
|
281
|
+
:actionable,
|
282
|
+
:with_shipping_address,
|
283
|
+
state: 'active',
|
284
|
+
paused: false
|
285
|
+
)
|
286
|
+
|
287
|
+
subscription.pause(actionable_date: Time.zone.yesterday)
|
288
|
+
|
289
|
+
expect(described_class.actionable).not_to include subscription
|
290
|
+
end
|
291
|
+
|
292
|
+
it 'pauses correctly' do
|
293
|
+
subscription = create(
|
294
|
+
:subscription,
|
295
|
+
:actionable,
|
296
|
+
:with_shipping_address,
|
297
|
+
state: 'active',
|
298
|
+
paused: false
|
299
|
+
)
|
300
|
+
|
301
|
+
subscription.pause(actionable_date: Time.zone.yesterday)
|
302
|
+
|
303
|
+
aggregate_failures do
|
304
|
+
expect(subscription.reload.paused).to be_truthy
|
305
|
+
expect(subscription.state).to eq('active')
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
it 'creates a paused event' do
|
310
|
+
subscription = create(
|
311
|
+
:subscription,
|
312
|
+
:actionable,
|
313
|
+
:with_shipping_address,
|
314
|
+
state: 'active',
|
315
|
+
paused: false
|
316
|
+
)
|
317
|
+
|
318
|
+
subscription.pause(actionable_date: Time.zone.yesterday)
|
319
|
+
|
320
|
+
expect(subscription.events.last).to have_attributes(event_type: 'subscription_paused')
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
context 'when nil is used as the actionable date' do
|
325
|
+
it 'sets actionable_date to nil' do
|
326
|
+
subscription = create(
|
327
|
+
:subscription,
|
328
|
+
:actionable,
|
329
|
+
:with_shipping_address,
|
330
|
+
state: 'active',
|
331
|
+
paused: false
|
332
|
+
)
|
333
|
+
|
334
|
+
subscription.pause(actionable_date: nil)
|
335
|
+
|
336
|
+
expect(subscription.reload.actionable_date).to eq(nil)
|
337
|
+
end
|
338
|
+
|
339
|
+
it 'is not actionable' do
|
340
|
+
subscription = create(
|
341
|
+
:subscription,
|
342
|
+
:actionable,
|
343
|
+
:with_shipping_address,
|
344
|
+
state: 'active',
|
345
|
+
paused: false
|
346
|
+
)
|
347
|
+
|
348
|
+
subscription.pause(actionable_date: nil)
|
349
|
+
|
350
|
+
expect(described_class.actionable).not_to include subscription
|
351
|
+
end
|
352
|
+
|
353
|
+
it 'pauses correctly' do
|
354
|
+
subscription = create(
|
355
|
+
:subscription,
|
356
|
+
:actionable,
|
357
|
+
:with_shipping_address,
|
358
|
+
state: 'active',
|
359
|
+
paused: false
|
360
|
+
)
|
361
|
+
|
362
|
+
subscription.pause(actionable_date: nil)
|
363
|
+
|
364
|
+
aggregate_failures do
|
365
|
+
expect(subscription.reload.paused).to be_truthy
|
366
|
+
expect(subscription.state).to eq('active')
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
it 'creates a paused event' do
|
371
|
+
subscription = create(
|
372
|
+
:subscription,
|
373
|
+
:actionable,
|
374
|
+
:with_shipping_address,
|
375
|
+
state: 'active',
|
376
|
+
paused: false
|
377
|
+
)
|
378
|
+
|
379
|
+
subscription.pause(actionable_date: nil)
|
380
|
+
|
381
|
+
expect(subscription.events.last).to have_attributes(event_type: 'subscription_paused')
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
context 'when a future date is used as the actionable date' do
|
386
|
+
it 'sets actionable_date to the specified date' do
|
387
|
+
subscription = create(
|
388
|
+
:subscription,
|
389
|
+
:actionable,
|
390
|
+
:with_shipping_address,
|
391
|
+
state: 'active',
|
392
|
+
paused: false
|
393
|
+
)
|
394
|
+
|
395
|
+
subscription.pause(actionable_date: (Time.zone.tomorrow + 1.day))
|
396
|
+
|
397
|
+
expect(subscription.reload.actionable_date).to eq((Time.zone.tomorrow + 1.day))
|
398
|
+
end
|
399
|
+
|
400
|
+
it 'is not actionable' do
|
401
|
+
subscription = create(
|
402
|
+
:subscription,
|
403
|
+
:actionable,
|
404
|
+
:with_shipping_address,
|
405
|
+
state: 'active',
|
406
|
+
paused: false
|
407
|
+
)
|
408
|
+
|
409
|
+
subscription.pause(actionable_date: (Time.zone.tomorrow + 1.day))
|
410
|
+
|
411
|
+
expect(described_class.actionable).not_to include subscription
|
412
|
+
end
|
413
|
+
|
414
|
+
it 'pauses correctly' do
|
415
|
+
subscription = create(
|
416
|
+
:subscription,
|
417
|
+
:actionable,
|
418
|
+
:with_shipping_address,
|
419
|
+
state: 'active',
|
420
|
+
paused: false
|
421
|
+
)
|
422
|
+
|
423
|
+
subscription.pause(actionable_date: (Time.zone.tomorrow + 1.day))
|
424
|
+
|
425
|
+
aggregate_failures do
|
426
|
+
expect(subscription.reload.paused).to be_truthy
|
427
|
+
expect(subscription.state).to eq('active')
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
it 'creates a paused event' do
|
432
|
+
subscription = create(
|
433
|
+
:subscription,
|
434
|
+
:actionable,
|
435
|
+
:with_shipping_address,
|
436
|
+
state: 'active',
|
437
|
+
paused: false
|
438
|
+
)
|
439
|
+
|
440
|
+
subscription.pause(actionable_date: (Time.zone.tomorrow + 1.day))
|
441
|
+
|
442
|
+
expect(subscription.events.last).to have_attributes(event_type: 'subscription_paused')
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
context 'when the actionable date has been reached' do
|
447
|
+
it 'is actionable' do
|
448
|
+
subscription = create(
|
449
|
+
:subscription,
|
450
|
+
:actionable,
|
451
|
+
:with_shipping_address,
|
452
|
+
state: 'active',
|
453
|
+
paused: true
|
454
|
+
)
|
455
|
+
|
456
|
+
subscription.next_actionable_date
|
457
|
+
|
458
|
+
expect(described_class.actionable).to include subscription
|
459
|
+
end
|
460
|
+
|
461
|
+
it 'processes and resumes the subscription' do
|
462
|
+
subscription = create(
|
463
|
+
:subscription,
|
464
|
+
:actionable,
|
465
|
+
:with_shipping_address,
|
466
|
+
state: 'active',
|
467
|
+
paused: true
|
468
|
+
)
|
469
|
+
expected_actionable_date = subscription.actionable_date + subscription.interval
|
470
|
+
|
471
|
+
SolidusSubscriptions::ProcessSubscriptionJob.perform_now(subscription)
|
472
|
+
|
473
|
+
aggregate_failures do
|
474
|
+
expect(subscription.reload.paused).to be_falsy
|
475
|
+
expect(subscription.installments.last.created_at).to be_within(1.hour).of(Time.zone.now)
|
476
|
+
expect(subscription.actionable_date).to eq(expected_actionable_date)
|
477
|
+
end
|
478
|
+
end
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
context 'when a canceled subscription is paused' do
|
483
|
+
it 'adds an error when the method is called on a subscription which is not active' do
|
484
|
+
subscription = create(
|
485
|
+
:subscription,
|
486
|
+
:actionable,
|
487
|
+
:with_shipping_address,
|
488
|
+
state: 'canceled',
|
489
|
+
paused: false
|
490
|
+
)
|
491
|
+
|
492
|
+
subscription.pause
|
493
|
+
|
494
|
+
expect(subscription.errors[:paused].first).to include 'not active'
|
495
|
+
end
|
496
|
+
|
497
|
+
it 'does not alter the subscription' do
|
498
|
+
subscription = create(
|
499
|
+
:subscription,
|
500
|
+
:actionable,
|
501
|
+
:with_shipping_address,
|
502
|
+
state: 'canceled',
|
503
|
+
paused: false
|
504
|
+
)
|
505
|
+
|
506
|
+
expect { subscription.pause }.not_to(change { subscription.reload })
|
507
|
+
end
|
508
|
+
|
509
|
+
it 'does not create an event' do
|
510
|
+
subscription = create(
|
511
|
+
:subscription,
|
512
|
+
:actionable,
|
513
|
+
:with_shipping_address,
|
514
|
+
state: 'canceled',
|
515
|
+
paused: false
|
516
|
+
)
|
517
|
+
|
518
|
+
subscription.pause
|
519
|
+
|
520
|
+
expect(subscription.events.last).not_to have_attributes(event_type: "subscription_paused")
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
context 'when a `pending_cancellation` subscription is paused' do
|
525
|
+
it 'adds an error when the method is called on a subscription which is not active' do
|
526
|
+
subscription = create(
|
527
|
+
:subscription,
|
528
|
+
:actionable,
|
529
|
+
:with_shipping_address,
|
530
|
+
state: 'pending_cancellation',
|
531
|
+
paused: false
|
532
|
+
)
|
533
|
+
|
534
|
+
subscription.pause
|
535
|
+
|
536
|
+
expect(subscription.errors[:paused].first).to include 'not active'
|
537
|
+
end
|
538
|
+
|
539
|
+
it 'does not alter the subscription' do
|
540
|
+
subscription = create(
|
541
|
+
:subscription,
|
542
|
+
:actionable,
|
543
|
+
:with_shipping_address,
|
544
|
+
state: 'pending_cancellation',
|
545
|
+
paused: false
|
546
|
+
)
|
547
|
+
|
548
|
+
expect { subscription.pause }.not_to(change { subscription.reload })
|
549
|
+
end
|
550
|
+
|
551
|
+
it 'does not create an event' do
|
552
|
+
subscription = create(
|
553
|
+
:subscription,
|
554
|
+
:actionable,
|
555
|
+
:with_shipping_address,
|
556
|
+
state: 'pending_cancellation',
|
557
|
+
paused: true
|
558
|
+
)
|
559
|
+
|
560
|
+
subscription.pause
|
561
|
+
|
562
|
+
expect(subscription.events.last).not_to have_attributes(event_type: "subscription_paused")
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
566
|
+
context 'when an `inactive` subscription is paused' do
|
567
|
+
it 'adds an error when the method is called on a subscription which is not active' do
|
568
|
+
subscription = create(
|
569
|
+
:subscription,
|
570
|
+
:actionable,
|
571
|
+
:with_shipping_address,
|
572
|
+
state: 'inactive',
|
573
|
+
paused: false
|
574
|
+
)
|
575
|
+
|
576
|
+
subscription.pause
|
577
|
+
|
578
|
+
expect(subscription.errors[:paused].first).to include 'not active'
|
579
|
+
end
|
580
|
+
|
581
|
+
it 'does not alter the subscription' do
|
582
|
+
subscription = create(
|
583
|
+
:subscription,
|
584
|
+
:actionable,
|
585
|
+
:with_shipping_address,
|
586
|
+
state: 'inactive',
|
587
|
+
paused: false
|
588
|
+
)
|
589
|
+
|
590
|
+
expect { subscription.pause }.not_to(change { subscription.reload })
|
591
|
+
end
|
592
|
+
|
593
|
+
it 'does not create an event' do
|
594
|
+
subscription = create(
|
595
|
+
:subscription,
|
596
|
+
:actionable,
|
597
|
+
:with_shipping_address,
|
598
|
+
state: 'inactive',
|
599
|
+
paused: false
|
600
|
+
)
|
601
|
+
|
602
|
+
subscription.pause
|
603
|
+
|
604
|
+
expect(subscription.events.last).not_to have_attributes(event_type: "subscription_paused")
|
605
|
+
end
|
606
|
+
end
|
607
|
+
end
|
608
|
+
|
609
|
+
describe '#resume' do
|
610
|
+
context 'when an active subscription is resumed' do
|
611
|
+
it 'sets the paused column to false' do
|
612
|
+
subscription = create(
|
613
|
+
:subscription,
|
614
|
+
:actionable,
|
615
|
+
:with_shipping_address,
|
616
|
+
state: 'active',
|
617
|
+
paused: true
|
618
|
+
)
|
619
|
+
|
620
|
+
subscription.resume
|
621
|
+
|
622
|
+
expect(subscription.reload.paused).to be_falsy
|
623
|
+
end
|
624
|
+
|
625
|
+
it 'does not change the state' do
|
626
|
+
subscription = create(
|
627
|
+
:subscription,
|
628
|
+
:actionable,
|
629
|
+
:with_shipping_address,
|
630
|
+
state: 'active',
|
631
|
+
paused: true
|
632
|
+
)
|
633
|
+
|
634
|
+
subscription.resume
|
635
|
+
|
636
|
+
expect(subscription.reload.state).to eq('active')
|
637
|
+
end
|
638
|
+
|
639
|
+
it 'creates a resumed event' do
|
640
|
+
subscription = create(
|
641
|
+
:subscription,
|
642
|
+
:actionable,
|
643
|
+
:with_shipping_address,
|
644
|
+
state: 'active',
|
645
|
+
paused: true
|
646
|
+
)
|
647
|
+
|
648
|
+
subscription.resume
|
649
|
+
|
650
|
+
expect(subscription.events.last).to have_attributes(event_type: 'subscription_resumed')
|
651
|
+
end
|
652
|
+
|
653
|
+
context 'when a past date is used as the actionable date' do
|
654
|
+
it 'sets actionable_date to the next day' do
|
655
|
+
subscription = create(
|
656
|
+
:subscription,
|
657
|
+
:actionable,
|
658
|
+
:with_shipping_address,
|
659
|
+
state: 'active',
|
660
|
+
paused: true
|
661
|
+
)
|
662
|
+
|
663
|
+
subscription.resume(actionable_date: Time.zone.yesterday)
|
664
|
+
|
665
|
+
expect(subscription.reload.actionable_date).to eq(Time.zone.tomorrow)
|
666
|
+
end
|
667
|
+
|
668
|
+
it 'is not actionable' do
|
669
|
+
subscription = create(
|
670
|
+
:subscription,
|
671
|
+
:actionable,
|
672
|
+
:with_shipping_address,
|
673
|
+
state: 'active',
|
674
|
+
paused: true
|
675
|
+
)
|
676
|
+
|
677
|
+
subscription.resume(actionable_date: Time.zone.yesterday)
|
678
|
+
|
679
|
+
expect(described_class.actionable).not_to include subscription
|
680
|
+
end
|
681
|
+
|
682
|
+
it 'resumes correctly' do
|
683
|
+
subscription = create(
|
684
|
+
:subscription,
|
685
|
+
:actionable,
|
686
|
+
:with_shipping_address,
|
687
|
+
state: 'active',
|
688
|
+
paused: true
|
689
|
+
)
|
690
|
+
|
691
|
+
subscription.resume(actionable_date: Time.zone.yesterday)
|
692
|
+
|
693
|
+
aggregate_failures do
|
694
|
+
expect(subscription.reload.paused).to be_falsy
|
695
|
+
expect(subscription.state).to eq('active')
|
696
|
+
end
|
697
|
+
end
|
698
|
+
|
699
|
+
it 'creates a resumed event' do
|
700
|
+
subscription = create(
|
701
|
+
:subscription,
|
702
|
+
:actionable,
|
703
|
+
:with_shipping_address,
|
704
|
+
state: 'active',
|
705
|
+
paused: true
|
706
|
+
)
|
707
|
+
|
708
|
+
subscription.resume(actionable_date: Time.zone.yesterday)
|
709
|
+
|
710
|
+
expect(subscription.events.last).to have_attributes(event_type: 'subscription_resumed')
|
711
|
+
end
|
712
|
+
end
|
713
|
+
|
714
|
+
context 'when today is used as the actionable date' do
|
715
|
+
it 'sets actionable_date to the next day' do
|
716
|
+
subscription = create(
|
717
|
+
:subscription,
|
718
|
+
:actionable,
|
719
|
+
:with_shipping_address,
|
720
|
+
state: 'active',
|
721
|
+
paused: true
|
722
|
+
)
|
723
|
+
|
724
|
+
subscription.resume(actionable_date: Time.zone.today)
|
725
|
+
|
726
|
+
expect(subscription.reload.actionable_date).to eq(Time.zone.tomorrow)
|
727
|
+
end
|
728
|
+
|
729
|
+
it 'is not actionable' do
|
730
|
+
subscription = create(
|
731
|
+
:subscription,
|
732
|
+
:actionable,
|
733
|
+
:with_shipping_address,
|
734
|
+
state: 'active',
|
735
|
+
paused: true
|
736
|
+
)
|
737
|
+
|
738
|
+
subscription.resume(actionable_date: Time.zone.today)
|
739
|
+
|
740
|
+
expect(described_class.actionable).not_to include subscription
|
741
|
+
end
|
742
|
+
|
743
|
+
it 'resumes correctly' do
|
744
|
+
subscription = create(
|
745
|
+
:subscription,
|
746
|
+
:actionable,
|
747
|
+
:with_shipping_address,
|
748
|
+
state: 'active',
|
749
|
+
paused: true
|
750
|
+
)
|
751
|
+
|
752
|
+
subscription.resume(actionable_date: Time.zone.today)
|
753
|
+
|
754
|
+
aggregate_failures do
|
755
|
+
expect(subscription.reload.paused).to be_falsy
|
756
|
+
expect(subscription.state).to eq('active')
|
757
|
+
end
|
758
|
+
end
|
759
|
+
|
760
|
+
it 'creates a resumed event' do
|
761
|
+
subscription = create(
|
762
|
+
:subscription,
|
763
|
+
:actionable,
|
764
|
+
:with_shipping_address,
|
765
|
+
state: 'active',
|
766
|
+
paused: true
|
767
|
+
)
|
768
|
+
|
769
|
+
subscription.resume(actionable_date: Time.zone.today)
|
770
|
+
|
771
|
+
expect(subscription.events.last).to have_attributes(event_type: 'subscription_resumed')
|
772
|
+
end
|
773
|
+
end
|
774
|
+
|
775
|
+
context 'when nil is used as the actionable date' do
|
776
|
+
it 'sets actionable_date to the next day' do
|
777
|
+
subscription = create(
|
778
|
+
:subscription,
|
779
|
+
:actionable,
|
780
|
+
:with_shipping_address,
|
781
|
+
state: 'active',
|
782
|
+
paused: true
|
783
|
+
)
|
784
|
+
|
785
|
+
subscription.resume(actionable_date: nil)
|
786
|
+
|
787
|
+
expect(subscription.reload.actionable_date).to eq(Time.zone.tomorrow)
|
788
|
+
end
|
789
|
+
|
790
|
+
it 'is not actionable' do
|
791
|
+
subscription = create(
|
792
|
+
:subscription,
|
793
|
+
:actionable,
|
794
|
+
:with_shipping_address,
|
795
|
+
state: 'active',
|
796
|
+
paused: true
|
797
|
+
)
|
798
|
+
|
799
|
+
subscription.resume(actionable_date: nil)
|
800
|
+
|
801
|
+
expect(described_class.actionable).not_to include subscription
|
802
|
+
end
|
803
|
+
|
804
|
+
it 'resumes correctly' do
|
805
|
+
subscription = create(
|
806
|
+
:subscription,
|
807
|
+
:actionable,
|
808
|
+
:with_shipping_address,
|
809
|
+
state: 'active',
|
810
|
+
paused: true
|
811
|
+
)
|
812
|
+
|
813
|
+
subscription.resume(actionable_date: nil)
|
814
|
+
|
815
|
+
aggregate_failures do
|
816
|
+
expect(subscription.reload.paused).to be_falsy
|
817
|
+
expect(subscription.state).to eq('active')
|
818
|
+
end
|
819
|
+
end
|
820
|
+
|
821
|
+
it 'creates a resumed event' do
|
822
|
+
subscription = create(
|
823
|
+
:subscription,
|
824
|
+
:actionable,
|
825
|
+
:with_shipping_address,
|
826
|
+
state: 'active',
|
827
|
+
paused: true
|
828
|
+
)
|
829
|
+
|
830
|
+
subscription.resume(actionable_date: nil)
|
831
|
+
|
832
|
+
expect(subscription.events.last).to have_attributes(event_type: 'subscription_resumed')
|
833
|
+
end
|
834
|
+
end
|
835
|
+
|
836
|
+
context 'when a future date is used as the actionable date' do
|
837
|
+
it 'sets actionable_date to the specified date' do
|
838
|
+
subscription = create(
|
839
|
+
:subscription,
|
840
|
+
:actionable,
|
841
|
+
:with_shipping_address,
|
842
|
+
state: 'active',
|
843
|
+
paused: true
|
844
|
+
)
|
845
|
+
|
846
|
+
subscription.resume(actionable_date: (Time.zone.tomorrow + 1.day))
|
847
|
+
|
848
|
+
expect(subscription.reload.actionable_date).to eq((Time.zone.tomorrow + 1.day))
|
849
|
+
end
|
850
|
+
|
851
|
+
it 'is not actionable' do
|
852
|
+
subscription = create(
|
853
|
+
:subscription,
|
854
|
+
:actionable,
|
855
|
+
:with_shipping_address,
|
856
|
+
state: 'active',
|
857
|
+
paused: true
|
858
|
+
)
|
859
|
+
|
860
|
+
subscription.resume(actionable_date: (Time.zone.tomorrow + 1.day))
|
861
|
+
|
862
|
+
expect(described_class.actionable).not_to include subscription
|
863
|
+
end
|
864
|
+
|
865
|
+
it 'resumes correctly' do
|
866
|
+
subscription = create(
|
867
|
+
:subscription,
|
868
|
+
:actionable,
|
869
|
+
:with_shipping_address,
|
870
|
+
state: 'active',
|
871
|
+
paused: true
|
872
|
+
)
|
873
|
+
|
874
|
+
subscription.resume(actionable_date: (Time.zone.tomorrow + 1.day))
|
875
|
+
|
876
|
+
aggregate_failures do
|
877
|
+
expect(subscription.reload.paused).to be_falsy
|
878
|
+
expect(subscription.state).to eq('active')
|
879
|
+
end
|
880
|
+
end
|
881
|
+
|
882
|
+
it 'creates a resumed event' do
|
883
|
+
subscription = create(
|
884
|
+
:subscription,
|
885
|
+
:actionable,
|
886
|
+
:with_shipping_address,
|
887
|
+
state: 'active',
|
888
|
+
paused: true
|
889
|
+
)
|
890
|
+
|
891
|
+
subscription.resume(actionable_date: (Time.zone.tomorrow + 1.day))
|
892
|
+
|
893
|
+
expect(subscription.events.last).to have_attributes(event_type: 'subscription_resumed')
|
894
|
+
end
|
895
|
+
end
|
896
|
+
end
|
897
|
+
|
898
|
+
context 'when a `canceled` subscription is resumed' do
|
899
|
+
it 'does not alter the subscription' do
|
900
|
+
subscription = create(
|
901
|
+
:subscription,
|
902
|
+
:actionable,
|
903
|
+
:with_shipping_address,
|
904
|
+
state: 'canceled',
|
905
|
+
paused: true
|
906
|
+
)
|
907
|
+
|
908
|
+
expect { subscription.resume }.not_to(change { subscription.reload })
|
909
|
+
end
|
910
|
+
|
911
|
+
it 'does not create an event' do
|
912
|
+
subscription = create(
|
913
|
+
:subscription,
|
914
|
+
:actionable,
|
915
|
+
:with_shipping_address,
|
916
|
+
state: 'canceled',
|
917
|
+
paused: true
|
918
|
+
)
|
919
|
+
|
920
|
+
subscription.resume
|
921
|
+
|
922
|
+
expect(subscription.events.last).not_to have_attributes(event_type: "subscription_resumed")
|
923
|
+
end
|
924
|
+
end
|
925
|
+
|
926
|
+
context 'when a `pending_cancellation` subscription is resumed' do
|
927
|
+
it 'does not alter the subscription' do
|
928
|
+
subscription = create(
|
929
|
+
:subscription,
|
930
|
+
:actionable,
|
931
|
+
:with_shipping_address,
|
932
|
+
state: 'pending_cancellation',
|
933
|
+
paused: true
|
934
|
+
)
|
935
|
+
|
936
|
+
expect { subscription.resume }.not_to(change { subscription.reload })
|
937
|
+
end
|
938
|
+
|
939
|
+
it 'does not create an event' do
|
940
|
+
subscription = create(
|
941
|
+
:subscription,
|
942
|
+
:actionable,
|
943
|
+
:with_shipping_address,
|
944
|
+
state: 'pending_cancellation',
|
945
|
+
paused: true
|
946
|
+
)
|
947
|
+
|
948
|
+
subscription.resume
|
949
|
+
|
950
|
+
expect(subscription.events.last).not_to have_attributes(event_type: "subscription_resumed")
|
951
|
+
end
|
952
|
+
end
|
953
|
+
|
954
|
+
context 'when an `inactive` subscription is resumed' do
|
955
|
+
it 'does not alter the subscription' do
|
956
|
+
subscription = create(
|
957
|
+
:subscription,
|
958
|
+
:actionable,
|
959
|
+
:with_shipping_address,
|
960
|
+
state: 'inactive',
|
961
|
+
paused: true
|
962
|
+
)
|
963
|
+
|
964
|
+
expect { subscription.resume }.not_to(change { subscription.reload })
|
965
|
+
end
|
966
|
+
|
967
|
+
it 'does not create an event' do
|
968
|
+
subscription = create(
|
969
|
+
:subscription,
|
970
|
+
:actionable,
|
971
|
+
:with_shipping_address,
|
972
|
+
state: 'inactive',
|
973
|
+
paused: true
|
974
|
+
)
|
975
|
+
|
976
|
+
subscription.resume
|
977
|
+
|
978
|
+
expect(subscription.events.last).not_to have_attributes(event_type: "subscription_resumed")
|
979
|
+
end
|
980
|
+
end
|
981
|
+
end
|
982
|
+
|
983
|
+
describe '#state_with_pause' do
|
984
|
+
it 'returns `paused` when the subscription is active and paused' do
|
985
|
+
subscription = create(
|
986
|
+
:subscription,
|
987
|
+
:with_shipping_address,
|
988
|
+
paused: true,
|
989
|
+
state: 'active'
|
990
|
+
)
|
991
|
+
|
992
|
+
expect(subscription.state_with_pause).to eq('paused')
|
993
|
+
end
|
994
|
+
|
995
|
+
it 'returns `active` when the subscription is active and not paused' do
|
996
|
+
subscription = create(
|
997
|
+
:subscription,
|
998
|
+
:with_shipping_address,
|
999
|
+
paused: false,
|
1000
|
+
state: 'active'
|
1001
|
+
)
|
1002
|
+
|
1003
|
+
expect(subscription.state_with_pause).to eq('active')
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
it 'returns `canceled` when the subscription is canceled and not paused' do
|
1007
|
+
subscription = create(
|
1008
|
+
:subscription,
|
1009
|
+
:with_shipping_address,
|
1010
|
+
paused: false,
|
1011
|
+
state: 'canceled'
|
1012
|
+
)
|
1013
|
+
|
1014
|
+
expect(subscription.state_with_pause).to eq('canceled')
|
1015
|
+
end
|
1016
|
+
end
|
1017
|
+
|
158
1018
|
describe '#skip' do
|
159
1019
|
subject { subscription.skip&.to_date }
|
160
1020
|
|
@@ -402,7 +1262,24 @@ RSpec.describe SolidusSubscriptions::Subscription, type: :model do
|
|
402
1262
|
describe '.ransackable_scopes' do
|
403
1263
|
subject { described_class.ransackable_scopes }
|
404
1264
|
|
405
|
-
it { is_expected.to match_array [:in_processing_state] }
|
1265
|
+
it { is_expected.to match_array [:in_processing_state, :with_subscribable] }
|
1266
|
+
end
|
1267
|
+
|
1268
|
+
describe '.with_subscribable' do
|
1269
|
+
let(:subscription) do
|
1270
|
+
create :subscription, :with_line_item
|
1271
|
+
end
|
1272
|
+
let(:other_subscription) do
|
1273
|
+
create :subscription, :with_line_item
|
1274
|
+
end
|
1275
|
+
|
1276
|
+
it 'can find subscription with line items of the provided subscribable' do
|
1277
|
+
subscribable = subscription.line_items.first.subscribable
|
1278
|
+
other_subscribable = other_subscription.line_items.first.subscribable
|
1279
|
+
|
1280
|
+
expect(described_class.with_subscribable(subscribable)).to match_array([subscription])
|
1281
|
+
expect(described_class.with_subscribable(other_subscribable)).to match_array([other_subscription])
|
1282
|
+
end
|
406
1283
|
end
|
407
1284
|
|
408
1285
|
describe '.in_processing_state' do
|