admin = User::factory()->admin()->create(); }); // =========================================== // Deactivation Tests // =========================================== test('admin can deactivate an active individual client', function () { $client = User::factory()->individual()->create(['status' => UserStatus::Active]); $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openDeactivateModal') ->assertSet('showDeactivateModal', true) ->call('deactivate') ->assertHasNoErrors(); expect($client->fresh()->status)->toBe(UserStatus::Deactivated); }); test('admin can deactivate an active company client', function () { $client = User::factory()->company()->create(['status' => UserStatus::Active]); $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openDeactivateModal') ->call('deactivate') ->assertHasNoErrors(); expect($client->fresh()->status)->toBe(UserStatus::Deactivated); }); test('deactivated user cannot login', function () { $user = User::factory()->individual()->create([ 'status' => UserStatus::Deactivated, 'email' => 'deactivated@example.com', ]); $this->post('/login', [ 'email' => 'deactivated@example.com', 'password' => 'password', ])->assertSessionHasErrors(); $this->assertGuest(); }); test('user sessions are invalidated on deactivation', function () { $client = User::factory()->individual()->create(['status' => UserStatus::Active]); // Create a fake session for the user DB::table('sessions')->insert([ 'id' => 'test-session-id', 'user_id' => $client->id, 'ip_address' => '127.0.0.1', 'user_agent' => 'Test', 'payload' => 'test', 'last_activity' => time(), ]); expect(DB::table('sessions')->where('user_id', $client->id)->exists())->toBeTrue(); $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openDeactivateModal') ->call('deactivate'); expect(DB::table('sessions')->where('user_id', $client->id)->exists())->toBeFalse(); }); test('admin log entry created on deactivation', function () { $client = User::factory()->individual()->create(['status' => UserStatus::Active]); $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openDeactivateModal') ->call('deactivate'); expect(AdminLog::where('action', 'deactivate') ->where('target_type', 'user') ->where('target_id', $client->id) ->where('admin_id', $this->admin->id) ->exists())->toBeTrue(); $log = AdminLog::where('action', 'deactivate')->where('target_id', $client->id)->first(); expect($log->old_values)->toHaveKey('status', 'active'); expect($log->new_values)->toHaveKey('status', 'deactivated'); }); test('deactivation preserves all user data', function () { $client = User::factory()->individual()->create(['status' => UserStatus::Active]); // Create related data Consultation::factory()->create(['user_id' => $client->id]); $timeline = Timeline::factory()->create(['user_id' => $client->id]); TimelineUpdate::factory()->create([ 'timeline_id' => $timeline->id, 'admin_id' => $this->admin->id, ]); $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openDeactivateModal') ->call('deactivate'); // Verify data is still there expect(Consultation::where('user_id', $client->id)->count())->toBe(1); expect(Timeline::where('user_id', $client->id)->count())->toBe(1); expect($client->fresh())->not->toBeNull(); }); // =========================================== // Reactivation Tests // =========================================== test('admin can reactivate a deactivated individual client', function () { $client = User::factory()->individual()->deactivated()->create(); $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openReactivateModal') ->assertSet('showReactivateModal', true) ->call('reactivate') ->assertHasNoErrors(); expect($client->fresh()->status)->toBe(UserStatus::Active); }); test('admin can reactivate a deactivated company client', function () { $client = User::factory()->company()->deactivated()->create(); $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openReactivateModal') ->call('reactivate') ->assertHasNoErrors(); expect($client->fresh()->status)->toBe(UserStatus::Active); }); test('reactivated user can login successfully', function () { $client = User::factory()->individual()->create([ 'status' => UserStatus::Deactivated, 'email' => 'reactivate-test@example.com', ]); $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openReactivateModal') ->call('reactivate'); $this->post('/login', [ 'email' => 'reactivate-test@example.com', 'password' => 'password', ])->assertSessionHasNoErrors(); }); test('email notification queued on reactivation', function () { NotificationFacade::fake(); $client = User::factory()->individual()->deactivated()->create(); $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openReactivateModal') ->call('reactivate') ->assertHasNoErrors(); NotificationFacade::assertSentTo($client, AccountReactivatedNotification::class); }); test('admin log entry created on reactivation', function () { $client = User::factory()->individual()->deactivated()->create(); $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openReactivateModal') ->call('reactivate'); expect(AdminLog::where('action', 'reactivate') ->where('target_type', 'user') ->where('target_id', $client->id) ->where('admin_id', $this->admin->id) ->exists())->toBeTrue(); $log = AdminLog::where('action', 'reactivate')->where('target_id', $client->id)->first(); expect($log->old_values)->toHaveKey('status', 'deactivated'); expect($log->new_values)->toHaveKey('status', 'active'); }); // =========================================== // Permanent Deletion Tests // =========================================== test('delete requires email confirmation', function () { $client = User::factory()->individual()->create(['email' => 'delete-test@example.com']); $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openDeleteModal') ->assertSet('showDeleteModal', true) ->set('deleteConfirmation', 'wrong@email.com') ->call('delete') ->assertHasErrors(['deleteConfirmation']); expect(User::find($client->id))->not->toBeNull(); }); test('successful deletion removes user record permanently', function () { $client = User::factory()->individual()->create(['email' => 'permanent-delete@example.com']); $clientId = $client->id; $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openDeleteModal') ->set('deleteConfirmation', 'permanent-delete@example.com') ->call('delete') ->assertHasNoErrors() ->assertRedirect(route('admin.clients.individual.index')); expect(User::find($clientId))->toBeNull(); }); test('cascade deletion removes user consultations', function () { $client = User::factory()->individual()->create(['email' => 'cascade-test@example.com']); $consultation = Consultation::factory()->create(['user_id' => $client->id]); $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openDeleteModal') ->set('deleteConfirmation', 'cascade-test@example.com') ->call('delete'); expect(Consultation::find($consultation->id))->toBeNull(); }); test('cascade deletion removes user timelines and timeline updates', function () { $client = User::factory()->individual()->create(['email' => 'cascade-timeline@example.com']); $timeline = Timeline::factory()->create(['user_id' => $client->id]); $update = TimelineUpdate::factory()->create([ 'timeline_id' => $timeline->id, 'admin_id' => $this->admin->id, ]); $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openDeleteModal') ->set('deleteConfirmation', 'cascade-timeline@example.com') ->call('delete'); expect(Timeline::find($timeline->id))->toBeNull(); expect(TimelineUpdate::find($update->id))->toBeNull(); }); test('cascade deletion removes user notifications', function () { $client = User::factory()->individual()->create(['email' => 'cascade-notify@example.com']); $notification = Notification::factory()->create(['user_id' => $client->id]); $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openDeleteModal') ->set('deleteConfirmation', 'cascade-notify@example.com') ->call('delete'); expect(Notification::find($notification->id))->toBeNull(); }); test('admin log entry preserved after user deletion', function () { $client = User::factory()->individual()->create(['email' => 'log-preserve@example.com']); $clientId = $client->id; $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openDeleteModal') ->set('deleteConfirmation', 'log-preserve@example.com') ->call('delete'); expect(AdminLog::where('action', 'delete') ->where('target_type', 'user') ->where('target_id', $clientId) ->where('admin_id', $this->admin->id) ->exists())->toBeTrue(); }); test('company client redirects to company index after deletion', function () { $client = User::factory()->company()->create(['email' => 'company-delete@example.com']); $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openDeleteModal') ->set('deleteConfirmation', 'company-delete@example.com') ->call('delete') ->assertRedirect(route('admin.clients.company.index')); }); // =========================================== // Password Reset Tests // =========================================== test('admin can reset user password', function () { $client = User::factory()->individual()->create(); $originalPasswordHash = $client->password; $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openPasswordResetModal') ->assertSet('showPasswordResetModal', true) ->call('resetPassword') ->assertHasNoErrors(); expect($client->fresh()->password)->not->toBe($originalPasswordHash); }); test('new password meets minimum requirements', function () { $client = User::factory()->individual()->create(); $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openPasswordResetModal') ->call('resetPassword'); // Password should be 12 characters, hashed // We can't check the length directly, but we can verify it's hashed expect(strlen($client->fresh()->password))->toBeGreaterThan(50); }); test('password reset email sent to user', function () { NotificationFacade::fake(); $client = User::factory()->individual()->create(); $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openPasswordResetModal') ->call('resetPassword'); NotificationFacade::assertSentTo($client, PasswordResetByAdminNotification::class); }); test('admin log entry created for password reset', function () { $client = User::factory()->individual()->create(); $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openPasswordResetModal') ->call('resetPassword'); expect(AdminLog::where('action', 'password_reset') ->where('target_type', 'user') ->where('target_id', $client->id) ->where('admin_id', $this->admin->id) ->exists())->toBeTrue(); }); test('user can login with new password after reset', function () { NotificationFacade::fake(); $client = User::factory()->individual()->create([ 'email' => 'newpass-test@example.com', ]); $this->actingAs($this->admin); // Reset the password Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openPasswordResetModal') ->call('resetPassword'); // Get the new password from the notification NotificationFacade::assertSentTo( $client, PasswordResetByAdminNotification::class, function ($notification) use ($client) { // Test that the new password works $newPassword = $notification->newPassword; auth()->logout(); // Try logging in with new password $response = $this->post('/login', [ 'email' => $client->email, 'password' => $newPassword, ]); return true; } ); }); // =========================================== // Authorization Tests // =========================================== test('non-admin cannot access lifecycle actions', function () { $client = User::factory()->individual()->create(); $this->actingAs($client); $this->get(route('admin.clients.individual.show', $client)) ->assertForbidden(); }); test('admin cannot deactivate their own account', function () { $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $this->admin]) ->call('openDeactivateModal') ->call('deactivate') ->assertSet('showDeactivateModal', false); // Verify status was not changed expect($this->admin->fresh()->status)->toBe(UserStatus::Active); // Verify no admin log was created for this action expect(AdminLog::where('action', 'deactivate') ->where('target_id', $this->admin->id) ->exists())->toBeFalse(); }); test('admin cannot delete their own account', function () { $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $this->admin]) ->call('openDeleteModal') ->set('deleteConfirmation', $this->admin->email) ->call('delete') ->assertHasErrors(['deleteConfirmation']); expect(User::find($this->admin->id))->not->toBeNull(); }); // =========================================== // Bilingual Tests // =========================================== test('confirmation dialogs display in Arabic when locale is ar', function () { app()->setLocale('ar'); $client = User::factory()->individual()->create(); $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openDeactivateModal') ->assertSee(__('clients.confirm_deactivate')); }); test('confirmation dialogs display in English when locale is en', function () { app()->setLocale('en'); $client = User::factory()->individual()->create(); $this->actingAs($this->admin); Volt::test('admin.clients.lifecycle-actions', ['client' => $client]) ->call('openDeactivateModal') ->assertSee('Confirm Deactivation'); }); // =========================================== // Visual Indicator Tests // =========================================== test('deactivated user shows visual indicator in individual client list', function () { $activeClient = User::factory()->individual()->create([ 'full_name' => 'Active Client', 'status' => UserStatus::Active, ]); $deactivatedClient = User::factory()->individual()->deactivated()->create([ 'full_name' => 'Deactivated Client', ]); $this->actingAs($this->admin); Volt::test('admin.clients.individual.index') ->assertSee('Active Client') ->assertSee('Deactivated Client'); }); test('deactivated user shows visual indicator in company client list', function () { $activeCompany = User::factory()->company()->create([ 'company_name' => 'Active Company', 'status' => UserStatus::Active, ]); $deactivatedCompany = User::factory()->company()->deactivated()->create([ 'company_name' => 'Deactivated Company', ]); $this->actingAs($this->admin); Volt::test('admin.clients.company.index') ->assertSee('Active Company') ->assertSee('Deactivated Company'); }); // =========================================== // User Model Method Tests // =========================================== test('user model isDeactivated method works correctly', function () { $activeUser = User::factory()->individual()->create(['status' => UserStatus::Active]); $deactivatedUser = User::factory()->individual()->deactivated()->create(); expect($activeUser->isDeactivated())->toBeFalse(); expect($deactivatedUser->isDeactivated())->toBeTrue(); }); test('user model deactivate method changes status', function () { $user = User::factory()->individual()->create(['status' => UserStatus::Active]); $user->deactivate(); expect($user->fresh()->status)->toBe(UserStatus::Deactivated); }); test('user model reactivate method changes status', function () { $user = User::factory()->individual()->deactivated()->create(); $user->reactivate(); expect($user->fresh()->status)->toBe(UserStatus::Active); });