Files
clinicpro/tests/Treatment/TreatmentWorkflowTest.php
T
hamedandClaude Opus 5 252e20bfe9 feat(treatment): select treatment behaviour by practice domain, not by if-branch
Everything that differs between specialties as data is already stored as data.
What is left is behaviour — when a case opens, what happens once a session ends —
so it becomes a TreatmentWorkflow resolved through a tagged-service registry.
The booking path calls one collaborator and never names a specialty; adding
dentistry is a new class, not an edit to confirmation.

A clinic that has chosen no practice domain still gets working multi-session
courses: DefaultTreatmentWorkflow answers for null and for any code without a
dedicated implementation, keeping "unset means behave as today, not error".
LaserTreatmentWorkflow is deliberately empty beyond claiming `beauty` — it is the
seam where laser-specific behaviour will land without disturbing anyone else.

Session due dates are anchored to the previous session's actual finish, so a
patient who comes twenty days late shifts the rest of their course instead of
getting the next session while it can still do nothing. Only the next session is
recomputed; later ones keep their estimate because they are anchored to nothing
yet.

Attachment targets the first session without an appointment rather than the
first open one: a patient booking again mid-course was otherwise matched to the
session that already had a booking, and the second appointment went nowhere.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 17:23:51 +03:30

70 lines
2.5 KiB
PHP

<?php
namespace App\Tests\Treatment;
use App\PracticeDomain\Entity\PracticeDomain;
use App\Tests\ApiTestCase;
use App\Treatment\Workflow\DefaultTreatmentWorkflow;
use App\Treatment\Workflow\LaserTreatmentWorkflow;
use App\Treatment\Workflow\TreatmentWorkflowRegistry;
class TreatmentWorkflowTest extends ApiTestCase
{
private TreatmentWorkflowRegistry $registry;
protected function setUp(): void
{
parent::setUp();
$this->registry = static::getContainer()->get(TreatmentWorkflowRegistry::class);
}
public function testBeautyResolvesToTheLaserWorkflow(): void
{
self::assertInstanceOf(LaserTreatmentWorkflow::class, $this->registry->for('beauty'));
}
/** حوزه‌ای که هنوز پیاده‌سازی ندارد باید رفتار پیش‌فرض بگیرد، نه خطا. */
public function testUnknownDomainFallsBackToTheDefaultWorkflow(): void
{
self::assertSame(
DefaultTreatmentWorkflow::class,
$this->registry->for('physiotherapy')::class,
);
}
/** نال یعنی «حوزه تنظیم نشده» و همان رفتار امروز، نه خطا. */
public function testNullDomainFallsBackToTheDefaultWorkflow(): void
{
self::assertSame(DefaultTreatmentWorkflow::class, $this->registry->for(null)::class);
}
public function testHasDedicatedWorkflowDistinguishesTheTwo(): void
{
self::assertTrue($this->registry->hasDedicatedWorkflow('beauty'));
self::assertFalse($this->registry->hasDedicatedWorkflow('physiotherapy'));
self::assertFalse($this->registry->hasDedicatedWorkflow(null));
}
/** پنل ادمین باید ببیند کدام حوزه هنوز workflow ندارد. */
public function testListExposesHasWorkflow(): void
{
$suffix = bin2hex(random_bytes(4));
foreach ([['beauty_' . $suffix, false], ['beauty', true]] as [$code, $expected]) {
if ($this->em->getRepository(PracticeDomain::class)->findOneBy(['code' => $code]) === null) {
$this->em->persist(new PracticeDomain($code, 'حوزهٔ آزمون'));
}
}
$this->em->flush();
$admin = $this->createUser(['ROLE_USER', 'ROLE_ADMIN']);
$body = $this->authJson('GET', '/api/v1/practice-domains', $admin);
$byCode = array_column($body['data'], 'has_workflow', 'code');
self::assertTrue($byCode['beauty']);
self::assertFalse($byCode['beauty_' . $suffix]);
}
}