feat(subscription): implement resource quota management based on subscription plans
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Resource;
|
||||
|
||||
use App\Resource\Entity\ClinicResource;
|
||||
use App\Shared\Constant\ErrorCodes;
|
||||
use App\Subscription\Entity\SubscriptionPlan;
|
||||
|
||||
/**
|
||||
* سقف منابع بر اساس پلن اشتراک: free یک منبع، basic سه، professional بینهایت.
|
||||
*
|
||||
* سقف روی همهٔ منابع محیط اعمال میشود — فعال و غیرفعال — وگرنه یک بار
|
||||
* غیرفعالکردن، سقف را دور میزد.
|
||||
*/
|
||||
class ResourceQuotaTest extends ResourceTestCase
|
||||
{
|
||||
public function testFreePlanAllowsExactlyOneResource(): void
|
||||
{
|
||||
$this->setPlanResourceQuota(1);
|
||||
|
||||
[$user, , $address] = $this->clinicWithAddress();
|
||||
$type = $this->resourceType($address);
|
||||
|
||||
$this->createResource($user, $address, $type, ['name' => 'اتاق ۱']);
|
||||
self::assertSame(201, $this->responseCode());
|
||||
|
||||
$body = $this->createResource($user, $address, $type, ['name' => 'اتاق ۲']);
|
||||
|
||||
self::assertSame(422, $this->responseCode(), json_encode($body, JSON_UNESCAPED_UNICODE));
|
||||
self::assertSame(ErrorCodes::ERR_RESOURCE_LIMIT_001, $body['errors'][0]['code']);
|
||||
}
|
||||
|
||||
public function testBasicPlanAllowsThreeResourcesAndRejectsTheFourth(): void
|
||||
{
|
||||
$this->setPlanResourceQuota(3);
|
||||
|
||||
[$user, , $address] = $this->clinicWithAddress();
|
||||
$type = $this->resourceType($address);
|
||||
|
||||
foreach (['اتاق ۱', 'اتاق ۲', 'اتاق ۳'] as $name) {
|
||||
$this->createResource($user, $address, $type, ['name' => $name]);
|
||||
self::assertSame(201, $this->responseCode(), $name);
|
||||
}
|
||||
|
||||
$body = $this->createResource($user, $address, $type, ['name' => 'اتاق ۴']);
|
||||
|
||||
self::assertSame(422, $this->responseCode());
|
||||
self::assertSame(ErrorCodes::ERR_RESOURCE_LIMIT_001, $body['errors'][0]['code']);
|
||||
self::assertStringContainsString('۳', $this->toPersianDigits($body['errors'][0]['message']));
|
||||
}
|
||||
|
||||
public function testProfessionalPlanIsUnlimited(): void
|
||||
{
|
||||
$this->setPlanResourceQuota(SubscriptionPlan::UNLIMITED);
|
||||
|
||||
[$user, , $address] = $this->clinicWithAddress();
|
||||
$type = $this->resourceType($address);
|
||||
|
||||
foreach (range(1, 5) as $i) {
|
||||
$this->createResource($user, $address, $type, ['name' => "اتاق $i"]);
|
||||
self::assertSame(201, $this->responseCode(), "resource #$i");
|
||||
}
|
||||
}
|
||||
|
||||
/** غیرفعالکردن منبع، جای خالی نمیسازد. */
|
||||
public function testDeactivatedResourcesStillCountTowardTheQuota(): void
|
||||
{
|
||||
$this->setPlanResourceQuota(1);
|
||||
|
||||
[$user, , $address] = $this->clinicWithAddress();
|
||||
$type = $this->resourceType($address);
|
||||
|
||||
$first = $this->createResource($user, $address, $type, ['name' => 'اتاق ۱']);
|
||||
$this->authJson('PATCH', '/api/v1/resource/' . $first['data']['uuid'], $user, ['active' => false]);
|
||||
self::assertSame(200, $this->responseCode());
|
||||
|
||||
$this->createResource($user, $address, $type, ['name' => 'اتاق ۲']);
|
||||
self::assertSame(422, $this->responseCode());
|
||||
}
|
||||
|
||||
/** حذف منبع، جا را واقعاً آزاد میکند — شمارش زندهٔ دیتابیس است نه شمارندهٔ ذخیرهشده. */
|
||||
public function testDeletingAResourceFreesTheSlot(): void
|
||||
{
|
||||
$this->setPlanResourceQuota(1);
|
||||
|
||||
[$user, , $address] = $this->clinicWithAddress();
|
||||
$type = $this->resourceType($address);
|
||||
|
||||
$first = $this->createResource($user, $address, $type, ['name' => 'اتاق ۱']);
|
||||
$this->authJson('DELETE', '/api/v1/resource/' . $first['data']['uuid'], $user);
|
||||
self::assertSame(200, $this->responseCode());
|
||||
|
||||
$this->createResource($user, $address, $type, ['name' => 'اتاق ۲']);
|
||||
self::assertSame(201, $this->responseCode());
|
||||
}
|
||||
|
||||
/** سقف مالِ همان محیط است؛ پر شدن یک کلینیک، کلینیک دیگر را قفل نمیکند. */
|
||||
public function testQuotaIsCountedPerTenantPair(): void
|
||||
{
|
||||
$this->setPlanResourceQuota(1);
|
||||
|
||||
[$userA, , $addressA] = $this->clinicWithAddress();
|
||||
$this->createResource($userA, $addressA, $this->resourceType($addressA), ['name' => 'اتاق A']);
|
||||
self::assertSame(201, $this->responseCode());
|
||||
|
||||
[$userB, , $addressB] = $this->clinicWithAddress('شعبهٔ کلینیک دوم');
|
||||
$this->createResource($userB, $addressB, $this->resourceType($addressB), ['name' => 'اتاق B']);
|
||||
|
||||
self::assertSame(201, $this->responseCode());
|
||||
self::assertSame(1, $this->em->getRepository(ClinicResource::class)->countForPair(
|
||||
$addressB->tenantEntityType(),
|
||||
$addressB->tenantEntityId(),
|
||||
));
|
||||
}
|
||||
|
||||
private function toPersianDigits(string $value): string
|
||||
{
|
||||
return strtr($value, ['0' => '۰', '1' => '۱', '2' => '۲', '3' => '۳', '4' => '۴',
|
||||
'5' => '۵', '6' => '۶', '7' => '۷', '8' => '۸', '9' => '۹']);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user