11import json
22from unittest .mock import MagicMock , patch
3+ from freezegun import freeze_time
34
45import requests
56from django .conf import settings
128129 }
129130]
130131
132+ class MockSubscriptionPlan (object ):
133+ def __init__ (self , params ):
134+ self .id = params ["new_plan" ]
135+ self .interval = 'year'
131136
132137class MockSubscription (object ):
133138 def __init__ (self , subscription_params ):
134139 self .schedule = subscription_params ["schedule_id" ]
135140 self .current_period_start = subscription_params ["start_date" ]
136141 self .current_period_end = subscription_params ["end_date" ]
142+ self .plan = MockSubscriptionPlan (subscription_params ["plan" ]) if subscription_params .get ("plan" ) is not None else None
137143 self .items = {
138144 "data" : [
139145 {
140146 "quantity" : subscription_params ["quantity" ],
141147 "id" : subscription_params ["id" ],
142- "plan" : {"id" : subscription_params ["name" ]},
148+ "plan" : {"id" : subscription_params ["name" ], "interval" : subscription_params . get ( "plan" , {}). get ( "interval" , "month" ) },
143149 }
144150 ]
145151 }
@@ -157,7 +163,6 @@ def __init__(self, subscription_params):
157163 def __getitem__ (self , key ):
158164 return getattr (self , key )
159165
160-
161166class StripeServiceTests (TestCase ):
162167 def setUp (self ):
163168 self .user = OwnerFactory ()
@@ -306,11 +311,13 @@ def test_delete_subscription_without_schedule_modifies_subscription_to_delete_at
306311 assert owner .plan_activated_users == [4 , 6 , 3 ]
307312 assert owner .plan_user_count == 9
308313
314+ @freeze_time ("2021-12-22T00:00:00" )
315+ @patch ("services.billing.stripe.Refund.create" )
309316 @patch ("services.billing.stripe.Subscription.modify" )
310317 @patch ("services.billing.stripe.Subscription.retrieve" )
311318 @patch ("services.billing.stripe.SubscriptionSchedule.release" )
312319 def test_delete_subscription_with_schedule_releases_schedule_and_cancels_subscription_at_end_of_billing_cycle_if_valid_plan (
313- self , schedule_release_mock , retrieve_subscription_mock , modify_mock
320+ self , schedule_release_mock , retrieve_subscription_mock , modify_mock , create_refund_mock
314321 ):
315322 plan = PlanName .CODECOV_PRO_YEARLY .value
316323 stripe_subscription_id = "sub_1K77Y5GlVGuVgOrkJrLjRnne"
@@ -338,6 +345,117 @@ def test_delete_subscription_with_schedule_releases_schedule_and_cancels_subscri
338345 cancel_at_period_end = True ,
339346 proration_behavior = "none" ,
340347 )
348+ create_refund_mock .assert_not_called ()
349+ owner .refresh_from_db ()
350+ assert owner .stripe_subscription_id == stripe_subscription_id
351+ assert owner .plan == plan
352+ assert owner .plan_activated_users == [4 , 6 , 3 ]
353+ assert owner .plan_user_count == 9
354+
355+ @freeze_time ("2021-12-17T00:00:00" )
356+ @patch ("services.billing.stripe.Subscription.modify" )
357+ @patch ("services.billing.stripe.Customer.modify" )
358+ @patch ("services.billing.stripe.Refund.create" )
359+ @patch ("services.billing.stripe.Invoice.list" )
360+ @patch ("services.billing.stripe.Subscription.cancel" )
361+ @patch ("services.billing.stripe.Subscription.retrieve" )
362+ @patch ("services.billing.stripe.SubscriptionSchedule.release" )
363+ def test_delete_subscription_with_schedule_releases_schedule_and_cancels_subscription_with_grace_month_refund_if_valid_plan (
364+ self , schedule_release_mock , retrieve_subscription_mock , cancel_sub_mock , list_invoice_mock , create_refund_mock , modify_customer_mock , modify_sub_mock
365+ ):
366+ with open ("./services/tests/samples/stripe_invoice.json" ) as f :
367+ stripe_invoice_response = json .load (f )
368+ stripe_invoice_response ["data" ] = stripe_invoice_response ["data" ] * 2
369+ list_invoice_mock .return_value = stripe_invoice_response
370+ plan = PlanName .CODECOV_PRO_YEARLY .value
371+ stripe_subscription_id = "sub_1K77Y5GlVGuVgOrkJrLjRnne"
372+ stripe_schedule_id = "sub_sched_sch1K77Y5GlVGuVgOrkJrLjRnne"
373+ charge = "ch_19yUQN2eZvKYlo2CQf7aWpSX"
374+ owner = OwnerFactory (
375+ stripe_subscription_id = stripe_subscription_id ,
376+ plan = plan ,
377+ plan_activated_users = [4 , 6 , 3 ],
378+ plan_user_count = 9 ,
379+ )
380+ subscription_params = {
381+ "schedule_id" : stripe_schedule_id ,
382+ "start_date" : 1639628096 ,
383+ "end_date" : 1644107871 ,
384+ "quantity" : 10 ,
385+ "name" : plan ,
386+ "id" : 215 ,
387+ "plan" : {
388+ "new_plan" : "plan_H6P3KZXwmAbqPS" ,
389+ "new_quantity" : 7 ,
390+ "subscription_id" : "sub_123" ,
391+ "interval" : "month" ,
392+ }
393+ }
394+
395+ retrieve_subscription_mock .return_value = MockSubscription (subscription_params )
396+ self .stripe .delete_subscription (owner )
397+ schedule_release_mock .assert_called_once_with (stripe_schedule_id )
398+ cancel_sub_mock .assert_called_once_with (stripe_subscription_id )
399+ list_invoice_mock .assert_called_once_with (subscription = stripe_subscription_id , status = "paid" )
400+ create_refund_mock .assert_called_once_with (charge = charge )
401+ modify_customer_mock .assert_called_once_with (owner .stripe_customer_id , balance = 0 )
402+ modify_sub_mock .assert_not_called ()
403+
404+ owner .refresh_from_db ()
405+ assert owner .stripe_subscription_id == stripe_subscription_id
406+ assert owner .plan == plan
407+ assert owner .plan_activated_users == [4 , 6 , 3 ]
408+ assert owner .plan_user_count == 9
409+
410+ @freeze_time ("2021-12-19T00:00:00" )
411+ @patch ("services.billing.stripe.Subscription.modify" )
412+ @patch ("services.billing.stripe.Customer.modify" )
413+ @patch ("services.billing.stripe.Refund.create" )
414+ @patch ("services.billing.stripe.Invoice.list" )
415+ @patch ("services.billing.stripe.Subscription.cancel" )
416+ @patch ("services.billing.stripe.Subscription.retrieve" )
417+ @patch ("services.billing.stripe.SubscriptionSchedule.release" )
418+ def test_delete_subscription_with_schedule_releases_schedule_and_cancels_subscription_with_grace_year_refund_if_valid_plan (
419+ self , schedule_release_mock , retrieve_subscription_mock , cancel_sub_mock , list_invoice_mock , create_refund_mock , modify_customer_mock , modify_sub_mock
420+ ):
421+ with open ("./services/tests/samples/stripe_invoice.json" ) as f :
422+ stripe_invoice_response = json .load (f )
423+ stripe_invoice_response ["data" ] = stripe_invoice_response ["data" ] * 2
424+ list_invoice_mock .return_value = stripe_invoice_response
425+ plan = PlanName .CODECOV_PRO_YEARLY .value
426+ stripe_subscription_id = "sub_1K77Y5GlVGuVgOrkJrLjRnne"
427+ stripe_schedule_id = "sub_sched_sch1K77Y5GlVGuVgOrkJrLjRnne"
428+ charge = "ch_19yUQN2eZvKYlo2CQf7aWpSX"
429+ owner = OwnerFactory (
430+ stripe_subscription_id = stripe_subscription_id ,
431+ plan = plan ,
432+ plan_activated_users = [4 , 6 , 3 ],
433+ plan_user_count = 9 ,
434+ )
435+ subscription_params = {
436+ "schedule_id" : stripe_schedule_id ,
437+ "start_date" : 1639628096 ,
438+ "end_date" : 1644107871 ,
439+ "quantity" : 10 ,
440+ "name" : plan ,
441+ "id" : 215 ,
442+ "plan" : {
443+ "new_plan" : "plan_H6P3KZXwmAbqPS" ,
444+ "new_quantity" : 7 ,
445+ "subscription_id" : "sub_123" ,
446+ "interval" : "year" ,
447+ }
448+ }
449+
450+ retrieve_subscription_mock .return_value = MockSubscription (subscription_params )
451+ self .stripe .delete_subscription (owner )
452+ schedule_release_mock .assert_called_once_with (stripe_schedule_id )
453+ cancel_sub_mock .assert_called_once_with (stripe_subscription_id )
454+ list_invoice_mock .assert_called_once_with (subscription = stripe_subscription_id , status = "paid" )
455+ create_refund_mock .assert_called_once_with (charge = charge )
456+ modify_customer_mock .assert_called_once_with (owner .stripe_customer_id , balance = 0 )
457+ modify_sub_mock .assert_not_called ()
458+
341459 owner .refresh_from_db ()
342460 assert owner .stripe_subscription_id == stripe_subscription_id
343461 assert owner .plan == plan
0 commit comments