Files
clinicpro/tests/Doctor/RepairImportedDoctorsCommandTest.php
T
hamed 801c6f96db Refactor doctor data repair commands into a single command
- Removed individual commands for backfilling specialty parents, surrogate roles, and fixing IRIMC names.
- Introduced RepairImportedDoctorsCommand to consolidate functionality.
- Implemented a step-based approach for repairs, allowing for idempotent execution.
- Added new service classes for handling specific repair steps, including BackfillSpecialtyParentsStep, BackfillSurrogateRoleStep, FixDegreeStep, and StripNameTitleStep.
- Created RepairOptions and RepairResult classes to manage step execution options and results.
- Updated tests to ensure new command structure and functionality are covered, including idempotency and dry-run behavior.
- Added IrimcDegreeMapper for mapping IRIMC titles to degrees.
2026-07-19 19:20:04 +03:30

120 lines
4.8 KiB
PHP

<?php
namespace App\Tests\Doctor;
use App\Doctor\Entity\Doctor;
use App\Doctor\Service\Repair\DoctorRepairStep;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
/**
* `app:doctors:repair` جای چهار کامند ترمیمی جداگانه را گرفته است. این تست سه
* قرارداد را قفل می‌کند: گام‌ها خودکار کشف می‌شوند، --dry-run هیچ‌چیز نمی‌نویسد،
* و اجرای دوم صفر تغییر می‌دهد (idempotent).
*/
class RepairImportedDoctorsCommandTest extends KernelTestCase
{
private function tester(): CommandTester
{
return new CommandTester(
(new Application(self::bootKernel()))->find('app:doctors:repair')
);
}
/** پزشک irimc با نام عنوان‌دار و درجهٔ جابه‌جا — دقیقاً شکل رکوردهای معیوب. */
private function makeBrokenDoctor(): Doctor
{
$em = static::getContainer()->get('doctrine')->getManager();
$mobile = '09' . str_pad((string) random_int(0, 999_999_999), 9, '0', STR_PAD_LEFT);
$user = new \App\Auth\Entity\User($mobile);
$user->setStatus(0);
$em->persist($user);
$doctor = new Doctor($user, 'دکتر آزمون ترمیم');
$doctor->setSource('irimc');
$doctor->setOwnerStatus('unclaimed');
$doctor->setInfo('تخصص بیماری‌های داخلی دکترای حرفه‌ای پزشکی');
$doctor->setDegree('expert'); // غلط: باید specialist باشد
$em->persist($doctor);
$em->flush();
return $doctor;
}
public function testEveryStepIsAutoDiscovered(): void
{
self::bootKernel();
try {
$steps = iterator_to_array(
static::getContainer()->get('test.doctor_repair_steps'),
false
);
} catch (ServiceNotFoundException) {
// بدون alias عمومی برای تگ، از خروجی --list به‌عنوان منبع حقیقت استفاده کن.
$tester = $this->tester();
$tester->execute(['--list' => true]);
$out = $tester->getDisplay();
foreach (['names', 'degrees', 'specialty-parents', 'surrogate-role'] as $name) {
$this->assertStringContainsString($name, $out, "گام «$name» در --list نیست");
}
return;
}
$names = array_map(fn (DoctorRepairStep $s) => $s->name(), $steps);
foreach (['names', 'degrees', 'specialty-parents', 'surrogate-role'] as $name) {
$this->assertContains($name, $names);
}
}
public function testDryRunWritesNothing(): void
{
$doctor = $this->makeBrokenDoctor();
$em = static::getContainer()->get('doctrine')->getManager();
$id = $doctor->getId();
$tester = $this->tester();
$tester->execute(['--dry-run' => true, '--only' => 'degrees,names']);
$tester->assertCommandIsSuccessful();
$em->clear();
$fresh = $em->getRepository(Doctor::class)->find($id);
$this->assertSame('expert', $fresh->getDegree(), 'dry-run نباید درجه را عوض کند');
$this->assertSame('دکتر آزمون ترمیم', $fresh->getName(), 'dry-run نباید نام را عوض کند');
}
public function testRepairFixesDegreeAndNameThenIsIdempotent(): void
{
$doctor = $this->makeBrokenDoctor();
$em = static::getContainer()->get('doctrine')->getManager();
$id = $doctor->getId();
$this->tester()->execute(['--only' => 'degrees,names']);
$em->clear();
$fresh = $em->getRepository(Doctor::class)->find($id);
$this->assertSame('specialist', $fresh->getDegree(), 'درجه باید از عنوان خام بازمحاسبه شود');
$this->assertSame('آزمون ترمیم', $fresh->getName(), 'پیشوند «دکتر» باید حذف شود');
// اجرای دوم: همان رکورد دیگر نباید در خروجی بیاید.
$second = $this->tester();
$second->execute(['--only' => 'degrees,names']);
$this->assertStringNotContainsString("#$id ", $second->getDisplay(), 'گام باید idempotent باشد');
}
public function testUnknownStepIsRejected(): void
{
$tester = $this->tester();
$tester->execute(['--only' => 'nonsense']);
$this->assertSame(2, $tester->getStatusCode(), 'گام ناشناخته باید INVALID برگرداند');
$this->assertStringContainsString('گام ناشناخته', $tester->getDisplay());
}
}