import pytest from datetime import datetime, timedelta from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker import app.models.db_base # Ensures all tables registered in metadata from app.core.database.db_session import Base from app.models.CustomerDeviceModel import CustomerDevice from app.models.ServiceModel import ServiceCatalog, ServiceJob, ServiceAppointment, ServiceJobAssignment from app.models.TechnicianModel import TechnicianProfile, TechnicianSkill, TechnicianWorkingHours from app.services.SlotAllocationService import SlotAllocationService # Setup in-memory SQLite database for testing DATABASE_URL = "sqlite:///:memory:" engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False}) TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) @pytest.fixture(scope="function") def db_session(): Base.metadata.create_all(bind=engine) db = TestingSessionLocal() try: yield db finally: db.close() Base.metadata.drop_all(bind=engine) def test_dynamic_slot_allocation_success(db_session): # 1. Setup seed catalog services service = ServiceCatalog( service_id="serv_1", name="Screen Replacement", base_price=5000.00, estimated_duration_minutes=60, workflow_type="REPAIR_QUOTE", active=True ) db_session.add(service) # 2. Setup technician profile tech = TechnicianProfile( technician_id="tech_1", user_id="user_tech_1", active=True ) db_session.add(tech) # 3. Setup technician skill skill = TechnicianSkill( technician_id="tech_1", service_type_id="serv_1", skill_level=3 ) db_session.add(skill) # 4. Setup working hours (Monday is weekday 0) wh = TechnicianWorkingHours( working_hour_id="wh_1", technician_id="tech_1", day_of_week=0, start_time="09:00", end_time="11:00" ) db_session.add(wh) db_session.commit() # Calculate slots for next Monday (e.g. 2026-08-31) target_date = datetime(2026, 8, 31).date() allocation_service = SlotAllocationService() slots = allocation_service.get_available_slots(db_session, target_date, "serv_1") # Since duration is 60 mins and working hours are 09:00 - 11:00: # 09:00 - 10:00 -> Slot 1 # 09:30 - 10:30 -> Slot 2 # 10:00 - 11:00 -> Slot 3 assert len(slots) == 3 assert slots[0]["start_time"] == "2026-08-31T09:00:00" assert slots[2]["start_time"] == "2026-08-31T10:00:00" def test_dynamic_slot_allocation_overlap(db_session): # 1. Setup seed catalog services & technician service = ServiceCatalog( service_id="serv_1", name="Screen Replacement", base_price=5000.00, estimated_duration_minutes=60, active=True ) db_session.add(service) tech = TechnicianProfile( technician_id="tech_1", user_id="user_tech_1", active=True ) db_session.add(tech) skill = TechnicianSkill( technician_id="tech_1", service_type_id="serv_1", skill_level=3 ) db_session.add(skill) wh = TechnicianWorkingHours( working_hour_id="wh_1", technician_id="tech_1", day_of_week=0, start_time="09:00", end_time="11:00" ) db_session.add(wh) # 2. Book an overlapping slot (09:30 - 10:30) device = CustomerDevice( device_id="dev_1", customer_id="cust_1", brand="Apple", model="iPhone 14" ) db_session.add(device) job = ServiceJob( job_id="job_1", job_no="SRV-12345", customer_id="cust_1", device_id="dev_1", service_id="serv_1", status="BOOKED" ) db_session.add(job) appt = ServiceAppointment( appointment_id="appt_1", service_job_id="job_1", scheduled_start=datetime(2026, 8, 31, 9, 30), scheduled_end=datetime(2026, 8, 31, 10, 30), status="CONFIRMED" ) db_session.add(appt) assignment = ServiceJobAssignment( assignment_id="assign_1", service_job_id="job_1", appointment_id="appt_1", technician_id="tech_1" ) db_session.add(assignment) db_session.commit() # Re-calculate availability target_date = datetime(2026, 8, 31).date() allocation_service = SlotAllocationService() slots = allocation_service.get_available_slots(db_session, target_date, "serv_1") # Overlapping appt is 09:30 - 10:30. # Slot 09:00 - 10:00 overlaps (ends after 09:30) -> overlap! # Slot 09:30 - 10:30 overlaps -> overlap! # Slot 10:00 - 11:00 overlaps (starts before 10:30) -> overlap! # So there should be 0 available slots! assert len(slots) == 0