Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions backend/tests/user/test_user_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
from django.utils import timezone
from gamedata.gamedata_cache_manager import GamedataCacheManager
from model_bakery import baker
from user.models import User
from user.tasks import user_handle_post_refresh
from user.models import User, VerificationCode, VerificationeCodeChoices
from user.models.verification_codes import EXPIRY_TIME
from user.tasks import user_handle_post_refresh, user_purge_verification_codes

pytestmark = [pytest.mark.django_db, pytest.mark.usefixtures('locmem_cache')]

Expand Down Expand Up @@ -85,3 +86,25 @@ def test_updates_last_login(self, django_capture_on_commit_callbacks: CaptureOnC

def test_missing_user_is_ignored(self) -> None:
user_handle_post_refresh(999_999)


def _code(user: User, purpose: VerificationeCodeChoices, age: timedelta, is_used: bool = False) -> VerificationCode:
code: VerificationCode = baker.make('user.VerificationCode', user=user, purpose=purpose, is_used=is_used)
# created_at is auto_now_add, so backdate after creation
VerificationCode.objects.filter(pk=code.pk).update(created_at=timezone.now() - age)
return code


class TestUserPurgeVerificationCodes:
@pytest.mark.parametrize('purpose', list(VerificationeCodeChoices))
def test_deletes_used_and_expired_keeps_active(self, purpose: VerificationeCodeChoices) -> None:
user: User = baker.make('user.User')
_code(user, purpose, timedelta(minutes=1), is_used=True)
_code(user, purpose, EXPIRY_TIME + timedelta(minutes=1))
active = _code(user, purpose, EXPIRY_TIME - timedelta(minutes=1))

user_purge_verification_codes()

remaining = set(VerificationCode.objects.values_list('pk', flat=True))
assert remaining == {active.pk}
assert User.objects.filter(pk=user.pk).exists()
13 changes: 12 additions & 1 deletion backend/user/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
from core.env import settings
from django.contrib.auth.models import update_last_login
from django.core.mail import EmailMultiAlternatives
from django.db.models import Q
from django.template.loader import render_to_string
from django.utils import timezone
from django.utils.html import strip_tags
from gamedata.services.fio_refresh import request_fio_refresh

from user.models import User
from user.models import User, VerificationCode
from user.models.verification_codes import EXPIRY_TIME

logger = structlog.get_logger(__name__)

Expand Down Expand Up @@ -81,3 +84,11 @@ def user_handle_post_refresh(user_id: int):
request_fio_refresh(user.id, 'token_refresh')
except User.DoesNotExist:
pass


@shared_task(name='user_purge_verification_codes', ignore_result=True)
def user_purge_verification_codes() -> None:
# same cutoff as VerificationCode.is_expired
cutoff = timezone.now() - EXPIRY_TIME
deleted, _ = VerificationCode.objects.filter(Q(is_used=True) | Q(created_at__lt=cutoff)).delete()
logger.info('verification_codes_purged', deleted=deleted)
Loading