c3lf-system-3/core/shipments/tests/v2/test_api.py
Jan Felix Wiebe 3d01f16750
Some checks failed
/ test (push) Failing after 0s
added public api for shipments
2025-04-11 22:13:05 +02:00

595 lines
24 KiB
Python

from django.test import TestCase, Client
from django.contrib.auth.models import Permission
from knox.models import AuthToken
from django.utils import timezone
from authentication.models import ExtendedUser
from inventory.models import Event, Item, Container
from tickets.models import IssueThread
from shipments.models import Shipment
class ShipmentApiTest(TestCase):
def setUp(self):
super().setUp()
# Create a test user with all permissions
self.user = ExtendedUser.objects.create_user('testuser', 'test@example.com', 'test')
self.user.user_permissions.add(*Permission.objects.all())
self.user.save()
# Create authentication token
self.token = AuthToken.objects.create(user=self.user)
self.client = Client(headers={'Authorization': 'Token ' + self.token[1]})
# Create test event and container
self.event = Event.objects.create(slug='test-event', name='Test Event')
self.container = Container.objects.create(name='Test Container')
# Create test items with container
self.item1 = Item.objects.create(description='Test Item 1', event=self.event, container=self.container)
self.item2 = Item.objects.create(description='Test Item 2', event=self.event, container=self.container)
# Create test ticket
self.ticket = IssueThread.objects.create(name='Test Ticket')
def test_list_shipments_empty(self):
"""Test listing shipments when none exist"""
response = self.client.get('/api/2/shipments/')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), [])
def test_create_shipment(self):
"""Test creating a new shipment"""
data = {
'recipient_name': 'John Smith',
'street_address': '123 Oxford Street',
'city': 'London',
'state_province': 'Greater London',
'postal_code': 'W1D 2HG',
'country': 'United Kingdom',
'related_items': [],
'related_tickets': []
}
response = self.client.post('/api/2/shipments/', data, content_type='application/json')
self.assertEqual(response.status_code, 201)
self.assertEqual(response.json()['recipient_name'], 'John Smith')
self.assertEqual(response.json()['state'], Shipment.CREATED)
self.assertEqual(response.json()['review_requirement'], None)
# Retrieve the created shipment and verify its details
shipment_id = response.json()['id']
get_response = self.client.get(f'/api/2/shipments/{shipment_id}/')
self.assertEqual(get_response.status_code, 200)
self.assertEqual(get_response.json()['id'], shipment_id)
self.assertEqual(get_response.json()['recipient_name'], 'John Smith')
self.assertEqual(get_response.json()['street_address'], '123 Oxford Street')
self.assertEqual(get_response.json()['city'], 'London')
self.assertEqual(get_response.json()['state_province'], 'Greater London')
self.assertEqual(get_response.json()['postal_code'], 'W1D 2HG')
self.assertEqual(get_response.json()['country'], 'United Kingdom')
self.assertEqual(get_response.json()['state'], Shipment.CREATED)
self.assertEqual(get_response.json()['review_requirement'], None)
def test_get_shipment(self):
"""Test retrieving a specific shipment"""
# Create a shipment first
shipment = Shipment.objects.create(
recipient_name='Maria Garcia',
street_address='Calle Gran Via 28',
city='Madrid',
state_province='Madrid',
postal_code='28013',
country='Spain'
)
response = self.client.get(f'/api/2/shipments/{shipment.id}/')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()['id'], shipment.id)
self.assertEqual(response.json()['recipient_name'], 'Maria Garcia')
def test_update_shipment(self):
"""Test updating a shipment"""
# Create a shipment first
shipment = Shipment.objects.create(
recipient_name='Hans Mueller',
street_address='Kurfürstendamm 216',
city='Berlin',
state_province='Berlin',
postal_code='10719',
country='Germany'
)
data = {
'recipient_name': 'Hans-Jürgen Mueller',
'address_supplements': 'Apartment 4B'
}
response = self.client.patch(f'/api/2/shipments/{shipment.id}/', data, content_type='application/json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()['recipient_name'], 'Hans-Jürgen Mueller')
self.assertEqual(response.json()['address_supplements'], 'Apartment 4B')
def test_delete_shipment(self):
"""Test deleting a shipment"""
# Create a shipment first
shipment = Shipment.objects.create(
recipient_name='Sophie Dubois',
street_address='15 Rue de Rivoli',
city='Paris',
state_province='Île-de-France',
postal_code='75001',
country='France'
)
response = self.client.delete(f'/api/2/shipments/{shipment.id}/')
self.assertEqual(response.status_code, 204)
# Verify it's soft deleted - use all_objects manager to see deleted items
self.assertTrue(Shipment.all_objects.filter(id=shipment.id).exists())
# Check if it's marked as deleted
shipment.refresh_from_db()
self.assertIsNotNone(shipment.deleted_at)
def test_add_items(self):
"""Test adding items to a shipment"""
# Create a shipment first
shipment = Shipment.objects.create(
recipient_name='Alessandro Romano',
street_address='Via del Corso 123',
city='Rome',
state_province='Lazio',
postal_code='00186',
country='Italy'
)
data = {
'item_ids': [self.item1.id, self.item2.id],
'review_requirement': Shipment.REVIEW_ITEMS
}
response = self.client.post(f'/api/2/shipments/{shipment.id}/add_items/', data, content_type='application/json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()['state'], Shipment.PENDING_REVIEW)
self.assertEqual(response.json()['review_requirement'], Shipment.REVIEW_ITEMS)
# Verify items were added
shipment.refresh_from_db()
self.assertEqual(shipment.related_items.count(), 2)
def test_approve_shipment(self):
"""Test approving a shipment"""
# Create a shipment and add items
shipment = Shipment.objects.create(
recipient_name='Yuki Tanaka',
street_address='1-1-1 Shibuya',
city='Tokyo',
state_province='Tokyo',
postal_code='150-0002',
country='Japan'
)
shipment.related_items.add(self.item1)
shipment.review_requirement = Shipment.REVIEW_ITEMS
shipment.state = Shipment.PENDING_REVIEW
shipment.save()
response = self.client.post(f'/api/2/shipments/{shipment.id}/approve/')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()['state'], Shipment.READY_FOR_SHIPPING)
def test_reject_shipment(self):
"""Test rejecting a shipment"""
# Create a shipment and add items
shipment = Shipment.objects.create(
recipient_name='Carlos Rodriguez',
street_address='Avenida Insurgentes Sur 1602',
city='Mexico City',
state_province='CDMX',
postal_code='03940',
country='Mexico'
)
shipment.related_items.add(self.item1)
shipment.review_requirement = Shipment.REVIEW_ITEMS
shipment.state = Shipment.PENDING_REVIEW
shipment.save()
response = self.client.post(f'/api/2/shipments/{shipment.id}/reject/')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()['state'], Shipment.REJECTED)
def test_mark_shipped(self):
"""Test marking a shipment as shipped"""
# Create a shipment and set it to READY_FOR_SHIPPING
shipment = Shipment.objects.create(
recipient_name='Anna Kowalski',
street_address='ul. Marszałkowska 126',
city='Warsaw',
state_province='Mazowieckie',
postal_code='00-008',
country='Poland',
state=Shipment.READY_FOR_SHIPPING
)
shipment.related_items.add(self.item1)
response = self.client.post(f'/api/2/shipments/{shipment.id}/mark_shipped/')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()['state'], Shipment.SHIPPED)
self.assertIsNotNone(response.json()['shipped_at'])
def test_mark_completed(self):
"""Test marking a shipment as completed"""
# Create a shipment and set it to SHIPPED
shipment = Shipment.objects.create(
recipient_name='Lars Andersen',
street_address='Strøget 1',
city='Copenhagen',
state_province='Capital Region',
postal_code='1095',
country='Denmark',
state=Shipment.SHIPPED,
shipped_at=timezone.now()
)
shipment.related_items.add(self.item1)
response = self.client.post(f'/api/2/shipments/{shipment.id}/mark_completed/')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()['state'], Shipment.COMPLETED)
self.assertIsNotNone(response.json()['completed_at'])
def test_invalid_state_transition(self):
"""Test that invalid state transitions are rejected"""
# Create a shipment in CREATED state
shipment = Shipment.objects.create(
recipient_name='Ahmed Hassan',
street_address='26 July Street',
city='Cairo',
state_province='Cairo Governorate',
postal_code='11511',
country='Egypt'
)
# Try to mark it as shipped directly (invalid transition)
response = self.client.post(f'/api/2/shipments/{shipment.id}/mark_shipped/')
self.assertEqual(response.status_code, 400)
self.assertIn('error', response.json())
self.assertIn('Invalid state transition', response.json()['error'])
# Verify state hasn't changed
shipment.refresh_from_db()
self.assertEqual(shipment.state, Shipment.CREATED)
def test_add_items_without_review_requirement(self):
"""Test adding items without specifying review requirement"""
# Create a shipment first
shipment = Shipment.objects.create(
recipient_name='Wei Chen',
street_address='88 Century Avenue',
city='Shanghai',
state_province='Shanghai',
postal_code='200120',
country='China'
)
data = {
'item_ids': [self.item1.id, self.item2.id]
}
response = self.client.post(f'/api/2/shipments/{shipment.id}/add_items/', data, content_type='application/json')
self.assertEqual(response.status_code, 400)
self.assertIn('error', response.json())
# Verify state hasn't changed
shipment.refresh_from_db()
self.assertEqual(shipment.state, Shipment.CREATED)
def test_approve_without_items(self):
"""Test approving a shipment without items"""
# Create a shipment with REVIEW_ITEMS requirement
shipment = Shipment.objects.create(
recipient_name='Raj Patel',
street_address='123 MG Road',
city='Mumbai',
state_province='Maharashtra',
postal_code='400001',
country='India',
state=Shipment.PENDING_REVIEW,
review_requirement=Shipment.REVIEW_ITEMS
)
response = self.client.post(f'/api/2/shipments/{shipment.id}/approve/')
self.assertEqual(response.status_code, 400)
self.assertIn('error', response.json())
# Verify state hasn't changed
shipment.refresh_from_db()
self.assertEqual(shipment.state, Shipment.PENDING_REVIEW)
def test_approve_without_address(self):
"""Test approving a shipment without complete address"""
# Create a shipment with REVIEW_ADDRESS requirement
shipment = Shipment.objects.create(
recipient_name='Sofia Oliveira',
street_address='Avenida Paulista 1000',
city='São Paulo',
state_province='São Paulo',
postal_code='01310-100',
country='', # Missing country
state=Shipment.PENDING_REVIEW,
review_requirement=Shipment.REVIEW_ADDRESS
)
shipment.related_items.add(self.item1)
response = self.client.post(f'/api/2/shipments/{shipment.id}/approve/')
self.assertEqual(response.status_code, 400)
self.assertIn('error', response.json())
# Verify state hasn't changed
shipment.refresh_from_db()
self.assertEqual(shipment.state, Shipment.PENDING_REVIEW)
def test_public_shipment_detail_success(self):
"""Test retrieving shipment details via public API with valid public_secret"""
# Create a shipment in PENDING_REVIEW state
shipment = Shipment.objects.create(
recipient_name='John Doe',
street_address='123 Main St',
city='Anytown',
state_province='State',
postal_code='12345',
country='Country',
state=Shipment.PENDING_REVIEW,
review_requirement=Shipment.REVIEW_ADDRESS
)
shipment.related_items.add(self.item1)
# Create a client without authentication
public_client = Client()
# Test retrieving shipment details
response = public_client.get(f'/api/2/public/shipments/{shipment.public_secret}/')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()['id'], shipment.id)
self.assertEqual(response.json()['recipient_name'], 'John Doe')
self.assertEqual(response.json()['state'], Shipment.PENDING_REVIEW)
self.assertEqual(len(response.json()['related_items']), 1)
def test_public_shipment_detail_not_found(self):
"""Test retrieving shipment details with non-existent public_secret"""
# Create a client without authentication
public_client = Client()
# Test with non-existent public_secret
response = public_client.get('/api/2/public/shipments/00000000-0000-0000-0000-000000000000/')
self.assertEqual(response.status_code, 404)
def test_public_shipment_detail_wrong_state(self):
"""Test retrieving shipment details when shipment is not in PENDING_REVIEW state"""
# Create a shipment in CREATED state
shipment = Shipment.objects.create(
recipient_name='Jane Smith',
street_address='456 Oak Ave',
city='Somewhere',
state_province='Province',
postal_code='67890',
country='Nation',
state=Shipment.CREATED
)
# Create a client without authentication
public_client = Client()
# Test retrieving shipment details
response = public_client.get(f'/api/2/public/shipments/{shipment.public_secret}/')
self.assertEqual(response.status_code, 404)
def test_public_shipment_update_success_address_only(self):
"""Test updating shipment with address information when review_requirement is ADDRESS"""
# Create a shipment in PENDING_REVIEW state with ADDRESS review requirement
shipment = Shipment.objects.create(
recipient_name='Alice Johnson',
street_address='789 Pine Rd',
city='Nowhere',
state_province='Region',
postal_code='13579',
country='Land',
state=Shipment.PENDING_REVIEW,
review_requirement=Shipment.REVIEW_ADDRESS
)
shipment.related_items.add(self.item1)
# Create a client without authentication
public_client = Client()
# Test updating shipment with address information
data = {
'recipient_name': 'Alice Johnson',
'street_address': '789 Pine Rd, Apt 4B',
'city': 'Nowhere',
'state_province': 'Region',
'postal_code': '13579',
'country': 'Land'
}
response = public_client.post(
f'/api/2/public/shipments/{shipment.public_secret}/update/',
data,
content_type='application/json'
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()['state'], Shipment.READY_FOR_SHIPPING)
self.assertEqual(response.json()['street_address'], '789 Pine Rd, Apt 4B')
# Verify state has changed
shipment.refresh_from_db()
self.assertEqual(shipment.state, Shipment.READY_FOR_SHIPPING)
def test_public_shipment_update_success_both_review(self):
"""Test updating shipment with address information when review_requirement is BOTH"""
# Create a shipment in PENDING_REVIEW state with BOTH review requirement
shipment = Shipment.objects.create(
recipient_name='Bob Wilson',
street_address='321 Elm St',
city='Elsewhere',
state_province='Territory',
postal_code='24680',
country='Realm',
state=Shipment.PENDING_REVIEW,
review_requirement=Shipment.REVIEW_BOTH
)
shipment.related_items.add(self.item1)
# Create a client without authentication
public_client = Client()
# Test updating shipment with address information
data = {
'recipient_name': 'Bob Wilson',
'street_address': '321 Elm St, Suite 7',
'city': 'Elsewhere',
'state_province': 'Territory',
'postal_code': '24680',
'country': 'Realm'
}
response = public_client.post(
f'/api/2/public/shipments/{shipment.public_secret}/update/',
data,
content_type='application/json'
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()['state'], Shipment.READY_FOR_SHIPPING)
self.assertEqual(response.json()['street_address'], '321 Elm St, Suite 7')
# Verify state has changed
shipment.refresh_from_db()
self.assertEqual(shipment.state, Shipment.READY_FOR_SHIPPING)
def test_public_shipment_update_success_items_only(self):
"""Test updating shipment with address information when review_requirement is ITEMS"""
# Create a shipment in PENDING_REVIEW state with ITEMS review requirement
shipment = Shipment.objects.create(
recipient_name='Carol Davis',
street_address='654 Maple Dr',
city='Anywhere',
state_province='District',
postal_code='97531',
country='Kingdom',
state=Shipment.PENDING_REVIEW,
review_requirement=Shipment.REVIEW_ITEMS
)
shipment.related_items.add(self.item1)
# Create a client without authentication
public_client = Client()
# Test updating shipment with address information
data = {
'recipient_name': 'Carol Davis',
'street_address': '654 Maple Dr, Unit 3',
'city': 'Anywhere',
'state_province': 'District',
'postal_code': '97531',
'country': 'Kingdom'
}
response = public_client.post(
f'/api/2/public/shipments/{shipment.public_secret}/update/',
data,
content_type='application/json'
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()['state'], Shipment.PENDING_REVIEW) # State should not change
self.assertEqual(response.json()['street_address'], '654 Maple Dr, Unit 3')
# Verify state has not changed
shipment.refresh_from_db()
self.assertEqual(shipment.state, Shipment.PENDING_REVIEW)
def test_public_shipment_update_not_found(self):
"""Test updating shipment with non-existent public_secret"""
# Create a client without authentication
public_client = Client()
# Test with non-existent public_secret
data = {
'recipient_name': 'Unknown Person',
'street_address': '123 Fake St',
'city': 'Fake City',
'state_province': 'Fake State',
'postal_code': '12345',
'country': 'Fake Country'
}
response = public_client.post(
'/api/2/public/shipments/00000000-0000-0000-0000-000000000000/update/',
data,
content_type='application/json'
)
self.assertEqual(response.status_code, 404)
def test_public_shipment_update_wrong_state(self):
"""Test updating shipment when it's not in PENDING_REVIEW state"""
# Create a shipment in CREATED state
shipment = Shipment.objects.create(
recipient_name='David Lee',
street_address='987 Cedar Ln',
city='Nowhere',
state_province='Province',
postal_code='13579',
country='Country',
state=Shipment.CREATED
)
# Create a client without authentication
public_client = Client()
# Test updating shipment
data = {
'recipient_name': 'David Lee',
'street_address': '987 Cedar Ln, Apt 2C',
'city': 'Nowhere',
'state_province': 'Province',
'postal_code': '13579',
'country': 'Country'
}
response = public_client.post(
f'/api/2/public/shipments/{shipment.public_secret}/update/',
data,
content_type='application/json'
)
self.assertEqual(response.status_code, 404)
# Verify state hasn't changed
shipment.refresh_from_db()
self.assertEqual(shipment.state, Shipment.CREATED)
def test_public_shipment_update_both_review_missing_items(self):
"""Test updating shipment with BOTH review requirement when items are missing"""
# Create a shipment in PENDING_REVIEW state with BOTH review requirement but no items
shipment = Shipment.objects.create(
recipient_name='Eve Brown',
street_address='147 Birch Ave',
city='Somewhere',
state_province='Region',
postal_code='24680',
country='Land',
state=Shipment.PENDING_REVIEW,
review_requirement=Shipment.REVIEW_BOTH
)
# Create a client without authentication
public_client = Client()
# Test updating shipment with address information
data = {
'recipient_name': 'Eve Brown',
'street_address': '147 Birch Ave, Suite 5',
'city': 'Somewhere',
'state_province': 'Region',
'postal_code': '24680',
'country': 'Land'
}
response = public_client.post(
f'/api/2/public/shipments/{shipment.public_secret}/update/',
data,
content_type='application/json'
)
self.assertEqual(response.status_code, 400)
self.assertIn('error', response.json())
self.assertIn('Items must be added before approval', response.json()['error'])
# Verify state hasn't changed
shipment.refresh_from_db()
self.assertEqual(shipment.state, Shipment.PENDING_REVIEW)