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
413 lines
16 KiB
Python
413 lines
16 KiB
Python
"""
|
|
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) |