find('app:doctors:purge-unclaimed') ); } private function makeDoctor(string $source, string $ownerStatus, int $userStatus = 0): Doctor { $em = static::getContainer()->get('doctrine')->getManager(); $mobile = ($userStatus === 0 ? 'imp_' : '09') . str_pad((string) random_int(0, 999_999_999), 9, '0', STR_PAD_LEFT); $user = new User($mobile); $user->setStatus($userStatus); $em->persist($user); $doctor = new Doctor($user, 'دکتر آزمون'); $doctor->setSource($source); $doctor->setOwnerStatus($ownerStatus); $em->persist($doctor); $em->flush(); return $doctor; } private function doctorExists(int $id): bool { $conn = static::getContainer()->get(Connection::class); return (bool) $conn->fetchOne('SELECT COUNT(*) FROM doctors WHERE id = ?', [$id]); } public function testDryRunDeletesNothing(): void { $doctor = $this->makeDoctor('irimc', 'unclaimed'); $id = $doctor->getId(); $tester = $this->tester(); $tester->execute([]); $tester->assertCommandIsSuccessful(); $this->assertTrue($this->doctorExists($id), 'dry-run نباید پزشک را حذف کند'); } public function testForceDeletesUnclaimedImportedDoctorAndSurrogate(): void { $doctor = $this->makeDoctor('irimc', 'unclaimed'); $id = $doctor->getId(); $surrogateId = $doctor->getUser()->getId(); $tester = $this->tester(); $tester->execute(['--force' => true]); $tester->assertCommandIsSuccessful(); $conn = static::getContainer()->get(Connection::class); $this->assertFalse($this->doctorExists($id), 'پزشک claim‌نشده باید حذف شود'); $this->assertFalse( (bool) $conn->fetchOne('SELECT COUNT(*) FROM users WHERE id = ?', [$surrogateId]), 'کاربر جانشین یتیم باید حذف شود', ); } /** برخلاف app:doctors:purge، این کامند روی prod قابل اجراست — ولی فقط با سوییچ صریح. */ private function prodTester(): CommandTester { self::bootKernel(); return new CommandTester( new PurgeUnclaimedDoctorsCommand(static::getContainer()->get(Connection::class), 'prod') ); } public function testRefusesOnProdWithoutExplicitSwitch(): void { $doctor = $this->makeDoctor('irimc', 'unclaimed'); $tester = $this->prodTester(); $tester->execute(['--force' => true]); $this->assertSame(Command::FAILURE, $tester->getStatusCode()); $this->assertStringContainsString('i-know-this-is-prod', $tester->getDisplay()); $this->assertTrue($this->doctorExists($doctor->getId()), 'گاردِ prod نباید چیزی حذف کند'); } public function testRunsOnProdWithExplicitSwitch(): void { $doctor = $this->makeDoctor('irimc', 'unclaimed'); $tester = $this->prodTester(); $tester->execute(['--force' => true, '--i-know-this-is-prod' => true]); $tester->assertCommandIsSuccessful(); $this->assertFalse($this->doctorExists($doctor->getId()), 'با سوییچ صریح باید روی prod حذف کند'); } public function testClaimedAndOtherSourceDoctorsSurvive(): void { $claimed = $this->makeDoctor('irimc', 'claimed', userStatus: 1); $manual = $this->makeDoctor('manual', 'unclaimed'); $this->tester()->execute(['--force' => true]); $this->assertTrue($this->doctorExists($claimed->getId()), 'پزشک claimed نباید حذف شود'); $this->assertTrue($this->doctorExists($manual->getId()), 'منبع غیر irimc نباید با پیش‌فرض حذف شود'); } }