Files
clinicpro/tests/Doctor/AddressPhoneIsNotPublicTest.php
T
hamedandClaude Opus 5 5d2594ff87 feat(privacy): keep venue phone numbers out of every public response
A doctor's office number sat next to the address on the public profile and in
the anonymous API payload, so harvesting the phone number of every practice in
the country was one unauthenticated request away. Street address and map
coordinates stay public — a patient needs those to find the place — but the
phone is now opt-in per caller: DoctorAddress::toArray() and the clinic
serializers only emit it when told to, and the public doctor/clinic endpoints
tell them to only when the caller may edit that profile (the same can_edit they
already compute). Owner-facing address CRUD keeps returning it unchanged.

The patient still gets the number where it is actually useful — their own
appointment. That payload also stops guessing: it used to serialise the
doctor's *first* address, so a booking made at the clinic or at a second office
showed the wrong street entirely. It now resolves the address recorded on the
appointment itself, which works the same for a personal office and a clinic
branch, and falls back to the clinic's own number when the address has none.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 16:31:52 +03:30

112 lines
4.8 KiB
PHP

<?php
namespace App\Tests\Doctor;
use App\Appointment\Entity\Appointment;
use App\Doctor\Entity\Doctor;
use App\Doctor\Entity\DoctorAddress;
use App\Tests\ApiTestCase;
/**
* شمارهٔ تماس مطب دادهٔ عمومی نیست.
*
* صفحهٔ عمومی پزشک آن را کنار آدرس نشان می‌داد و پاسخ عمومی API هم همان را داشت،
* پس جمع‌آوری انبوهِ شمارهٔ مطب‌ها یک درخواست بی‌نام فاصله داشت. آدرس و موقعیت
* عمومی می‌مانند — بیمار برای پیدا کردن مطب به آن‌ها نیاز دارد — ولی شماره فقط به
* صاحب پروفایل و به بیماری که نوبتش همان‌جاست نشان داده می‌شود.
*/
class AddressPhoneIsNotPublicTest extends ApiTestCase
{
private function doctorWithAddress(string $phone = '03536666666'): array
{
$user = $this->createUser(['ROLE_DOCTOR']);
$doctor = new Doctor($user, 'دکتر تماس');
$this->em->persist($doctor);
$address = DoctorAddress::forDoctor($doctor)
->setName('مطب مرکزی')
->setAddress('یزد، خیابان کاشانی')
->setTelephone($phone);
$this->em->persist($address);
$this->em->flush();
// درخواستِ کرنل همین EM را می‌بیند و کالکشنِ آدرس‌های این نمونه از لحظهٔ
// ساخت خالی مانده است؛ بدون clear، پاسخ آدرسی نشان نمی‌دهد.
$this->em->clear();
return [
$this->em->getRepository(Doctor::class)->find($doctor->getId()),
$this->em->getRepository(DoctorAddress::class)->find($address->getId()),
$this->em->getRepository(\App\Auth\Entity\User::class)->find($user->getId()),
];
}
public function testPublicDoctorResponseKeepsTheAddressButDropsThePhone(): void
{
[$doctor, , ] = $this->doctorWithAddress();
$this->client->request('GET', '/api/v1/doctor/' . $doctor->getUuid());
$this->assertSame(200, $this->responseCode());
$payload = json_decode($this->client->getResponse()->getContent(), true);
$address = $payload['data']['data']['address'][0];
$this->assertSame('یزد، خیابان کاشانی', $address['address'], 'آدرس باید عمومی بماند');
$this->assertNull($address['telephone'], 'شمارهٔ مطب نباید عمومی باشد');
}
public function testTheDoctorStillSeesTheirOwnPhone(): void
{
[$doctor, , $owner] = $this->doctorWithAddress();
$body = $this->authJson('GET', '/api/v1/doctor/' . $doctor->getUuid(), $owner);
$this->assertSame('03536666666', $body['data']['data']['address'][0]['telephone']);
}
public function testThePatientSeesTheVenuePhoneOnTheirOwnAppointment(): void
{
[$doctor, $address, ] = $this->doctorWithAddress();
$patient = $this->createUser(['ROLE_USER']);
$appointment = $this->newAppointment($doctor, $patient, time() + 3600, time() + 5400);
$appointment->setAddressId((int) $address->getId());
$this->em->persist($appointment);
$this->em->flush();
$body = $this->authJson('GET', '/api/v1/appointments/user', $patient);
$row = $body['data']['data'][0];
$this->assertSame($appointment->getUuid(), $row['uuid']);
$this->assertSame('یزد، خیابان کاشانی', $row['address']['address']);
$this->assertSame('03536666666', $row['address']['telephone']);
}
/**
* پیش از این نشانی نوبت «اولین آدرس پزشک» بود، پس بیمارِ شعبهٔ دوم نشانی و شمارهٔ
* شعبهٔ اول را می‌دید — نه فقط ناقص، که نشانیِ اشتباه.
*/
public function testTheAppointmentShowsItsOwnVenueNotTheDoctorsFirstOne(): void
{
[$doctor, , ] = $this->doctorWithAddress('03511111111');
$second = DoctorAddress::forDoctor($doctor)
->setName('مطب دوم')
->setAddress('یزد، صفائیه')
->setTelephone('03522222222');
$this->em->persist($second);
$this->em->flush();
$patient = $this->createUser(['ROLE_USER']);
$appointment = $this->newAppointment($doctor, $patient, time() + 3600, time() + 5400);
$appointment->setAddressId((int) $second->getId());
$this->em->persist($appointment);
$this->em->flush();
$row = $this->authJson('GET', '/api/v1/appointments/user', $patient)['data']['data'][0];
$this->assertSame('یزد، صفائیه', $row['address']['address']);
$this->assertSame('03522222222', $row['address']['telephone']);
}
}