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); $this->em->persist(new UserActiveContext($owner, $clinic->getUuid(), 'clinic')); $this->em->flush(); return [$owner, $clinic, $doctor]; } public function testOwnerReadsDefaultsBeforeAnythingIsSaved(): void { [$owner] = $this->clinic(); $res = $this->authJson('GET', self::URI, $owner); self::assertSame(200, $this->responseCode()); self::assertFalse($res['data']['enabled']); self::assertSame('none', $res['data']['reset_policy']); self::assertSame(0, $res['data']['counter']); self::assertTrue($res['data']['can_edit']); } /** ✅ موفق: ذخیرهٔ الگو و پیش‌نمایش شمارهٔ بعدی. */ public function testOwnerSavesPatternAndGetsPreview(): void { [$owner] = $this->clinic(); $res = $this->authJson('PUT', self::URI, $owner, [ 'enabled' => true, 'pattern' => 'MD-{SEQ:4}', 'reset_policy' => 'none', ]); self::assertSame(200, $this->responseCode()); self::assertTrue($res['data']['enabled']); self::assertSame('MD-{SEQ:4}', $res['data']['pattern']); self::assertSame('MD-0001', $res['data']['next_preview']); // GET بعدی همان مقدارِ ذخیره‌شده را می‌دهد (نه پیش‌فرض). $read = $this->authJson('GET', self::URI, $owner); self::assertSame('MD-{SEQ:4}', $read['data']['pattern']); } /** ❌ خطا: منشی نه می‌نویسد، نه `can_edit` می‌گیرد. */ public function testSecretaryCannotWriteButCanRead(): void { [, $clinic, $doctor] = $this->clinic(); $secretary = $this->createUser(['ROLE_SECRETARY']); $this->em->persist(new DoctorSecretary($doctor, $secretary, $clinic)); $this->em->persist(new UserActiveContext($secretary, $clinic->getUuid(), 'clinic')); $this->em->flush(); $read = $this->authJson('GET', self::URI, $secretary); self::assertSame(200, $this->responseCode()); self::assertFalse($read['data']['can_edit']); $this->authJson('PUT', self::URI, $secretary, [ 'enabled' => true, 'pattern' => 'X-{SEQ}', 'reset_policy' => 'none', ]); self::assertSame(403, $this->responseCode()); } /** ❌ خطا: پزشکِ مهمانِ کلینیک هم صاحب آن محیط نیست. */ public function testGuestDoctorCannotChangeTheClinicPattern(): void { [, $clinic, $doctor] = $this->clinic(); $this->em->persist(new UserActiveContext($doctor->getUser(), $clinic->getUuid(), 'clinic')); $this->em->flush(); $this->authJson('PUT', self::URI, $doctor->getUser(), [ 'enabled' => true, 'pattern' => 'X-{SEQ}', 'reset_policy' => 'none', ]); self::assertSame(403, $this->responseCode()); } /** ❌ خطا: الگوهای نامعتبر همگی ۴۲۲ با فیلد درست. */ public function testInvalidPatternsAreRejected(): void { [$owner] = $this->clinic(); foreach ([ ['MD-{FOO}-{SEQ}', 'none'], // توکن ناشناخته ['MD-0001', 'none'], // بدون {SEQ} ['', 'none'], // خالی ['{YY}-{SEQ}', 'none'], // سال بدون ریست ['{MM}-{SEQ}', 'yearly'], // ماه بدون ریست ماهانه ] as [$pattern, $reset]) { $res = $this->authJson('PUT', self::URI, $owner, [ 'enabled' => true, 'pattern' => $pattern, 'reset_policy' => $reset, ]); self::assertSame(422, $this->responseCode(), "pattern: $pattern"); self::assertSame('pattern', $res['errors'][0]['field'] ?? null); } // سیاست ریستِ ناشناخته هم رد می‌شود، ولی فیلدش reset_policy است. $res = $this->authJson('PUT', self::URI, $owner, [ 'enabled' => true, 'pattern' => 'X-{SEQ}', 'reset_policy' => 'weekly', ]); self::assertSame(422, $this->responseCode()); self::assertSame('reset_policy', $res['errors'][0]['field'] ?? null); } /** ⚠️ مرزی: تغییر الگو شمارنده را صفر نمی‌کند — شماره‌های صادرشده وجود دارند. */ public function testChangingThePatternKeepsTheCounter(): void { [$owner, $clinic] = $this->clinic(); $this->authJson('PUT', self::URI, $owner, [ 'enabled' => true, 'pattern' => 'A-{SEQ:3}', 'reset_policy' => 'none', ]); $repo = $this->em->getRepository(\App\Patient\Entity\RecordNumberPattern::class); $row = $repo->findForPair('clinic', $clinic->getId()); $row->incrementCounter(); $row->incrementCounter(); $this->em->flush(); $res = $this->authJson('PUT', self::URI, $owner, [ 'enabled' => true, 'pattern' => 'B-{SEQ:3}', 'reset_policy' => 'none', ]); self::assertSame(2, $res['data']['counter']); self::assertSame('B-003', $res['data']['next_preview']); } }