2026-08-09 06:12:43 +00:00
|
|
|
import pytest
|
|
|
|
|
from django.urls import reverse
|
|
|
|
|
from django.contrib.auth.models import User
|
|
|
|
|
from rest_framework import status
|
|
|
|
|
from rest_framework.test import APIClient
|
2026-08-12 07:51:11 +00:00
|
|
|
from api.models import Product, Order, ReturnRequest, Wallet, WalletTransaction, SupplierProfile
|
2026-08-09 06:12:43 +00:00
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def api_client():
|
|
|
|
|
return APIClient()
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def create_user(db):
|
2026-08-12 07:51:11 +00:00
|
|
|
user = User.objects.create_user(username='demo_seller', email='demo@example.com', password='Password123!')
|
|
|
|
|
SupplierProfile.objects.create(user=user, phone='1234567890')
|
2026-08-18 06:00:22 +00:00
|
|
|
Order.objects.create(
|
|
|
|
|
supplier=user,
|
|
|
|
|
order_id='OR-8721-demo',
|
|
|
|
|
date='2026-08-05',
|
|
|
|
|
item='Handmade Wool Blanket',
|
|
|
|
|
quantity=1,
|
|
|
|
|
customer='Alice Smith',
|
|
|
|
|
total=85.00,
|
|
|
|
|
status='Pending Acceptance'
|
|
|
|
|
)
|
|
|
|
|
ReturnRequest.objects.create(
|
|
|
|
|
supplier=user,
|
|
|
|
|
return_id='RET-5510-demo',
|
|
|
|
|
order_id='OR-3920-demo',
|
|
|
|
|
customer='Bob Johnson',
|
|
|
|
|
item='Silk Scarf',
|
|
|
|
|
reason='Ordered wrong color variant.',
|
|
|
|
|
status='Pending Approval'
|
|
|
|
|
)
|
2026-08-09 06:12:43 +00:00
|
|
|
return user
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_user_registration(api_client):
|
|
|
|
|
url = reverse('register')
|
|
|
|
|
data = {
|
|
|
|
|
'username': 'new_seller',
|
|
|
|
|
'email': 'new@example.com',
|
|
|
|
|
'phone': '1234567890',
|
2026-08-12 07:51:11 +00:00
|
|
|
'password': 'SecurePassword123!'
|
2026-08-09 06:12:43 +00:00
|
|
|
}
|
|
|
|
|
response = api_client.post(url, data, format='json')
|
|
|
|
|
assert response.status_code == status.HTTP_201_CREATED
|
2026-08-12 07:51:11 +00:00
|
|
|
assert response.data['user']['username'] == 'new_seller'
|
2026-08-09 06:12:43 +00:00
|
|
|
assert User.objects.filter(username='new_seller').exists()
|
|
|
|
|
|
2026-08-10 12:31:54 +00:00
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_user_logout(api_client, create_user):
|
|
|
|
|
url = reverse('logout')
|
|
|
|
|
api_client.force_authenticate(user=create_user)
|
|
|
|
|
response = api_client.post(url)
|
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
|
|
|
assert response.data['success'] is True
|
|
|
|
|
|
2026-08-09 06:12:43 +00:00
|
|
|
@pytest.mark.django_db
|
2026-08-12 07:51:11 +00:00
|
|
|
def test_otp_verification(api_client, create_user):
|
2026-08-09 06:12:43 +00:00
|
|
|
url = reverse('verify-otp')
|
2026-08-12 07:51:11 +00:00
|
|
|
api_client.force_authenticate(user=create_user)
|
|
|
|
|
|
2026-08-09 06:12:43 +00:00
|
|
|
# Correct OTP
|
2026-08-12 07:51:11 +00:00
|
|
|
response = api_client.post(url, {'type': 'phone', 'otp': '123456'}, format='json')
|
2026-08-09 06:12:43 +00:00
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
|
|
|
assert response.data['verified'] is True
|
|
|
|
|
|
|
|
|
|
# Incorrect OTP
|
2026-08-12 07:51:11 +00:00
|
|
|
response = api_client.post(url, {'type': 'phone', 'otp': '000000'}, format='json')
|
2026-08-09 06:12:43 +00:00
|
|
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
|
|
|
assert response.data['verified'] is False
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_profile_update(api_client, create_user):
|
|
|
|
|
url = reverse('profile')
|
|
|
|
|
api_client.force_authenticate(user=create_user)
|
|
|
|
|
|
|
|
|
|
data = {
|
|
|
|
|
'store_name': 'New Artisan Handloom',
|
|
|
|
|
'business_bio': 'Beautiful handmade rugs.',
|
|
|
|
|
'gstin': '29AAAAA1111A1Z1',
|
2026-08-12 07:51:11 +00:00
|
|
|
'is_gstin_verified': True,
|
|
|
|
|
'aadhar_s3_key': 'suppliers/1/aadhar.pdf',
|
|
|
|
|
'pan_s3_key': 'suppliers/1/pan.pdf',
|
2026-08-18 06:00:22 +00:00
|
|
|
'logo_s3_key': 'suppliers/1/logo.jpg',
|
|
|
|
|
'business_type': 'individual_maker',
|
|
|
|
|
'store_slug': 'new-artisan-handloom',
|
|
|
|
|
'categories': ['Sustainable Products', 'Home Decor']
|
2026-08-09 06:12:43 +00:00
|
|
|
}
|
|
|
|
|
response = api_client.put(url, data, format='json')
|
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
2026-08-12 07:51:11 +00:00
|
|
|
create_user.profile.refresh_from_db()
|
2026-08-09 06:12:43 +00:00
|
|
|
assert create_user.profile.store_name == 'New Artisan Handloom'
|
|
|
|
|
assert create_user.profile.is_gstin_verified is True
|
2026-08-18 06:00:22 +00:00
|
|
|
assert create_user.profile.business_type == 'individual_maker'
|
|
|
|
|
assert create_user.profile.store_slug == 'new-artisan-handloom'
|
|
|
|
|
assert set(create_user.profile.categories.values_list('name', flat=True)) == {'Sustainable Products', 'Home Decor'}
|
2026-08-09 06:12:43 +00:00
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_product_crud(api_client, create_user):
|
|
|
|
|
api_client.force_authenticate(user=create_user)
|
|
|
|
|
|
|
|
|
|
# Create product
|
|
|
|
|
url = reverse('product-list')
|
|
|
|
|
data = {
|
|
|
|
|
'title': 'Test Indigo Shawl',
|
|
|
|
|
'category': 'Apparel',
|
|
|
|
|
'price': '85.00',
|
|
|
|
|
'stock': 10,
|
|
|
|
|
'sku': 'TEST-SHAWL-01'
|
|
|
|
|
}
|
|
|
|
|
response = api_client.post(url, data, format='json')
|
|
|
|
|
assert response.status_code == status.HTTP_201_CREATED
|
|
|
|
|
assert Product.objects.filter(sku='TEST-SHAWL-01').exists()
|
|
|
|
|
|
2026-08-12 07:51:11 +00:00
|
|
|
# List products (returns paginated structure under 'results')
|
2026-08-09 06:12:43 +00:00
|
|
|
response = api_client.get(url)
|
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
2026-08-12 07:51:11 +00:00
|
|
|
assert len(response.data['results']) == 1
|
2026-08-09 06:12:43 +00:00
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_bulk_upload(api_client, create_user):
|
|
|
|
|
api_client.force_authenticate(user=create_user)
|
|
|
|
|
url = reverse('bulk-upload')
|
|
|
|
|
data = {
|
|
|
|
|
'products': [
|
|
|
|
|
{'title': 'Bulk Bowl', 'category': 'Kitchenware', 'price': '65.00', 'stock': 15, 'sku': 'BOWL-WAL-12'},
|
2026-08-12 07:51:11 +00:00
|
|
|
{'title': 'Bulk Tablecloth', 'category': 'Textiles', 'price': '48.00', 'stock': 30, 'sku': 'LINE-COT-15'}
|
2026-08-09 06:12:43 +00:00
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
response = api_client.post(url, data, format='json')
|
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
|
|
|
assert response.data['success'] is True
|
|
|
|
|
assert Product.objects.filter(sku='BOWL-WAL-12').exists()
|
|
|
|
|
assert Product.objects.filter(sku='LINE-COT-15').exists()
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_order_actions(api_client, create_user):
|
|
|
|
|
api_client.force_authenticate(user=create_user)
|
|
|
|
|
|
|
|
|
|
# Query list to auto-create mock orders
|
|
|
|
|
list_url = reverse('order-list')
|
|
|
|
|
api_client.get(list_url)
|
|
|
|
|
|
|
|
|
|
order = Order.objects.filter(supplier=create_user, status='Pending Acceptance').first()
|
|
|
|
|
assert order is not None
|
|
|
|
|
|
|
|
|
|
# Accept order
|
|
|
|
|
accept_url = reverse('order-accept', args=[order.id])
|
|
|
|
|
response = api_client.post(accept_url)
|
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
|
|
|
assert response.data['status'] == 'Ready to Ship'
|
|
|
|
|
assert response.data['carrier'] == 'DHL Express'
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_return_actions(api_client, create_user):
|
|
|
|
|
api_client.force_authenticate(user=create_user)
|
|
|
|
|
|
|
|
|
|
# Query list to auto-create mock return requests
|
|
|
|
|
list_url = reverse('return-list')
|
|
|
|
|
api_client.get(list_url)
|
|
|
|
|
|
|
|
|
|
ret = ReturnRequest.objects.filter(supplier=create_user, status='Pending Approval').first()
|
|
|
|
|
assert ret is not None
|
|
|
|
|
|
|
|
|
|
# Approve return
|
|
|
|
|
action_url = reverse('return-action', args=[ret.id])
|
|
|
|
|
response = api_client.post(action_url, {'action': 'Approved'}, format='json')
|
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
|
|
|
assert response.data['status'] == 'In Transit'
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_wallet_withdrawal(api_client, create_user):
|
|
|
|
|
api_client.force_authenticate(user=create_user)
|
|
|
|
|
wallet = Wallet.objects.create(supplier=create_user, outstanding=1000.00, withdrawn=500.00)
|
|
|
|
|
|
|
|
|
|
url = reverse('withdraw')
|
|
|
|
|
# Successful withdrawal
|
|
|
|
|
response = api_client.post(url, {'amount': '200.00'}, format='json')
|
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
|
|
|
assert response.data['outstanding'] == '800.00'
|
|
|
|
|
assert response.data['withdrawn'] == '700.00'
|
|
|
|
|
assert WalletTransaction.objects.filter(wallet=wallet).exists()
|
|
|
|
|
|
|
|
|
|
# Invalid amount (exceeds outstanding)
|
|
|
|
|
response = api_client.post(url, {'amount': '1500.00'}, format='json')
|
|
|
|
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
2026-08-20 05:51:14 +00:00
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_document_cloudfront_url_generation(api_client, create_user):
|
|
|
|
|
from django.conf import settings
|
|
|
|
|
from api.views import get_document_url
|
|
|
|
|
|
|
|
|
|
profile = create_user.profile
|
|
|
|
|
profile.aadhar_s3_key = 'suppliers/123/aadhar.pdf'
|
|
|
|
|
profile.save()
|
|
|
|
|
|
|
|
|
|
# Case 1: CloudFront Domain set
|
|
|
|
|
settings.AWS_CLOUDFRONT_DOMAIN = 'd12345.cloudfront.net'
|
|
|
|
|
url = get_document_url(profile.aadhar_s3_key)
|
|
|
|
|
assert url == 'https://d12345.cloudfront.net/suppliers/123/aadhar.pdf'
|
|
|
|
|
|
|
|
|
|
# Case 2: CloudFront Domain set with protocol prefix
|
|
|
|
|
settings.AWS_CLOUDFRONT_DOMAIN = 'https://d98765.cloudfront.net/'
|
|
|
|
|
url = get_document_url(profile.aadhar_s3_key)
|
|
|
|
|
assert url == 'https://d98765.cloudfront.net/suppliers/123/aadhar.pdf'
|
|
|
|
|
|
|
|
|
|
# Case 3: CloudFront Domain empty, fallback to Local Mock
|
|
|
|
|
settings.AWS_CLOUDFRONT_DOMAIN = ''
|
|
|
|
|
url = get_document_url(profile.aadhar_s3_key)
|
|
|
|
|
assert 'mock-download' in url
|
|
|
|
|
|
|
|
|
|
# Case 4: API Response includes aadhar_url, pan_url, logo_url
|
|
|
|
|
url = reverse('profile')
|
|
|
|
|
api_client.force_authenticate(user=create_user)
|
|
|
|
|
response = api_client.get(url)
|
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
|
|
|
assert 'aadhar_url' in response.data['profile']
|
|
|
|
|
assert 'pan_url' in response.data['profile']
|
|
|
|
|
assert 'logo_url' in response.data['profile']
|
|
|
|
|
|
|
|
|
|
@pytest.mark.django_db
|
|
|
|
|
def test_mock_download_view(api_client, create_user):
|
|
|
|
|
api_client.force_authenticate(user=create_user)
|
|
|
|
|
|
|
|
|
|
# 1. Upload mock file first using PUT request
|
|
|
|
|
upload_url = reverse('mock-upload') + '?key=test_key.pdf'
|
|
|
|
|
file_content = b'PDF content'
|
|
|
|
|
upload_resp = api_client.put(upload_url, data=file_content, content_type='application/octet-stream')
|
|
|
|
|
assert upload_resp.status_code == status.HTTP_200_OK
|
|
|
|
|
|
|
|
|
|
# 2. Download it
|
|
|
|
|
download_url = reverse('mock-download') + '?key=test_key.pdf'
|
|
|
|
|
download_resp = api_client.get(download_url)
|
|
|
|
|
assert download_resp.status_code == status.HTTP_200_OK
|
|
|
|
|
assert b"".join(download_resp.streaming_content) == file_content
|