Skip to content

Commit

Permalink
fix tests, prevent do a charge twice
Browse files Browse the repository at this point in the history
  • Loading branch information
jefer94 committed May 31, 2024
1 parent 1215056 commit eab6d82
Show file tree
Hide file tree
Showing 8 changed files with 305 additions and 249 deletions.
11 changes: 6 additions & 5 deletions breathecode/payments/management/commands/make_charges.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,21 @@ def handle(self, *args, **options):
avoid_expire_these_statuses = Q(status='EXPIRED') | Q(status='ERROR') | Q(status='PAYMENT_ISSUE') | Q(
status='FULLY_PAID') | Q(status='FREE_TRIAL') | Q(status='CANCELLED') | Q(status='DEPRECATED')

args = (Q(valid_until__isnull=True) | Q(valid_until__gt=utc_now), )
subscription_args = (Q(valid_until__isnull=True) | Q(valid_until__gt=utc_now), )
financing_args = (Q(plan_expires_at__isnull=True) | Q(plan_expires_at__gt=utc_now), )
params = {
'next_payment_at__lte': utc_now + timedelta(days=1),
}

subscriptions = Subscription.objects.filter(*args, **params)
plan_financings = PlanFinancing.objects.filter(*args, **params)

Subscription.objects.filter(valid_until__lte=utc_now).exclude(avoid_expire_these_statuses).update(
status='EXPIRED')

PlanFinancing.objects.filter(valid_until__lte=utc_now).exclude(avoid_expire_these_statuses).update(
PlanFinancing.objects.filter(plan_expires_at__lte=utc_now).exclude(avoid_expire_these_statuses).update(
status='EXPIRED')

subscriptions = Subscription.objects.filter(*subscription_args, **params)
plan_financings = PlanFinancing.objects.filter(*financing_args, **params)

for status in statuses:
subscriptions = subscriptions.exclude(status=status)

Expand Down
313 changes: 176 additions & 137 deletions breathecode/payments/tasks.py

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,10 @@ def test_with_two_plan_financings__wrong_cases(bc: Breathecode, delta, status, u
valid_until = utc_now + delta
plan_financing = {
'next_payment_at': valid_until,
'valid_until': UTC_NOW + relativedelta(months=random.randint(12, 24)),
'valid_until': UTC_NOW + relativedelta(months=random.randint(1, 12)),
'status': status,
'monthly_price': (random.random() * 99) + 1,
'plan_expires_at': UTC_NOW + relativedelta(months=random.randint(1, 12)),
'plan_expires_at': UTC_NOW + relativedelta(months=random.randint(12, 24)),
}

model = bc.database.create(plan_financing=(2, plan_financing))
Expand All @@ -145,10 +145,10 @@ def test_with_two_plan_financings__expired(bc: Breathecode, delta, status, statu
valid_until = utc_now + delta
plan_financing = {
'next_payment_at': valid_until,
'valid_until': valid_until,
'valid_until': UTC_NOW + relativedelta(months=random.randint(1, 12)),
'status': status,
'monthly_price': (random.random() * 99) + 1,
'plan_expires_at': UTC_NOW + relativedelta(months=random.randint(1, 12)),
'plan_expires_at': valid_until,
}

model = bc.database.create(plan_financing=(2, plan_financing))
Expand Down Expand Up @@ -177,10 +177,10 @@ def test_with_two_plan_financings__valid_cases(bc: Breathecode, delta, status, u
next_payment_at = utc_now - delta
plan_financing = {
'next_payment_at': next_payment_at,
'valid_until': valid_until,
'valid_until': UTC_NOW + relativedelta(months=random.randint(1, 12)),
'status': status,
'monthly_price': (random.random() * 99) + 1,
'plan_expires_at': UTC_NOW + relativedelta(months=random.randint(1, 12)),
'plan_expires_at': valid_until,
}

model = bc.database.create(plan_financing=(2, plan_financing))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ def test_subscription_was_created(self):
'monthly_price':
model.invoice.amount,
'valid_until':
model.invoice.paid_at + relativedelta(months=months),
model.invoice.paid_at + relativedelta(months=months - 1),
'next_payment_at':
model.invoice.paid_at + relativedelta(months=1),
'plan_expires_at':
Expand Down Expand Up @@ -292,7 +292,7 @@ def test_subscription_was_created__bag_with_cohort(self):
'selected_cohort_set_id':
1,
'valid_until':
model.invoice.paid_at + relativedelta(months=months),
model.invoice.paid_at + relativedelta(months=months - 1),
'next_payment_at':
model.invoice.paid_at + relativedelta(months=1),
'plan_expires_at':
Expand Down Expand Up @@ -364,7 +364,7 @@ def test_subscription_was_created__bag_with_event_type_set(self):
'selected_event_type_set_id':
1,
'valid_until':
model.invoice.paid_at + relativedelta(months=months),
model.invoice.paid_at + relativedelta(months=months - 1),
'next_payment_at':
model.invoice.paid_at + relativedelta(months=1),
'plan_expires_at':
Expand Down Expand Up @@ -436,7 +436,7 @@ def test_subscription_was_created__bag_with_mentorship_service_set(self):
'selected_mentorship_service_set_id':
1,
'valid_until':
model.invoice.paid_at + relativedelta(months=months),
model.invoice.paid_at + relativedelta(months=months - 1),
'next_payment_at':
model.invoice.paid_at + relativedelta(months=1),
'plan_expires_at':
Expand Down
102 changes: 94 additions & 8 deletions breathecode/payments/tests/tasks/tests_charge_plan_financing.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,20 +184,92 @@ def test_plan_financing_without_invoices(self):
🔽🔽🔽 PlanFinancing process to charge
"""

@patch('logging.Logger.info', MagicMock())
@patch('logging.Logger.error', MagicMock())
@patch('breathecode.notify.actions.send_email_message', MagicMock())
@patch('breathecode.payments.tasks.renew_plan_financing_consumables.delay', MagicMock())
@patch('mixer.main.LOGGER.info', MagicMock())
@patch('django.utils.timezone.now', MagicMock(return_value=UTC_NOW))
def test_plan_financing_was_paid_this_month(self):
delta = relativedelta(months=random.randint(1, 12))
plan_financing = {
'valid_until': UTC_NOW + delta,
'next_payment_at': UTC_NOW + delta,
'monthly_price': (random.random() * 99) + 1,
'plan_expires_at': UTC_NOW + relativedelta(months=random.randint(1, 12)),
}
plan = {'is_renewable': False}
invoice = {'paid_at': UTC_NOW - relativedelta(hours=24, seconds=1)}
bag = {'how_many_installments': 3}
with patch('django.utils.timezone.now', MagicMock(return_value=UTC_NOW - relativedelta(months=2))):
model = self.bc.database.create(academy=1,
plan_financing=plan_financing,
invoice=invoice,
plan=plan,
bag=bag)

with patch('breathecode.payments.services.stripe.Stripe.pay',
MagicMock(side_effect=fake_stripe_pay(paid_at=UTC_NOW, academy=model.academy))):
# remove prints from mixer
logging.Logger.info.call_args_list = []
logging.Logger.error.call_args_list = []

charge_plan_financing.delay(1)

self.assertEqual(self.bc.database.list_of('admissions.Cohort'), [])

self.assertEqual(logging.Logger.info.call_args_list, [
call('Starting charge_plan_financing for id 1'),
])
self.assertEqual(logging.Logger.error.call_args_list, [
call('PlanFinancing with id 1 was paid this month', exc_info=True),
])

self.assertEqual(self.bc.database.list_of('payments.Bag'), [
self.bc.format.to_dict(model.bag),
])
self.assertEqual(self.bc.database.list_of('payments.Invoice'), [
self.bc.format.to_dict(model.invoice),
])

self.assertEqual(self.bc.database.list_of('payments.PlanFinancing'), [
{
**self.bc.format.to_dict(model.plan_financing),
'status': 'ACTIVE',
},
])
self.assertEqual(notify_actions.send_email_message.call_args_list, [])
self.bc.check.calls(activity_tasks.add_activity.delay.call_args_list, [
call(1, 'bag_created', related_type='payments.Bag', related_id=1),
])

"""
🔽🔽🔽 PlanFinancing process to charge
"""

@patch('logging.Logger.info', MagicMock())
@patch('logging.Logger.error', MagicMock())
@patch('breathecode.notify.actions.send_email_message', MagicMock())
@patch('breathecode.payments.tasks.renew_plan_financing_consumables.delay', MagicMock())
@patch('mixer.main.LOGGER.info', MagicMock())
@patch('django.utils.timezone.now', MagicMock(return_value=UTC_NOW))
def test_plan_financing_process_to_charge(self):
delta = relativedelta(months=random.randint(1, 12))
plan_financing = {
'valid_until': UTC_NOW + relativedelta(minutes=1),
'valid_until': UTC_NOW + delta,
'next_payment_at': UTC_NOW - delta,
'monthly_price': (random.random() * 99) + 1,
'plan_expires_at': UTC_NOW + relativedelta(months=random.randint(1, 12)),
}
plan = {'is_renewable': False}
model = self.bc.database.create(academy=1, plan_financing=plan_financing, invoice=1, plan=plan)
invoice = {'paid_at': UTC_NOW - relativedelta(hours=24, seconds=1)}
bag = {'how_many_installments': 3}
with patch('django.utils.timezone.now', MagicMock(return_value=UTC_NOW - relativedelta(months=2))):
model = self.bc.database.create(academy=1,
plan_financing=plan_financing,
invoice=invoice,
plan=plan,
bag=bag)

with patch('breathecode.payments.services.stripe.Stripe.pay',
MagicMock(side_effect=fake_stripe_pay(paid_at=UTC_NOW, academy=model.academy))):
Expand Down Expand Up @@ -239,7 +311,7 @@ def test_plan_financing_process_to_charge(self):
{
**self.bc.format.to_dict(model.plan_financing),
'status': 'ACTIVE',
'next_payment_at': UTC_NOW + relativedelta(months=1),
'next_payment_at': model.plan_financing.next_payment_at + (delta + relativedelta(months=1)),
},
])
self.assertEqual(notify_actions.send_email_message.call_args_list, [
Expand Down Expand Up @@ -274,7 +346,9 @@ def test_plan_financing_error_when_try_to_charge(self):
'plan_expires_at': UTC_NOW + relativedelta(months=random.randint(1, 12)),
}
plan = {'is_renewable': False}
model = self.bc.database.create(plan_financing=plan_financing, invoice=1, plan=plan)
bag = {'how_many_installments': 3}
with patch('django.utils.timezone.now', MagicMock(return_value=UTC_NOW - relativedelta(months=2))):
model = self.bc.database.create(plan_financing=plan_financing, invoice=1, plan=plan, bag=bag)

with patch('breathecode.payments.services.stripe.Stripe.pay', MagicMock(side_effect=Exception('fake error'))):
# remove prints from mixer
Expand Down Expand Up @@ -331,9 +405,9 @@ def test_plan_financing_error_when_try_to_charge(self):
@patch('django.utils.timezone.now', MagicMock(return_value=UTC_NOW))
def test_plan_financing_is_over(self):
plan_financing = {
'valid_until': UTC_NOW - relativedelta(minutes=1),
'valid_until': UTC_NOW + relativedelta(months=random.randint(1, 12)),
'monthly_price': (random.random() * 99) + 1,
'plan_expires_at': UTC_NOW + relativedelta(months=random.randint(1, 12)),
'plan_expires_at': UTC_NOW - relativedelta(minutes=1),
}
plan = {'is_renewable': False}
model = self.bc.database.create(plan_financing=plan_financing, invoice=1, plan=plan)
Expand Down Expand Up @@ -443,7 +517,13 @@ def test_plan_financing_process_to_charge__but_a_undexpected_exception_is_raised
}
invoice = {'paid_at': UTC_NOW - relativedelta(hours=24, seconds=1)}
plan = {'is_renewable': False}
model = self.bc.database.create(academy=1, plan_financing=plan_financing, invoice=invoice, plan=plan)
bag = {'how_many_installments': 3}
with patch('django.utils.timezone.now', MagicMock(return_value=UTC_NOW - relativedelta(months=2))):
model = self.bc.database.create(academy=1,
plan_financing=plan_financing,
invoice=invoice,
plan=plan,
bag=bag)

error = self.bc.fake.text()

Expand Down Expand Up @@ -501,7 +581,13 @@ def test_plan_financing_process_to_charge__but_a_undexpected_exception_is_raised
}
invoice = {'paid_at': UTC_NOW - relativedelta(hours=random.randint(1, 23))}
plan = {'is_renewable': False}
model = self.bc.database.create(academy=1, plan_financing=plan_financing, invoice=invoice, plan=plan)
bag = {'how_many_installments': 3}
with patch('django.utils.timezone.now', MagicMock(return_value=UTC_NOW - relativedelta(months=2))):
model = self.bc.database.create(academy=1,
plan_financing=plan_financing,
invoice=invoice,
plan=plan,
bag=bag)

error = self.bc.fake.text()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import logging
import os
import random
from datetime import timedelta
from unittest.mock import MagicMock, call, patch

import pytest
Expand Down Expand Up @@ -188,6 +189,7 @@ def test_subscription_process_to_charge(self):
subscription = {
'pay_every': unit,
'pay_every_unit': unit_type,
'next_payment_at': UTC_NOW - relativedelta(days=25, months=unit * 2),
}
model = self.bc.database.create(subscription=subscription, invoice=1)

Expand Down Expand Up @@ -226,13 +228,17 @@ def test_subscription_process_to_charge(self):
'paid_at': UTC_NOW,
}),
])
next_payment_at = model.subscription.next_payment_at
delta = calculate_relative_delta(unit, unit_type)
for _ in range(3):
next_payment_at += delta

self.assertEqual(self.bc.database.list_of('payments.Subscription'), [
{
**self.bc.format.to_dict(model.subscription),
'status': 'ACTIVE',
'paid_at': UTC_NOW,
'next_payment_at': UTC_NOW + calculate_relative_delta(unit, unit_type),
'next_payment_at': next_payment_at,
},
])

Expand Down
57 changes: 10 additions & 47 deletions breathecode/payments/tests/tasks/tests_renew_consumables.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ def test_plan_financing_is_over(self):
def test_plan_financing_without_be_paid(self):
plan_financing = {
'monthly_price': random.random() * 99.99 + 0.01,
'plan_expires_at': UTC_NOW - relativedelta(seconds=1),
'valid_until': UTC_NOW + relativedelta(minutes=3),
'plan_expires_at': UTC_NOW + relativedelta(minutes=3),
'valid_until': UTC_NOW - relativedelta(seconds=1),
}
plan = {'is_renewable': False}

Expand Down Expand Up @@ -137,8 +137,8 @@ def test_plan_financing_without_be_paid(self):
def test_plan_financing_with_plan_service_item_without_a_resource_linked(self):
plan_financing = {
'monthly_price': random.random() * 99.99 + 0.01,
'plan_expires_at': UTC_NOW - relativedelta(seconds=1),
'valid_until': UTC_NOW + relativedelta(minutes=3),
'plan_expires_at': UTC_NOW + relativedelta(minutes=3),
'valid_until': UTC_NOW - relativedelta(seconds=1),
'next_payment_at': UTC_NOW + relativedelta(minutes=3),
}
plan = {'is_renewable': False}
Expand Down Expand Up @@ -173,8 +173,8 @@ def test_plan_financing_with_plan_service_item_without_a_resource_linked(self):
def test_plan_financing_with_plan_service_item_with_two_cohorts_linked(self):
plan_financing = {
'monthly_price': random.random() * 99.99 + 0.01,
'plan_expires_at': UTC_NOW - relativedelta(seconds=4),
'valid_until': UTC_NOW + relativedelta(minutes=5),
'plan_expires_at': UTC_NOW + relativedelta(minutes=5),
'valid_until': UTC_NOW - relativedelta(seconds=4),
'next_payment_at': UTC_NOW + relativedelta(minutes=3),
}
plan = {'is_renewable': False}
Expand Down Expand Up @@ -215,43 +215,6 @@ def test_plan_financing_with_plan_service_item_with_two_cohorts_linked(self):
}),
])

"""
🔽🔽🔽 ServiceStockScheduler with PlanFinancing with Plan which is over
"""

@patch('logging.Logger.info', MagicMock())
@patch('logging.Logger.error', MagicMock())
@patch('django.utils.timezone.now', MagicMock(return_value=UTC_NOW))
def test_plan_financing_with_plan_than_is_over(self):
plan_financing = {
'monthly_price': random.random() * 99.99 + 0.01,
'plan_expires_at': UTC_NOW - relativedelta(days=3),
'valid_until': UTC_NOW + relativedelta(days=4),
'next_payment_at': UTC_NOW + relativedelta(days=2),
}

plan = {'time_of_life': 1, 'time_of_life_unit': 'DAY', 'is_renewable': False}

model = self.bc.database.create(service_stock_scheduler=1,
plan_financing=plan_financing,
plan_service_item_handler=1,
cohort=2,
plan=plan)

logging.Logger.info.call_args_list = []
logging.Logger.error.call_args_list = []

with patch('django.utils.timezone.now', MagicMock(return_value=UTC_NOW + relativedelta(days=1, minutes=3))):
renew_consumables.delay(1)

self.assertEqual(logging.Logger.info.call_args_list, [
call('Starting renew_consumables for service stock scheduler 1'),
call('The services related to PlanFinancing 1 is over'),
])
self.assertEqual(logging.Logger.error.call_args_list, [])

self.assertEqual(self.bc.database.list_of('payments.Consumable'), [])

"""
🔽🔽🔽 ServiceStockScheduler with PlanFinancing with a PlanServiceItem linked to a resource
"""
Expand All @@ -262,8 +225,8 @@ def test_plan_financing_with_plan_than_is_over(self):
def test_plan_financing_with_plan_service_item_with_two_mentorship_services_linked(self):
plan_financing = {
'monthly_price': random.random() * 99.99 + 0.01,
'plan_expires_at': UTC_NOW - relativedelta(seconds=4),
'valid_until': UTC_NOW + relativedelta(minutes=5),
'plan_expires_at': UTC_NOW + relativedelta(minutes=5),
'valid_until': UTC_NOW - relativedelta(seconds=4),
'next_payment_at': UTC_NOW + relativedelta(minutes=3),
}

Expand Down Expand Up @@ -315,8 +278,8 @@ def test_plan_financing_with_plan_service_item_with_two_mentorship_services_link
def test_plan_financing_with_plan_service_item__do_not_needs_renew(self):
plan_financing = {
'monthly_price': random.random() * 99.99 + 0.01,
'plan_expires_at': UTC_NOW - relativedelta(seconds=4),
'valid_until': UTC_NOW + relativedelta(minutes=5),
'plan_expires_at': UTC_NOW + relativedelta(minutes=5),
'valid_until': UTC_NOW - relativedelta(seconds=4),
'next_payment_at': UTC_NOW + relativedelta(minutes=3),
}

Expand Down
Loading

0 comments on commit eab6d82

Please sign in to comment.