Files
clinicpro/tests/Patient/PatientUpdateProfileTest.php
T
hamedandClaude Opus 4.8 580bc983b3 feat(patient): complete "اطلاعات پرونده" demographic form
Backend:
- Add profile columns field_of_study, province_id, city_id, postal_code,
  referral_source (UserProfile + migration).
- Extend PATCH /api/v1/patient/{uuid} to persist all demographic fields
  and return them in the patient profile payload.
- Support editable mobile (login identifier): validation, uniqueness,
  User.setMobileNumber, new ERR_PROFILE_002.
- Update docs/api/patient.md.

Frontend:
- New reusable Input, Field, and PatientRecordInfoForm (RHF + Zod).
- usePatient/useUpdatePatient hooks and patientForm mapping helpers.
- Extend the existing "info" tab in MyPatientsPage to the full field set
  via the shared form (province/city/insurance options, Jalali date).

Tests: Patient entity + PATCH integration (PHPUnit); form, hooks, and
mapping helpers (Vitest).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 11:51:18 +03:30

124 lines
4.7 KiB
PHP

<?php
namespace App\Tests\Patient;
use App\Auth\Entity\User;
use App\Doctor\Entity\Doctor;
use App\Patient\Entity\PatientRecord;
use App\Tests\ApiTestCase;
/**
* Integration coverage for PATCH /api/v1/patient/{uuid}:
* the extended demographic fields persist, and mobile edits validate,
* enforce uniqueness, and rewrite the patient's login identifier.
*
* Gate: a doctor caller falls back to the seeded `free` plan, which grants
* `patient_records` in db_test — so no explicit subscription is needed.
*/
class PatientUpdateProfileTest extends ApiTestCase
{
private User $doctorUser;
private int $doctorId;
protected function setUp(): void
{
parent::setUp();
$this->doctorUser = $this->createUser(['ROLE_DOCTOR']);
$doctor = new Doctor($this->doctorUser, 'دکتر تست');
$this->em->persist($doctor);
$this->em->flush();
$this->doctorId = $doctor->getId();
}
/** Create a patient User + PatientRecord owned by the test doctor; return the record uuid. */
private function makeRecord(?string $mobile = null): array
{
$patient = $this->createUser(['ROLE_USER'], $mobile);
$patient->setRealName('ساغر صابری نژاد');
$this->em->persist($patient);
$record = new PatientRecord('doctor', $this->doctorId, $patient, 'doctor', $this->doctorId);
$this->em->persist($record);
$this->em->flush();
return [$record->getUuid(), $patient];
}
public function testUpdatePersistsExtendedDemographicFields(): void
{
[$uuid] = $this->makeRecord();
$res = $this->authJson('PATCH', "/api/v1/patient/{$uuid}", $this->doctorUser, [
'fathers_name' => 'رضا',
'education' => 'کارشناسی',
'field_of_study' => 'نرم‌افزار',
'job' => 'مهندس',
'province_id' => 8,
'city_id' => 42,
'postal_code' => '8913746351',
'referral_source' => 'اینستاگرام',
'description' => 'یادداشت آزمایشی',
]);
self::assertSame(200, $this->responseCode());
$profile = $res['data']['profile'] ?? [];
self::assertSame('رضا', $profile['fathers_name']);
self::assertSame('کارشناسی', $profile['education']);
self::assertSame('نرم‌افزار', $profile['field_of_study']);
self::assertSame('مهندس', $profile['job']);
self::assertSame(8, $profile['province_id']);
self::assertSame(42, $profile['city_id']);
self::assertSame('8913746351', $profile['postal_code']);
self::assertSame('اینستاگرام', $profile['referral_source']);
self::assertSame('یادداشت آزمایشی', $profile['description']);
}
public function testEmptyStringClearsFieldToNull(): void
{
[$uuid] = $this->makeRecord();
$this->authJson('PATCH', "/api/v1/patient/{$uuid}", $this->doctorUser, ['job' => 'مهندس']);
$res = $this->authJson('PATCH', "/api/v1/patient/{$uuid}", $this->doctorUser, ['job' => '']);
self::assertSame(200, $this->responseCode());
self::assertNull($res['data']['profile']['job']);
}
public function testMobileEditRewritesLoginIdentifier(): void
{
[$uuid, $patient] = $this->makeRecord();
$newMobile = '09' . str_pad((string) random_int(0, 999_999_999), 9, '0', STR_PAD_LEFT);
$res = $this->authJson('PATCH', "/api/v1/patient/{$uuid}", $this->doctorUser, ['mobile' => $newMobile]);
self::assertSame(200, $this->responseCode());
self::assertSame($newMobile, $res['data']['profile']['mobile']);
$this->em->refresh($patient);
self::assertSame($newMobile, $patient->getMobileNumber());
self::assertSame($newMobile, $patient->getUserIdentifier());
}
public function testMobileTakenByAnotherUserReturns409(): void
{
$taken = '09' . str_pad((string) random_int(0, 999_999_999), 9, '0', STR_PAD_LEFT);
$this->createUser(['ROLE_USER'], $taken); // occupy the number
[$uuid] = $this->makeRecord();
$res = $this->authJson('PATCH', "/api/v1/patient/{$uuid}", $this->doctorUser, ['mobile' => $taken]);
self::assertSame(409, $this->responseCode());
self::assertSame('ERR_PROFILE_002', $res['errors'][0]['code']);
}
public function testInvalidMobileReturns422(): void
{
[$uuid] = $this->makeRecord();
$res = $this->authJson('PATCH', "/api/v1/patient/{$uuid}", $this->doctorUser, ['mobile' => '12345']);
self::assertSame(422, $this->responseCode());
self::assertSame('ERR_VALIDATION_001', $res['errors'][0]['code']);
}
}