service = new CalendarService; }); it('generates valid ics file', function () { $consultation = Consultation::factory()->approved()->create([ 'booking_date' => '2024-03-15', 'booking_time' => '10:00:00', ]); $ics = $this->service->generateIcs($consultation); expect($ics) ->toContain('BEGIN:VCALENDAR') ->toContain('BEGIN:VEVENT') ->toContain('DTSTART') ->toContain('DTEND') ->toContain('END:VCALENDAR'); }); it('includes correct duration of 45 minutes', function () { $consultation = Consultation::factory()->approved()->create([ 'booking_date' => '2024-03-15', 'booking_time' => '10:00:00', ]); $ics = $this->service->generateIcs($consultation); // Start at 10:00, end at 10:45 (45 min default) expect($ics) ->toContain('DTSTART;TZID=Asia/Jerusalem:20240315T100000') ->toContain('DTEND;TZID=Asia/Jerusalem:20240315T104500'); }); it('generates Arabic content for Arabic-preferring users', function () { $user = User::factory()->create(['preferred_language' => 'ar']); $consultation = Consultation::factory()->approved()->for($user)->create(); $ics = $this->service->generateIcs($consultation); expect($ics) ->toContain('استشارة مع مكتب ليبرا للمحاماة') ->toContain('رقم الحجز'); }); it('generates English content for English-preferring users', function () { $user = User::factory()->create(['preferred_language' => 'en']); $consultation = Consultation::factory()->approved()->for($user)->create(); $ics = $this->service->generateIcs($consultation); expect($ics) ->toContain('Consultation with Libra Law Firm') ->toContain('Booking Reference'); }); it('defaults to Arabic when user has default language setting', function () { // The database defaults preferred_language to 'ar', so we test with the default $user = User::factory()->create(['preferred_language' => 'ar']); $consultation = Consultation::factory()->approved()->for($user)->create(); $ics = $this->service->generateIcs($consultation); expect($ics) ->toContain('استشارة مع مكتب ليبرا للمحاماة'); }); it('includes payment info for paid consultations', function () { $consultation = Consultation::factory()->approved()->create([ 'consultation_type' => ConsultationType::Paid, 'payment_amount' => 150.00, ]); $ics = $this->service->generateIcs($consultation); expect($ics)->toContain('150.00'); }); it('does not include payment info for free consultations', function () { $user = User::factory()->create(['preferred_language' => 'en']); $consultation = Consultation::factory()->approved()->free()->for($user)->create(); $ics = $this->service->generateIcs($consultation); expect($ics) ->toContain('Consultation Type: Free') ->not->toContain('Amount:'); }); it('includes 1-hour reminder alarm', function () { $consultation = Consultation::factory()->approved()->create(); $ics = $this->service->generateIcs($consultation); expect($ics) ->toContain('BEGIN:VALARM') ->toContain('TRIGGER:-PT1H') ->toContain('ACTION:DISPLAY') ->toContain('END:VALARM'); }); it('includes proper timezone definition', function () { $consultation = Consultation::factory()->approved()->create(); $ics = $this->service->generateIcs($consultation); expect($ics) ->toContain('BEGIN:VTIMEZONE') ->toContain('TZID:Asia/Jerusalem') ->toContain('END:VTIMEZONE'); }); it('includes unique identifier', function () { $consultation = Consultation::factory()->approved()->create(); $ics = $this->service->generateIcs($consultation); expect($ics)->toContain('UID:consultation-'.$consultation->id.'@libra.ps'); }); it('includes location from config', function () { $consultation = Consultation::factory()->approved()->create(); $ics = $this->service->generateIcs($consultation); expect($ics)->toContain('LOCATION:'); }); it('uses CRLF line endings per RFC 5545', function () { $consultation = Consultation::factory()->approved()->create(); $ics = $this->service->generateIcs($consultation); // Check for CRLF line endings expect($ics)->toContain("\r\n"); }); it('escapes special characters in content', function () { $user = User::factory()->create(['preferred_language' => 'en']); $consultation = Consultation::factory()->approved()->for($user)->create(); $ics = $this->service->generateIcs($consultation); // The location should have escaped commas if present // Location is: "Libra Law Firm, Palestine" which has a comma expect($ics)->toContain('Libra Law Firm\, Palestine'); }); it('returns download response with correct headers', function () { $consultation = Consultation::factory()->approved()->create([ 'booking_date' => '2024-03-15', ]); $response = $this->service->generateDownloadResponse($consultation); expect($response->headers->get('Content-Type')) ->toBe('text/calendar; charset=utf-8'); expect($response->headers->get('Content-Disposition')) ->toContain('consultation-2024-03-15.ics'); }); it('generates correct filename based on booking date', function () { $consultation = Consultation::factory()->approved()->create([ 'booking_date' => '2025-06-20', ]); $response = $this->service->generateDownloadResponse($consultation); expect($response->headers->get('Content-Disposition')) ->toContain('consultation-2025-06-20.ics'); });