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

View File

@@ -0,0 +1,459 @@
"""
Unit tests for Beauty Models
Tests for beauty module models:
- Client
- Service
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.beauty.models.client import Client
from backend.src.modules.beauty.models.service import Service
User = get_user_model()
class ClientModelTest(TestCase):
"""Test cases for Client model"""
def setUp(self):
self.tenant = Tenant.objects.create(
name='Test Beauty Salon',
schema_name='test_beauty',
domain='testbeauty.com',
business_type='beauty'
)
self.user = User.objects.create_user(
username='receptionist',
email='receptionist@test.com',
password='test123',
tenant=self.tenant,
role='staff'
)
self.client_data = {
'tenant': self.tenant,
'client_number': 'C2024010001',
'first_name': 'Siti',
'last_name': 'Binti Ahmad',
'ic_number': '000101-01-0001',
'passport_number': '',
'nationality': 'Malaysian',
'gender': 'female',
'date_of_birth': date(1995, 1, 1),
'email': 'siti.client@test.com',
'phone': '+60123456789',
'whatsapp_number': '+60123456789',
'emergency_contact_name': 'Ahmad Bin Ibrahim',
'emergency_contact_phone': '+60123456788',
'emergency_contact_relationship': 'Husband',
'address': '123 Beauty Street',
'city': 'Kuala Lumpur',
'state': 'KUL',
'postal_code': '50000',
'occupation': 'Office Worker',
'company': 'Test Company',
'skin_type': 'normal',
'hair_type': 'straight',
'allergies': 'None',
'skin_conditions': 'None',
'medications': 'None',
'pregnancy_status': False,
'pregnancy_due_date': None,
'breastfeeding': False,
'preferred_services': ['facial', 'manicure'],
'membership_tier': 'basic',
'loyalty_points': 0,
'total_spent': Decimal('0.00'),
'visit_count': 0,
'last_visit_date': None,
'preferred_stylist': '',
'preferred_appointment_time': 'morning',
'marketing_consent': True,
'sms_consent': True,
'email_consent': True,
'photo_consent': False,
'medical_consent': True,
'privacy_consent': True,
'notes': 'New client',
'referral_source': 'walk_in',
'referred_by': '',
'is_active': True,
'created_by': self.user
}
def test_create_client(self):
"""Test creating a new client"""
client = Client.objects.create(**self.client_data)
self.assertEqual(client.tenant, self.tenant)
self.assertEqual(client.client_number, self.client_data['client_number'])
self.assertEqual(client.first_name, self.client_data['first_name'])
self.assertEqual(client.last_name, self.client_data['last_name'])
self.assertEqual(client.ic_number, self.client_data['ic_number'])
self.assertEqual(client.gender, self.client_data['gender'])
self.assertEqual(client.skin_type, self.client_data['skin_type'])
self.assertEqual(client.membership_tier, self.client_data['membership_tier'])
self.assertEqual(client.loyalty_points, self.client_data['loyalty_points'])
self.assertTrue(client.is_active)
def test_client_string_representation(self):
"""Test client string representation"""
client = Client.objects.create(**self.client_data)
self.assertEqual(str(client), f"{client.first_name} {client.last_name} ({client.client_number})")
def test_client_full_name(self):
"""Test client full name property"""
client = Client.objects.create(**self.client_data)
self.assertEqual(client.full_name, f"{client.first_name} {client.last_name}")
def test_client_age(self):
"""Test client age calculation"""
client = Client.objects.create(**self.client_data)
# Age should be calculated based on date of birth
today = date.today()
expected_age = today.year - client.date_of_birth.year
if today.month < client.date_of_birth.month or (today.month == client.date_of_birth.month and today.day < client.date_of_birth.day):
expected_age -= 1
self.assertEqual(client.age, expected_age)
def test_client_malaysian_ic_validation(self):
"""Test Malaysian IC number validation"""
# Valid IC number
client = Client.objects.create(**self.client_data)
self.assertEqual(client.ic_number, self.client_data['ic_number'])
# Invalid IC number format
invalid_data = self.client_data.copy()
invalid_data['ic_number'] = '123'
with self.assertRaises(Exception):
Client.objects.create(**invalid_data)
def test_client_gender_choices(self):
"""Test client gender validation"""
invalid_data = self.client_data.copy()
invalid_data['gender'] = 'invalid_gender'
with self.assertRaises(Exception):
Client.objects.create(**invalid_data)
def test_client_membership_tier_choices(self):
"""Test client membership tier validation"""
invalid_data = self.client_data.copy()
invalid_data['membership_tier'] = 'invalid_tier'
with self.assertRaises(Exception):
Client.objects.create(**invalid_data)
def test_client_skin_type_choices(self):
"""Test client skin type validation"""
invalid_data = self.client_data.copy()
invalid_data['skin_type'] = 'invalid_skin'
with self.assertRaises(Exception):
Client.objects.create(**invalid_data)
def test_client_hair_type_choices(self):
"""Test client hair type validation"""
invalid_data = self.client_data.copy()
invalid_data['hair_type'] = 'invalid_hair'
with self.assertRaises(Exception):
Client.objects.create(**invalid_data)
def test_client_phone_validation(self):
"""Test Malaysian phone number validation"""
# Valid Malaysian phone numbers
client = Client.objects.create(**self.client_data)
self.assertEqual(client.phone, self.client_data['phone'])
self.assertEqual(client.whatsapp_number, self.client_data['whatsapp_number'])
# Invalid phone
invalid_data = self.client_data.copy()
invalid_data['phone'] = '12345'
with self.assertRaises(Exception):
Client.objects.create(**invalid_data)
def test_client_medical_information(self):
"""Test client medical information validation"""
client = Client.objects.create(**self.client_data)
self.assertEqual(client.allergies, self.client_data['allergies'])
self.assertEqual(client.skin_conditions, self.client_data['skin_conditions'])
self.assertEqual(client.medications, self.client_data['medications'])
self.assertFalse(client.pregnancy_status)
self.assertFalse(client.breastfeeding)
def test_client_consent_preferences(self):
"""Test client consent preferences"""
client = Client.objects.create(**self.client_data)
self.assertTrue(client.marketing_consent)
self.assertTrue(client.sms_consent)
self.assertTrue(client.email_consent)
self.assertFalse(client.photo_consent)
self.assertTrue(client.medical_consent)
self.assertTrue(client.privacy_consent)
def test_client_loyalty_program(self):
"""Test client loyalty program features"""
client = Client.objects.create(**self.client_data)
self.assertEqual(client.loyalty_points, 0)
self.assertEqual(client.total_spent, Decimal('0.00'))
self.assertEqual(client.visit_count, 0)
# Test tier progression logic
self.assertEqual(client.membership_tier, 'basic')
def test_client_referral_source_choices(self):
"""Test client referral source validation"""
# Test valid referral sources
valid_sources = ['walk_in', 'friend', 'social_media', 'advertisement', 'online', 'other']
for source in valid_sources:
data = self.client_data.copy()
data['referral_source'] = source
client = Client.objects.create(**data)
self.assertEqual(client.referral_source, source)
# Test invalid referral source
invalid_data = self.client_data.copy()
invalid_data['referral_source'] = 'invalid_source'
with self.assertRaises(Exception):
Client.objects.create(**invalid_data)
class ServiceModelTest(TestCase):
"""Test cases for Service model"""
def setUp(self):
self.tenant = Tenant.objects.create(
name='Test Beauty Salon',
schema_name='test_beauty',
domain='testbeauty.com',
business_type='beauty'
)
self.user = User.objects.create_user(
username='manager',
email='manager@test.com',
password='test123',
tenant=self.tenant,
role='admin'
)
self.service_data = {
'tenant': self.tenant,
'service_code': 'FAC-BASIC-001',
'name': 'Basic Facial Treatment',
'description': 'A basic facial treatment for all skin types',
'service_category': 'facial',
'duration': 60, # minutes
'base_price': Decimal('80.00'),
'premium_price': Decimal('120.00'),
'vip_price': Decimal('100.00'),
'tax_rate': 6.0, # SST
'is_taxable': True,
'commission_rate': 20.0, # percentage
'difficulty_level': 'basic',
'experience_required': 0, # years
'min_age': 16,
'max_age': 65,
'suitable_for_skin_types': ['normal', 'dry', 'oily', 'combination', 'sensitive'],
'suitable_for_hair_types': [],
'pregnancy_safe': True,
'breastfeeding_safe': True,
'requires_patch_test': False,
'has_contraindications': False,
'contraindications': '',
'equipment_required': ['Facial steamer', 'Cleansing brush', 'Toner'],
'products_used': ['Cleanser', 'Toner', 'Moisturizer', 'Sunscreen'],
'steps': ['Cleansing', 'Exfoliation', 'Massage', 'Mask', 'Moisturizing'],
'aftercare_instructions': 'Avoid direct sunlight for 24 hours',
'frequency_limit_days': 7,
'is_active': True,
'is_popular': True,
'is_new': False,
'is_promotional': False,
'kkm_approval_required': False,
'kkm_approval_number': '',
'min_booking_notice_hours': 2,
'cancellation_policy_hours': 24,
'late_arrival_policy_minutes': 15,
'no_show_policy': 'fee',
'created_by': self.user
}
def test_create_service(self):
"""Test creating a new service"""
service = Service.objects.create(**self.service_data)
self.assertEqual(service.tenant, self.tenant)
self.assertEqual(service.service_code, self.service_data['service_code'])
self.assertEqual(service.name, self.service_data['name'])
self.assertEqual(service.service_category, self.service_data['service_category'])
self.assertEqual(service.duration, self.service_data['duration'])
self.assertEqual(service.base_price, self.service_data['base_price'])
self.assertEqual(service.tax_rate, self.service_data['tax_rate'])
self.assertEqual(service.difficulty_level, self.service_data['difficulty_level'])
self.assertTrue(service.is_active)
self.assertTrue(service.is_popular)
def test_service_string_representation(self):
"""Test service string representation"""
service = Service.objects.create(**self.service_data)
self.assertEqual(str(service), service.name)
def test_service_price_with_tax(self):
"""Test service price calculation with tax"""
service = Service.objects.create(**self.service_data)
# Base price with tax
expected_base_with_tax = service.base_price * (1 + service.tax_rate / 100)
self.assertEqual(service.base_price_with_tax, expected_base_with_tax)
# Premium price with tax
expected_premium_with_tax = service.premium_price * (1 + service.tax_rate / 100)
self.assertEqual(service.premium_price_with_tax, expected_premium_with_tax)
# VIP price with tax
expected_vip_with_tax = service.vip_price * (1 + service.tax_rate / 100)
self.assertEqual(service.vip_price_with_tax, expected_vip_with_tax)
def test_service_category_choices(self):
"""Test service category validation"""
invalid_data = self.service_data.copy()
invalid_data['service_category'] = 'invalid_category'
with self.assertRaises(Exception):
Service.objects.create(**invalid_data)
def test_service_difficulty_level_choices(self):
"""Test service difficulty level validation"""
invalid_data = self.service_data.copy()
invalid_data['difficulty_level'] = 'invalid_difficulty'
with self.assertRaises(Exception):
Service.objects.create(**invalid_data)
def test_service_tax_calculation(self):
"""Test service tax calculation"""
service = Service.objects.create(**self.service_data)
# Tax amount for base price
expected_tax = service.base_price * (service.tax_rate / 100)
self.assertEqual(service.tax_amount, expected_tax)
def test_service_commission_calculation(self):
"""Test service commission calculation"""
service = Service.objects.create(**self.service_data)
# Commission amount for base price
expected_commission = service.base_price * (service.commission_rate / 100)
self.assertEqual(service.commission_amount, expected_commission)
def test_service_age_validation(self):
"""Test service age validation"""
# Valid age range
service = Service.objects.create(**self.service_data)
self.assertEqual(service.min_age, self.service_data['min_age'])
self.assertEqual(service.max_age, self.service_data['max_age'])
# Invalid age range (min > max)
invalid_data = self.service_data.copy()
invalid_data['min_age'] = 30
invalid_data['max_age'] = 20
with self.assertRaises(Exception):
Service.objects.create(**invalid_data)
def test_service_malaysian_sst_validation(self):
"""Test Malaysian SST validation"""
# Valid SST rate
service = Service.objects.create(**self.service_data)
self.assertEqual(service.tax_rate, 6.0) # Standard SST rate
# Invalid SST rate (negative)
invalid_data = self.service_data.copy()
invalid_data['tax_rate'] = -1.0
with self.assertRaises(Exception):
Service.objects.create(**invalid_data)
def test_service_duration_validation(self):
"""Test service duration validation"""
# Valid duration
service = Service.objects.create(**self.service_data)
self.assertEqual(service.duration, self.service_data['duration'])
# Invalid duration (too short)
invalid_data = self.service_data.copy()
invalid_data['duration'] = 0
with self.assertRaises(Exception):
Service.objects.create(**invalid_data)
def test_service_price_validation(self):
"""Test service price validation"""
# Valid prices
service = Service.objects.create(**self.service_data)
self.assertEqual(service.base_price, self.service_data['base_price'])
self.assertEqual(service.premium_price, self.service_data['premium_price'])
self.assertEqual(service.vip_price, self.service_data['vip_price'])
# Invalid price (negative)
invalid_data = self.service_data.copy()
invalid_data['base_price'] = Decimal('-10.00')
with self.assertRaises(Exception):
Service.objects.create(**invalid_data)
def test_service_suitability_validation(self):
"""Test service suitability validation"""
service = Service.objects.create(**self.service_data)
# Check skin type suitability
self.assertIn('normal', service.suitable_for_skin_types)
self.assertIn('sensitive', service.suitable_for_skin_types)
# Check pregnancy safety
self.assertTrue(service.pregnancy_safe)
self.assertTrue(service.breastfeeding_safe)
def test_service_malaysian_beauty_regulations(self):
"""Test Malaysian beauty industry regulations"""
service = Service.objects.create(**self.service_data)
self.assertEqual(service.tax_rate, 6.0) # SST compliance
self.assertFalse(service.kkm_approval_required) # KKM approval status
# Test service requiring KKM approval
data = self.service_data.copy()
data['name'] = 'Advanced Laser Treatment'
data['kkm_approval_required'] = True
data['kkm_approval_number'] = 'KKM/2024/001234'
service_kkm = Service.objects.create(**data)
self.assertTrue(service_kkm.kkm_approval_required)
self.assertEqual(service_kkm.kkm_approval_number, data['kkm_approval_number'])
def test_service_booking_policies(self):
"""Test service booking policies"""
service = Service.objects.create(**self.service_data)
self.assertEqual(service.min_booking_notice_hours, 2)
self.assertEqual(service.cancellation_policy_hours, 24)
self.assertEqual(service.late_arrival_policy_minutes, 15)
self.assertEqual(service.no_show_policy, 'fee')

View File

@@ -0,0 +1,340 @@
"""
Unit tests for Core Models
Tests for all core models:
- Tenant
- User
- Subscription
- Module
- PaymentTransaction
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, timedelta
from backend.src.core.models.tenant import Tenant
from backend.src.core.models.user import User
from backend.src.core.models.subscription import Subscription
from backend.src.core.models.module import Module
from backend.src.core.models.payment import PaymentTransaction
User = get_user_model()
class TenantModelTest(TestCase):
"""Test cases for Tenant model"""
def setUp(self):
self.tenant_data = {
'name': 'Test Business Sdn Bhd',
'schema_name': 'test_business',
'domain': 'testbusiness.com',
'business_type': 'retail',
'registration_number': '202401000001',
'tax_id': 'MY123456789',
'contact_email': 'contact@testbusiness.com',
'contact_phone': '+60123456789',
'address': '123 Test Street, Kuala Lumpur',
'city': 'Kuala Lumpur',
'state': 'KUL',
'postal_code': '50000',
'country': 'Malaysia',
'is_active': True
}
def test_create_tenant(self):
"""Test creating a new tenant"""
tenant = Tenant.objects.create(**self.tenant_data)
self.assertEqual(tenant.name, self.tenant_data['name'])
self.assertEqual(tenant.schema_name, self.tenant_data['schema_name'])
self.assertEqual(tenant.business_type, self.tenant_data['business_type'])
self.assertTrue(tenant.is_active)
self.assertEqual(tenant.subscription_tier, 'free')
self.assertIsNotNone(tenant.created_at)
def test_tenant_string_representation(self):
"""Test tenant string representation"""
tenant = Tenant.objects.create(**self.tenant_data)
self.assertEqual(str(tenant), f"{tenant.name} ({tenant.schema_name})")
def test_tenant_business_type_choices(self):
"""Test tenant business type validation"""
invalid_data = self.tenant_data.copy()
invalid_data['business_type'] = 'invalid_type'
with self.assertRaises(Exception):
Tenant.objects.create(**invalid_data)
def test_tenant_malaysian_business_validation(self):
"""Test Malaysian business registration validation"""
# Valid registration number
tenant = Tenant.objects.create(**self.tenant_data)
self.assertEqual(tenant.registration_number, self.tenant_data['registration_number'])
# Invalid registration number format
invalid_data = self.tenant_data.copy()
invalid_data['registration_number'] = '123'
with self.assertRaises(Exception):
Tenant.objects.create(**invalid_data)
def test_tenant_phone_validation(self):
"""Test Malaysian phone number validation"""
# Valid Malaysian phone number
tenant = Tenant.objects.create(**self.tenant_data)
self.assertEqual(tenant.contact_phone, self.tenant_data['contact_phone'])
# Invalid phone number
invalid_data = self.tenant_data.copy()
invalid_data['contact_phone'] = '12345'
with self.assertRaises(Exception):
Tenant.objects.create(**invalid_data)
class UserModelTest(TestCase):
"""Test cases for User model"""
def setUp(self):
self.tenant = Tenant.objects.create(
name='Test Business Sdn Bhd',
schema_name='test_business',
domain='testbusiness.com',
business_type='retail'
)
self.user_data = {
'username': 'testuser',
'email': 'user@test.com',
'first_name': 'Test',
'last_name': 'User',
'phone': '+60123456789',
'ic_number': '000101-01-0001',
'tenant': self.tenant,
'role': 'owner',
'is_active': True
}
def test_create_user(self):
"""Test creating a new user"""
user = User.objects.create_user(**self.user_data)
self.assertEqual(user.username, self.user_data['username'])
self.assertEqual(user.email, self.user_data['email'])
self.assertEqual(user.tenant, self.tenant)
self.assertEqual(user.role, self.user_data['role'])
self.assertTrue(user.is_active)
self.assertFalse(user.is_staff)
def test_create_superuser(self):
"""Test creating a superuser"""
superuser = User.objects.create_superuser(
username='admin',
email='admin@test.com',
password='admin123'
)
self.assertTrue(superuser.is_staff)
self.assertTrue(superuser.is_superuser)
self.assertEqual(superuser.role, 'admin')
def test_user_string_representation(self):
"""Test user string representation"""
user = User.objects.create_user(**self.user_data)
self.assertEqual(str(user), user.email)
def test_user_full_name(self):
"""Test user full name property"""
user = User.objects.create_user(**self.user_data)
self.assertEqual(user.full_name, f"{user.first_name} {user.last_name}")
def test_user_malaysian_ic_validation(self):
"""Test Malaysian IC number validation"""
# Valid IC number
user = User.objects.create_user(**self.user_data)
self.assertEqual(user.ic_number, self.user_data['ic_number'])
# Invalid IC number
invalid_data = self.user_data.copy()
invalid_data['ic_number'] = '123'
with self.assertRaises(Exception):
User.objects.create_user(**invalid_data)
def test_user_role_choices(self):
"""Test user role validation"""
invalid_data = self.user_data.copy()
invalid_data['role'] = 'invalid_role'
with self.assertRaises(Exception):
User.objects.create_user(**invalid_data)
class SubscriptionModelTest(TestCase):
"""Test cases for Subscription model"""
def setUp(self):
self.tenant = Tenant.objects.create(
name='Test Business Sdn Bhd',
schema_name='test_business',
domain='testbusiness.com',
business_type='retail'
)
self.subscription_data = {
'tenant': self.tenant,
'plan': 'premium',
'status': 'active',
'start_date': date.today(),
'end_date': date.today() + timedelta(days=30),
'amount': Decimal('299.00'),
'currency': 'MYR',
'billing_cycle': 'monthly',
'auto_renew': True
}
def test_create_subscription(self):
"""Test creating a new subscription"""
subscription = Subscription.objects.create(**self.subscription_data)
self.assertEqual(subscription.tenant, self.tenant)
self.assertEqual(subscription.plan, self.subscription_data['plan'])
self.assertEqual(subscription.status, self.subscription_data['status'])
self.assertEqual(subscription.amount, self.subscription_data['amount'])
self.assertTrue(subscription.auto_renew)
def test_subscription_string_representation(self):
"""Test subscription string representation"""
subscription = Subscription.objects.create(**self.subscription_data)
expected = f"{self.tenant.name} - Premium ({subscription.status})"
self.assertEqual(str(subscription), expected)
def test_subscription_is_active_property(self):
"""Test subscription is_active property"""
# Active subscription
subscription = Subscription.objects.create(**self.subscription_data)
self.assertTrue(subscription.is_active)
# Expired subscription
subscription.end_date = date.today() - timedelta(days=1)
subscription.save()
self.assertFalse(subscription.is_active)
# Cancelled subscription
subscription.status = 'cancelled'
subscription.end_date = date.today() + timedelta(days=30)
subscription.save()
self.assertFalse(subscription.is_active)
def test_subscription_status_choices(self):
"""Test subscription status validation"""
invalid_data = self.subscription_data.copy()
invalid_data['status'] = 'invalid_status'
with self.assertRaises(Exception):
Subscription.objects.create(**invalid_data)
class ModuleModelTest(TestCase):
"""Test cases for Module model"""
def setUp(self):
self.module_data = {
'name': 'Retail Management',
'code': 'retail',
'description': 'Complete retail management solution',
'category': 'industry',
'version': '1.0.0',
'is_active': True,
'is_core': False,
'dependencies': ['core'],
'config_schema': {'features': ['inventory', 'sales']},
'pricing_tier': 'premium'
}
def test_create_module(self):
"""Test creating a new module"""
module = Module.objects.create(**self.module_data)
self.assertEqual(module.name, self.module_data['name'])
self.assertEqual(module.code, self.module_data['code'])
self.assertEqual(module.category, self.module_data['category'])
self.assertTrue(module.is_active)
self.assertFalse(module.is_core)
def test_module_string_representation(self):
"""Test module string representation"""
module = Module.objects.create(**self.module_data)
self.assertEqual(str(module), module.name)
def test_module_category_choices(self):
"""Test module category validation"""
invalid_data = self.module_data.copy()
invalid_data['category'] = 'invalid_category'
with self.assertRaises(Exception):
Module.objects.create(**invalid_data)
class PaymentTransactionModelTest(TestCase):
"""Test cases for PaymentTransaction model"""
def setUp(self):
self.tenant = Tenant.objects.create(
name='Test Business Sdn Bhd',
schema_name='test_business',
domain='testbusiness.com',
business_type='retail'
)
self.subscription = Subscription.objects.create(
tenant=self.tenant,
plan='premium',
status='active',
start_date=date.today(),
end_date=date.today() + timedelta(days=30),
amount=Decimal('299.00'),
currency='MYR'
)
self.payment_data = {
'tenant': self.tenant,
'subscription': self.subscription,
'transaction_id': 'PAY-2024010001',
'amount': Decimal('299.00'),
'currency': 'MYR',
'payment_method': 'fpx',
'status': 'completed',
'payment_date': timezone.now(),
'description': 'Monthly subscription payment'
}
def test_create_payment_transaction(self):
"""Test creating a new payment transaction"""
payment = PaymentTransaction.objects.create(**self.payment_data)
self.assertEqual(payment.tenant, self.tenant)
self.assertEqual(payment.subscription, self.subscription)
self.assertEqual(payment.transaction_id, self.payment_data['transaction_id'])
self.assertEqual(payment.amount, self.payment_data['amount'])
self.assertEqual(payment.status, self.payment_data['status'])
def test_payment_string_representation(self):
"""Test payment transaction string representation"""
payment = PaymentTransaction.objects.create(**self.payment_data)
expected = f"PAY-2024010001 - RM299.00 ({payment.status})"
self.assertEqual(str(payment), expected)
def test_payment_method_choices(self):
"""Test payment method validation"""
invalid_data = self.payment_data.copy()
invalid_data['payment_method'] = 'invalid_method'
with self.assertRaises(Exception):
PaymentTransaction.objects.create(**invalid_data)
def test_payment_status_choices(self):
"""Test payment status validation"""
invalid_data = self.payment_data.copy()
invalid_data['status'] = 'invalid_status'
with self.assertRaises(Exception):
PaymentTransaction.objects.create(**invalid_data)

View File

@@ -0,0 +1,413 @@
"""
Unit tests for Education Models
Tests for education module models:
- Student
- Class
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.education.models.student import Student
from backend.src.modules.education.models.class_model import Class
User = get_user_model()
class StudentModelTest(TestCase):
"""Test cases for Student model"""
def setUp(self):
self.tenant = Tenant.objects.create(
name='Test Education Center',
schema_name='test_education',
domain='testeducation.com',
business_type='education'
)
self.user = User.objects.create_user(
username='admin',
email='admin@test.com',
password='test123',
tenant=self.tenant,
role='admin'
)
self.student_data = {
'tenant': self.tenant,
'student_id': 'S2024010001',
'first_name': 'Ahmad',
'last_name': 'Bin Ibrahim',
'ic_number': '000101-01-0001',
'gender': 'male',
'date_of_birth': date(2010, 1, 1),
'nationality': 'Malaysian',
'religion': 'Islam',
'race': 'Malay',
'email': 'ahmad.student@test.com',
'phone': '+60123456789',
'address': '123 Student Street',
'city': 'Kuala Lumpur',
'state': 'KUL',
'postal_code': '50000',
'father_name': 'Ibrahim Bin Ali',
'father_phone': '+60123456788',
'father_occupation': 'Engineer',
'mother_name': 'Aminah Binti Ahmad',
'mother_phone': '+60123456787',
'mother_occupation': 'Teacher',
'emergency_contact_name': 'Ibrahim Bin Ali',
'emergency_contact_phone': '+60123456788',
'emergency_contact_relationship': 'Father',
'previous_school': 'SK Test Primary',
'previous_grade': '6A',
'current_grade': 'Form 1',
'stream': 'science',
'admission_date': date.today(),
'graduation_date': None,
'status': 'active',
'medical_conditions': 'None',
'allergies': 'None',
'special_needs': 'None',
'is_active': True,
'created_by': self.user
}
def test_create_student(self):
"""Test creating a new student"""
student = Student.objects.create(**self.student_data)
self.assertEqual(student.tenant, self.tenant)
self.assertEqual(student.student_id, self.student_data['student_id'])
self.assertEqual(student.first_name, self.student_data['first_name'])
self.assertEqual(student.last_name, self.student_data['last_name'])
self.assertEqual(student.ic_number, self.student_data['ic_number'])
self.assertEqual(student.gender, self.student_data['gender'])
self.assertEqual(student.current_grade, self.student_data['current_grade'])
self.assertEqual(student.stream, self.student_data['stream'])
self.assertEqual(student.status, self.student_data['status'])
self.assertTrue(student.is_active)
def test_student_string_representation(self):
"""Test student string representation"""
student = Student.objects.create(**self.student_data)
self.assertEqual(str(student), f"{student.first_name} {student.last_name} ({student.student_id})")
def test_student_full_name(self):
"""Test student full name property"""
student = Student.objects.create(**self.student_data)
self.assertEqual(student.full_name, f"{student.first_name} {student.last_name}")
def test_student_age(self):
"""Test student age calculation"""
student = Student.objects.create(**self.student_data)
# Age should be calculated based on date of birth
today = date.today()
expected_age = today.year - student.date_of_birth.year
if today.month < student.date_of_birth.month or (today.month == student.date_of_birth.month and today.day < student.date_of_birth.day):
expected_age -= 1
self.assertEqual(student.age, expected_age)
def test_student_malaysian_ic_validation(self):
"""Test Malaysian IC number validation"""
# Valid IC number
student = Student.objects.create(**self.student_data)
self.assertEqual(student.ic_number, self.student_data['ic_number'])
# Invalid IC number format
invalid_data = self.student_data.copy()
invalid_data['ic_number'] = '123'
with self.assertRaises(Exception):
Student.objects.create(**invalid_data)
def test_student_gender_choices(self):
"""Test student gender validation"""
invalid_data = self.student_data.copy()
invalid_data['gender'] = 'invalid_gender'
with self.assertRaises(Exception):
Student.objects.create(**invalid_data)
def test_student_grade_validation(self):
"""Test student grade validation"""
# Test valid grades
valid_grades = ['Form 1', 'Form 2', 'Form 3', 'Form 4', 'Form 5', 'Form 6']
for grade in valid_grades:
data = self.student_data.copy()
data['current_grade'] = grade
student = Student.objects.create(**data)
self.assertEqual(student.current_grade, grade)
# Test invalid grade
invalid_data = self.student_data.copy()
invalid_data['current_grade'] = 'Form 7'
with self.assertRaises(Exception):
Student.objects.create(**invalid_data)
def test_student_stream_choices(self):
"""Test student stream validation"""
# Test valid streams
valid_streams = ['science', 'arts', 'commerce', 'technical']
for stream in valid_streams:
data = self.student_data.copy()
data['stream'] = stream
student = Student.objects.create(**data)
self.assertEqual(student.stream, stream)
# Test invalid stream
invalid_data = self.student_data.copy()
invalid_data['stream'] = 'invalid_stream'
with self.assertRaises(Exception):
Student.objects.create(**invalid_data)
def test_student_status_choices(self):
"""Test student status validation"""
# Test valid statuses
valid_statuses = ['active', 'inactive', 'graduated', 'transferred', 'suspended']
for status in valid_statuses:
data = self.student_data.copy()
data['status'] = status
student = Student.objects.create(**data)
self.assertEqual(student.status, status)
# Test invalid status
invalid_data = self.student_data.copy()
invalid_data['status'] = 'invalid_status'
with self.assertRaises(Exception):
Student.objects.create(**invalid_data)
def test_student_parent_information(self):
"""Test student parent information validation"""
student = Student.objects.create(**self.student_data)
self.assertEqual(student.father_name, self.student_data['father_name'])
self.assertEqual(student.mother_name, self.student_data['mother_name'])
self.assertEqual(student.emergency_contact_name, self.student_data['emergency_contact_name'])
def test_student_malaysian_education_info(self):
"""Test Malaysian education specific information"""
student = Student.objects.create(**self.student_data)
self.assertEqual(student.religion, self.student_data['religion'])
self.assertEqual(student.race, self.student_data['race'])
self.assertEqual(student.previous_school, self.student_data['previous_school'])
self.assertEqual(student.previous_grade, self.student_data['previous_grade'])
class ClassModelTest(TestCase):
"""Test cases for Class model"""
def setUp(self):
self.tenant = Tenant.objects.create(
name='Test Education Center',
schema_name='test_education',
domain='testeducation.com',
business_type='education'
)
self.teacher = User.objects.create_user(
username='teacher',
email='teacher@test.com',
password='test123',
tenant=self.tenant,
role='staff'
)
self.student = Student.objects.create(
tenant=self.tenant,
student_id='S2024010001',
first_name='Ahmad',
last_name='Bin Ibrahim',
ic_number='000101-01-0001',
gender='male',
date_of_birth=date(2010, 1, 1),
current_grade='Form 1',
stream='science',
admission_date=date.today(),
status='active'
)
self.class_data = {
'tenant': self.tenant,
'class_name': 'Mathematics Form 1',
'class_code': 'MATH-F1-2024',
'grade': 'Form 1',
'stream': 'science',
'subject': 'Mathematics',
'academic_year': '2024',
'semester': '1',
'teacher': self.teacher,
'room': 'B1-01',
'max_students': 30,
'schedule_days': ['Monday', 'Wednesday', 'Friday'],
'start_time': time(8, 0),
'end_time': time(9, 30),
'start_date': date.today(),
'end_date': date.today() + timedelta(days=180),
'is_active': True,
'syllabus': 'KSSM Mathematics Form 1',
'objectives': 'Complete KSSM Mathematics syllabus',
'assessment_methods': 'Tests, Assignments, Projects',
'created_by': self.teacher
}
def test_create_class(self):
"""Test creating a new class"""
class_obj = Class.objects.create(**self.class_data)
self.assertEqual(class_obj.tenant, self.tenant)
self.assertEqual(class_obj.class_name, self.class_data['class_name'])
self.assertEqual(class_obj.class_code, self.class_data['class_code'])
self.assertEqual(class_obj.grade, self.class_data['grade'])
self.assertEqual(class_obj.stream, self.class_data['stream'])
self.assertEqual(class_obj.subject, self.class_data['subject'])
self.assertEqual(class_obj.teacher, self.teacher)
self.assertEqual(class_obj.max_students, self.class_data['max_students'])
self.assertTrue(class_obj.is_active)
def test_class_string_representation(self):
"""Test class string representation"""
class_obj = Class.objects.create(**self.class_data)
self.assertEqual(str(class_obj), f"{class_obj.class_name} ({class_obj.class_code})")
def test_class_duration(self):
"""Test class duration calculation"""
class_obj = Class.objects.create(**self.class_data)
# Duration should be 90 minutes
self.assertEqual(class_obj.duration, 90)
def test_class_grade_validation(self):
"""Test class grade validation"""
# Test valid grades
valid_grades = ['Form 1', 'Form 2', 'Form 3', 'Form 4', 'Form 5', 'Form 6']
for grade in valid_grades:
data = self.class_data.copy()
data['grade'] = grade
class_obj = Class.objects.create(**data)
self.assertEqual(class_obj.grade, grade)
# Test invalid grade
invalid_data = self.class_data.copy()
invalid_data['grade'] = 'Form 7'
with self.assertRaises(Exception):
Class.objects.create(**invalid_data)
def test_class_stream_choices(self):
"""Test class stream validation"""
# Test valid streams
valid_streams = ['science', 'arts', 'commerce', 'technical']
for stream in valid_streams:
data = self.class_data.copy()
data['stream'] = stream
class_obj = Class.objects.create(**data)
self.assertEqual(class_obj.stream, stream)
# Test invalid stream
invalid_data = self.class_data.copy()
invalid_data['stream'] = 'invalid_stream'
with self.assertRaises(Exception):
Class.objects.create(**invalid_data)
def test_class_semester_choices(self):
"""Test class semester validation"""
# Test valid semesters
valid_semesters = ['1', '2']
for semester in valid_semesters:
data = self.class_data.copy()
data['semester'] = semester
class_obj = Class.objects.create(**data)
self.assertEqual(class_obj.semester, semester)
# Test invalid semester
invalid_data = self.class_data.copy()
invalid_data['semester'] = '3'
with self.assertRaises(Exception):
Class.objects.create(**invalid_data)
def test_class_schedule_validation(self):
"""Test class schedule validation"""
# Valid schedule
class_obj = Class.objects.create(**self.class_data)
self.assertEqual(class_obj.schedule_days, self.class_data['schedule_days'])
self.assertEqual(class_obj.start_time, self.class_data['start_time'])
self.assertEqual(class_obj.end_time, self.class_data['end_time'])
# Invalid time range (end before start)
invalid_data = self.class_data.copy()
invalid_data['start_time'] = time(10, 0)
invalid_data['end_time'] = time(9, 30)
with self.assertRaises(Exception):
Class.objects.create(**invalid_data)
def test_class_student_enrollment(self):
"""Test class student enrollment"""
class_obj = Class.objects.create(**self.class_data)
# Add student to class
class_obj.students.add(self.student)
self.assertIn(self.student, class_obj.students.all())
self.assertEqual(class_obj.students.count(), 1)
def test_class_capacity_validation(self):
"""Test class capacity validation"""
class_obj = Class.objects.create(**self.class_data)
# Test capacity
self.assertEqual(class_obj.max_students, 30)
# Test is_full method
self.assertFalse(class_obj.is_full)
# Add students up to capacity
for i in range(30):
student_data = self.student.__dict__.copy()
student_data['student_id'] = f'S202401{i:04d}'
student_data['first_name'] = f'Student{i}'
student_data.pop('id', None)
student_data.pop('_state', None)
student = Student.objects.create(**student_data)
class_obj.students.add(student)
# Should be full now
self.assertTrue(class_obj.is_full)
def test_class_malaysian_education_features(self):
"""Test Malaysian education specific features"""
class_obj = Class.objects.create(**self.class_data)
self.assertEqual(class_obj.subject, self.class_data['subject'])
self.assertEqual(class_obj.academic_year, self.class_data['academic_year'])
self.assertEqual(class_obj.syllabus, self.class_data['syllabus'])
def test_class_date_validation(self):
"""Test class date validation"""
# Valid date range
class_obj = Class.objects.create(**self.class_data)
self.assertLessEqual(class_obj.start_date, class_obj.end_date)
# Invalid date range (end before start)
invalid_data = self.class_data.copy()
invalid_data['start_date'] = date.today()
invalid_data['end_date'] = date.today() - timedelta(days=1)
with self.assertRaises(Exception):
Class.objects.create(**invalid_data)

View File

@@ -0,0 +1,323 @@
"""
Unit tests for Healthcare Models
Tests for healthcare module models:
- Patient
- Appointment
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.healthcare.models.patient import Patient
from backend.src.modules.healthcare.models.appointment import Appointment
User = get_user_model()
class PatientModelTest(TestCase):
"""Test cases for Patient model"""
def setUp(self):
self.tenant = Tenant.objects.create(
name='Test Healthcare Sdn Bhd',
schema_name='test_healthcare',
domain='testhealthcare.com',
business_type='healthcare'
)
self.user = User.objects.create_user(
username='doctor',
email='doctor@test.com',
password='test123',
tenant=self.tenant,
role='staff'
)
self.patient_data = {
'tenant': self.tenant,
'patient_id': 'P2024010001',
'first_name': 'John',
'last_name': 'Doe',
'ic_number': '000101-01-0001',
'passport_number': '',
'nationality': 'Malaysian',
'gender': 'male',
'date_of_birth': date(1990, 1, 1),
'blood_type': 'O+',
'email': 'john.doe@test.com',
'phone': '+60123456789',
'emergency_contact_name': 'Jane Doe',
'emergency_contact_phone': '+60123456788',
'emergency_contact_relationship': 'Spouse',
'address': '123 Test Street',
'city': 'Kuala Lumpur',
'state': 'KUL',
'postal_code': '50000',
'medical_history': 'No significant medical history',
'allergies': 'None known',
'current_medications': 'None',
'chronic_conditions': 'None',
'last_visit_date': None,
'is_active': True,
'created_by': self.user
}
def test_create_patient(self):
"""Test creating a new patient"""
patient = Patient.objects.create(**self.patient_data)
self.assertEqual(patient.tenant, self.tenant)
self.assertEqual(patient.patient_id, self.patient_data['patient_id'])
self.assertEqual(patient.first_name, self.patient_data['first_name'])
self.assertEqual(patient.last_name, self.patient_data['last_name'])
self.assertEqual(patient.ic_number, self.patient_data['ic_number'])
self.assertEqual(patient.gender, self.patient_data['gender'])
self.assertEqual(patient.blood_type, self.patient_data['blood_type'])
self.assertTrue(patient.is_active)
def test_patient_string_representation(self):
"""Test patient string representation"""
patient = Patient.objects.create(**self.patient_data)
self.assertEqual(str(patient), f"{patient.first_name} {patient.last_name} ({patient.patient_id})")
def test_patient_full_name(self):
"""Test patient full name property"""
patient = Patient.objects.create(**self.patient_data)
self.assertEqual(patient.full_name, f"{patient.first_name} {patient.last_name}")
def test_patient_age(self):
"""Test patient age calculation"""
patient = Patient.objects.create(**self.patient_data)
# Age should be calculated based on date of birth
today = date.today()
expected_age = today.year - patient.date_of_birth.year
if today.month < patient.date_of_birth.month or (today.month == patient.date_of_birth.month and today.day < patient.date_of_birth.day):
expected_age -= 1
self.assertEqual(patient.age, expected_age)
def test_patient_malaysian_ic_validation(self):
"""Test Malaysian IC number validation"""
# Valid IC number
patient = Patient.objects.create(**self.patient_data)
self.assertEqual(patient.ic_number, self.patient_data['ic_number'])
# Invalid IC number format
invalid_data = self.patient_data.copy()
invalid_data['ic_number'] = '123'
with self.assertRaises(Exception):
Patient.objects.create(**invalid_data)
def test_patient_gender_choices(self):
"""Test patient gender validation"""
invalid_data = self.patient_data.copy()
invalid_data['gender'] = 'invalid_gender'
with self.assertRaises(Exception):
Patient.objects.create(**invalid_data)
def test_patient_blood_type_choices(self):
"""Test patient blood type validation"""
invalid_data = self.patient_data.copy()
invalid_data['blood_type'] = 'Z+'
with self.assertRaises(Exception):
Patient.objects.create(**invalid_data)
def test_patient_phone_validation(self):
"""Test Malaysian phone number validation"""
# Valid Malaysian phone number
patient = Patient.objects.create(**self.patient_data)
self.assertEqual(patient.phone, self.patient_data['phone'])
# Invalid phone number
invalid_data = self.patient_data.copy()
invalid_data['phone'] = '12345'
with self.assertRaises(Exception):
Patient.objects.create(**invalid_data)
def test_patient_medical_info_validation(self):
"""Test patient medical information validation"""
# Test with medical conditions
data = self.patient_data.copy()
data['chronic_conditions'] = 'Diabetes, Hypertension'
data['allergies'] = 'Penicillin, Sulfa drugs'
patient = Patient.objects.create(**data)
self.assertEqual(patient.chronic_conditions, 'Diabetes, Hypertension')
self.assertEqual(patient.allergies, 'Penicillin, Sulfa drugs')
class AppointmentModelTest(TestCase):
"""Test cases for Appointment model"""
def setUp(self):
self.tenant = Tenant.objects.create(
name='Test Healthcare Sdn Bhd',
schema_name='test_healthcare',
domain='testhealthcare.com',
business_type='healthcare'
)
self.doctor = User.objects.create_user(
username='doctor',
email='doctor@test.com',
password='test123',
tenant=self.tenant,
role='staff'
)
self.patient = Patient.objects.create(
tenant=self.tenant,
patient_id='P2024010001',
first_name='John',
last_name='Doe',
ic_number='000101-01-0001',
gender='male',
date_of_birth=date(1990, 1, 1),
blood_type='O+',
phone='+60123456789',
created_by=self.doctor
)
self.appointment_data = {
'tenant': self.tenant,
'patient': self.patient,
'doctor': self.doctor,
'appointment_number': 'APT-2024010001',
'appointment_date': date.today() + timedelta(days=1),
'appointment_time': time(10, 0),
'end_time': time(10, 30),
'appointment_type': 'consultation',
'status': 'scheduled',
'reason': 'General checkup',
'notes': '',
'is_telemedicine': False,
'telemedicine_link': '',
'reminder_sent': False,
'created_by': self.doctor
}
def test_create_appointment(self):
"""Test creating a new appointment"""
appointment = Appointment.objects.create(**self.appointment_data)
self.assertEqual(appointment.tenant, self.tenant)
self.assertEqual(appointment.patient, self.patient)
self.assertEqual(appointment.doctor, self.doctor)
self.assertEqual(appointment.appointment_number, self.appointment_data['appointment_number'])
self.assertEqual(appointment.status, self.appointment_data['status'])
self.assertEqual(appointment.appointment_type, self.appointment_data['appointment_type'])
self.assertFalse(appointment.is_telemedicine)
def test_appointment_string_representation(self):
"""Test appointment string representation"""
appointment = Appointment.objects.create(**self.appointment_data)
expected = f"{self.patient.full_name} - {appointment.appointment_date} at {appointment.appointment_time}"
self.assertEqual(str(appointment), expected)
def test_appointment_duration(self):
"""Test appointment duration calculation"""
appointment = Appointment.objects.create(**self.appointment_data)
# Duration should be 30 minutes
self.assertEqual(appointment.duration, 30)
def test_appointment_is_upcoming(self):
"""Test appointment upcoming status"""
# Future appointment
appointment = Appointment.objects.create(**self.appointment_data)
self.assertTrue(appointment.is_upcoming)
# Past appointment
appointment.appointment_date = date.today() - timedelta(days=1)
appointment.save()
self.assertFalse(appointment.is_upcoming)
def test_appointment_status_choices(self):
"""Test appointment status validation"""
invalid_data = self.appointment_data.copy()
invalid_data['status'] = 'invalid_status'
with self.assertRaises(Exception):
Appointment.objects.create(**invalid_data)
def test_appointment_type_choices(self):
"""Test appointment type validation"""
invalid_data = self.appointment_data.copy()
invalid_data['appointment_type'] = 'invalid_type'
with self.assertRaises(Exception):
Appointment.objects.create(**invalid_data)
def test_appointment_time_validation(self):
"""Test appointment time validation"""
# Valid time range
appointment = Appointment.objects.create(**self.appointment_data)
self.assertEqual(appointment.appointment_time, self.appointment_data['appointment_time'])
self.assertEqual(appointment.end_time, self.appointment_data['end_time'])
# Invalid time range (end before start)
invalid_data = self.appointment_data.copy()
invalid_data['appointment_time'] = time(11, 0)
invalid_data['end_time'] = time(10, 30)
with self.assertRaises(Exception):
Appointment.objects.create(**invalid_data)
def test_appointment_conflict_detection(self):
"""Test appointment conflict detection"""
# Create first appointment
appointment1 = Appointment.objects.create(**self.appointment_data)
# Try to create conflicting appointment
conflict_data = self.appointment_data.copy()
conflict_data['appointment_number'] = 'APT-2024010002'
conflict_data['appointment_time'] = time(10, 15)
conflict_data['end_time'] = time(10, 45)
# This should not raise an exception but conflict detection should be available
appointment2 = Appointment.objects.create(**conflict_data)
# Check if there's a conflict
self.assertTrue(
appointment1.appointment_date == appointment2.appointment_date and
appointment1.doctor == appointment2.doctor and
(
(appointment1.appointment_time <= appointment2.appointment_time < appointment1.end_time) or
(appointment2.appointment_time <= appointment1.appointment_time < appointment2.end_time)
)
)
def test_telemedicine_appointment(self):
"""Test telemedicine appointment features"""
data = self.appointment_data.copy()
data['is_telemedicine'] = True
data['telemedicine_link'] = 'https://meet.test.com/room/12345'
appointment = Appointment.objects.create(**data)
self.assertTrue(appointment.is_telemedicine)
self.assertEqual(appointment.telemedicine_link, data['telemedicine_link'])
def test_appointment_reminder_features(self):
"""Test appointment reminder features"""
appointment = Appointment.objects.create(**self.appointment_data)
# Initially no reminder sent
self.assertFalse(appointment.reminder_sent)
# Mark reminder as sent
appointment.reminder_sent = True
appointment.reminder_sent_at = timezone.now()
appointment.save()
self.assertTrue(appointment.reminder_sent)
self.assertIsNotNone(appointment.reminder_sent_at)

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)

View File

@@ -0,0 +1,350 @@
"""
Unit tests for Retail Models
Tests for retail module models:
- Product
- Sale
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
from backend.src.core.models.tenant import Tenant
from backend.src.core.models.user import User
from backend.src.modules.retail.models.product import Product
from backend.src.modules.retail.models.sale import Sale, SaleItem
User = get_user_model()
class ProductModelTest(TestCase):
"""Test cases for Product model"""
def setUp(self):
self.tenant = Tenant.objects.create(
name='Test Business Sdn Bhd',
schema_name='test_business',
domain='testbusiness.com',
business_type='retail'
)
self.user = User.objects.create_user(
username='testuser',
email='user@test.com',
password='test123',
tenant=self.tenant
)
self.product_data = {
'tenant': self.tenant,
'sku': 'PRD-001',
'name': 'Test Product',
'description': 'A test product for unit testing',
'category': 'electronics',
'brand': 'Test Brand',
'barcode': '1234567890123',
'unit': 'piece',
'current_stock': 100,
'minimum_stock': 10,
'maximum_stock': 500,
'reorder_point': 15,
'purchase_price': Decimal('50.00'),
'selling_price': Decimal('100.00'),
'wholesale_price': Decimal('80.00'),
'tax_rate': 10.0,
'is_taxable': True,
'is_active': True,
'requires_prescription': False,
'is_halal': True,
'msme_certified': True,
'created_by': self.user
}
def test_create_product(self):
"""Test creating a new product"""
product = Product.objects.create(**self.product_data)
self.assertEqual(product.tenant, self.tenant)
self.assertEqual(product.sku, self.product_data['sku'])
self.assertEqual(product.name, self.product_data['name'])
self.assertEqual(product.current_stock, self.product_data['current_stock'])
self.assertEqual(product.purchase_price, self.product_data['purchase_price'])
self.assertEqual(product.selling_price, self.product_data['selling_price'])
self.assertTrue(product.is_active)
self.assertTrue(product.is_halal)
def test_product_string_representation(self):
"""Test product string representation"""
product = Product.objects.create(**self.product_data)
self.assertEqual(str(product), f"{product.name} ({product.sku})")
def test_product_is_low_stock(self):
"""Test product low stock detection"""
product = Product.objects.create(**self.product_data)
# Normal stock level
self.assertFalse(product.is_low_stock)
# Low stock level
product.current_stock = 5
product.save()
self.assertTrue(product.is_low_stock)
def test_product_profit_margin(self):
"""Test product profit margin calculation"""
product = Product.objects.create(**self.product_data)
expected_margin = ((product.selling_price - product.purchase_price) / product.selling_price) * 100
self.assertAlmostEqual(product.profit_margin, expected_margin)
def test_product_category_choices(self):
"""Test product category validation"""
invalid_data = self.product_data.copy()
invalid_data['category'] = 'invalid_category'
with self.assertRaises(Exception):
Product.objects.create(**invalid_data)
def test_product_unit_choices(self):
"""Test product unit validation"""
invalid_data = self.product_data.copy()
invalid_data['unit'] = 'invalid_unit'
with self.assertRaises(Exception):
Product.objects.create(**invalid_data)
def test_product_barcode_validation(self):
"""Test product barcode validation"""
# Valid barcode
product = Product.objects.create(**self.product_data)
self.assertEqual(product.barcode, self.product_data['barcode'])
# Invalid barcode (too long)
invalid_data = self.product_data.copy()
invalid_data['barcode'] = '1' * 14
with self.assertRaises(Exception):
Product.objects.create(**invalid_data)
def test_product_stock_validation(self):
"""Test product stock validation"""
invalid_data = self.product_data.copy()
invalid_data['current_stock'] = -1
with self.assertRaises(Exception):
Product.objects.create(**invalid_data)
invalid_data['current_stock'] = 0
invalid_data['minimum_stock'] = -5
with self.assertRaises(Exception):
Product.objects.create(**invalid_data)
class SaleModelTest(TestCase):
"""Test cases for Sale and SaleItem models"""
def setUp(self):
self.tenant = Tenant.objects.create(
name='Test Business Sdn Bhd',
schema_name='test_business',
domain='testbusiness.com',
business_type='retail'
)
self.user = User.objects.create_user(
username='testuser',
email='user@test.com',
password='test123',
tenant=self.tenant
)
self.product1 = Product.objects.create(
tenant=self.tenant,
sku='PRD-001',
name='Product 1',
category='electronics',
unit='piece',
current_stock=100,
minimum_stock=10,
purchase_price=Decimal('50.00'),
selling_price=Decimal('100.00'),
tax_rate=10.0,
created_by=self.user
)
self.product2 = Product.objects.create(
tenant=self.tenant,
sku='PRD-002',
name='Product 2',
category='electronics',
unit='piece',
current_stock=50,
minimum_stock=5,
purchase_price=Decimal('30.00'),
selling_price=Decimal('60.00'),
tax_rate=10.0,
created_by=self.user
)
self.sale_data = {
'tenant': self.tenant,
'invoice_number': 'INV-2024010001',
'customer_name': 'Test Customer',
'customer_email': 'customer@test.com',
'customer_phone': '+60123456789',
'customer_ic': '000101-01-0001',
'sale_date': timezone.now(),
'status': 'completed',
'payment_method': 'cash',
'payment_status': 'paid',
'sales_person': self.user,
'notes': 'Test sale for unit testing'
}
def test_create_sale(self):
"""Test creating a new sale"""
sale = Sale.objects.create(**self.sale_data)
self.assertEqual(sale.tenant, self.tenant)
self.assertEqual(sale.invoice_number, self.sale_data['invoice_number'])
self.assertEqual(sale.customer_name, self.sale_data['customer_name'])
self.assertEqual(sale.status, self.sale_data['status'])
self.assertEqual(sale.payment_status, self.sale_data['payment_status'])
self.assertEqual(sale.sales_person, self.user)
def test_sale_string_representation(self):
"""Test sale string representation"""
sale = Sale.objects.create(**self.sale_data)
self.assertEqual(str(sale), f"Invoice #{sale.invoice_number} - {sale.customer_name}")
def test_create_sale_item(self):
"""Test creating a sale item"""
sale = Sale.objects.create(**self.sale_data)
sale_item_data = {
'sale': sale,
'product': self.product1,
'quantity': 2,
'unit_price': Decimal('100.00'),
'discount_percentage': 0.0,
'tax_rate': 10.0,
'notes': 'Test sale item'
}
sale_item = SaleItem.objects.create(**sale_item_data)
self.assertEqual(sale_item.sale, sale)
self.assertEqual(sale_item.product, self.product1)
self.assertEqual(sale_item.quantity, 2)
self.assertEqual(sale_item.unit_price, Decimal('100.00'))
def test_sale_item_subtotal(self):
"""Test sale item subtotal calculation"""
sale = Sale.objects.create(**self.sale_data)
sale_item = SaleItem.objects.create(
sale=sale,
product=self.product1,
quantity=2,
unit_price=Decimal('100.00'),
tax_rate=10.0
)
expected_subtotal = Decimal('200.00') # 2 * 100.00
self.assertEqual(sale_item.subtotal, expected_subtotal)
def test_sale_item_tax_amount(self):
"""Test sale item tax amount calculation"""
sale = Sale.objects.create(**self.sale_data)
sale_item = SaleItem.objects.create(
sale=sale,
product=self.product1,
quantity=2,
unit_price=Decimal('100.00'),
tax_rate=10.0
)
expected_tax = Decimal('20.00') # 200.00 * 0.10
self.assertEqual(sale_item.tax_amount, expected_tax)
def test_sale_item_total_amount(self):
"""Test sale item total amount calculation"""
sale = Sale.objects.create(**self.sale_data)
sale_item = SaleItem.objects.create(
sale=sale,
product=self.product1,
quantity=2,
unit_price=Decimal('100.00'),
tax_rate=10.0
)
expected_total = Decimal('220.00') # 200.00 + 20.00
self.assertEqual(sale_item.total_amount, expected_total)
def test_sale_calculate_totals(self):
"""Test sale total calculations"""
sale = Sale.objects.create(**self.sale_data)
# Create multiple sale items
SaleItem.objects.create(
sale=sale,
product=self.product1,
quantity=2,
unit_price=Decimal('100.00'),
tax_rate=10.0
)
SaleItem.objects.create(
sale=sale,
product=self.product2,
quantity=1,
unit_price=Decimal('60.00'),
tax_rate=10.0
)
# Test the calculate_totals method
sale.calculate_totals()
expected_subtotal = Decimal('260.00') # 200.00 + 60.00
expected_tax = Decimal('26.00') # 20.00 + 6.00
expected_total = Decimal('286.00') # 260.00 + 26.00
self.assertEqual(sale.subtotal_amount, expected_subtotal)
self.assertEqual(sale.tax_amount, expected_tax)
self.assertEqual(sale.total_amount, expected_total)
def test_sale_status_choices(self):
"""Test sale status validation"""
invalid_data = self.sale_data.copy()
invalid_data['status'] = 'invalid_status'
with self.assertRaises(Exception):
Sale.objects.create(**invalid_data)
def test_sale_payment_method_choices(self):
"""Test sale payment method validation"""
invalid_data = self.sale_data.copy()
invalid_data['payment_method'] = 'invalid_method'
with self.assertRaises(Exception):
Sale.objects.create(**invalid_data)
def test_malaysian_customer_validation(self):
"""Test Malaysian customer validation"""
# Valid Malaysian IC
sale = Sale.objects.create(**self.sale_data)
self.assertEqual(sale.customer_ic, self.sale_data['customer_ic'])
# Valid Malaysian phone
self.assertEqual(sale.customer_phone, self.sale_data['customer_phone'])
# Invalid phone number
invalid_data = self.sale_data.copy()
invalid_data['customer_phone'] = '12345'
with self.assertRaises(Exception):
Sale.objects.create(**invalid_data)