- Implemented SidebarStaff component tests to ensure staff users see only their dashboard and services. - Created StaffMyServicesPage to display assigned services for staff users. - Added migration to link clinic staff rows to user accounts for ROLE_STAFF access. - Defined StaffPermissions class for static permissions related to staff role. - Introduced StaffRouteGuardSubscriber to restrict API access for staff users. - Developed StaffAccountService for managing staff user accounts and linking them to clinic staff. - Added comprehensive tests for StaffAccountService to validate user creation, mobile number handling, and account attachment. - Implemented tests for staff dashboard access to ensure proper permissions and access control. - Created tests for staff login context to verify correct environment visibility based on user roles.
160 lines
6.6 KiB
PHP
160 lines
6.6 KiB
PHP
<?php
|
|
|
|
namespace App\Tests\Staff;
|
|
|
|
use App\Auth\Repository\UserRepository;
|
|
use App\Doctor\Entity\Doctor;
|
|
use App\Shared\Constant\ErrorCodes;
|
|
use App\Shared\Exception\AppException;
|
|
use App\Staff\Entity\ClinicStaff;
|
|
use App\Staff\Repository\ClinicStaffRepository;
|
|
use App\Staff\Service\StaffAccountService;
|
|
use App\Tests\ApiTestCase;
|
|
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
|
|
|
/**
|
|
* ساخت/اتصال/قطع حساب کاربری پرسنل: یک ردیف ClinicStaff به یک User با
|
|
* ROLE_STAFF وصل میشود بدون اینکه کاربر موجود آسیب ببیند.
|
|
*/
|
|
class StaffAccountServiceTest extends ApiTestCase
|
|
{
|
|
/** سرویس تکمصرفه است و کانتینر inlineاش میکند، پس با وابستگیهای واقعی ساخته میشود. */
|
|
private function service(): StaffAccountService
|
|
{
|
|
return new StaffAccountService(
|
|
static::getContainer()->get(UserRepository::class),
|
|
static::getContainer()->get(ClinicStaffRepository::class),
|
|
static::getContainer()->get(UserPasswordHasherInterface::class),
|
|
);
|
|
}
|
|
|
|
private function newStaff(int $entityId, string $name = 'زهرا احمدی'): ClinicStaff
|
|
{
|
|
$staff = new ClinicStaff('doctor', $entityId, $name);
|
|
$this->em->persist($staff);
|
|
$this->em->flush();
|
|
|
|
return $staff;
|
|
}
|
|
|
|
private function newDoctorOwner(): Doctor
|
|
{
|
|
$owner = $this->createUser(['ROLE_DOCTOR']);
|
|
$doctor = new Doctor($owner, 'دکتر تست');
|
|
$this->em->persist($doctor);
|
|
$this->em->flush();
|
|
|
|
return $doctor;
|
|
}
|
|
|
|
public function testCreatesNewUserWithStaffRole(): void
|
|
{
|
|
$doctor = $this->newDoctorOwner();
|
|
$staff = $this->newStaff($doctor->getId());
|
|
$mobile = '0912' . str_pad((string) random_int(0, 9_999_999), 7, '0', STR_PAD_LEFT);
|
|
|
|
$user = $this->service()->attachAccount($staff, $mobile, 'Staff@1234', $doctor->getUser());
|
|
|
|
self::assertContains('ROLE_STAFF', $user->getRoles());
|
|
self::assertSame($mobile, $user->getMobileNumber());
|
|
self::assertSame('زهرا احمدی', $user->getRealName());
|
|
self::assertTrue($staff->hasAccount());
|
|
self::assertSame($user->getId(), $staff->getUser()?->getId());
|
|
self::assertSame($mobile, $staff->getPhone());
|
|
|
|
$hasher = static::getContainer()->get(UserPasswordHasherInterface::class);
|
|
self::assertTrue($hasher->isPasswordValid($user, 'Staff@1234'));
|
|
}
|
|
|
|
/** کاربر موجود فقط نقش میگیرد؛ رمز و نامش پاک نمیشود. */
|
|
public function testReusesExistingUserAndKeepsPassword(): void
|
|
{
|
|
$doctor = $this->newDoctorOwner();
|
|
$existing = $this->createUser(['ROLE_USER']);
|
|
$existing->setRealName('نام قبلی');
|
|
$hasher = static::getContainer()->get(UserPasswordHasherInterface::class);
|
|
$existing->setPasswordHash($hasher->hashPassword($existing, 'Old@12345'));
|
|
$this->em->flush();
|
|
|
|
$staff = $this->newStaff($doctor->getId());
|
|
$user = $this->service()->attachAccount($staff, $existing->getMobileNumber(), null, $doctor->getUser());
|
|
|
|
self::assertSame($existing->getId(), $user->getId());
|
|
self::assertContains('ROLE_STAFF', $user->getRoles());
|
|
self::assertContains('ROLE_USER', $user->getRoles());
|
|
self::assertSame('نام قبلی', $user->getRealName());
|
|
self::assertTrue($hasher->isPasswordValid($user, 'Old@12345'));
|
|
}
|
|
|
|
public function testRejectsInvalidMobile(): void
|
|
{
|
|
$doctor = $this->newDoctorOwner();
|
|
$staff = $this->newStaff($doctor->getId());
|
|
|
|
$this->expectException(AppException::class);
|
|
$this->expectExceptionMessage(ErrorCodes::message(ErrorCodes::ERR_STAFF_MOBILE_INVALID));
|
|
|
|
$this->service()->attachAccount($staff, '12345', 'Staff@1234', $doctor->getUser());
|
|
}
|
|
|
|
public function testRejectsOwnerMobile(): void
|
|
{
|
|
$doctor = $this->newDoctorOwner();
|
|
$staff = $this->newStaff($doctor->getId());
|
|
|
|
try {
|
|
$this->service()->attachAccount($staff, $doctor->getUser()->getMobileNumber(), null, $doctor->getUser());
|
|
self::fail('owner mobile must be rejected');
|
|
} catch (AppException $e) {
|
|
self::assertSame(ErrorCodes::ERR_STAFF_MOBILE_INVALID, $e->getErrorCode());
|
|
self::assertSame(422, $e->getHttpStatus());
|
|
}
|
|
}
|
|
|
|
/** مرزی: همان شماره، همان محیط، ردیف دوم → 409. */
|
|
public function testRejectsDuplicateMobileInSameEntity(): void
|
|
{
|
|
$doctor = $this->newDoctorOwner();
|
|
$first = $this->newStaff($doctor->getId(), 'پرسنل اول');
|
|
$second = $this->newStaff($doctor->getId(), 'پرسنل دوم');
|
|
$mobile = '0912' . str_pad((string) random_int(0, 9_999_999), 7, '0', STR_PAD_LEFT);
|
|
|
|
$this->service()->attachAccount($first, $mobile, null, $doctor->getUser());
|
|
|
|
try {
|
|
$this->service()->attachAccount($second, $mobile, null, $doctor->getUser());
|
|
self::fail('duplicate mobile in the same entity must be rejected');
|
|
} catch (AppException $e) {
|
|
self::assertSame(ErrorCodes::ERR_STAFF_MOBILE_TAKEN, $e->getErrorCode());
|
|
self::assertSame(409, $e->getHttpStatus());
|
|
}
|
|
}
|
|
|
|
/** ارقام فارسی از هر کلاینتی بیاید، شمارهٔ ذخیرهشده لاتین است. */
|
|
public function testNormalizesPersianDigits(): void
|
|
{
|
|
$doctor = $this->newDoctorOwner();
|
|
$staff = $this->newStaff($doctor->getId());
|
|
|
|
$user = $this->service()->attachAccount($staff, '۰۹۱۲۳۴۵۶۷۸۹', null, $doctor->getUser());
|
|
|
|
self::assertSame('09123456789', $user->getMobileNumber());
|
|
}
|
|
|
|
/** تغییر شماره یعنی تغییر نامکاربری ورود؛ ردیف پرسنل همان میماند. */
|
|
public function testReattachWithNewMobileMovesTheAccount(): void
|
|
{
|
|
$doctor = $this->newDoctorOwner();
|
|
$staff = $this->newStaff($doctor->getId());
|
|
$first = '0912' . str_pad((string) random_int(0, 9_999_999), 7, '0', STR_PAD_LEFT);
|
|
$second = '0913' . str_pad((string) random_int(0, 9_999_999), 7, '0', STR_PAD_LEFT);
|
|
|
|
$this->service()->attachAccount($staff, $first, null, $doctor->getUser());
|
|
$user = $this->service()->attachAccount($staff, $second, null, $doctor->getUser());
|
|
|
|
self::assertSame($second, $user->getMobileNumber());
|
|
self::assertSame($second, $staff->getPhone());
|
|
self::assertSame($user->getId(), $staff->getUser()?->getId());
|
|
}
|
|
}
|