Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
pip install coverage
- name: Run Tests
run: |
coverage run --source=appointment manage.py test appointment.tests --verbosity=1
coverage run --source=appointment manage.py test appointment.tests --parallel=10 --shuffle --verbosity=1
coverage report
coverage xml
- name: Upload to Codecov
Expand Down
11 changes: 8 additions & 3 deletions appointment/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import random
import string
import uuid
from decimal import Decimal, InvalidOperation

from babel.numbers import get_currency_symbol
from django.conf import settings
Expand Down Expand Up @@ -67,7 +68,7 @@ class Service(models.Model):
name = models.CharField(max_length=100, blank=False)
description = models.TextField(blank=True, null=True)
duration = models.DurationField(validators=[MinValueValidator(datetime.timedelta(seconds=1))])
price = models.DecimalField(max_digits=6, decimal_places=2, validators=[MinValueValidator(0)])
price = models.DecimalField(max_digits=8, decimal_places=2, validators=[MinValueValidator(0)])
down_payment = models.DecimalField(max_digits=6, decimal_places=2, default=0, validators=[MinValueValidator(0)])
image = models.ImageField(upload_to='services/', blank=True, null=True)
currency = models.CharField(max_length=3, default='USD', validators=[MaxLengthValidator(3), MinLengthValidator(3)])
Expand Down Expand Up @@ -435,7 +436,7 @@ class Appointment(models.Model):
want_reminder = models.BooleanField(default=False)
additional_info = models.TextField(blank=True, null=True)
paid = models.BooleanField(default=False)
amount_to_pay = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True)
amount_to_pay = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
id_request = models.CharField(max_length=100, blank=True, null=True)

# meta datas
Expand Down Expand Up @@ -596,7 +597,7 @@ def is_owner(self, staff_user_id):
def to_dict(self):
return {
"id": self.id,
"client_name": self.client.get_full_name(),
"client_name": self.get_client_name(),
"client_email": self.client.email,
"start_time": self.appointment_request.start_time.strftime('%Y-%m-%d %H:%M'),
"end_time": self.appointment_request.end_time.strftime('%Y-%m-%d %H:%M'),
Expand Down Expand Up @@ -664,6 +665,10 @@ def clean(self):
if self.lead_time is not None and self.finish_time is not None:
if self.lead_time >= self.finish_time:
raise ValidationError(_("Lead time must be before finish time"))
if self.appointment_buffer_time is not None and self.appointment_buffer_time < 0:
raise ValidationError(_("Appointment buffer time cannot be negative"))
if self.slot_duration is not None and self.slot_duration <= 0:
raise ValidationError(_("Slot duration must be greater than 0"))

def save(self, *args, **kwargs):
self.clean()
Expand Down
111 changes: 71 additions & 40 deletions appointment/tests/base/base_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,84 @@

from django.test import TestCase

from appointment.models import Appointment, AppointmentRequest, Service, StaffMember
from appointment.tests.mixins.base_mixin import (
AppointmentMixin, AppointmentRequestMixin, AppointmentRescheduleHistoryMixin, ServiceMixin, StaffMemberMixin,
UserMixin
)
from appointment.utils.db_helpers import get_user_model


class BaseTest(TestCase, UserMixin, StaffMemberMixin, ServiceMixin, AppointmentRequestMixin, AppointmentMixin,
AppointmentRescheduleHistoryMixin):
def setUp(self):
# Users
self.user1 = self.create_user_(email="[email protected]", username="tester1")
self.user2 = self.create_user_(first_name="Tester2", email="[email protected]", username="tester2")
self.client1 = self.create_user_(first_name="Client1", email="[email protected]", username="client1")
self.client2 = self.create_user_(first_name="Client2", email="[email protected]", username="client2")
class BaseTest(TestCase, UserMixin, StaffMemberMixin, ServiceMixin, AppointmentRequestMixin,
AppointmentMixin, AppointmentRescheduleHistoryMixin):
service1 = None
service2 = None
staff_member1 = None
staff_member2 = None
users = None

# Services
self.service1 = self.create_service_()
self.service2 = self.create_service_(name="Service 2")
USER_SPECS = {
'staff1': {"first_name": "Daniel", "email": "[email protected]",
"username": "daniel.jackson"},
'staff2': {"first_name": "Samantha", "email": "[email protected]",
"username": "samantha.carter"},
'client1': {"first_name": "Georges", "email": "[email protected]",
"username": "georges.hammond"},
'client2': {"first_name": "Tealc", "email": "[email protected]", "username": "tealc.kree"},
'superuser': {"first_name": "Jack", "email": "[email protected]", "username": "jack.o.neill"},
}

# Staff Members
self.staff_member1 = self.create_staff_member_(user=self.user1, service=self.service1)
self.staff_member2 = self.create_staff_member_(user=self.user2, service=self.service2)
@classmethod
def setUpTestData(cls):
cls.users = {key: cls.create_user_(**details) for key, details in cls.USER_SPECS.items()}
cls.service1 = cls.create_service_(
name="Stargate Activation", duration=timedelta(hours=1), price=100000, description="Activate the Stargate")
cls.service2 = cls.create_service_(
name="Dial Home Device Repair", duration=timedelta(hours=2), price=200000, description="Repair the DHD")
# Mapping services to staff members
cls.staff_member1 = cls.create_staff_member_(user=cls.users['staff1'], service=cls.service1)
cls.staff_member2 = cls.create_staff_member_(user=cls.users['staff2'], service=cls.service2)

def create_appt_request_for_sm1(self, **kwargs):
@classmethod
def tearDownClass(cls):
super().tearDownClass()
# Clean up any class-level resources
cls.clean_all_data()

@classmethod
def clean_all_data(cls):
Appointment.objects.all().delete()
AppointmentRequest.objects.all().delete()
StaffMember.objects.all().delete()
Service.objects.all().delete()
get_user_model().objects.all().delete()

def create_appt_request_for_sm1(self, service=None, staff_member=None, **kwargs):
"""Create an appointment request for staff_member1."""
return self.create_appointment_request_(service=self.service1, staff_member=self.staff_member1, **kwargs)
service = service or self.service1
staff_member = staff_member or self.staff_member1
return self.create_appointment_request_(service=service, staff_member=staff_member, **kwargs)

def create_appt_request_for_sm2(self, **kwargs):
def create_appt_request_for_sm2(self, service=None, staff_member=None, **kwargs):
"""Create an appointment request for staff_member2."""
return self.create_appointment_request_(service=self.service2, staff_member=self.staff_member2, **kwargs)
service = service or self.service2
staff_member = staff_member or self.staff_member2
return self.create_appointment_request_(service=service, staff_member=staff_member, **kwargs)

def create_appointment_for_user1(self, appointment_request=None):
def create_appt_for_sm1(self, appointment_request=None):
if not appointment_request:
appointment_request = self.create_appt_request_for_sm1()
return self.create_appointment_(user=self.client1, appointment_request=appointment_request)
return self.create_appointment_(user=self.users['client1'], appointment_request=appointment_request)

def create_appointment_for_user2(self, appointment_request=None):
def create_appt_for_sm2(self, appointment_request=None):
if not appointment_request:
appointment_request = self.create_appt_request_for_sm2()
return self.create_appointment_(user=self.client2, appointment_request=appointment_request)
return self.create_appointment_(user=self.users['client2'], appointment_request=appointment_request)

def create_appointment_reschedule_for_user1(self, appointment_request=None, reason_for_rescheduling="Reason"):
def create_appt_reschedule_for_sm1(self, appointment_request=None, reason_for_rescheduling="Gate Malfunction"):
if not appointment_request:
appointment_request = self.create_appt_request_for_sm1()
date_ = appointment_request.date + timedelta(days=1)
date_ = appointment_request.date + timedelta(days=7)
return self.create_reschedule_history_(
appointment_request=appointment_request,
date_=date_,
Expand All @@ -59,23 +92,21 @@ def create_appointment_reschedule_for_user1(self, appointment_request=None, reas
def need_normal_login(self):
self.client.force_login(self.create_user_())

def need_staff_login(self, user=None):
if user is not None:
user.is_staff = True
user.save()
self.client.force_login(user)
self.user1.is_staff = True
self.user1.save()
self.client.force_login(self.user1)
def need_staff_login(self):
self.staff = self.users['staff1']
self.staff.is_staff = True
self.staff.save()
self.client.force_login(self.staff)

def need_superuser_login(self):
self.user1.is_superuser = True
self.user1.save()
self.client.force_login(self.user1)
self.superuser = self.users['superuser']
self.superuser.is_superuser = True
self.superuser.save()
self.client.force_login(self.superuser)

def clean_staff_member_objects(self, user=None):
def clean_staff_member_objects(self, staff=None):
"""Delete all AppointmentRequests and Appointments linked to the StaffMember instance of self.user1."""
if user is None:
user = self.user1
self.clean_appointment_for_user(user)
self.clean_appt_request_for_user(user)
if staff is None:
staff = self.users['staff1']
self.clean_appointment_for_user_(staff)
self.clean_appt_request_for_user_(staff)
22 changes: 12 additions & 10 deletions appointment/tests/mixins/base_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ def __init__(self):
pass

@classmethod
def create_user_(cls, first_name="Tester", email="[email protected]", username="test_user",
password="Kfdqi3!?n"):
user_model = get_user_model()
return user_model.objects.create_user(
def create_user_(cls, first_name="Janet", email="[email protected]", username="janet.fraiser",
password="G0a'uld$Emp1re"):
return get_user_model().objects.create_user(
first_name=first_name,
email=email,
username=username,
Expand All @@ -27,11 +26,13 @@ def __init__(self):
pass

@classmethod
def create_service_(cls, name="Test Service", duration=timedelta(hours=1), price=100):
def create_service_(cls, name="Quantum Mirror Assessment", duration=timedelta(hours=1), price=50000,
description="Assess the Quantum Mirror"):
return Service.objects.create(
name=name,
duration=duration,
price=price
price=price,
description=description
)


Expand Down Expand Up @@ -62,7 +63,7 @@ def create_appointment_request_(cls, service, staff_member, date_=date.today(),
)

@classmethod
def clean_appt_request_for_user(cls, user):
def clean_appt_request_for_user_(cls, user):
AppointmentRequest.objects.filter(staff_member__user=user).delete()


Expand All @@ -71,7 +72,8 @@ def __init__(self):
pass

@classmethod
def create_appointment_(cls, user, appointment_request, phone="1234567890", address="Some City, Some State"):
def create_appointment_(cls, user, appointment_request, phone="+12392340543",
address="Stargate Command, Cheyenne Mountain Complex, Colorado Springs, CO"):
return Appointment.objects.create(
client=user,
appointment_request=appointment_request,
Expand All @@ -80,7 +82,7 @@ def create_appointment_(cls, user, appointment_request, phone="1234567890",
)

@classmethod
def clean_appointment_for_user(cls, user):
def clean_appointment_for_user_(cls, user):
Appointment.objects.filter(client=user).delete()


Expand All @@ -90,7 +92,7 @@ def __init__(self):

@classmethod
def create_reschedule_history_(cls, appointment_request, date_, start_time, end_time, staff_member,
reason_for_rescheduling=""):
reason_for_rescheduling="Zat'nik'tel Discharge"):
return AppointmentRescheduleHistory.objects.create(
appointment_request=appointment_request,
date=date_,
Expand Down
Loading