283 lines
12 KiB
Python
283 lines
12 KiB
Python
from unittest.mock import patch
|
|
|
|
from django.contrib.auth import get_user_model
|
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
|
from django.test import TestCase, override_settings
|
|
from django.urls import reverse
|
|
|
|
from orders.models import Order, OrderItem
|
|
from products.models import Product
|
|
|
|
User = get_user_model()
|
|
|
|
SAMPLE_GIF = bytes.fromhex(
|
|
'47494638396101000100800000000000ffffff21f90401000000002c00000000010001000002024401003b'
|
|
)
|
|
|
|
|
|
class OrderPaymentFlowTests(TestCase):
|
|
def setUp(self):
|
|
self.user = User.objects.create_user(username='alice', password='password123', email='alice@example.com')
|
|
self.client.force_login(self.user)
|
|
image = SimpleUploadedFile('product.gif', SAMPLE_GIF, content_type='image/gif')
|
|
self.product = Product.objects.create(
|
|
name='Wireless Mouse',
|
|
description='A test product for order payments.',
|
|
price='1250.00',
|
|
listing_type='sale',
|
|
image=image,
|
|
category='Electronics',
|
|
stock=8,
|
|
)
|
|
self.order = Order.objects.create(
|
|
user=self.user,
|
|
total_price='1250.00',
|
|
full_name='Alice Example',
|
|
phone='+9779800000000',
|
|
address='Kathmandu',
|
|
)
|
|
OrderItem.objects.create(order=self.order, product=self.product, quantity=1, price='1250.00')
|
|
|
|
def test_payment_page_does_not_fake_pay_order(self):
|
|
response = self.client.get(reverse('payment', args=[self.order.id]))
|
|
self.assertEqual(response.status_code, 200)
|
|
self.order.refresh_from_db()
|
|
self.assertEqual(self.order.payment_status, 'Unpaid')
|
|
self.assertEqual(self.order.status, 'Pending')
|
|
|
|
@override_settings(
|
|
PAYMENT_CURRENCY='NPR',
|
|
ESEWA_SANDBOX=True,
|
|
ESEWA_PRODUCT_CODE='EPAYTEST',
|
|
ESEWA_SECRET_KEY='secret-key',
|
|
ESEWA_FORM_URL='https://esewa.test/form',
|
|
ESEWA_STATUS_URL='https://esewa.test/status/',
|
|
)
|
|
def test_posting_esewa_returns_redirect_form_and_sets_pending_state(self):
|
|
response = self.client.post(reverse('payment', args=[self.order.id]), {'payment': 'esewa'})
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertTemplateUsed(response, 'order/payment_redirect.html')
|
|
self.assertContains(response, 'https://esewa.test/form')
|
|
|
|
self.order.refresh_from_db()
|
|
self.assertEqual(self.order.payment_provider, 'esewa')
|
|
self.assertEqual(self.order.payment_method, 'eSewa')
|
|
self.assertEqual(self.order.payment_status, 'Pending')
|
|
self.assertEqual(self.order.payment_currency, 'NPR')
|
|
self.assertTrue(self.order.payment_session_id.startswith(f'ORD-{self.order.id}-'))
|
|
self.assertContains(response, self.order.payment_session_id)
|
|
|
|
@override_settings(PAYMENT_CURRENCY='NPR')
|
|
@patch('orders.views.verify_esewa_payment')
|
|
def test_esewa_return_marks_order_paid_only_after_verified_status(self, mock_verify_esewa_payment):
|
|
self.order.payment_method = 'eSewa'
|
|
self.order.payment_provider = 'esewa'
|
|
self.order.payment_status = 'Pending'
|
|
self.order.payment_session_id = 'ORD-1-ABC123'
|
|
self.order.payment_currency = 'NPR'
|
|
self.order.save()
|
|
|
|
mock_verify_esewa_payment.return_value = {
|
|
'status': 'Paid',
|
|
'reference': 'ESEWA-REF-123',
|
|
'session_id': 'ORD-1-ABC123',
|
|
'currency': 'NPR',
|
|
'message': 'eSewa payment verified successfully.',
|
|
}
|
|
|
|
response = self.client.get(reverse('esewa_return') + f'?order_id={self.order.id}&data=dummy')
|
|
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertEqual(response['Location'], reverse('success') + f'?order_id={self.order.id}')
|
|
self.order.refresh_from_db()
|
|
self.assertEqual(self.order.payment_status, 'Paid')
|
|
self.assertEqual(self.order.status, 'Paid')
|
|
self.assertEqual(self.order.payment_reference, 'ESEWA-REF-123')
|
|
self.assertEqual(self.order.payment_provider, 'esewa')
|
|
self.assertIsNotNone(self.order.paid_at)
|
|
|
|
@override_settings(
|
|
PAYMENT_CURRENCY='NPR',
|
|
KHALTI_SECRET_KEY='test_secret',
|
|
KHALTI_INITIATE_URL='https://khalti.test/initiate/',
|
|
KHALTI_LOOKUP_URL='https://khalti.test/lookup/',
|
|
)
|
|
@patch('orders.views.initiate_khalti_payment')
|
|
def test_posting_khalti_redirects_to_payment_url(self, mock_initiate_khalti_payment):
|
|
mock_initiate_khalti_payment.return_value = {
|
|
'payment_url': 'https://pay.khalti.test/session/123',
|
|
'session_id': 'pidx_test_123',
|
|
'reference': 'ORDER-1-TEST123',
|
|
'currency': 'NPR',
|
|
}
|
|
|
|
response = self.client.post(reverse('payment', args=[self.order.id]), {'payment': 'khalti'})
|
|
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertEqual(response['Location'], 'https://pay.khalti.test/session/123')
|
|
self.order.refresh_from_db()
|
|
self.assertEqual(self.order.payment_provider, 'khalti')
|
|
self.assertEqual(self.order.payment_method, 'Khalti')
|
|
self.assertEqual(self.order.payment_status, 'Pending')
|
|
self.assertEqual(self.order.payment_session_id, 'pidx_test_123')
|
|
self.assertEqual(self.order.payment_reference, 'ORDER-1-TEST123')
|
|
|
|
@override_settings(PAYMENT_CURRENCY='NPR')
|
|
@patch('orders.views.verify_khalti_payment')
|
|
def test_khalti_return_marks_order_paid_only_after_verified_lookup(self, mock_verify_khalti_payment):
|
|
self.order.payment_method = 'Khalti'
|
|
self.order.payment_provider = 'khalti'
|
|
self.order.payment_status = 'Pending'
|
|
self.order.payment_session_id = 'pidx_test_123'
|
|
self.order.payment_reference = 'ORDER-1-TEST123'
|
|
self.order.payment_currency = 'NPR'
|
|
self.order.save()
|
|
|
|
mock_verify_khalti_payment.return_value = {
|
|
'status': 'Paid',
|
|
'reference': 'TXN-456',
|
|
'session_id': 'pidx_test_123',
|
|
'currency': 'NPR',
|
|
'message': 'Khalti payment verified successfully.',
|
|
}
|
|
|
|
response = self.client.get(reverse('khalti_return') + f'?order_id={self.order.id}&pidx=pidx_test_123')
|
|
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertEqual(response['Location'], reverse('success') + f'?order_id={self.order.id}')
|
|
self.order.refresh_from_db()
|
|
self.assertEqual(self.order.payment_status, 'Paid')
|
|
self.assertEqual(self.order.status, 'Paid')
|
|
self.assertEqual(self.order.payment_reference, 'TXN-456')
|
|
self.assertEqual(self.order.payment_provider, 'khalti')
|
|
self.assertIsNotNone(self.order.paid_at)
|
|
|
|
def test_cash_on_delivery_does_not_mark_order_as_paid(self):
|
|
response = self.client.post(reverse('payment', args=[self.order.id]), {'payment': 'cod'})
|
|
|
|
self.assertEqual(response.status_code, 302)
|
|
self.order.refresh_from_db()
|
|
self.assertEqual(self.order.payment_method, 'Cash on Delivery')
|
|
self.assertEqual(self.order.payment_status, 'Pending')
|
|
self.assertEqual(self.order.status, 'Pending')
|
|
|
|
|
|
class CheckoutLocationFlowTests(TestCase):
|
|
def setUp(self):
|
|
self.user = User.objects.create_user(username='buyer', password='password123', email='buyer@example.com')
|
|
self.user.profile.phone = '+9779811111111'
|
|
self.user.profile.location_label = 'Baneshwor, Kathmandu'
|
|
self.user.profile.default_address = 'Old Baneshwor Chowk\nKathmandu'
|
|
self.user.profile.latitude = '27.693400'
|
|
self.user.profile.longitude = '85.335000'
|
|
self.user.profile.location_accuracy_m = '20.50'
|
|
self.user.profile.save()
|
|
|
|
image = SimpleUploadedFile('checkout.gif', SAMPLE_GIF, content_type='image/gif')
|
|
self.product = Product.objects.create(
|
|
name='Smart Watch',
|
|
description='Checkout location test product.',
|
|
price='2500.00',
|
|
listing_type='sale',
|
|
image=image,
|
|
category='Electronics',
|
|
stock=5,
|
|
)
|
|
self.client.force_login(self.user)
|
|
session = self.client.session
|
|
session['cart'] = {str(self.product.id): 1}
|
|
session.save()
|
|
|
|
def test_checkout_prefills_saved_profile_delivery_details(self):
|
|
response = self.client.get(reverse('checkout'))
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertEqual(response.context['delivery']['phone'], '+9779811111111')
|
|
self.assertEqual(response.context['delivery']['location_label'], 'Baneshwor, Kathmandu')
|
|
self.assertEqual(response.context['delivery']['address'], 'Old Baneshwor Chowk\nKathmandu')
|
|
self.assertEqual(response.context['delivery']['latitude'], '27.693400')
|
|
self.assertEqual(response.context['delivery']['longitude'], '85.335000')
|
|
|
|
def test_checkout_saves_order_delivery_snapshot_and_updates_profile(self):
|
|
response = self.client.post(
|
|
reverse('checkout'),
|
|
{
|
|
'full_name': 'Buyer Example',
|
|
'phone': '+9779801234567',
|
|
'location_label': 'Lalitpur, Kupondole',
|
|
'address': 'Kupondole Height\nLalitpur',
|
|
'delivery_notes': 'Call before reaching the gate',
|
|
'latitude': '27.685100',
|
|
'longitude': '85.316700',
|
|
'location_accuracy_m': '12.75',
|
|
'save_as_default': 'on',
|
|
},
|
|
)
|
|
|
|
order = Order.objects.get(user=self.user)
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertEqual(response['Location'], reverse('payment', args=[order.id]))
|
|
self.assertEqual(order.phone, '+9779801234567')
|
|
self.assertEqual(order.location_label, 'Lalitpur, Kupondole')
|
|
self.assertEqual(order.delivery_notes, 'Call before reaching the gate')
|
|
self.assertIsNotNone(order.latitude)
|
|
self.assertIsNotNone(order.longitude)
|
|
|
|
self.user.profile.refresh_from_db()
|
|
self.assertEqual(self.user.profile.phone, '+9779801234567')
|
|
self.assertEqual(self.user.profile.location_label, 'Lalitpur, Kupondole')
|
|
self.assertEqual(self.user.profile.default_address, 'Kupondole Height\nLalitpur')
|
|
|
|
def test_checkout_can_prefill_from_recent_delivery_shortcut(self):
|
|
recent_order = Order.objects.create(
|
|
user=self.user,
|
|
total_price='2500.00',
|
|
status='Delivered',
|
|
payment_status='Paid',
|
|
payment_method='Cash on Delivery',
|
|
full_name='Buyer Example',
|
|
phone='+9779809999999',
|
|
address='Naxal Chowk\nKathmandu',
|
|
location_label='Naxal, Kathmandu',
|
|
latitude='27.717300',
|
|
longitude='85.331900',
|
|
location_accuracy_m='11.25',
|
|
delivery_notes='Ring the bell once',
|
|
)
|
|
|
|
response = self.client.get(reverse('checkout') + f'?delivery_shortcut=order-{recent_order.id}')
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertEqual(response.context['delivery']['phone'], '+9779809999999')
|
|
self.assertEqual(response.context['delivery']['location_label'], 'Naxal, Kathmandu')
|
|
self.assertEqual(response.context['delivery']['address'], 'Naxal Chowk\nKathmandu')
|
|
self.assertEqual(response.context['delivery']['delivery_notes'], 'Ring the bell once')
|
|
self.assertEqual(response.context['delivery']['selected_shortcut_id'], f'order-{recent_order.id}')
|
|
self.assertTrue(any(shortcut['selected'] for shortcut in response.context['delivery_shortcuts']))
|
|
|
|
def test_checkout_save_as_default_clears_stale_profile_gps_when_manual_address_has_no_coordinates(self):
|
|
response = self.client.post(
|
|
reverse('checkout'),
|
|
{
|
|
'full_name': 'Buyer Example',
|
|
'phone': '+9779801234567',
|
|
'location_label': 'Bhaktapur Durbar Area',
|
|
'address': 'Taumadhi Square\nBhaktapur',
|
|
'delivery_notes': 'Call after arrival',
|
|
'save_as_default': 'on',
|
|
},
|
|
)
|
|
|
|
order = Order.objects.get(user=self.user)
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertEqual(response['Location'], reverse('payment', args=[order.id]))
|
|
|
|
self.user.profile.refresh_from_db()
|
|
self.assertEqual(self.user.profile.default_address, 'Taumadhi Square\nBhaktapur')
|
|
self.assertIsNone(self.user.profile.latitude)
|
|
self.assertIsNone(self.user.profile.longitude)
|
|
self.assertIsNone(self.user.profile.location_accuracy_m)
|
|
self.assertIsNone(self.user.profile.location_updated_at)
|
|
|