get(route('admin.consultations.index')) ->assertRedirect(route('login')); }); test('client cannot access consultations management page', function () { $client = User::factory()->individual()->create(); $this->actingAs($client) ->get(route('admin.consultations.index')) ->assertForbidden(); }); test('admin can access consultations management page', function () { $admin = User::factory()->admin()->create(); $this->actingAs($admin) ->get(route('admin.consultations.index')) ->assertOk(); }); // ========================================== // CONSULTATIONS LIST VIEW TESTS // ========================================== test('consultations list displays all consultations', function () { $admin = User::factory()->admin()->create(); $client = User::factory()->individual()->create(['full_name' => 'Test Client']); Consultation::factory()->approved()->create(['user_id' => $client->id]); $this->actingAs($admin); Volt::test('admin.consultations.index') ->assertSee('Test Client'); }); test('consultations list filters by status', function () { $admin = User::factory()->admin()->create(); $approvedClient = User::factory()->individual()->create(['full_name' => 'Approved Client']); $completedClient = User::factory()->individual()->create(['full_name' => 'Completed Client']); Consultation::factory()->approved()->create(['user_id' => $approvedClient->id]); Consultation::factory()->completed()->create(['user_id' => $completedClient->id]); $this->actingAs($admin); Volt::test('admin.consultations.index') ->set('statusFilter', 'approved') ->assertSee('Approved Client') ->assertDontSee('Completed Client'); }); test('consultations list filters by consultation type', function () { $admin = User::factory()->admin()->create(); $freeClient = User::factory()->individual()->create(['full_name' => 'Free Client']); $paidClient = User::factory()->individual()->create(['full_name' => 'Paid Client']); Consultation::factory()->approved()->free()->create(['user_id' => $freeClient->id]); Consultation::factory()->approved()->paid()->create(['user_id' => $paidClient->id]); $this->actingAs($admin); Volt::test('admin.consultations.index') ->set('typeFilter', 'free') ->assertSee('Free Client') ->assertDontSee('Paid Client'); }); test('consultations list filters by payment status', function () { $admin = User::factory()->admin()->create(); $pendingClient = User::factory()->individual()->create(['full_name' => 'Pending Payment']); $receivedClient = User::factory()->individual()->create(['full_name' => 'Received Payment']); Consultation::factory()->approved()->create([ 'user_id' => $pendingClient->id, 'consultation_type' => ConsultationType::Paid, 'payment_status' => PaymentStatus::Pending, ]); Consultation::factory()->approved()->create([ 'user_id' => $receivedClient->id, 'consultation_type' => ConsultationType::Paid, 'payment_status' => PaymentStatus::Received, ]); $this->actingAs($admin); Volt::test('admin.consultations.index') ->set('paymentFilter', 'pending') ->assertSee('Pending Payment') ->assertDontSee('Received Payment'); }); test('consultations list searches by client name', function () { $admin = User::factory()->admin()->create(); $targetUser = User::factory()->individual()->create(['full_name' => 'John Doe']); $otherUser = User::factory()->individual()->create(['full_name' => 'Jane Smith']); Consultation::factory()->approved()->create(['user_id' => $targetUser->id]); Consultation::factory()->approved()->create(['user_id' => $otherUser->id]); $this->actingAs($admin); Volt::test('admin.consultations.index') ->set('search', 'John') ->assertSee('John Doe') ->assertDontSee('Jane Smith'); }); test('consultations list filters by date range', function () { $admin = User::factory()->admin()->create(); $oldClient = User::factory()->individual()->create(['full_name' => 'Old Client']); $newClient = User::factory()->individual()->create(['full_name' => 'New Client']); Consultation::factory()->approved()->create([ 'user_id' => $oldClient->id, 'booking_date' => now()->subDays(10), ]); Consultation::factory()->approved()->create([ 'user_id' => $newClient->id, 'booking_date' => now()->addDays(5), ]); $this->actingAs($admin); Volt::test('admin.consultations.index') ->set('dateFrom', now()->format('Y-m-d')) ->assertSee('New Client') ->assertDontSee('Old Client'); }); // ========================================== // STATUS MANAGEMENT TESTS // ========================================== test('admin can mark consultation as completed', function () { $admin = User::factory()->admin()->create(); $consultation = Consultation::factory()->approved()->create(); $this->actingAs($admin); Volt::test('admin.consultations.index') ->call('markCompleted', $consultation->id) ->assertHasNoErrors(); expect($consultation->fresh()->status)->toBe(ConsultationStatus::Completed); }); test('cannot mark pending consultation as completed', function () { $admin = User::factory()->admin()->create(); $consultation = Consultation::factory()->pending()->create(); $this->actingAs($admin); Volt::test('admin.consultations.index') ->call('markCompleted', $consultation->id); expect($consultation->fresh()->status)->toBe(ConsultationStatus::Pending); }); test('admin can mark consultation as no-show', function () { $admin = User::factory()->admin()->create(); $consultation = Consultation::factory()->approved()->create(); $this->actingAs($admin); Volt::test('admin.consultations.index') ->call('markNoShow', $consultation->id) ->assertHasNoErrors(); expect($consultation->fresh()->status)->toBe(ConsultationStatus::NoShow); }); test('cannot mark completed consultation as no-show', function () { $admin = User::factory()->admin()->create(); $consultation = Consultation::factory()->completed()->create(); $this->actingAs($admin); Volt::test('admin.consultations.index') ->call('markNoShow', $consultation->id); expect($consultation->fresh()->status)->toBe(ConsultationStatus::Completed); }); test('admin can cancel approved consultation', function () { Notification::fake(); $admin = User::factory()->admin()->create(); $client = User::factory()->individual()->create(); $consultation = Consultation::factory()->approved()->create(['user_id' => $client->id]); $this->actingAs($admin); Volt::test('admin.consultations.index') ->call('cancel', $consultation->id) ->assertHasNoErrors(); expect($consultation->fresh()->status)->toBe(ConsultationStatus::Cancelled); Notification::assertSentTo($client, ConsultationCancelled::class); }); test('admin can cancel pending consultation', function () { Notification::fake(); $admin = User::factory()->admin()->create(); $client = User::factory()->individual()->create(); $consultation = Consultation::factory()->pending()->create(['user_id' => $client->id]); $this->actingAs($admin); Volt::test('admin.consultations.index') ->call('cancel', $consultation->id) ->assertHasNoErrors(); expect($consultation->fresh()->status)->toBe(ConsultationStatus::Cancelled); }); test('cannot cancel already cancelled consultation', function () { Notification::fake(); $admin = User::factory()->admin()->create(); $consultation = Consultation::factory()->cancelled()->create(); $this->actingAs($admin); Volt::test('admin.consultations.index') ->call('cancel', $consultation->id); Notification::assertNothingSent(); }); // ========================================== // PAYMENT TRACKING TESTS // ========================================== test('admin can mark payment as received for paid consultation', function () { $admin = User::factory()->admin()->create(); $consultation = Consultation::factory()->approved()->create([ 'consultation_type' => ConsultationType::Paid, 'payment_amount' => 150.00, 'payment_status' => PaymentStatus::Pending, ]); $this->actingAs($admin); Volt::test('admin.consultations.index') ->call('markPaymentReceived', $consultation->id) ->assertHasNoErrors(); expect($consultation->fresh()) ->payment_status->toBe(PaymentStatus::Received) ->payment_received_at->not->toBeNull(); }); test('cannot mark payment received for free consultation', function () { $admin = User::factory()->admin()->create(); $consultation = Consultation::factory()->approved()->free()->create(); $this->actingAs($admin); Volt::test('admin.consultations.index') ->call('markPaymentReceived', $consultation->id); expect($consultation->fresh()->payment_status)->toBe(PaymentStatus::NotApplicable); }); test('cannot mark payment received twice', function () { $admin = User::factory()->admin()->create(); $consultation = Consultation::factory()->approved()->create([ 'consultation_type' => ConsultationType::Paid, 'payment_status' => PaymentStatus::Received, 'payment_received_at' => now()->subDay(), ]); $originalReceivedAt = $consultation->payment_received_at; $this->actingAs($admin); Volt::test('admin.consultations.index') ->call('markPaymentReceived', $consultation->id); expect($consultation->fresh()->payment_received_at->timestamp) ->toBe($originalReceivedAt->timestamp); }); // ========================================== // RESCHEDULE TESTS // ========================================== test('admin can access consultation detail page', function () { $admin = User::factory()->admin()->create(); $consultation = Consultation::factory()->approved()->create(); $this->actingAs($admin) ->get(route('admin.consultations.show', $consultation)) ->assertOk(); }); test('admin can reschedule consultation to available slot', function () { Notification::fake(); $admin = User::factory()->admin()->create(); $client = User::factory()->individual()->create(); $consultation = Consultation::factory()->approved()->create([ 'user_id' => $client->id, 'booking_date' => now()->addDays(3), 'booking_time' => '10:00:00', ]); // Create working hours for the new date WorkingHour::factory()->create([ 'day_of_week' => now()->addDays(5)->dayOfWeek, 'is_active' => true, 'start_time' => '09:00', 'end_time' => '17:00', ]); $newDate = now()->addDays(5)->format('Y-m-d'); $newTime = '14:00'; $this->actingAs($admin); // Mock the availability service $this->mock(AvailabilityService::class) ->shouldReceive('getAvailableSlots') ->andReturn(['14:00', '15:00', '16:00']); Volt::test('admin.consultations.show', ['consultation' => $consultation]) ->set('showRescheduleModal', true) ->set('newDate', $newDate) ->set('newTime', $newTime) ->call('reschedule') ->assertHasNoErrors(); expect($consultation->fresh()) ->booking_date->format('Y-m-d')->toBe($newDate) ->booking_time->toBe($newTime); Notification::assertSentTo($client, ConsultationRescheduled::class); }); test('cannot reschedule to unavailable slot', function () { $admin = User::factory()->admin()->create(); $consultation = Consultation::factory()->approved()->create(); $newDate = now()->addDays(5)->format('Y-m-d'); $newTime = '14:00'; $this->actingAs($admin); // Mock availability service - slot not available $this->mock(AvailabilityService::class) ->shouldReceive('getAvailableSlots') ->andReturn(['10:00', '11:00']); // 14:00 not in list Volt::test('admin.consultations.show', ['consultation' => $consultation]) ->set('showRescheduleModal', true) ->set('newDate', $newDate) ->set('newTime', $newTime) ->call('reschedule') ->assertHasErrors(['newTime']); }); test('cannot reschedule to past date', function () { $admin = User::factory()->admin()->create(); $consultation = Consultation::factory()->approved()->create(); $this->actingAs($admin); Volt::test('admin.consultations.show', ['consultation' => $consultation]) ->set('showRescheduleModal', true) ->set('newDate', now()->subDay()->format('Y-m-d')) ->set('newTime', '10:00') ->call('reschedule') ->assertHasErrors(['newDate']); }); // ========================================== // ADMIN NOTES TESTS // ========================================== test('admin can add note to consultation', function () { $admin = User::factory()->admin()->create(); $consultation = Consultation::factory()->approved()->create(); $this->actingAs($admin); Volt::test('admin.consultations.show', ['consultation' => $consultation]) ->set('newNote', 'Client requested Arabic documents') ->call('addNote') ->assertHasNoErrors(); $notes = $consultation->fresh()->admin_notes; expect($notes)->toHaveCount(1) ->and($notes[0]['text'])->toBe('Client requested Arabic documents') ->and($notes[0]['admin_id'])->toBe($admin->id); }); test('admin can update note', function () { $admin = User::factory()->admin()->create(); $consultation = Consultation::factory()->approved()->create([ 'admin_notes' => [ ['text' => 'Original note', 'admin_id' => $admin->id, 'created_at' => now()->toISOString()], ], ]); $this->actingAs($admin); Volt::test('admin.consultations.show', ['consultation' => $consultation]) ->call('startEditNote', 0) ->set('editingNoteText', 'Updated note') ->call('updateNote') ->assertHasNoErrors(); $notes = $consultation->fresh()->admin_notes; expect($notes[0]['text'])->toBe('Updated note') ->and($notes[0])->toHaveKey('updated_at'); }); test('admin can delete note', function () { $admin = User::factory()->admin()->create(); $consultation = Consultation::factory()->approved()->create([ 'admin_notes' => [ ['text' => 'Note 1', 'admin_id' => $admin->id, 'created_at' => now()->toISOString()], ['text' => 'Note 2', 'admin_id' => $admin->id, 'created_at' => now()->toISOString()], ], ]); $this->actingAs($admin); Volt::test('admin.consultations.show', ['consultation' => $consultation]) ->call('deleteNote', 0) ->assertHasNoErrors(); $notes = $consultation->fresh()->admin_notes; expect($notes)->toHaveCount(1) ->and($notes[0]['text'])->toBe('Note 2'); }); // ========================================== // AUDIT LOG TESTS // ========================================== test('audit log entry created on status change to completed', function () { $admin = User::factory()->admin()->create(); $consultation = Consultation::factory()->approved()->create(); $this->actingAs($admin); Volt::test('admin.consultations.index') ->call('markCompleted', $consultation->id); expect(AdminLog::query() ->where('admin_id', $admin->id) ->where('action', 'status_change') ->where('target_type', 'consultation') ->where('target_id', $consultation->id) ->exists() )->toBeTrue(); }); test('audit log entry created on payment received', function () { $admin = User::factory()->admin()->create(); $consultation = Consultation::factory()->approved()->create([ 'consultation_type' => ConsultationType::Paid, 'payment_status' => PaymentStatus::Pending, ]); $this->actingAs($admin); Volt::test('admin.consultations.index') ->call('markPaymentReceived', $consultation->id); expect(AdminLog::query() ->where('admin_id', $admin->id) ->where('action', 'payment_received') ->where('target_type', 'consultation') ->where('target_id', $consultation->id) ->exists() )->toBeTrue(); }); test('audit log entry created on reschedule', function () { Notification::fake(); $admin = User::factory()->admin()->create(); $client = User::factory()->individual()->create(); $consultation = Consultation::factory()->approved()->create([ 'user_id' => $client->id, ]); $newDate = now()->addDays(5)->format('Y-m-d'); $this->actingAs($admin); $this->mock(AvailabilityService::class) ->shouldReceive('getAvailableSlots') ->andReturn(['14:00']); Volt::test('admin.consultations.show', ['consultation' => $consultation]) ->set('showRescheduleModal', true) ->set('newDate', $newDate) ->set('newTime', '14:00') ->call('reschedule'); expect(AdminLog::query() ->where('admin_id', $admin->id) ->where('action', 'reschedule') ->where('target_type', 'consultation') ->where('target_id', $consultation->id) ->exists() )->toBeTrue(); }); // ========================================== // CLIENT HISTORY TESTS // ========================================== test('admin can access client consultation history', function () { $admin = User::factory()->admin()->create(); $client = User::factory()->individual()->create(); $this->actingAs($admin) ->get(route('admin.clients.consultation-history', $client)) ->assertOk(); }); test('client consultation history displays consultations', function () { $admin = User::factory()->admin()->create(); $client = User::factory()->individual()->create(); Consultation::factory()->approved()->create([ 'user_id' => $client->id, 'booking_date' => now()->addDays(5), ]); $this->actingAs($admin); Volt::test('admin.clients.consultation-history', ['user' => $client]) ->assertOk(); }); test('client consultation history shows statistics', function () { $admin = User::factory()->admin()->create(); $client = User::factory()->individual()->create(); Consultation::factory()->completed()->count(2)->create(['user_id' => $client->id]); Consultation::factory()->cancelled()->create(['user_id' => $client->id]); Consultation::factory()->noShow()->create(['user_id' => $client->id]); $this->actingAs($admin); Volt::test('admin.clients.consultation-history', ['user' => $client]) ->assertSee('4') // total ->assertSee('2'); // completed }); // ========================================== // BILINGUAL TESTS // ========================================== test('cancellation notification sent in client preferred language', function () { Notification::fake(); $admin = User::factory()->admin()->create(); $arabicUser = User::factory()->individual()->create(['preferred_language' => 'ar']); $consultation = Consultation::factory()->approved()->create(['user_id' => $arabicUser->id]); $this->actingAs($admin); Volt::test('admin.consultations.index') ->call('cancel', $consultation->id); Notification::assertSentTo($arabicUser, ConsultationCancelled::class, function ($notification) { return $notification->consultation->user->preferred_language === 'ar'; }); });