project initialization
Some checks failed
System Monitoring / Health Checks (push) Has been cancelled
System Monitoring / Performance Monitoring (push) Has been cancelled
System Monitoring / Database Monitoring (push) Has been cancelled
System Monitoring / Cache Monitoring (push) Has been cancelled
System Monitoring / Log Monitoring (push) Has been cancelled
System Monitoring / Resource Monitoring (push) Has been cancelled
System Monitoring / Uptime Monitoring (push) Has been cancelled
System Monitoring / Backup Monitoring (push) Has been cancelled
System Monitoring / Security Monitoring (push) Has been cancelled
System Monitoring / Monitoring Dashboard (push) Has been cancelled
System Monitoring / Alerting (push) Has been cancelled
Security Scanning / Dependency Scanning (push) Has been cancelled
Security Scanning / Code Security Scanning (push) Has been cancelled
Security Scanning / Secrets Scanning (push) Has been cancelled
Security Scanning / Container Security Scanning (push) Has been cancelled
Security Scanning / Compliance Checking (push) Has been cancelled
Security Scanning / Security Dashboard (push) Has been cancelled
Security Scanning / Security Remediation (push) Has been cancelled

This commit is contained in:
2025-10-05 02:37:33 +08:00
parent 2cbb6d5fa1
commit b3fff546e9
226 changed files with 97805 additions and 35 deletions

View File

@@ -0,0 +1,470 @@
"""
Unit tests for Logistics Models
Tests for logistics module models:
- Shipment
- Vehicle
Author: Claude
"""
import pytest
from django.test import TestCase
from django.contrib.auth import get_user_model
from django.utils import timezone
from decimal import Decimal
from datetime import date, time, timedelta
from backend.src.core.models.tenant import Tenant
from backend.src.core.models.user import User
from backend.src.modules.logistics.models.shipment import Shipment
from backend.src.modules.logistics.models.vehicle import Vehicle
User = get_user_model()
class ShipmentModelTest(TestCase):
"""Test cases for Shipment model"""
def setUp(self):
self.tenant = Tenant.objects.create(
name='Test Logistics Sdn Bhd',
schema_name='test_logistics',
domain='testlogistics.com',
business_type='logistics'
)
self.user = User.objects.create_user(
username='dispatcher',
email='dispatcher@test.com',
password='test123',
tenant=self.tenant,
role='staff'
)
self.shipment_data = {
'tenant': self.tenant,
'tracking_number': 'TRK-2024010001-MY',
'order_number': 'ORD-2024010001',
'sender_name': 'Test Sender',
'sender_company': 'Test Company',
'sender_phone': '+60123456789',
'sender_email': 'sender@test.com',
'sender_address': '123 Sender Street',
'sender_city': 'Kuala Lumpur',
'sender_state': 'KUL',
'sender_postal_code': '50000',
'receiver_name': 'Test Receiver',
'receiver_company': 'Test Receiver Company',
'receiver_phone': '+60123456788',
'receiver_email': 'receiver@test.com',
'receiver_address': '456 Receiver Street',
'receiver_city': 'Penang',
'receiver_state': 'PNG',
'receiver_postal_code': '10000',
'origin_state': 'KUL',
'destination_state': 'PNG',
'service_type': 'express',
'package_type': 'document',
'weight': Decimal('1.5'),
'length': Decimal('30.0'),
'width': Decimal('20.0'),
'height': Decimal('10.0'),
'declared_value': Decimal('100.00'),
'currency': 'MYR',
'shipping_cost': Decimal('15.00'),
'payment_method': 'cash',
'payment_status': 'paid',
'status': 'processing',
'priority': 'normal',
'special_instructions': 'Handle with care',
'insurance_required': False,
'insurance_amount': Decimal('0.00'),
'estimated_delivery': date.today() + timedelta(days=2),
'actual_delivery': None,
'proof_of_delivery': '',
'delivery_confirmation': False,
'created_by': self.user
}
def test_create_shipment(self):
"""Test creating a new shipment"""
shipment = Shipment.objects.create(**self.shipment_data)
self.assertEqual(shipment.tenant, self.tenant)
self.assertEqual(shipment.tracking_number, self.shipment_data['tracking_number'])
self.assertEqual(shipment.order_number, self.shipment_data['order_number'])
self.assertEqual(shipment.sender_name, self.shipment_data['sender_name'])
self.assertEqual(shipment.receiver_name, self.shipment_data['receiver_name'])
self.assertEqual(shipment.service_type, self.shipment_data['service_type'])
self.assertEqual(shipment.weight, self.shipment_data['weight'])
self.assertEqual(shipment.shipping_cost, self.shipment_data['shipping_cost'])
self.assertEqual(shipment.status, self.shipment_data['status'])
def test_shipment_string_representation(self):
"""Test shipment string representation"""
shipment = Shipment.objects.create(**self.shipment_data)
self.assertEqual(str(shipment), f"{shipment.tracking_number} - {shipment.sender_name} to {shipment.receiver_name}")
def test_shipment_volume_calculation(self):
"""Test shipment volume calculation"""
shipment = Shipment.objects.create(**self.shipment_data)
# Volume = length × width × height (in cm)
expected_volume = Decimal('6000.0') # 30.0 × 20.0 × 10.0
self.assertEqual(shipment.volume, expected_volume)
def test_shipment_delivery_status(self):
"""Test shipment delivery status"""
shipment = Shipment.objects.create(**self.shipment_data)
# Not delivered yet
self.assertFalse(shipment.is_delivered)
# Mark as delivered
shipment.status = 'delivered'
shipment.actual_delivery = date.today()
shipment.delivery_confirmation = True
shipment.save()
self.assertTrue(shipment.is_delivered)
def test_shipment_delayed_status(self):
"""Test shipment delayed status"""
shipment = Shipment.objects.create(**self.shipment_data)
# Not delayed (estimated delivery is in future)
self.assertFalse(shipment.is_delayed)
# Mark as delayed (past estimated delivery)
shipment.estimated_delivery = date.today() - timedelta(days=1)
shipment.status = 'in_transit'
shipment.save()
self.assertTrue(shipment.is_delayed)
def test_shipment_service_type_choices(self):
"""Test shipment service type validation"""
invalid_data = self.shipment_data.copy()
invalid_data['service_type'] = 'invalid_service'
with self.assertRaises(Exception):
Shipment.objects.create(**invalid_data)
def test_shipment_package_type_choices(self):
"""Test shipment package type validation"""
invalid_data = self.shipment_data.copy()
invalid_data['package_type'] = 'invalid_package'
with self.assertRaises(Exception):
Shipment.objects.create(**invalid_data)
def test_shipment_status_choices(self):
"""Test shipment status validation"""
invalid_data = self.shipment_data.copy()
invalid_data['status'] = 'invalid_status'
with self.assertRaises(Exception):
Shipment.objects.create(**invalid_data)
def test_shipment_priority_choices(self):
"""Test shipment priority validation"""
invalid_data = self.shipment_data.copy()
invalid_data['priority'] = 'invalid_priority'
with self.assertRaises(Exception):
Shipment.objects.create(**invalid_data)
def test_shipment_malaysian_phone_validation(self):
"""Test Malaysian phone number validation"""
# Valid Malaysian phone numbers
shipment = Shipment.objects.create(**self.shipment_data)
self.assertEqual(shipment.sender_phone, self.shipment_data['sender_phone'])
self.assertEqual(shipment.receiver_phone, self.shipment_data['receiver_phone'])
# Invalid sender phone
invalid_data = self.shipment_data.copy()
invalid_data['sender_phone'] = '12345'
with self.assertRaises(Exception):
Shipment.objects.create(**invalid_data)
# Invalid receiver phone
invalid_data = self.shipment_data.copy()
invalid_data['receiver_phone'] = '67890'
with self.assertRaises(Exception):
Shipment.objects.create(**invalid_data)
def test_shipment_malaysian_state_validation(self):
"""Test Malaysian state validation"""
# Valid Malaysian states
shipment = Shipment.objects.create(**self.shipment_data)
self.assertEqual(shipment.sender_state, self.shipment_data['sender_state'])
self.assertEqual(shipment.receiver_state, self.shipment_data['receiver_state'])
# Invalid sender state
invalid_data = self.shipment_data.copy()
invalid_data['sender_state'] = 'XX'
with self.assertRaises(Exception):
Shipment.objects.create(**invalid_data)
def test_shipment_weight_validation(self):
"""Test shipment weight validation"""
# Valid weight
shipment = Shipment.objects.create(**self.shipment_data)
self.assertEqual(shipment.weight, self.shipment_data['weight'])
# Invalid weight (negative)
invalid_data = self.shipment_data.copy()
invalid_data['weight'] = Decimal('-1.0')
with self.assertRaises(Exception):
Shipment.objects.create(**invalid_data)
def test_shipment_tracking_number_format(self):
"""Test shipment tracking number format"""
shipment = Shipment.objects.create(**self.shipment_data)
# Should end with -MY for Malaysia
self.assertTrue(shipment.tracking_number.endswith('-MY'))
# Should be unique
with self.assertRaises(Exception):
Shipment.objects.create(**self.shipment_data)
def test_shipment_dimensions_validation(self):
"""Test shipment dimensions validation"""
# Valid dimensions
shipment = Shipment.objects.create(**self.shipment_data)
self.assertEqual(shipment.length, self.shipment_data['length'])
self.assertEqual(shipment.width, self.shipment_data['width'])
self.assertEqual(shipment.height, self.shipment_data['height'])
# Invalid dimensions (negative)
invalid_data = self.shipment_data.copy()
invalid_data['length'] = Decimal('-1.0')
with self.assertRaises(Exception):
Shipment.objects.create(**invalid_data)
class VehicleModelTest(TestCase):
"""Test cases for Vehicle model"""
def setUp(self):
self.tenant = Tenant.objects.create(
name='Test Logistics Sdn Bhd',
schema_name='test_logistics',
domain='testlogistics.com',
business_type='logistics'
)
self.user = User.objects.create_user(
username='manager',
email='manager@test.com',
password='test123',
tenant=self.tenant,
role='admin'
)
self.vehicle_data = {
'tenant': self.tenant,
'vehicle_number': 'V1234',
'registration_number': 'WAB1234',
'vehicle_type': 'van',
'make': 'Toyota',
'model': 'Hiace',
'year': 2020,
'color': 'White',
'chassis_number': 'MR0HE3CD5L123456',
'engine_number': '2TR123456',
'capacity': 1000, # kg
'volume_capacity': 10.0, # cubic meters
'fuel_type': 'petrol',
'fuel_capacity': 70, # liters
'current_fuel': 50, # liters
'purchase_date': date(2020, 1, 1),
'purchase_price': Decimal('120000.00'),
'insurance_policy': 'INS-2024-001234',
'insurance_expiry': date.today() + timedelta(days=365),
'road_tax_expiry': date.today() + timedelta(days=180),
'inspection_expiry': date.today() + timedelta(days=90),
'current_mileage': 50000,
'last_service_mileage': 45000,
'next_service_mileage': 55000,
'status': 'active',
'assigned_driver': None,
'gps_device_id': 'GPS001234',
'is_active': True,
'notes': 'Well-maintained vehicle',
'created_by': self.user
}
def test_create_vehicle(self):
"""Test creating a new vehicle"""
vehicle = Vehicle.objects.create(**self.vehicle_data)
self.assertEqual(vehicle.tenant, self.tenant)
self.assertEqual(vehicle.vehicle_number, self.vehicle_data['vehicle_number'])
self.assertEqual(vehicle.registration_number, self.vehicle_data['registration_number'])
self.assertEqual(vehicle.vehicle_type, self.vehicle_data['vehicle_type'])
self.assertEqual(vehicle.make, self.vehicle_data['make'])
self.assertEqual(vehicle.model, self.vehicle_data['model'])
self.assertEqual(vehicle.year, self.vehicle_data['year'])
self.assertEqual(vehicle.capacity, self.vehicle_data['capacity'])
self.assertEqual(vehicle.status, self.vehicle_data['status'])
self.assertTrue(vehicle.is_active)
def test_vehicle_string_representation(self):
"""Test vehicle string representation"""
vehicle = Vehicle.objects.create(**self.vehicle_data)
self.assertEqual(str(vehicle), f"{vehicle.make} {vehicle.model} ({vehicle.registration_number})")
def test_vehicle_age(self):
"""Test vehicle age calculation"""
vehicle = Vehicle.objects.create(**self.vehicle_data)
# Age should be calculated based on purchase date
today = date.today()
expected_age = today.year - vehicle.purchase_date.year
if today.month < vehicle.purchase_date.month or (today.month == vehicle.purchase_date.month and today.day < vehicle.purchase_date.day):
expected_age -= 1
self.assertEqual(vehicle.age, expected_age)
def test_vehicle_service_due(self):
"""Test vehicle service due status"""
vehicle = Vehicle.objects.create(**self.vehicle_data)
# Service not due yet
self.assertFalse(vehicle.service_due)
# Mark as service due
vehicle.current_mileage = 56000
vehicle.save()
self.assertTrue(vehicle.service_due)
def test_vehicle_insurance_expiry_status(self):
"""Test vehicle insurance expiry status"""
vehicle = Vehicle.objects.create(**self.vehicle_data)
# Insurance not expired
self.assertFalse(vehicle.insurance_expired)
# Mark as expired
vehicle.insurance_expiry = date.today() - timedelta(days=1)
vehicle.save()
self.assertTrue(vehicle.insurance_expired)
def test_vehicle_road_tax_expiry_status(self):
"""Test vehicle road tax expiry status"""
vehicle = Vehicle.objects.create(**self.vehicle_data)
# Road tax not expired
self.assertFalse(vehicle.road_tax_expired)
# Mark as expired
vehicle.road_tax_expiry = date.today() - timedelta(days=1)
vehicle.save()
self.assertTrue(vehicle.road_tax_expired)
def test_vehicle_inspection_expiry_status(self):
"""Test vehicle inspection expiry status"""
vehicle = Vehicle.objects.create(**self.vehicle_data)
# Inspection not expired
self.assertFalse(vehicle.inspection_expired)
# Mark as expired
vehicle.inspection_expiry = date.today() - timedelta(days=1)
vehicle.save()
self.assertTrue(vehicle.inspection_expired)
def test_vehicle_type_choices(self):
"""Test vehicle type validation"""
invalid_data = self.vehicle_data.copy()
invalid_data['vehicle_type'] = 'invalid_type'
with self.assertRaises(Exception):
Vehicle.objects.create(**invalid_data)
def test_vehicle_fuel_type_choices(self):
"""Test vehicle fuel type validation"""
invalid_data = self.vehicle_data.copy()
invalid_data['fuel_type'] = 'invalid_fuel'
with self.assertRaises(Exception):
Vehicle.objects.create(**invalid_data)
def test_vehicle_status_choices(self):
"""Test vehicle status validation"""
invalid_data = self.vehicle_data.copy()
invalid_data['status'] = 'invalid_status'
with self.assertRaises(Exception):
Vehicle.objects.create(**invalid_data)
def test_vehicle_malaysian_registration_validation(self):
"""Test Malaysian vehicle registration validation"""
# Valid registration number
vehicle = Vehicle.objects.create(**self.vehicle_data)
self.assertEqual(vehicle.registration_number, self.vehicle_data['registration_number'])
# Invalid registration number format
invalid_data = self.vehicle_data.copy()
invalid_data['registration_number'] = 'ABC123'
with self.assertRaises(Exception):
Vehicle.objects.create(**invalid_data)
def test_vehicle_mileage_validation(self):
"""Test vehicle mileage validation"""
# Valid mileage
vehicle = Vehicle.objects.create(**self.vehicle_data)
self.assertEqual(vehicle.current_mileage, self.vehicle_data['current_mileage'])
# Invalid mileage (negative)
invalid_data = self.vehicle_data.copy()
invalid_data['current_mileage'] = -1000
with self.assertRaises(Exception):
Vehicle.objects.create(**invalid_data)
def test_vehicle_capacity_validation(self):
"""Test vehicle capacity validation"""
# Valid capacity
vehicle = Vehicle.objects.create(**self.vehicle_data)
self.assertEqual(vehicle.capacity, self.vehicle_data['capacity'])
# Invalid capacity (negative)
invalid_data = self.vehicle_data.copy()
invalid_data['capacity'] = -100
with self.assertRaises(Exception):
Vehicle.objects.create(**invalid_data)
def test_vehicle_year_validation(self):
"""Test vehicle year validation"""
# Valid year
vehicle = Vehicle.objects.create(**self.vehicle_data)
self.assertEqual(vehicle.year, self.vehicle_data['year'])
# Invalid year (too old)
invalid_data = self.vehicle_data.copy()
invalid_data['year'] = 1950
with self.assertRaises(Exception):
Vehicle.objects.create(**invalid_data)
def test_vehicle_fuel_level_validation(self):
"""Test vehicle fuel level validation"""
# Valid fuel level
vehicle = Vehicle.objects.create(**self.vehicle_data)
self.assertEqual(vehicle.current_fuel, self.vehicle_data['current_fuel'])
# Invalid fuel level (negative)
invalid_data = self.vehicle_data.copy()
invalid_data['current_fuel'] = -10
with self.assertRaises(Exception):
Vehicle.objects.create(**invalid_data)
# Invalid fuel level (exceeds capacity)
invalid_data = self.vehicle_data.copy()
invalid_data['current_fuel'] = 100
with self.assertRaises(Exception):
Vehicle.objects.create(**invalid_data)