createUser(['ROLE_USER', 'ROLE_DOCTOR']); $doctor = new Doctor($user, $name); $doctor->setMobileNumber($user->getMobileNumber()); $this->em->persist($doctor); $this->em->flush(); return $doctor; } private function makeClinicWith(Doctor ...$doctors): array { $owner = $this->createUser(['ROLE_USER', 'ROLE_CLINIC']); $clinic = new Clinic($owner); $clinic->setName('کلینیک تست'); foreach ($doctors as $d) { $clinic->getDoctors()->add($d); } $this->em->persist($clinic); $this->em->flush(); return [$owner, $clinic]; } private function addressFor(Doctor $doctor): DoctorAddress { $address = DoctorAddress::forDoctor($doctor); $this->em->persist($address); $this->em->flush(); return $address; } private function schedulePayload(Doctor $doctor, int $locationId, string $start): array { return [ 'doctor_uuid' => $doctor->getUuid(), 'schedule' => [ ['day' => 'saturday', 'sessions' => [ ['active' => true, 'location_id' => $locationId, 'start' => $start, 'end' => '12:00'], ]], ], ]; } public function testClinicOwnerCanReadAndWriteMemberDoctorSchedule(): void { $doctor = $this->makeDoctor('دکتر عضو'); [$owner] = $this->makeClinicWith($doctor); $address = $this->addressFor($doctor); $this->authJson('POST', '/api/v1/appointment-settings/weekly-schedule', $owner, $this->schedulePayload($doctor, $address->getId(), '09:00')); self::assertSame(201, $this->responseCode()); $this->authJson('GET', "/api/v1/appointment-settings/weekly-schedule/{$doctor->getUuid()}", $owner); self::assertSame(200, $this->responseCode()); $this->authJson('PATCH', "/api/v1/appointment-settings/weekly-schedule/{$doctor->getUuid()}", $owner, [ 'schedule' => [ ['day' => 'saturday', 'sessions' => [ ['active' => true, 'location_id' => $address->getId(), 'start' => '10:00', 'end' => '13:00'], ]], ], ]); self::assertSame(200, $this->responseCode()); } public function testClinicOwnerCannotTouchOutsideDoctor(): void { $member = $this->makeDoctor('دکتر عضو'); [$owner] = $this->makeClinicWith($member); $stranger = $this->makeDoctor('دکتر بیرونی'); $address = $this->addressFor($stranger); $this->authJson('POST', '/api/v1/appointment-settings/weekly-schedule', $owner, $this->schedulePayload($stranger, $address->getId(), '09:00')); self::assertSame(403, $this->responseCode()); } public function testDoctorKeepsFullAccessToOwnSchedule(): void { $doctor = $this->makeDoctor('دکتر مستقل'); $address = $this->addressFor($doctor); $this->authJson('POST', '/api/v1/appointment-settings/weekly-schedule', $doctor->getUser(), $this->schedulePayload($doctor, $address->getId(), '09:00')); self::assertSame(201, $this->responseCode()); $this->authJson('GET', "/api/v1/appointment-settings/weekly-schedule/{$doctor->getUuid()}", $doctor->getUser()); self::assertSame(200, $this->responseCode()); } public function testDoctorCannotTouchAnotherDoctorSchedule(): void { $mine = $this->makeDoctor('دکتر یک'); $theirs = $this->makeDoctor('دکتر دو'); $address = $this->addressFor($theirs); $this->authJson('POST', '/api/v1/appointment-settings/weekly-schedule', $mine->getUser(), $this->schedulePayload($theirs, $address->getId(), '09:00')); self::assertSame(403, $this->responseCode()); } public function testAdminCanWriteAnyDoctorSchedule(): void { $doctor = $this->makeDoctor('دکتر هدف'); $admin = $this->createUser(['ROLE_USER', 'ROLE_ADMIN']); $address = $this->addressFor($doctor); $this->authJson('POST', '/api/v1/appointment-settings/weekly-schedule', $admin, $this->schedulePayload($doctor, $address->getId(), '09:00')); self::assertSame(201, $this->responseCode()); } public function testMemberDoctorLosesAccessWhenPermissionRevoked(): void { $doctor = $this->makeDoctor('دکتر عضو'); $other = $this->makeDoctor('دکتر دیگر'); [, $clinic] = $this->makeClinicWith($doctor, $other); $address = $this->addressFor($other); $perm = static::getContainer()->get(ClinicDoctorPermissionRepository::class)->getOrCreate($clinic, $doctor); $perm->mergePermissions(['resources' => ['appointment_settings' => ['update' => false, 'view' => false]]]); $this->em->flush(); // پزشک همچنان به برنامهٔ خودش دسترسی دارد؛ مجوز کلینیک فقط دیگران را محدود می‌کند $this->authJson('POST', '/api/v1/appointment-settings/weekly-schedule', $doctor->getUser(), $this->schedulePayload($other, $address->getId(), '09:00')); self::assertSame(403, $this->responseCode()); } public function testEditingOneDoctorDoesNotAffectAnother(): void { $first = $this->makeDoctor('دکتر اول'); $second = $this->makeDoctor('دکتر دوم'); [$owner] = $this->makeClinicWith($first, $second); $addrFirst = $this->addressFor($first); $addrSecond = $this->addressFor($second); $this->authJson('POST', '/api/v1/appointment-settings/weekly-schedule', $owner, $this->schedulePayload($first, $addrFirst->getId(), '08:00')); $this->authJson('POST', '/api/v1/appointment-settings/weekly-schedule', $owner, $this->schedulePayload($second, $addrSecond->getId(), '16:00')); $this->authJson('PATCH', "/api/v1/appointment-settings/weekly-schedule/{$first->getUuid()}", $owner, [ 'schedule' => [ ['day' => 'saturday', 'sessions' => [ ['active' => true, 'location_id' => $addrFirst->getId(), 'start' => '11:00', 'end' => '15:00'], ]], ], ]); self::assertSame(200, $this->responseCode()); $this->em->clear(); $reloaded = $this->em->getRepository(WeeklySchedule::class)->findOneBy([ 'doctor' => $this->em->getRepository(Doctor::class)->find($second->getId()), ]); self::assertSame('16:00', $reloaded->getSetting()[0]['sessions'][0]['start'], "the other doctor's schedule is untouched"); } }