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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +57 -9
  3. data/.github/dependabot.yml +7 -0
  4. data/.rubocop.yml +3 -0
  5. data/CHANGELOG.md +201 -169
  6. data/Gemfile +14 -1
  7. data/README.md +17 -7
  8. data/app/controllers/solidus_subscriptions/api/v1/subscriptions_controller.rb +24 -0
  9. data/app/controllers/spree/admin/subscriptions_controller.rb +32 -4
  10. data/app/decorators/models/solidus_subscriptions/spree/wallet_payment_source/report_default_change_to_subscriptions.rb +2 -2
  11. data/app/jobs/solidus_subscriptions/create_subscription_job.rb +11 -0
  12. data/app/jobs/solidus_subscriptions/process_installment_job.rb +1 -1
  13. data/app/models/solidus_subscriptions/subscription.rb +75 -31
  14. data/app/subscribers/solidus_subscriptions/churn_buster_subscriber.rb +1 -0
  15. data/app/subscribers/solidus_subscriptions/event_storage_subscriber.rb +1 -0
  16. data/app/subscribers/solidus_subscriptions/order_subscriber.rb +16 -0
  17. data/app/views/spree/admin/shared/_subscription_actions.html.erb +27 -8
  18. data/app/views/spree/admin/shared/_subscription_sidebar.html.erb +2 -0
  19. data/app/views/spree/admin/subscriptions/_state_pill.html.erb +3 -2
  20. data/app/views/spree/admin/subscriptions/index.html.erb +50 -13
  21. data/bin/sandbox +1 -6
  22. data/config/locales/en.yml +16 -0
  23. data/config/routes.rb +9 -3
  24. data/db/migrate/20210905145955_add_paused_to_subscriptions.rb +5 -0
  25. data/lib/decorators/api/controllers/solidus_subscriptions/spree/api/line_items_controller/create_subscription_line_items.rb +14 -0
  26. data/lib/generators/solidus_subscriptions/install/install_generator.rb +16 -0
  27. data/lib/generators/solidus_subscriptions/install/templates/app/controllers/concerns/create_subscription.rb +17 -0
  28. data/lib/generators/solidus_subscriptions/install/templates/app/views/cart_line_items/_subscription_fields.html.erb +30 -0
  29. data/lib/generators/solidus_subscriptions/install/templates/initializer.rb +3 -1
  30. data/lib/solidus_subscriptions/configuration.rb +29 -6
  31. data/lib/solidus_subscriptions/dispatcher/payment_failed_dispatcher.rb +2 -2
  32. data/lib/solidus_subscriptions/dispatcher/success_dispatcher.rb +2 -2
  33. data/lib/solidus_subscriptions/engine.rb +28 -0
  34. data/lib/solidus_subscriptions/permission_sets/default_customer.rb +1 -1
  35. data/lib/solidus_subscriptions/processing_error_handlers/rails_logger.rb +25 -0
  36. data/lib/solidus_subscriptions/subscription_generator.rb +6 -0
  37. data/lib/solidus_subscriptions/version.rb +1 -1
  38. data/solidus_subscriptions.gemspec +6 -6
  39. data/spec/controllers/spree/api/line_items_controller_spec.rb +63 -28
  40. data/spec/controllers/spree/api/users_controller_spec.rb +3 -0
  41. data/spec/decorators/controllers/solidus_subscriptions/spree/orders_controller/create_subscription_line_items_spec.rb +4 -4
  42. data/spec/decorators/models/solidus_subscriptions/spree/variant/auto_delete_from_subscriptions_spec.rb +2 -2
  43. data/spec/features/admin/subscriptions_spec.rb +13 -3
  44. data/spec/jobs/solidus_subscriptions/create_subscription_job_spec.rb +20 -0
  45. data/spec/jobs/solidus_subscriptions/process_installment_job_spec.rb +17 -0
  46. data/spec/lib/solidus_subscriptions/dispatcher/payment_failed_dispatcher_spec.rb +3 -3
  47. data/spec/lib/solidus_subscriptions/dispatcher/success_dispatcher_spec.rb +3 -3
  48. data/spec/lib/solidus_subscriptions/subscription_generator_spec.rb +1 -1
  49. data/spec/models/solidus_subscriptions/installment_spec.rb +1 -1
  50. data/spec/models/solidus_subscriptions/subscription_spec.rb +893 -16
  51. data/spec/models/spree/wallet_payment_source_spec.rb +3 -3
  52. data/spec/requests/api/v1/subscriptions_spec.rb +229 -0
  53. data/spec/subscribers/solidus_subscriptions/churn_buster_subscriber_spec.rb +8 -8
  54. data/spec/subscribers/solidus_subscriptions/order_subscriber_spec.rb +13 -0
  55. metadata +24 -17
  56. data/app/decorators/models/solidus_subscriptions/spree/order/finalize_creates_subscriptions.rb +0 -25
  57. data/config/initializers/subscribers.rb +0 -9
  58. data/spec/decorators/models/solidus_subscriptions/spree/order/finalize_creates_subscriptions_spec.rb +0 -32
  59. /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('Spree::Event', class_spy(Spree::Event))
31
+ stub_const('SolidusSupport::LegacyEventCompat::Bus', class_spy(SolidusSupport::LegacyEventCompat::Bus))
32
32
 
33
33
  subscription = create(:subscription)
34
34
 
35
- expect(Spree::Event).to have_received(:fire).with(
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('Spree::Event', class_spy(Spree::Event))
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(Spree::Event).to have_received(:fire).with(
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('Spree::Event', class_spy(Spree::Event))
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(Spree::Event).to have_received(:fire).with(
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('Spree::Event', class_spy(Spree::Event))
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(Spree::Event).to have_received(:fire).with(
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('Spree::Event', class_spy(Spree::Event))
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(Spree::Event).to have_received(:fire).with(
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