fix two checks that only broke in the case they exist for

بازبینیِ کارِ امروز، سه مورد پیدا کرد.

/api/health وقتی مهاجرتِ اعمال‌نشده وجود داشت می‌شکست: کدِ فهرست‌کردنِ
مهاجرت‌ها به migration.key تکیه می‌کرد که اصلاً وجود ندارد (فیلدها app_label
و name هستند). یعنی دقیقاً در همان حالتی که این بررسی برایش ساخته شده،
به‌جای نامِ مهاجرت‌ها فقط AttributeError برمی‌گرداند. حالا تستی دارد که
مهاجرتِ معلق را شبیه‌سازی می‌کند — چیزی که نبودش باعث شد اولین بار غلط بماند.

وارد کردنِ سوال با replace عددِ غلط گزارش می‌کرد: delete() در جنگو مجموعِ
همه‌ی ردیف‌های آبشاری را می‌دهد، پس «۵ سوال پاک شد» در واقع گزینه‌ها و
پاسخ‌ها را هم می‌شمرد.

deploy.sh بررسیِ متغیرهای محیطی‌اش با تنظیماتِ جدید ناسازگار شده بود:
متغیرهایی را اجباری می‌دانست که حالا پیش‌فرض دارند، و فقط شل را می‌خواند نه
.env — یعنی روی سرورِ درست هم دیپلوی را رد می‌کرد. حالا به‌جای تکرارِ منطقِ
تنظیمات، از خودِ جنگو می‌پرسد چه چیزی مؤثر است و گزارش می‌دهد.

کوچک‌تر: بررسیِ سلامت یک درخواست می‌زند نه دو تا، و شکستِ ری‌استارت دیگر
اسکریپت را بی‌صدا تمام نمی‌کند.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-13 21:42:36 +03:30
parent e5d2427560
commit 3dacf3a50f
4 changed files with 101 additions and 37 deletions

View File

@@ -180,3 +180,52 @@ class HealthWarningTests(TestCase):
def test_allowed_origins_include_the_deployed_frontend(self):
body = self.client.get('/api/health').json()
self.assertIn('https://math.tadrisino2.ir', body['allowedOrigins'])
class MigrationCheckTests(TestCase):
"""
این بررسی مهم‌ترین چیزی‌ست که /api/health می‌گوید، و تنها وقتی اهمیت دارد
که واقعاً مهاجرتِ اعمال‌نشده باشد — یعنی همان حالتی که تستش سخت است و
برای همین اولین بار غلط نوشته شد (Migration فیلدِ key ندارد).
"""
def test_pending_migrations_are_listed_by_name(self):
from unittest.mock import patch
from django.db.migrations import Migration
from config import health
fake = Migration('0009_something', 'content')
with patch.object(health.MigrationExecutor, 'migration_plan', return_value=[(fake, False)]):
result = health._check_migrations()
self.assertFalse(result['ok'])
self.assertEqual(result['pending'], ['content.0009_something'])
self.assertNotIn('error', result, 'نباید به استثنا بیفتد')
def test_a_clean_database_reports_nothing_pending(self):
from config import health
result = health._check_migrations()
self.assertTrue(result['ok'])
self.assertEqual(result['pending'], [])
class ReplaceCountTests(TestCase):
"""عددی که به کاربر گزارش می‌شود باید تعدادِ سوال باشد، نه مجموعِ ردیف‌های آبشاری."""
def test_replace_reports_the_number_of_questions_not_of_rows(self):
grade = Grade.objects.create(title='پایه نهم', icon='', color='')
unit = Unit.objects.create(grade=grade, title='فصل ۱', order=1)
node = Node.objects.create(unit=unit, title='درس ۱', order=1)
lesson = Lesson.objects.create(node=node, title='نوع ۱')
payload = json.dumps([{
'lessonId': lesson.pk, 'prompt': 'سوال یک', 'type': 'multiple_choice',
'choices': ['۱', '۲', '۳', '۴'], 'correct': 0,
}], ensure_ascii=False).encode()
import_questions(parse(payload)) # ۱ سوال + ۴ گزینه = ۵ ردیف
result = import_questions(parse(payload), replace=True)
self.assertEqual(result['removed'], 1, 'باید ۱ باشد نه ۵')