""" حفاظ‌های بازبینیِ امنیتیِ پیش از تحویل (محتوا، پنل و ورودی‌ها). هر تست رفتاری را نگه می‌دارد که پیش‌تر غلط بود. """ import io import json from django.contrib.auth.models import Group, User as AuthUser from django.core.cache import cache from django.core.management import call_command from django.test import TestCase from rest_framework.test import APIClient from accounts.authentication import issue_access_token from accounts.models import User from content.models import Choice, Grade, Lesson, Node, Question, QuestionReport, Unit, UserAnswer class SupervisorCannotEditContentTests(TestCase): """admin_view فقط is_staff را می‌سنجد؛ صفحه‌های ورودِ فایل اجازه‌ی مدل را جدا چک نمی‌کردند.""" def setUp(self): cache.clear() call_command('setup_supervisor_group', stdout=io.StringIO()) sup = AuthUser.objects.create_user('sup', 's@x.c', 'pw12345!', is_staff=True) sup.groups.add(Group.objects.get(name='Supervisor')) self.client.force_login(sup) grade = Grade.objects.create(title='G', icon='', color='', number=55) unit = Unit.objects.create(grade=grade, title='U', order=1) node = Node.objects.create(unit=unit, title='N', order=1) self.lesson = Lesson.objects.create(node=node, title='L', intro_text='x') self.q = Question.objects.create(lesson=self.lesson, type='multiple_choice', prompt='p?') ok = Choice.objects.create(question=self.q, value='1', is_correct=True) Choice.objects.create(question=self.q, value='2', is_correct=False) self.student = User.objects.create(phone='09120009999', is_guest=False) UserAnswer.objects.create(user_id=self.student.id, question=self.q, choice=ok, correct=True) def test_question_import_with_replace_is_refused(self): f = io.BytesIO(json.dumps([{ 'lessonId': self.lesson.id, 'prompt': 'HACKED', 'type': 'multiple_choice', 'choices': ['a', 'b'], 'correct': 0, }]).encode()) f.name = 'q.json' r = self.client.post('/admin/content/question/import/', {'file': f, 'replace': 'on'}) self.assertEqual(r.status_code, 403) self.assertEqual(list(Question.objects.filter(lesson=self.lesson).values_list('prompt', flat=True)), ['p?']) self.assertEqual(UserAnswer.objects.filter(user_id=self.student.id).count(), 1) def test_curriculum_import_and_content_browser_are_refused(self): self.assertEqual(self.client.get('/admin/content/grade/import-curriculum/').status_code, 403) self.assertEqual(self.client.get('/admin/content/question/import/').status_code, 403) self.assertEqual(self.client.get(f'/admin/content/question/browse/questions/{self.lesson.id}/').status_code, 403) def test_mark_resolved_action_is_refused(self): report = QuestionReport.objects.create(question=self.q, reason='wrong_answer', user_id=self.student.id) self.client.post('/admin/content/questionreport/', {'action': 'mark_resolved', '_selected_action': [report.pk]}) report.refresh_from_db() self.assertNotEqual(report.status, 'resolved') class NonAsciiDigitsAre400Tests(TestCase): """'²'.isdigit() هم True است ولی int('²') خطا می‌دهد؛ این ورودی‌ها ۵۰۰ می‌دادند.""" def setUp(self): cache.clear() user = User.objects.create(device_id='digits', is_guest=True) self.app = APIClient() self.app.credentials(HTTP_AUTHORIZATION=f'Bearer {issue_access_token(user.id)}') def test_character_and_unit_ids(self): self.assertEqual(self.app.post('/api/characters/select', {'characterId': 'abc'}, format='json').status_code, 400) self.assertEqual(self.app.post('/api/characters/select', {'characterId': '²'}, format='json').status_code, 400) self.assertEqual(self.app.get('/api/progress/nodes', {'unit': '²'}).status_code, 400) def test_ticket_text_is_capped(self): r = self.app.post('/api/support/bug-reports', {'title': 't', 'description': 'x' * 5001}, format='json') self.assertEqual(r.status_code, 400) ok = self.app.post('/api/support/bug-reports', {'title': 't', 'description': 'x' * 5000}, format='json') self.assertIn(ok.status_code, (200, 201))