setActive($active); $this->em->persist($domain); $this->em->flush(); return $domain; } public function testAdminCreatesADomainAndOthersCanListIt(): void { $admin = $this->createUser(['ROLE_USER', 'ROLE_ADMIN']); $code = $this->uniqueCode('beauty'); $created = $this->authJson('POST', '/api/v1/practice-domains', $admin, [ 'code' => $code, 'name' => 'کلینیک زیبایی', ]); self::assertSame(201, $this->responseCode(), json_encode($created, JSON_UNESCAPED_UNICODE)); self::assertSame($code, $created['data']['code']); $clinicUser = $this->createUser(['ROLE_USER', 'ROLE_CLINIC']); $list = $this->authJson('GET', '/api/v1/practice-domains', $clinicUser); self::assertSame(200, $this->responseCode()); self::assertContains($code, array_column($list['data'], 'code')); } public function testDuplicateCodeIsRejected(): void { $admin = $this->createUser(['ROLE_USER', 'ROLE_ADMIN']); $code = $this->uniqueCode('beauty'); $this->newDomain($code, 'کلینیک زیبایی'); $body = $this->authJson('POST', '/api/v1/practice-domains', $admin, [ 'code' => $code, 'name' => 'تکراری', ]); self::assertSame(422, $this->responseCode()); self::assertSame('code', $body['errors'][0]['field']); } public function testInvalidCodeIsRejected(): void { $admin = $this->createUser(['ROLE_USER', 'ROLE_ADMIN']); $body = $this->authJson('POST', '/api/v1/practice-domains', $admin, [ 'code' => 'Beauty Clinic', 'name' => 'x', ]); self::assertSame(422, $this->responseCode()); self::assertSame('code', $body['errors'][0]['field']); } public function testNonAdminCannotCreateOrUpdate(): void { $clinicUser = $this->createUser(['ROLE_USER', 'ROLE_CLINIC']); $domain = $this->newDomain($this->uniqueCode('beauty'), 'کلینیک زیبایی'); $this->authJson('POST', '/api/v1/practice-domains', $clinicUser, [ 'code' => $this->uniqueCode('dental'), 'name' => 'دندانپزشکی', ]); self::assertSame(403, $this->responseCode()); $this->authJson('PATCH', '/api/v1/practice-domain/' . $domain->getUuid(), $clinicUser, ['name' => 'nope']); self::assertSame(403, $this->responseCode()); } /** کد لنگرِ TreatmentWorkflow است؛ ویرایشش workflow را بی‌صدا از کار می‌اندازد. */ public function testCodeCannotBeChanged(): void { $admin = $this->createUser(['ROLE_USER', 'ROLE_ADMIN']); $code = $this->uniqueCode('beauty'); $domain = $this->newDomain($code, 'کلینیک زیبایی'); $body = $this->authJson('PATCH', '/api/v1/practice-domain/' . $domain->getUuid(), $admin, [ 'code' => 'hijacked', 'name' => 'نام تازه', ]); self::assertSame(200, $this->responseCode()); self::assertSame($code, $body['data']['code']); self::assertSame('نام تازه', $body['data']['name']); } /** مدیر کلینیک نباید حوزه‌ای را ببیند که پلتفرم بازنشسته‌اش کرده. */ public function testInactiveDomainIsHiddenFromNonAdmins(): void { $liveCode = $this->uniqueCode('beauty'); $retiredCode = $this->uniqueCode('retired'); $this->newDomain($liveCode, 'کلینیک زیبایی'); $this->newDomain($retiredCode, 'بازنشسته', false); $clinicUser = $this->createUser(['ROLE_USER', 'ROLE_CLINIC']); $codes = array_column($this->authJson('GET', '/api/v1/practice-domains', $clinicUser)['data'], 'code'); self::assertContains($liveCode, $codes); self::assertNotContains($retiredCode, $codes); $admin = $this->createUser(['ROLE_USER', 'ROLE_ADMIN']); $adminCodes = array_column($this->authJson('GET', '/api/v1/practice-domains', $admin)['data'], 'code'); self::assertContains($retiredCode, $adminCodes); } public function testClinicPatchAssignsClearsAndRejectsUnknownDomain(): void { $domain = $this->newDomain($this->uniqueCode('beauty'), 'کلینیک زیبایی'); $owner = $this->createUser(['ROLE_USER', 'ROLE_CLINIC']); $clinic = new Clinic($owner); $clinic->setName('کلینیک آزمون'); $this->em->persist($clinic); $this->em->flush(); $uuid = $clinic->getUuid(); $uri = '/api/v1/clinic/' . $uuid; $domainId = $domain->getId(); // درخواستِ کرنل روی EntityManager دیگری می‌نویسد، پس identity map محلی کهنه // می‌ماند و بدون clear همان نمونهٔ قبلی برمی‌گردد نه وضعیت واقعیِ دیتابیس. $reload = function () use ($uuid): ?Clinic { $this->em->clear(); return $this->em->getRepository(Clinic::class)->findOneBy(['uuid' => $uuid]); }; $this->authJson('PATCH', $uri, $owner, ['practice_domain_uuid' => $domain->getUuid()]); self::assertSame(200, $this->responseCode()); self::assertSame($domainId, $reload()?->getPracticeDomain()?->getId()); // کلید نبودن یعنی «دست نزن» $this->authJson('PATCH', $uri, $owner, ['info' => 'توضیح']); self::assertSame($domainId, $reload()?->getPracticeDomain()?->getId()); // رشتهٔ خالی یعنی «پاک کن» $this->authJson('PATCH', $uri, $owner, ['practice_domain_uuid' => '']); self::assertNull($reload()?->getPracticeDomain()); // uuid ناشناس بی‌صدا رد نمی‌شود $body = $this->authJson('PATCH', $uri, $owner, [ 'practice_domain_uuid' => '00000000-0000-0000-0000-000000000000', ]); self::assertSame(422, $this->responseCode()); self::assertSame('practice_domain_uuid', $body['errors'][0]['field']); } }