write command test

This commit is contained in:
imperosol 2024-10-09 13:12:09 +02:00
parent 5a8052ae47
commit 465e0f31d9
4 changed files with 108 additions and 35 deletions

View File

@ -1,7 +1,8 @@
from datetime import timedelta from datetime import timedelta
from dateutil.relativedelta import relativedelta
from django.conf import settings from django.conf import settings
from django.utils.timezone import now from django.utils.timezone import localdate, now
from model_bakery import seq from model_bakery import seq
from model_bakery.recipe import Recipe, related from model_bakery.recipe import Recipe, related
@ -11,13 +12,13 @@ from subscription.models import Subscription
active_subscription = Recipe( active_subscription = Recipe(
Subscription, Subscription,
subscription_start=now() - timedelta(days=30), subscription_start=localdate() - timedelta(days=30),
subscription_end=now() + timedelta(days=30), subscription_end=localdate() + timedelta(days=30),
) )
ended_subscription = Recipe( ended_subscription = Recipe(
Subscription, Subscription,
subscription_start=now() - timedelta(days=60), subscription_start=localdate() - timedelta(days=60),
subscription_end=now() - timedelta(days=30), subscription_end=localdate() - timedelta(days=30),
) )
subscriber_user = Recipe( subscriber_user = Recipe(
@ -36,6 +37,17 @@ old_subscriber_user = Recipe(
) )
"""A user with an ended subscription.""" """A user with an ended subscription."""
__inactivity = localdate() - settings.SITH_ACCOUNT_INACTIVITY_DELTA
very_old_subscriber_user = old_subscriber_user.extend(
subscriptions=related(
ended_subscription.extend(
subscription_start=__inactivity - relativedelta(months=6, days=1),
subscription_end=__inactivity - relativedelta(days=1),
)
)
)
"""A user which subscription ended enough time ago to be considered as inactive."""
ae_board_membership = Recipe( ae_board_membership = Recipe(
Membership, Membership,
start_date=now() - timedelta(days=30), start_date=now() - timedelta(days=30),

View File

@ -7,12 +7,15 @@ from django.test import Client, TestCase
from django.urls import reverse from django.urls import reverse
from django.utils.timezone import now from django.utils.timezone import now
from model_bakery import baker, seq from model_bakery import baker, seq
from model_bakery.recipe import Recipe, related from model_bakery.recipe import Recipe
from core.baker_recipes import old_subscriber_user, subscriber_user from core.baker_recipes import (
old_subscriber_user,
subscriber_user,
very_old_subscriber_user,
)
from core.models import User from core.models import User
from counter.models import Counter, Refilling, Selling from counter.models import Counter, Refilling, Selling
from subscription.models import Subscription
class TestSearchUsers(TestCase): class TestSearchUsers(TestCase):
@ -121,9 +124,6 @@ class TestFilterInactive(TestCase):
def setUpTestData(cls): def setUpTestData(cls):
time_active = now() - settings.SITH_ACCOUNT_INACTIVITY_DELTA + timedelta(days=1) time_active = now() - settings.SITH_ACCOUNT_INACTIVITY_DELTA + timedelta(days=1)
time_inactive = time_active - timedelta(days=3) time_inactive = time_active - timedelta(days=3)
very_old_subscriber = old_subscriber_user.extend(
subscriptions=related(Recipe(Subscription, subscription_end=time_inactive))
)
counter, seller = baker.make(Counter), baker.make(User) counter, seller = baker.make(Counter), baker.make(User)
sale_recipe = Recipe( sale_recipe = Recipe(
Selling, Selling,
@ -137,7 +137,7 @@ class TestFilterInactive(TestCase):
baker.make(User), baker.make(User),
subscriber_user.make(), subscriber_user.make(),
old_subscriber_user.make(), old_subscriber_user.make(),
*very_old_subscriber.make(_quantity=3), *very_old_subscriber_user.make(_quantity=3),
] ]
sale_recipe.make(customer=cls.users[3].customer, date=time_active) sale_recipe.make(customer=cls.users[3].customer, date=time_active)
baker.make( baker.make(

View File

@ -4,7 +4,7 @@ from smtplib import SMTPException
from django.conf import settings from django.conf import settings
from django.core.mail import send_mail from django.core.mail import send_mail
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.db.models import Exists, OuterRef, Subquery from django.db.models import Exists, OuterRef, QuerySet, Subquery
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.timezone import localdate, now from django.utils.timezone import localdate, now
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
@ -26,23 +26,7 @@ class Command(BaseCommand):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def handle(self, *args, **options): def handle(self, *args, **options):
ongoing_dump_operation = AccountDump.objects.ongoing().filter( users = list(self._get_users())
customer__user=OuterRef("pk")
)
users = list(
User.objects
.filter_inactive()
.filter(customer__amount__gt=0)
.exclude(Exists(ongoing_dump_operation))
.annotate(
last_subscription_date=Subquery(
Subscription.objects.filter(member=OuterRef("pk"))
.order_by("-subscription_end")
.values("subscription_end")[:1]
),
)
.select_related("customer")
)
self.stdout.write(f"{len(users)} users will be warned of their account dump") self.stdout.write(f"{len(users)} users will be warned of their account dump")
dumps = [] dumps = []
for user in users: for user in users:
@ -57,6 +41,25 @@ class Command(BaseCommand):
AccountDump.objects.bulk_create(dumps) AccountDump.objects.bulk_create(dumps)
self.stdout.write("Finished !") self.stdout.write("Finished !")
@staticmethod
def _get_users() -> QuerySet[User]:
ongoing_dump_operation = AccountDump.objects.ongoing().filter(
customer__user=OuterRef("pk")
)
return (
User.objects.filter_inactive()
.filter(customer__amount__gt=0)
.exclude(Exists(ongoing_dump_operation))
.annotate(
last_subscription_date=Subquery(
Subscription.objects.filter(member=OuterRef("pk"))
.order_by("-subscription_end")
.values("subscription_end")[:1]
),
)
.select_related("customer")
)
def _send_mail(self, user: User) -> bool: def _send_mail(self, user: User) -> bool:
"""Send the warning email to the given user. """Send the warning email to the given user.

View File

@ -1,7 +1,65 @@
import pytest from datetime import timedelta
from django.conf import settings
from django.core import mail
from django.core.management import call_command
from django.test import TestCase
from django.utils.timezone import now
from model_bakery import baker
from model_bakery.recipe import Recipe
from core.baker_recipes import subscriber_user, very_old_subscriber_user
from counter.management.commands.dump_warning_mail import Command
from counter.models import AccountDump, Customer, Refilling
@pytest.mark.django_db class TestAccountDumpWarningMailCommand(TestCase):
def test_account_dump(): @classmethod
# TODO write the fucking test def setUpTestData(cls):
pass # delete existing customers to avoid side effect
Customer.objects.all().delete()
refill_recipe = Recipe(Refilling, amount=10)
cls.notified_users = very_old_subscriber_user.make(_quantity=3)
inactive_date = (
now() - settings.SITH_ACCOUNT_INACTIVITY_DELTA - timedelta(days=1)
)
refill_recipe.make(
customer=(u.customer for u in cls.notified_users),
date=inactive_date,
_quantity=len(cls.notified_users),
)
cls.not_notified_users = [
subscriber_user.make(),
very_old_subscriber_user.make(), # inactive, but account already empty
very_old_subscriber_user.make(), # inactive, but with a recent transaction
very_old_subscriber_user.make(), # inactive, but already warned
]
refill_recipe.make(
customer=cls.not_notified_users[2].customer, date=now() - timedelta(days=1)
)
refill_recipe.make(
customer=cls.not_notified_users[3].customer, date=inactive_date
)
baker.make(
AccountDump,
customer=cls.not_notified_users[3].customer,
dump_operation=None,
)
def test_user_selection(self):
"""Test that the user to warn are well selected."""
users = list(Command._get_users())
assert len(users) == 3
assert set(users) == set(self.notified_users)
def test_command(self):
"""The actual command test."""
call_command("dump_warning_mail")
# 1 already existing + 3 new account dump objects
assert AccountDump.objects.count() == 4
sent_mails = list(mail.outbox)
assert len(sent_mails) == 3
target_emails = {u.email for u in self.notified_users}
for sent in sent_mails:
assert len(sent.to) == 1
assert sent.to[0] in target_emails