""" حفاظ‌های بازبینیِ امنیتیِ پیش از تحویل (پرداخت). هر تست رفتاری را نگه می‌دارد که پیش‌تر غلط بود. """ from unittest.mock import patch from django.contrib.auth import get_user_model from django.contrib.auth.models import Group from django.core.cache import cache from django.core.management import call_command from django.test import TestCase, override_settings from rest_framework.test import APIClient from accounts.authentication import issue_access_token from accounts.models import User from discount.models import DiscountCode from subscription.models import Plan from subscription.services import credit_balance from .bazaar import BazaarPurchaseResult from .models import Order, Transaction from .services import apply_order_payment_result def client_for(user): c = APIClient() c.credentials(HTTP_AUTHORIZATION=f'Bearer {issue_access_token(user.id)}') return c class _Base(TestCase): def setUp(self): cache.clear() self.user = User.objects.create(phone='09120009901', is_guest=False) self.app = client_for(self.user) self.pack = Plan.objects.get(kind='pack', question_credits=20) def tearDown(self): cache.clear() def pack_order(self, **extra): r = self.app.post('/api/subscription/purchase', {'planId': str(self.pack.id), **extra}, format='json') return Order.objects.get(pk=r.json()['orderId']) class SupervisorCannotRunMoneyActionsTests(_Base): """گروهِ Supervisor فقط view_order دارد؛ actionِ بی‌permissions برای هر بیننده‌ی فهرست اجرا می‌شد.""" def setUp(self): super().setUp() call_command('setup_supervisor_group', verbosity=0) staff = get_user_model().objects.create_user('sup', 'sup@example.com', 'pw-for-tests-only', is_staff=True) staff.groups.add(Group.objects.get(name='Supervisor')) self.client.force_login(staff) def test_refund_is_refused(self): order = self.pack_order() apply_order_payment_result(order=order, gateway='zarinpal', gateway_ref_id='R1', new_status='paid', event='t') self.client.post('/admin/payment/order/', {'action': 'refund', '_selected_action': [order.pk], 'confirm': 'yes'}) order.refresh_from_db() self.assertEqual(order.status, 'paid') self.assertEqual(credit_balance(self.user.id), 20) def test_cancel_is_refused(self): order = Order.objects.create(user_id=self.user.id, amount=1000, idempotency_key='sec-c1') self.client.post('/admin/payment/order/', {'action': 'cancel_pending', '_selected_action': [order.pk]}) order.refresh_from_db() self.assertEqual(order.status, 'pending') class CallbackWithoutAuthorityTests(_Base): def test_missing_authority_is_400_and_touches_no_order(self): DiscountCode.objects.create(code='SEC50', type='percent', value=50, max_uses=10) order = self.pack_order() self.assertEqual(self.app.post('/api/discount/redeem', {'code': 'SEC50', 'orderId': str(order.id)}, format='json').status_code, 200) Order.objects.create(user_id=self.user.id, amount=1000, idempotency_key='sec-b') # دومین سفارشِ بازنشده resp = APIClient().get('/api/payment/zarinpal/callback', {'Status': 'NOK'}) self.assertEqual(resp.status_code, 400) order.refresh_from_db() self.assertEqual(order.status, 'pending') class PlanEditedBetweenOrderAndPaymentTests(_Base): def test_buyer_gets_what_the_order_sold(self): order = self.pack_order() Plan.objects.filter(pk=self.pack.pk).update(price=order.amount * 5, question_credits=150, hearts_days=60) apply_order_payment_result(order=order, gateway='zarinpal', gateway_ref_id='P1', new_status='paid', event='t') self.assertEqual(credit_balance(self.user.id), 20) self.assertEqual(order.question_credits, 20) @override_settings(BAZAAR_PISHKHAN_API_SECRET='x', BAZAAR_PACKAGE_NAME='com.example') class BazaarSecondTokenOnPaidOrderTests(_Base): def setUp(self): super().setUp() Plan.objects.filter(pk=self.pack.pk).update(bazaar_sku='sec-pack20') def test_second_token_is_409_and_stays_usable(self): order = self.pack_order(paymentMethod='bazaar') valid = BazaarPurchaseResult(is_valid=True, purchase_state=0) with patch('payment.bazaar.verify_purchase', return_value=valid): first = self.app.post('/api/payment/bazaar/verify', {'orderId': str(order.id), 'purchaseToken': 'TOKEN-1'}, format='json') again = self.app.post('/api/payment/bazaar/verify', {'orderId': str(order.id), 'purchaseToken': 'TOKEN-1'}, format='json') second = self.app.post('/api/payment/bazaar/verify', {'orderId': str(order.id), 'purchaseToken': 'TOKEN-2'}, format='json') self.assertEqual(first.json()['status'], 'paid') self.assertEqual(again.status_code, 200) # همان توکن دوباره: بی‌اثر و بی‌خطا self.assertEqual(second.status_code, 409) self.assertFalse(Transaction.objects.filter(gateway_ref_id='TOKEN-2').exists()) r2 = self.app.post('/api/subscription/purchase', {'planId': str(self.pack.id), 'paymentMethod': 'bazaar'}, format='json', HTTP_IDEMPOTENCY_KEY='sec-new') with patch('payment.bazaar.verify_purchase', return_value=valid): fresh = self.app.post('/api/payment/bazaar/verify', {'orderId': r2.json()['orderId'], 'purchaseToken': 'TOKEN-2'}, format='json') self.assertEqual(fresh.json()['status'], 'paid') self.assertEqual(credit_balance(self.user.id), 40)