""" مسیرهای خواندنی‌ای که تا پیش از این سقفِ نرخ نداشتند (بند ۳۰ در docs/security-before-production.md). هر تست نرخِ `read` را روی ۳ در دقیقه می‌گذارد و می‌بیند درخواستِ چهارم ۴۲۹ می‌گیرد. """ from unittest import mock from django.core.cache import cache from django.test import TestCase from rest_framework.test import APIClient from rest_framework.throttling import ScopedRateThrottle from accounts.authentication import issue_access_token from accounts.models import User from content.models import Grade, Lesson, Node, Unit from notifications.models import Notification from support.models import Ticket class ReadEndpointsAreThrottledTests(TestCase): def setUp(self): cache.clear() grade = Grade.objects.create(title='G', icon='', color='', number=77) 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.user = User.objects.create(phone='09120008888') self.notification = Notification.objects.create(user_id=self.user.id, title='t', body='b') self.ticket = Ticket.objects.create(user_id=self.user.id, subject='s') self.client = APIClient() def _auth(self): self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {issue_access_token(self.user.id)}') def _hammer(self, method, url, scope='read'): rates = dict(ScopedRateThrottle.THROTTLE_RATES, **{scope: '3/min'}) with mock.patch.object(ScopedRateThrottle, 'THROTTLE_RATES', rates): codes = [getattr(self.client, method)(url).status_code for _ in range(5)] self.assertEqual(codes[3:], [429, 429], codes) self.assertNotIn(429, codes[:3], codes) def test_lesson_intro_without_login(self): self._hammer('get', f'/api/lessons/{self.lesson.id}/intro') def test_lesson_questions_without_login(self): self._hammer('get', f'/api/lessons/{self.lesson.id}/questions') def test_progress_nodes(self): self._auth() self._hammer('get', '/api/progress/nodes') def test_hearts(self): self._auth() self._hammer('get', '/api/user/hearts') def test_notification_mark_read(self): self._auth() self._hammer('patch', f'/api/notifications/{self.notification.id}/read') def test_ticket_detail(self): self._auth() self._hammer('get', f'/api/support/tickets/{self.ticket.id}') # مسیرهای دسته‌ی دوم. شناسه‌ها عمداً وجود ندارند: throttle پیش از ویو اجرا می‌شود، پس # ۴۰۴ هم یک درخواستِ شمرده‌شده است و جلوی ساختنِ داده‌ی هر مسیر را می‌گیرد. def test_plans_without_login(self): self._hammer('get', '/api/subscription/plans') def test_subscription_status(self): self._auth() self._hammer('get', '/api/subscription/status') def test_order_detail(self): self._auth() self._hammer('get', '/api/payment/orders/999999') def test_my_offers(self): self._auth() self._hammer('get', '/api/discount/offers/mine') def test_affiliate_me(self): self._auth() self._hammer('get', '/api/affiliate/me') def test_affiliate_conversions(self): self._auth() self._hammer('get', '/api/affiliate/conversions') def test_conversation_messages(self): self._auth() self._hammer('get', '/api/ai/conversations/999999/messages') def test_message_feedback(self): self._auth() self._hammer('post', '/api/ai/messages/999999/feedback', scope='user_profile_write') def test_logout(self): self._auth() self._hammer('post', '/api/auth/logout', scope='user_profile_write')