- subscription.0017/0018, payment.0013: pack plans deleted, QuestionCredit/HeartsPass models and Plan.kind / question_credits / hearts_days / Order.question_credits / hearts_days dropped. The only paid pack order was the company owner's test account (user 26). - Assistant: no credit spending; quota is the daily cap only (free 2, every plan 4, gift subscription 4). - Bazaar: verify_purchase is subscription-only (no inapp guess), start/purchase responses lose productType, verify refuses an order without a subscription. A paid order with no subscription now logs ERROR. - Removed POST /api/payment/orders (internal; no caller). Its permission tests stay. - Pack-based tests rewritten on subscriptions (refund, security, Bazaar token); pack-only test files deleted. - docs: profit table for 4/day on both payment routes plus free users; handbook, operations, CLAUDE.md current. 949 tests green. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
71 lines
3.7 KiB
Python
71 lines
3.7 KiB
Python
"""
|
||
فصلِ اولِ هر پایه رایگان است و بقیه اشتراکِ زمانی میخواهند (۲۰۲۶-۱۰-۱۰).
|
||
بازیکردنِ فصلِ قفل از هر مسیری (سوالهای درس، آزمونِ جزیره، جواب، پایانِ درس/جزیره) ۴۰۲ میگیرد.
|
||
"""
|
||
from django.core.cache import cache
|
||
from django.test import TestCase
|
||
from django.utils import timezone
|
||
from rest_framework.test import APIClient
|
||
|
||
from accounts.authentication import issue_access_token
|
||
from accounts.models import User
|
||
from subscription.models import Subscription
|
||
|
||
from .models import Choice, Grade, Lesson, Node, Question, Unit
|
||
|
||
|
||
class PaywallTests(TestCase):
|
||
def setUp(self):
|
||
cache.clear()
|
||
grade = Grade.objects.create(title='پایه نهم', icon='', color='')
|
||
self.free = self._node(Unit.objects.create(grade=grade, title='فصل ۱', order=1))
|
||
self.paid = self._node(Unit.objects.create(grade=grade, title='فصل ۲', order=2))
|
||
self.user = User.objects.create(phone='09120007901', grade_id=grade.id)
|
||
self.client = APIClient()
|
||
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {issue_access_token(self.user.id)}')
|
||
|
||
def _node(self, unit):
|
||
node = Node.objects.create(unit=unit, title=unit.title, order=1)
|
||
lesson = Lesson.objects.create(node=node, title='درس')
|
||
q = Question.objects.create(lesson=lesson, type='multiple_choice', prompt='؟')
|
||
self.right = Choice.objects.create(question=q, value='۱', is_correct=True)
|
||
node.q, node.choice, node.lesson = q, self.right, lesson
|
||
return node
|
||
|
||
def _every_route(self, node):
|
||
return [
|
||
self.client.get(f'/api/lessons/{node.lesson.id}/questions').status_code,
|
||
self.client.get(f'/api/nodes/{node.id}/quiz').status_code,
|
||
self.client.post(f'/api/questions/{node.q.id}/answer', {'selectedOptionId': str(node.choice.id)},
|
||
format='json').status_code,
|
||
self.client.post(f'/api/nodes/{node.id}/quiz/complete', {'correctCount': 1, 'totalQuestions': 1},
|
||
format='json').status_code,
|
||
]
|
||
|
||
def test_the_second_unit_is_closed_without_a_subscription(self):
|
||
self.assertEqual(self._every_route(self.paid), [402, 402, 402, 402])
|
||
home = self.client.get('/api/home').json()
|
||
self.assertEqual([u['requiresSubscription'] for u in home['units']], [False, True])
|
||
|
||
def test_the_first_unit_is_free(self):
|
||
self.assertNotIn(402, self._every_route(self.free))
|
||
|
||
def test_an_active_subscription_opens_everything_and_an_expired_one_does_not(self):
|
||
sub = Subscription.objects.create(user_id=self.user.id, status='active',
|
||
expires_at=timezone.now() + timezone.timedelta(days=3))
|
||
self.assertNotIn(402, self._every_route(self.paid))
|
||
self.assertEqual([u['requiresSubscription'] for u in self.client.get('/api/home').json()['units']],
|
||
[False, False])
|
||
sub.expires_at = timezone.now() - timezone.timedelta(minutes=1)
|
||
sub.save()
|
||
self.assertEqual(self.client.get(f'/api/nodes/{self.paid.id}/quiz').status_code, 402)
|
||
|
||
def test_status_reports_plan_title_and_quota(self):
|
||
from subscription.models import Plan
|
||
plan = Plan.objects.get(on_sale=True, duration_days=90)
|
||
Subscription.objects.create(user_id=self.user.id, plan=plan, status='active',
|
||
expires_at=timezone.now() + timezone.timedelta(days=10))
|
||
body = self.client.get('/api/subscription/status').json()
|
||
self.assertEqual((body['active'], body['planTitle'], body['planDurationDays'], body['aiDailyQuota']),
|
||
(True, 'سهماهه', 90, 4))
|