You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

176 lines
6.9 KiB

import datetime
from django.utils import timezone
from django.test import TestCase
from unittest.mock import patch
from django.core.files.uploadedfile import SimpleUploadedFile
from dj_language.models import Language
from apps.account.models import StudentUser, User, Notification
from apps.course.models.course import Course, CourseCategory
from apps.quiz.models import Quiz, QuizParticipant
from apps.account.tasks import check_low_quiz_results_task
class QuizNotificationFlowTests(TestCase):
def setUp(self):
# Create languages
self.lang_ru, _ = Language.objects.update_or_create(
code='ru',
defaults={'name': 'Russian', 'status': True, 'countries': []}
)
self.lang_en, _ = Language.objects.update_or_create(
code='en',
defaults={'name': 'English', 'status': True, 'countries': []}
)
# Create category
self.category = CourseCategory.objects.create(
name=[{"title": "Category", "language_code": "en"}],
slug=[{"title": "category", "language_code": "en"}]
)
# Create student user
self.student = StudentUser.objects.create(
email='student_quiz@example.com',
username='student_quiz',
fullname='Quiz Student',
language=self.lang_ru,
fcm='token_quiz_student'
)
# Create Course
thumbnail = SimpleUploadedFile('thumb.jpg', b'filecontent', content_type='image/jpeg')
self.course = Course.objects.create(
title=[
{"title": "Course 1", "language_code": "en"},
{"title": "Курс 1", "language_code": "ru"}
],
slug=[{"title": "course-1", "language_code": "en"}],
category=self.category,
thumbnail=thumbnail,
video_type=Course.VedioTypeChoices.YOUTUBE_LINK,
video_link='https://example.com/video',
is_online=False,
level=Course.LevelChoices.BEGINNER,
duration=10,
lessons_count=0,
description='Description',
short_description='Short description',
status=[{"title": "ongoing", "language_code": "en"}],
is_free=True,
)
# Create Quiz
self.quiz = Quiz.objects.create(
course=self.course,
title=[
{"title": "Quiz A", "language_code": "en"},
{"title": "Тест А", "language_code": "ru"}
],
each_question_timing=60,
status=True
)
@patch('apps.account.notification_service.send_notification')
def test_low_quiz_result_triggers_on_intervals(self, mock_send):
# Event: Low Quiz Result (score < 70)
# Create failed attempt exactly 2 days ago
attempt_2_days = QuizParticipant.objects.create(
quiz=self.quiz,
user=self.student,
started_at=timezone.now() - datetime.timedelta(days=2, hours=1),
ended_at=timezone.now() - datetime.timedelta(days=2),
total_timing=300,
question_score=50,
timing_score=0,
total_score=50
)
# Run periodic task
check_low_quiz_results_task()
# Verify reminder sent to the student (Russian template check)
notif = Notification.objects.filter(user=self.student, title="Предложение пересдать тест").first()
self.assertIsNotNone(notif)
self.assertIn("Тест А", notif.message)
self.assertTrue(mock_send.called)
@patch('apps.account.notification_service.send_notification')
def test_no_reminder_if_passed_initially(self, mock_send):
# Create successful attempt exactly 2 days ago (score = 80)
QuizParticipant.objects.create(
quiz=self.quiz,
user=self.student,
started_at=timezone.now() - datetime.timedelta(days=2, hours=1),
ended_at=timezone.now() - datetime.timedelta(days=2),
total_timing=300,
question_score=80,
timing_score=0,
total_score=80
)
check_low_quiz_results_task()
self.assertFalse(Notification.objects.filter(user=self.student, title="Предложение пересдать тест").exists())
self.assertFalse(mock_send.called)
@patch('apps.account.notification_service.send_notification')
def test_no_reminder_if_retaken_and_passed(self, mock_send):
# Create failed attempt exactly 7 days ago
attempt_failed = QuizParticipant.objects.create(
quiz=self.quiz,
user=self.student,
started_at=timezone.now() - datetime.timedelta(days=7, hours=1),
ended_at=timezone.now() - datetime.timedelta(days=7),
total_timing=300,
question_score=40,
timing_score=0,
total_score=40
)
# Create a newer successful attempt 3 days ago
QuizParticipant.objects.create(
quiz=self.quiz,
user=self.student,
started_at=timezone.now() - datetime.timedelta(days=3, hours=1),
ended_at=timezone.now() - datetime.timedelta(days=3),
total_timing=300,
question_score=90,
timing_score=0,
total_score=90
)
check_low_quiz_results_task()
# Should not suggest retake since they passed subsequently
self.assertFalse(Notification.objects.filter(user=self.student, title="Предложение пересдать тест").exists())
@patch('apps.account.notification_service.send_notification')
def test_no_reminder_if_already_retaken_even_if_failed(self, mock_send):
# Create failed attempt exactly 21 days ago
attempt_failed = QuizParticipant.objects.create(
quiz=self.quiz,
user=self.student,
started_at=timezone.now() - datetime.timedelta(days=21, hours=1),
ended_at=timezone.now() - datetime.timedelta(days=21),
total_timing=300,
question_score=30,
timing_score=0,
total_score=30
)
# Create a newer attempt 10 days ago (also failed, e.g. score = 50)
# Since they already attempted a retake 10 days ago, we shouldn't trigger the 21-day reminder of the first failure.
QuizParticipant.objects.create(
quiz=self.quiz,
user=self.student,
started_at=timezone.now() - datetime.timedelta(days=10, hours=1),
ended_at=timezone.now() - datetime.timedelta(days=10),
total_timing=300,
question_score=50,
timing_score=0,
total_score=50
)
check_low_quiz_results_task()
self.assertFalse(Notification.objects.filter(user=self.student, title="Предложение пересдать тест").exists())