create([ 'day_of_week' => $day, 'start_time' => '09:00', 'end_time' => '17:00', 'is_active' => true, ]); } }); describe('getDateStatus', function () { it('returns "past" for yesterday', function () { $service = new AvailabilityService; $yesterday = Carbon::yesterday(); expect($service->getDateStatus($yesterday))->toBe('past'); }); it('returns "closed" for non-working days (weekends)', function () { $service = new AvailabilityService; // Friday is day 5, Saturday is day 6 - both are weekends in our setup $friday = Carbon::now()->next(Carbon::FRIDAY); expect($service->getDateStatus($friday))->toBe('closed'); }); it('returns "blocked" for fully blocked date', function () { $sunday = Carbon::now()->next(Carbon::SUNDAY); BlockedTime::factory()->create([ 'block_date' => $sunday->toDateString(), 'start_time' => null, 'end_time' => null, ]); $service = new AvailabilityService; expect($service->getDateStatus($sunday))->toBe('blocked'); }); it('returns "full" when all slots are booked', function () { $sunday = Carbon::now()->next(Carbon::SUNDAY); $slots = ['09:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00']; foreach ($slots as $slot) { Consultation::factory()->approved()->create([ 'booking_date' => $sunday->toDateString(), 'booking_time' => $slot, ]); } $service = new AvailabilityService; expect($service->getDateStatus($sunday))->toBe('full'); }); it('returns "partial" when some slots are booked', function () { $sunday = Carbon::now()->next(Carbon::SUNDAY); Consultation::factory()->approved()->create([ 'booking_date' => $sunday->toDateString(), 'booking_time' => '10:00', ]); $service = new AvailabilityService; expect($service->getDateStatus($sunday))->toBe('partial'); }); it('returns "available" for working day with no bookings', function () { $sunday = Carbon::now()->next(Carbon::SUNDAY); $service = new AvailabilityService; expect($service->getDateStatus($sunday))->toBe('available'); }); }); describe('getAvailableSlots', function () { it('returns empty array for non-working days', function () { $service = new AvailabilityService; // Friday is day 5 - a non-working day in our setup $friday = Carbon::now()->next(Carbon::FRIDAY); expect($service->getAvailableSlots($friday))->toBe([]); }); it('excludes booked consultation times', function () { $sunday = Carbon::now()->next(Carbon::SUNDAY); Consultation::factory()->pending()->create([ 'booking_date' => $sunday->toDateString(), 'booking_time' => '10:00', ]); $service = new AvailabilityService; $slots = $service->getAvailableSlots($sunday); expect($slots)->not->toContain('10:00'); }); it('excludes blocked time ranges', function () { $sunday = Carbon::now()->next(Carbon::SUNDAY); BlockedTime::factory()->timeRange('14:00', '16:00')->create([ 'block_date' => $sunday->toDateString(), ]); $service = new AvailabilityService; $slots = $service->getAvailableSlots($sunday); expect($slots)->not->toContain('14:00'); expect($slots)->not->toContain('15:00'); }); it('includes pending and approved consultations as booked', function () { $sunday = Carbon::now()->next(Carbon::SUNDAY); Consultation::factory()->pending()->create([ 'booking_date' => $sunday->toDateString(), 'booking_time' => '09:00', ]); Consultation::factory()->approved()->create([ 'booking_date' => $sunday->toDateString(), 'booking_time' => '10:00', ]); // Cancelled should NOT block the slot Consultation::factory()->create([ 'booking_date' => $sunday->toDateString(), 'booking_time' => '11:00', 'status' => ConsultationStatus::Cancelled, ]); $service = new AvailabilityService; $slots = $service->getAvailableSlots($sunday); expect($slots)->not->toContain('09:00'); expect($slots)->not->toContain('10:00'); expect($slots)->toContain('11:00'); }); it('returns all slots blocked when full day is blocked', function () { $sunday = Carbon::now()->next(Carbon::SUNDAY); BlockedTime::factory()->allDay()->create([ 'block_date' => $sunday->toDateString(), ]); $service = new AvailabilityService; $slots = $service->getAvailableSlots($sunday); expect($slots)->toBe([]); }); }); describe('getMonthAvailability', function () { it('returns status for every day in the month', function () { $service = new AvailabilityService; $availability = $service->getMonthAvailability(2025, 1); expect($availability)->toHaveCount(31); }); it('handles year rollover correctly (December to January)', function () { $service = new AvailabilityService; $december = $service->getMonthAvailability(2024, 12); $january = $service->getMonthAvailability(2025, 1); expect($december)->toHaveKey('2024-12-31'); expect($january)->toHaveKey('2025-01-01'); }); it('returns correct status types', function () { $service = new AvailabilityService; $availability = $service->getMonthAvailability(now()->year, now()->month); $validStatuses = ['past', 'closed', 'blocked', 'full', 'partial', 'available']; foreach ($availability as $status) { expect($validStatuses)->toContain($status); } }); });