398 lines
13 KiB
PHP
398 lines
13 KiB
PHP
<?php
|
|
|
|
use App\Models\Consultation;
|
|
use App\Models\User;
|
|
use App\Notifications\ConsultationReminder24h;
|
|
use App\Notifications\ConsultationReminder2h;
|
|
use Illuminate\Support\Facades\Notification;
|
|
|
|
it('sends 24h reminder for upcoming consultation', function () {
|
|
Notification::fake();
|
|
|
|
$consultation = Consultation::factory()->approved()->create([
|
|
'booking_date' => now()->addHours(24)->toDateString(),
|
|
'booking_time' => now()->addHours(24)->format('H:i:s'),
|
|
'reminder_24h_sent_at' => null,
|
|
]);
|
|
|
|
$this->artisan('reminders:send-24h')
|
|
->assertSuccessful();
|
|
|
|
Notification::assertSentTo(
|
|
$consultation->user,
|
|
ConsultationReminder24h::class
|
|
);
|
|
|
|
expect($consultation->fresh()->reminder_24h_sent_at)->not->toBeNull();
|
|
});
|
|
|
|
it('does not send 24h reminder for cancelled consultation', function () {
|
|
Notification::fake();
|
|
|
|
$consultation = Consultation::factory()->cancelled()->create([
|
|
'booking_date' => now()->addHours(24)->toDateString(),
|
|
'booking_time' => now()->addHours(24)->format('H:i:s'),
|
|
]);
|
|
|
|
$this->artisan('reminders:send-24h')
|
|
->assertSuccessful();
|
|
|
|
Notification::assertNotSentTo($consultation->user, ConsultationReminder24h::class);
|
|
});
|
|
|
|
it('does not send 24h reminder for no-show consultation', function () {
|
|
Notification::fake();
|
|
|
|
$consultation = Consultation::factory()->noShow()->create([
|
|
'booking_date' => now()->addHours(24)->toDateString(),
|
|
'booking_time' => now()->addHours(24)->format('H:i:s'),
|
|
]);
|
|
|
|
$this->artisan('reminders:send-24h')
|
|
->assertSuccessful();
|
|
|
|
Notification::assertNotSentTo($consultation->user, ConsultationReminder24h::class);
|
|
});
|
|
|
|
it('does not send duplicate 24h reminders', function () {
|
|
Notification::fake();
|
|
|
|
$consultation = Consultation::factory()->approved()->create([
|
|
'booking_date' => now()->addHours(24)->toDateString(),
|
|
'booking_time' => now()->addHours(24)->format('H:i:s'),
|
|
'reminder_24h_sent_at' => now()->subHour(),
|
|
]);
|
|
|
|
$this->artisan('reminders:send-24h')
|
|
->assertSuccessful();
|
|
|
|
Notification::assertNotSentTo($consultation->user, ConsultationReminder24h::class);
|
|
});
|
|
|
|
it('sends 2h reminder for upcoming consultation', function () {
|
|
Notification::fake();
|
|
|
|
$consultation = Consultation::factory()->approved()->create([
|
|
'booking_date' => now()->addHours(2)->toDateString(),
|
|
'booking_time' => now()->addHours(2)->format('H:i:s'),
|
|
'reminder_2h_sent_at' => null,
|
|
]);
|
|
|
|
$this->artisan('reminders:send-2h')
|
|
->assertSuccessful();
|
|
|
|
Notification::assertSentTo(
|
|
$consultation->user,
|
|
ConsultationReminder2h::class
|
|
);
|
|
|
|
expect($consultation->fresh()->reminder_2h_sent_at)->not->toBeNull();
|
|
});
|
|
|
|
it('does not send 2h reminder for cancelled consultation', function () {
|
|
Notification::fake();
|
|
|
|
$consultation = Consultation::factory()->cancelled()->create([
|
|
'booking_date' => now()->addHours(2)->toDateString(),
|
|
'booking_time' => now()->addHours(2)->format('H:i:s'),
|
|
]);
|
|
|
|
$this->artisan('reminders:send-2h')
|
|
->assertSuccessful();
|
|
|
|
Notification::assertNotSentTo($consultation->user, ConsultationReminder2h::class);
|
|
});
|
|
|
|
it('does not send duplicate 2h reminders', function () {
|
|
Notification::fake();
|
|
|
|
$consultation = Consultation::factory()->approved()->create([
|
|
'booking_date' => now()->addHours(2)->toDateString(),
|
|
'booking_time' => now()->addHours(2)->format('H:i:s'),
|
|
'reminder_2h_sent_at' => now()->subMinutes(30),
|
|
]);
|
|
|
|
$this->artisan('reminders:send-2h')
|
|
->assertSuccessful();
|
|
|
|
Notification::assertNotSentTo($consultation->user, ConsultationReminder2h::class);
|
|
});
|
|
|
|
it('includes payment reminder for unpaid consultations in 24h reminder', function () {
|
|
Notification::fake();
|
|
|
|
$consultation = Consultation::factory()->approved()->paid()->create([
|
|
'booking_date' => now()->addHours(24)->toDateString(),
|
|
'booking_time' => now()->addHours(24)->format('H:i:s'),
|
|
'payment_status' => 'pending',
|
|
'payment_amount' => 200.00,
|
|
]);
|
|
|
|
$this->artisan('reminders:send-24h')
|
|
->assertSuccessful();
|
|
|
|
Notification::assertSentTo(
|
|
$consultation->user,
|
|
ConsultationReminder24h::class,
|
|
function ($notification) {
|
|
return $notification->consultation->consultation_type->value === 'paid'
|
|
&& $notification->consultation->payment_status->value === 'pending';
|
|
}
|
|
);
|
|
});
|
|
|
|
it('respects user language preference for 24h reminders', function () {
|
|
Notification::fake();
|
|
|
|
$user = User::factory()->create(['preferred_language' => 'en']);
|
|
$consultation = Consultation::factory()->approved()->create([
|
|
'user_id' => $user->id,
|
|
'booking_date' => now()->addHours(24)->toDateString(),
|
|
'booking_time' => now()->addHours(24)->format('H:i:s'),
|
|
]);
|
|
|
|
$this->artisan('reminders:send-24h')
|
|
->assertSuccessful();
|
|
|
|
Notification::assertSentTo($user, ConsultationReminder24h::class);
|
|
});
|
|
|
|
it('does not send 24h reminder for pending consultation', function () {
|
|
Notification::fake();
|
|
|
|
$consultation = Consultation::factory()->pending()->create([
|
|
'booking_date' => now()->addHours(24)->toDateString(),
|
|
'booking_time' => now()->addHours(24)->format('H:i:s'),
|
|
]);
|
|
|
|
$this->artisan('reminders:send-24h')
|
|
->assertSuccessful();
|
|
|
|
Notification::assertNotSentTo($consultation->user, ConsultationReminder24h::class);
|
|
});
|
|
|
|
it('does not send 24h reminder for completed consultation', function () {
|
|
Notification::fake();
|
|
|
|
$consultation = Consultation::factory()->completed()->create([
|
|
'booking_date' => now()->addHours(24)->toDateString(),
|
|
'booking_time' => now()->addHours(24)->format('H:i:s'),
|
|
]);
|
|
|
|
$this->artisan('reminders:send-24h')
|
|
->assertSuccessful();
|
|
|
|
Notification::assertNotSentTo($consultation->user, ConsultationReminder24h::class);
|
|
});
|
|
|
|
it('does not send 2h reminder for rejected consultation', function () {
|
|
Notification::fake();
|
|
|
|
$consultation = Consultation::factory()->rejected()->create([
|
|
'booking_date' => now()->addHours(2)->toDateString(),
|
|
'booking_time' => now()->addHours(2)->format('H:i:s'),
|
|
]);
|
|
|
|
$this->artisan('reminders:send-2h')
|
|
->assertSuccessful();
|
|
|
|
Notification::assertNotSentTo($consultation->user, ConsultationReminder2h::class);
|
|
});
|
|
|
|
// 2-Hour Reminder Additional Tests (Story 8.7)
|
|
|
|
it('2h command uses 7-minute window for matching', function () {
|
|
Notification::fake();
|
|
|
|
// Create consultation at exactly 2h + 8 minutes (outside window)
|
|
$outsideWindow = Consultation::factory()->approved()->create([
|
|
'booking_date' => now()->addHours(2)->addMinutes(8)->toDateString(),
|
|
'booking_time' => now()->addHours(2)->addMinutes(8)->format('H:i:s'),
|
|
'reminder_2h_sent_at' => null,
|
|
]);
|
|
|
|
$this->artisan('reminders:send-2h')
|
|
->assertSuccessful();
|
|
|
|
Notification::assertNotSentTo($outsideWindow->user, ConsultationReminder2h::class);
|
|
});
|
|
|
|
it('2h command sends reminder within 7-minute window', function () {
|
|
Notification::fake();
|
|
|
|
// Create consultation at exactly 2h + 6 minutes (inside window)
|
|
$insideWindow = Consultation::factory()->approved()->create([
|
|
'booking_date' => now()->addHours(2)->addMinutes(6)->toDateString(),
|
|
'booking_time' => now()->addHours(2)->addMinutes(6)->format('H:i:s'),
|
|
'reminder_2h_sent_at' => null,
|
|
]);
|
|
|
|
$this->artisan('reminders:send-2h')
|
|
->assertSuccessful();
|
|
|
|
Notification::assertSentTo($insideWindow->user, ConsultationReminder2h::class);
|
|
});
|
|
|
|
it('2h command only checks consultations scheduled for today', function () {
|
|
Notification::fake();
|
|
|
|
// Create consultation 2 hours from now but for tomorrow's date
|
|
$tomorrowConsultation = Consultation::factory()->approved()->create([
|
|
'booking_date' => now()->addDay()->toDateString(),
|
|
'booking_time' => now()->addHours(2)->format('H:i:s'),
|
|
'reminder_2h_sent_at' => null,
|
|
]);
|
|
|
|
$this->artisan('reminders:send-2h')
|
|
->assertSuccessful();
|
|
|
|
Notification::assertNotSentTo($tomorrowConsultation->user, ConsultationReminder2h::class);
|
|
});
|
|
|
|
it('does not send 2h reminder for pending consultation', function () {
|
|
Notification::fake();
|
|
|
|
$consultation = Consultation::factory()->pending()->create([
|
|
'booking_date' => now()->addHours(2)->toDateString(),
|
|
'booking_time' => now()->addHours(2)->format('H:i:s'),
|
|
]);
|
|
|
|
$this->artisan('reminders:send-2h')
|
|
->assertSuccessful();
|
|
|
|
Notification::assertNotSentTo($consultation->user, ConsultationReminder2h::class);
|
|
});
|
|
|
|
it('does not send 2h reminder for completed consultation', function () {
|
|
Notification::fake();
|
|
|
|
$consultation = Consultation::factory()->completed()->create([
|
|
'booking_date' => now()->addHours(2)->toDateString(),
|
|
'booking_time' => now()->addHours(2)->format('H:i:s'),
|
|
]);
|
|
|
|
$this->artisan('reminders:send-2h')
|
|
->assertSuccessful();
|
|
|
|
Notification::assertNotSentTo($consultation->user, ConsultationReminder2h::class);
|
|
});
|
|
|
|
it('does not send 2h reminder for no-show consultation', function () {
|
|
Notification::fake();
|
|
|
|
$consultation = Consultation::factory()->noShow()->create([
|
|
'booking_date' => now()->addHours(2)->toDateString(),
|
|
'booking_time' => now()->addHours(2)->format('H:i:s'),
|
|
]);
|
|
|
|
$this->artisan('reminders:send-2h')
|
|
->assertSuccessful();
|
|
|
|
Notification::assertNotSentTo($consultation->user, ConsultationReminder2h::class);
|
|
});
|
|
|
|
it('respects user language preference for 2h reminders', function () {
|
|
Notification::fake();
|
|
|
|
$user = User::factory()->create(['preferred_language' => 'en']);
|
|
$consultation = Consultation::factory()->approved()->create([
|
|
'user_id' => $user->id,
|
|
'booking_date' => now()->addHours(2)->toDateString(),
|
|
'booking_time' => now()->addHours(2)->format('H:i:s'),
|
|
]);
|
|
|
|
$this->artisan('reminders:send-2h')
|
|
->assertSuccessful();
|
|
|
|
Notification::assertSentTo($user, ConsultationReminder2h::class);
|
|
});
|
|
|
|
it('shows payment reminder for unpaid paid consultation in 2h reminder', function () {
|
|
Notification::fake();
|
|
|
|
$consultation = Consultation::factory()->approved()->paid()->create([
|
|
'booking_date' => now()->addHours(2)->toDateString(),
|
|
'booking_time' => now()->addHours(2)->format('H:i:s'),
|
|
'payment_status' => 'pending',
|
|
'payment_amount' => 200.00,
|
|
]);
|
|
|
|
$this->artisan('reminders:send-2h')
|
|
->assertSuccessful();
|
|
|
|
Notification::assertSentTo(
|
|
$consultation->user,
|
|
ConsultationReminder2h::class,
|
|
function ($notification) {
|
|
return $notification->consultation->consultation_type->value === 'paid'
|
|
&& $notification->consultation->payment_status->value === 'pending';
|
|
}
|
|
);
|
|
});
|
|
|
|
it('hides payment reminder when payment received in 2h reminder', function () {
|
|
Notification::fake();
|
|
|
|
$consultation = Consultation::factory()->approved()->paid()->create([
|
|
'booking_date' => now()->addHours(2)->toDateString(),
|
|
'booking_time' => now()->addHours(2)->format('H:i:s'),
|
|
'payment_status' => 'received',
|
|
'payment_amount' => 200.00,
|
|
]);
|
|
|
|
$this->artisan('reminders:send-2h')
|
|
->assertSuccessful();
|
|
|
|
Notification::assertSentTo(
|
|
$consultation->user,
|
|
ConsultationReminder2h::class,
|
|
function ($notification) {
|
|
return $notification->consultation->payment_status->value === 'received';
|
|
}
|
|
);
|
|
});
|
|
|
|
// 2-Hour Reminder Email Content Tests (Story 8.7)
|
|
|
|
it('2h reminder notification has correct subject in Arabic', function () {
|
|
$user = User::factory()->create(['preferred_language' => 'ar']);
|
|
$consultation = Consultation::factory()->approved()->create([
|
|
'user_id' => $user->id,
|
|
]);
|
|
|
|
$notification = new ConsultationReminder2h($consultation);
|
|
$mail = $notification->toMail($user);
|
|
|
|
expect($mail->subject)->toBe('استشارتك بعد ساعتين');
|
|
});
|
|
|
|
it('2h reminder notification has correct subject in English', function () {
|
|
$user = User::factory()->create(['preferred_language' => 'en']);
|
|
$consultation = Consultation::factory()->approved()->create([
|
|
'user_id' => $user->id,
|
|
]);
|
|
|
|
$notification = new ConsultationReminder2h($consultation);
|
|
$mail = $notification->toMail($user);
|
|
|
|
expect($mail->subject)->toBe('Your consultation is in 2 hours');
|
|
});
|
|
|
|
it('2h reminder email does not include calendar download link', function () {
|
|
$user = User::factory()->create(['preferred_language' => 'en']);
|
|
$consultation = Consultation::factory()->approved()->create([
|
|
'user_id' => $user->id,
|
|
]);
|
|
|
|
$notification = new ConsultationReminder2h($consultation);
|
|
$mail = $notification->toMail($user);
|
|
|
|
// The view should be emails.reminder-2h which doesn't have calendar link
|
|
expect($mail->view)->toBe('emails.reminder-2h');
|
|
});
|
|
|
|
it('2h reminder notification is queued', function () {
|
|
expect(ConsultationReminder2h::class)
|
|
->toImplement(\Illuminate\Contracts\Queue\ShouldQueue::class);
|
|
});
|