Files
MathApp/content/tests_island_progress.py
Zareian 1c7c4b8eca API output without Persian diacritics or written ezafe ی (company decision); tutor answers too
plain_persian() strips kasra/fatha/damma/shadda/sukun/hamza-above and the written «ه‌ی» ezafe, keeping tanwin. Applied
at output, not in data: PlainPersianJSONRenderer runs one byte-level pass over every JSON response (~5 ms per MB
locally), and to_plain() cleans tutor answers including the stream. Rewriting titles in the DB/seed would have broken
question binding by title path and SeedRecord keys. Tests updated to expect the cleaned text; config/tests_plain_persian.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 16:57:17 +03:30

99 lines
5.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
جزیره‌ها روی نقشه باید با بازی کردن درس‌ها واقعاً تمام شوند (۲۰۲۶-۰۹-۲۵).
دو چیز با هم جلوی پیشرفت را گرفته بود:
۱. جزیره فقط وقتی تمام می‌شد که *همه* درس‌هایش تمام شده باشند، ولی ۵۸۵ درس
هنوز سوال ندارند و اپ برایشان دکمه شروع ندارد — ۱۸۱ جزیره از ۲۶۹.
۲. فرانت با کلیک روی جزیره همیشه lessonIds[0] را باز می‌کرد، یعنی درس اول
را بارها و بارها.
"""
from django.core.cache import cache
from django.test import TestCase
from rest_framework.test import APIClient
from accounts.authentication import issue_access_token
from accounts.models import User
from .models import Choice, Grade, Lesson, Node, Question, Unit
from .testing import record_answers
class IslandProgressTests(TestCase):
def setUp(self):
cache.clear()
grade = Grade.objects.create(title='پایه نهم', icon='g', color='#fff')
unit = Unit.objects.create(grade=grade, title='فصل ۱', order=1)
self.first = Node.objects.create(unit=unit, title='جزیره اول', order=1)
self.second = Node.objects.create(unit=unit, title='جزیره دوم', order=2)
self.a = self._lesson(self.first, 'الف', with_question=True)
self.empty = self._lesson(self.first, 'بی‌سوال', with_question=False)
self.b = self._lesson(self.first, 'ب', with_question=True)
self._lesson(self.second, 'ج', with_question=True)
self.user = User.objects.create(phone='09120000950', grade_id=grade.id)
self.client = APIClient()
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {issue_access_token(self.user.id)}')
def tearDown(self):
cache.clear()
def _lesson(self, node, title, *, with_question):
lesson = Lesson.objects.create(node=node, title=title, intro_text='x', mascot_animation='x')
if with_question:
self._question(lesson)
return lesson
def _question(self, lesson):
question = Question.objects.create(lesson=lesson, type='multiple_choice', prompt='؟')
Choice.objects.create(question=question, value='۱', is_correct=True)
Choice.objects.create(question=question, value='۲', is_correct=False)
def _nodes(self):
units = self.client.get('/api/home').json()['units']
return {n['title']: n for n in units[0]['nodes']}
def _complete(self, lesson):
record_answers(self.user.id, lesson=lesson) # مثلِ اپ: اول جواب، بعد پایان
response = self.client.post(f'/api/lessons/{lesson.id}/complete',
{'correctCount': 1, 'totalQuestions': 1}, format='json')
self.assertEqual(response.status_code, 200, response.content)
def test_tapping_the_island_opens_the_next_unfinished_lesson(self):
nodes = self._nodes()
self.assertEqual(nodes['جزیره اول']['nextLessonId'], self.a.id)
self.assertEqual(nodes['جزیره اول']['lessonIds'][0], self.a.id)
self._complete(self.a)
first = self._nodes()['جزیره اول']
# درسِ بی‌سوال رد می‌شود؛ lessonIds[0] همان است که فرانت باز می‌کند.
self.assertEqual(first['nextLessonId'], self.b.id)
self.assertEqual(first['lessonIds'][0], self.b.id)
self.assertCountEqual(first['lessonIds'], [self.a.id, self.empty.id, self.b.id])
def test_a_lesson_without_questions_does_not_hold_the_island(self):
self._complete(self.a)
self._complete(self.b)
nodes = self._nodes()
self.assertEqual(nodes['جزیره اول']['status'], 'completed')
self.assertEqual(nodes['جزیره دوم']['status'], 'unlocked')
# همه‌ی درس‌ها تمام شده‌اند: برای مرور، درسِ اول.
self.assertEqual(nodes['جزیره اول']['nextLessonId'], self.a.id)
def test_progress_counts_only_lessons_that_can_be_played(self):
self._complete(self.a)
percent = self.client.get('/api/home').json()['stats']['progressPercent']
self.assertAlmostEqual(percent, 33.33, places=2) # ۱ از ۳ درسِ سوال‌دار
def test_new_content_is_offered_but_progress_never_goes_backwards(self):
self._complete(self.a)
self._complete(self.b)
self.assertEqual(self._nodes()['جزیره اول']['status'], 'completed')
self._question(self.empty) # حالا این درس هم بازی‌شدنی است
nodes = self._nodes()
# نقشه‌ی کش‌شده باطل شد: درسِ تازه پیشنهاد می‌شود…
self.assertEqual(nodes['جزیره اول']['nextLessonId'], self.empty.id)
# …ولی جزیره‌ی تمام‌شده باز و جزیره‌ی بعدی قفل نمی‌شود.
self.assertEqual(nodes['جزیره اول']['status'], 'completed')
self.assertEqual(nodes['جزیره دوم']['status'], 'unlocked')