import importlib from types import SimpleNamespace from django.apps import apps from django.db import connection from django.test import TestCase from accounts.models import User from ai_tutor.models import Conversation from campaigns.models import MessageCampaign from content.models import Grade, Unit, grade_number_from_title migration = importlib.import_module('content.migrations.0008_grade_id_equals_number') class GradeNumberTests(TestCase): def test_number_is_read_from_title(self): self.assertEqual(grade_number_from_title('پایه هشتم'), 8) self.assertEqual(grade_number_from_title('پایه دوازدهم'), 12) self.assertEqual(grade_number_from_title('پایهٔ یازدهم'), 11) self.assertIsNone(grade_number_from_title('[SEED] پایه سوم')) self.assertIsNone(grade_number_from_title('ریاضی')) def test_new_grade_takes_its_number_as_id(self): # بدونِ این، «پایه چهارم»ی که بعداً اضافه شود id=13 می‌گرفت. grade = Grade.objects.create(title='پایه چهارم', icon='g', color='#000') self.assertEqual((grade.number, grade.pk), (4, 4)) def test_unnumbered_grade_stays_out_of_one_to_twelve(self): grade = Grade.objects.create(title='[DEMO] نمونه', icon='g', color='#000') self.assertIsNone(grade.number) self.assertGreater(grade.pk, 12) # و بعدش هنوز جا برای پایه‌ی واقعی هست self.assertEqual(Grade.objects.create(title='پایه نهم', icon='g', color='#000').pk, 9) def test_duplicate_title_does_not_break_save(self): Grade.objects.create(title='پایه دهم', icon='g', color='#000') second = Grade.objects.create(title='پایه دهم', icon='g', color='#000') self.assertIsNone(second.number) def test_grades_api_is_ordered_by_number(self): for title in ('پایه نهم', 'پایه سوم', 'پایه دوازدهم', 'پایه هشتم'): Grade.objects.create(title=title, icon='g', color='#000') rows = self.client.get('/api/grades').json()['grades'] self.assertEqual([r['number'] for r in rows], [3, 8, 9, 12]) self.assertEqual([r['id'] for r in rows], [3, 8, 9, 12]) class GradeRenumberMigrationTests(TestCase): """همان وضعیتِ سرور پیش از migration: idها به ترتیبِ واردشدنِ کتاب‌ها.""" def _grade(self, pk, title): # save() را دور می‌زنیم تا idِ قدیمی و بی‌شماره بسازیم، مثلِ دیتای واقعی Grade.objects.bulk_create([Grade(pk=pk, title=title, icon='g', color='#000')]) return pk def test_ids_become_numbers_and_every_reference_follows(self): old = { # id قدیمی → عنوان (ترتیبِ سرورِ واقعی) 1: 'پایه نهم', 2: 'پایه ششم', 5: 'پایه هشتم', 8: 'پایه دوازدهم', 9: '[DEMO] نمونه', } for pk, title in old.items(): self._grade(pk, title) Unit.objects.create(grade_id=pk, title=f'فصلِ {title}') student = User.objects.create(grade_id=5) # هشتم conversation = Conversation.objects.create(user_id=student.id, grade_id=8) # دوازدهم campaign = MessageCampaign.objects.create( title='t', body='b', channel='sms', target_filter={'type': 'grade', 'grade_id': 1}) # نهم migration.forwards(apps, SimpleNamespace(connection=connection)) by_title = {g.title: g for g in Grade.objects.all()} self.assertEqual({t: (g.pk, g.number) for t, g in by_title.items() if g.number}, { 'پایه نهم': (9, 9), 'پایه ششم': (6, 6), 'پایه هشتم': (8, 8), 'پایه دوازدهم': (12, 12), }) self.assertGreater(by_title['[DEMO] نمونه'].pk, 12) for grade in by_title.values(): self.assertEqual(list(grade.units.values_list('title', flat=True)), [f'فصلِ {grade.title}']) student.refresh_from_db() conversation.refresh_from_db() campaign.refresh_from_db() self.assertEqual((student.grade_id, conversation.grade_id), (8, 12)) self.assertEqual(campaign.target_filter, {'type': 'grade', 'grade_id': 9})