createUser(['ROLE_CLINIC']); $clinic = new Clinic($owner); $this->em->persist($clinic); $doctor = new Doctor($this->createUser(['ROLE_DOCTOR']), 'دکتر تست'); $this->em->persist($doctor); $clinic->getDoctors()->add($doctor); $secretary = $this->createUser(['ROLE_SECRETARY']); $rel = new DoctorSecretary($doctor, $secretary, DoctorSecretary::OWNER_CLINIC, $clinic); $this->em->persist($rel); $this->em->persist(new UserActiveContext($secretary, $clinic->getUuid())); return [$secretary, $rel]; } public function testInventoryDeniedByDefault(): void { // DEFAULT_PERMISSIONS: inventory.* = false [$secretary] = $this->makeClinicSecretary(); $this->em->flush(); $this->authJson('GET', '/api/v1/inventory-items', $secretary); $this->assertSame(403, $this->responseCode()); } public function testInventoryAllowedWhenGranted(): void { [$secretary, $rel] = $this->makeClinicSecretary(); $rel->mergePermissions(['resources' => ['inventory' => ['view' => true]]]); $this->em->flush(); $this->authJson('GET', '/api/v1/inventory-items', $secretary); $this->assertSame(200, $this->responseCode()); } public function testPatientCreateDeniedByDefault(): void { // DEFAULT_PERMISSIONS: patients.create = false (view is true) [$secretary] = $this->makeClinicSecretary(); $this->em->flush(); $this->authJson('POST', '/api/v1/patient', $secretary, ['name' => 'x']); $this->assertSame(403, $this->responseCode()); } public function testInventoryCreateDeniedButViewGranted(): void { [$secretary, $rel] = $this->makeClinicSecretary(); $rel->mergePermissions(['resources' => ['inventory' => ['view' => true, 'create' => false]]]); $this->em->flush(); // مشاهده مجاز $this->authJson('GET', '/api/v1/inventory-items', $secretary); $this->assertSame(200, $this->responseCode()); // ایجاد ممنوع $this->authJson('POST', '/api/v1/inventory-item', $secretary, ['name' => 'گاز استریل']); $this->assertSame(403, $this->responseCode()); } // service-items (listAllItems) فقط توگلِ permission را می‌سنجد — برخلاف // service-sections که پیش از آن، گیتِ اشتراک (assertServicesGate) هم دارد. public function testServicesDeniedByDefault(): void { // DEFAULT_PERMISSIONS: services.* = false [$secretary] = $this->makeClinicSecretary(); $this->em->flush(); $this->authJson('GET', '/api/v1/service-items', $secretary); $this->assertSame(403, $this->responseCode()); } public function testServicesAllowedWhenGranted(): void { [$secretary, $rel] = $this->makeClinicSecretary(); $rel->mergePermissions(['resources' => ['services' => ['view' => true]]]); $this->em->flush(); $this->authJson('GET', '/api/v1/service-items', $secretary); $this->assertSame(200, $this->responseCode()); } public function testServicesCreateDeniedButViewGranted(): void { [$secretary, $rel] = $this->makeClinicSecretary(); $rel->mergePermissions(['resources' => ['services' => ['view' => true, 'create' => false]]]); $this->em->flush(); // مشاهده مجاز $this->authJson('GET', '/api/v1/service-items', $secretary); $this->assertSame(200, $this->responseCode()); // ایجاد ممنوع — گیتِ permission پیش از گیتِ اشتراک اجرا می‌شود. $this->authJson('POST', '/api/v1/service-section', $secretary, ['name' => 'بخش تست']); $this->assertSame(403, $this->responseCode()); } // ── Phase B resources ───────────────────────────────────────────────────── public function testStaffDeniedByDefault(): void { [$secretary] = $this->makeClinicSecretary(); $this->em->flush(); $this->authJson('GET', '/api/v1/staff', $secretary); $this->assertSame(403, $this->responseCode()); } public function testStaffAllowedWhenGranted(): void { [$secretary, $rel] = $this->makeClinicSecretary(); $rel->mergePermissions(['resources' => ['staff' => ['view' => true]]]); $this->em->flush(); $this->authJson('GET', '/api/v1/staff', $secretary); $this->assertSame(200, $this->responseCode()); } public function testStaffCreateDeniedButViewGranted(): void { [$secretary, $rel] = $this->makeClinicSecretary(); $rel->mergePermissions(['resources' => ['staff' => ['view' => true, 'create' => false]]]); $this->em->flush(); $this->authJson('GET', '/api/v1/staff', $secretary); $this->assertSame(200, $this->responseCode()); $this->authJson('POST', '/api/v1/staff', $secretary, ['full_name' => 'خانم تست']); $this->assertSame(403, $this->responseCode()); } public function testDiscountsDeniedByDefault(): void { [$secretary] = $this->makeClinicSecretary(); $this->em->flush(); $this->authJson('GET', '/api/v1/admin/discount-rules', $secretary); $this->assertSame(403, $this->responseCode()); } public function testDiscountsAllowedWhenGranted(): void { [$secretary, $rel] = $this->makeClinicSecretary(); $rel->mergePermissions(['resources' => ['discounts' => ['view' => true]]]); $this->em->flush(); $this->authJson('GET', '/api/v1/admin/discount-rules', $secretary); $this->assertSame(200, $this->responseCode()); } public function testSmsDeniedByDefault(): void { [$secretary] = $this->makeClinicSecretary(); $this->em->flush(); $this->authJson('GET', '/api/v1/sms/wallet/balance', $secretary); $this->assertSame(403, $this->responseCode()); } public function testSmsAllowedWhenGranted(): void { [$secretary, $rel] = $this->makeClinicSecretary(); $rel->mergePermissions(['resources' => ['sms' => ['view' => true]]]); $this->em->flush(); $this->authJson('GET', '/api/v1/sms/wallet/balance', $secretary); $this->assertSame(200, $this->responseCode()); } public function testClinicDoctorsDeniedByDefault(): void { [$secretary, , $clinic] = $this->makeClinicSecretaryWithClinic(); $this->em->flush(); $this->authJson('GET', "/api/v1/admin/clinic/{$clinic->getUuid()}/doctor-permissions", $secretary); $this->assertSame(403, $this->responseCode()); } public function testClinicDoctorsAllowedWhenGranted(): void { [$secretary, $rel, $clinic] = $this->makeClinicSecretaryWithClinic(); $rel->mergePermissions(['resources' => ['clinic_doctors' => ['view' => true]]]); $this->em->flush(); $this->authJson('GET', "/api/v1/admin/clinic/{$clinic->getUuid()}/doctor-permissions", $secretary); $this->assertSame(200, $this->responseCode()); } public function testAppointmentSettingsDeniedByDefault(): void { [$secretary, , $clinic, $doctor] = $this->makeClinicSecretaryWithClinic(); $this->em->flush(); // clinic_uuid لازم است تا محیطِ کلینیک حل شود (مثل پزشکِ عضو کلینیک). $this->authJson('GET', "/api/v1/appointment-settings/holidays/list/{$doctor->getUuid()}?clinic_uuid={$clinic->getUuid()}", $secretary); $this->assertSame(403, $this->responseCode()); } public function testAppointmentSettingsAllowedWhenGranted(): void { [$secretary, $rel, $clinic, $doctor] = $this->makeClinicSecretaryWithClinic(); $rel->mergePermissions(['resources' => ['appointment_settings' => ['view' => true]]]); $this->em->flush(); $this->authJson('GET', "/api/v1/appointment-settings/holidays/list/{$doctor->getUuid()}?clinic_uuid={$clinic->getUuid()}", $secretary); $this->assertSame(200, $this->responseCode()); } /** مثل makeClinicSecretary اما clinic و doctor را هم برمی‌گرداند. */ private function makeClinicSecretaryWithClinic(): array { $owner = $this->createUser(['ROLE_CLINIC']); $clinic = new Clinic($owner); $this->em->persist($clinic); $doctor = new Doctor($this->createUser(['ROLE_DOCTOR']), 'دکتر تست'); $this->em->persist($doctor); $clinic->getDoctors()->add($doctor); $secretary = $this->createUser(['ROLE_SECRETARY']); $rel = new DoctorSecretary($doctor, $secretary, DoctorSecretary::OWNER_CLINIC, $clinic); $this->em->persist($rel); $this->em->persist(new UserActiveContext($secretary, $clinic->getUuid())); return [$secretary, $rel, $clinic, $doctor]; } }