Files
clinicpro/tests/Subscription/GrantSubscriptionTest.php
T
hamed a6a965a2aa feat: add admin subscription granting feature
- Implemented the ability for admins to grant subscriptions to doctors and clinics without payment.
- Added new API endpoint `/api/v1/admin/subscription/grant` for granting subscriptions.
- Updated the subscription model to track the admin who granted the subscription.
- Enhanced the subscription report to include details about granted subscriptions.
- Introduced a new `is_granted` field to indicate if a subscription was granted by an admin.
- Updated the database schema to support the new functionality with a migration.
- Added tests to ensure the correct behavior of the subscription granting process.
2026-08-09 13:43:30 +03:30

110 lines
4.2 KiB
PHP

<?php
namespace App\Tests\Subscription;
use App\Auth\Entity\User;
use App\Config\Repository\SiteConfigRepository;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Exception\AppException;
use App\Subscription\Entity\ClinicSubscription;
use App\Subscription\Entity\SubscriptionPeriod;
use App\Subscription\Entity\SubscriptionPlan;
use App\Subscription\Repository\ClinicSubscriptionRepository;
use App\Subscription\Repository\SubscriptionPeriodRepository;
use App\Subscription\Repository\SubscriptionPlanRepository;
use App\Subscription\Service\SubscriptionService;
use PHPUnit\Framework\TestCase;
/**
* An admin granting a subscription must produce a paid-tier subscription with no
* payment attached, an audit trail, and no side effect on the target's unused
* trial.
*/
class GrantSubscriptionTest extends TestCase
{
private ?ClinicSubscription $saved = null;
private function service(?SubscriptionPeriod $period, ?ClinicSubscription $active): SubscriptionService
{
$subscriptionRepo = $this->createMock(ClinicSubscriptionRepository::class);
$subscriptionRepo->method('findActive')->willReturn($active);
$subscriptionRepo->method('save')->willReturnCallback(function (ClinicSubscription $s): void {
$this->saved = $s;
});
$periodRepo = $this->createMock(SubscriptionPeriodRepository::class);
$periodRepo->method('findByUuid')->willReturn($period);
return new SubscriptionService(
$subscriptionRepo,
$this->createMock(SubscriptionPlanRepository::class),
$periodRepo,
$this->createMock(SiteConfigRepository::class),
);
}
private function period(int $durationMonths): SubscriptionPeriod
{
$period = $this->createMock(SubscriptionPeriod::class);
$period->method('getPlan')->willReturn($this->createMock(SubscriptionPlan::class));
$period->method('getDurationMonths')->willReturn($durationMonths);
return $period;
}
public function testGrantCreatesSubscriptionWithoutPaymentAndRecordsTheAdmin(): void
{
$admin = $this->createMock(User::class);
$service = $this->service($this->period(1), null);
$subscription = $service->grant('doctor', 7, 'period-uuid', $admin);
$this->assertSame($subscription, $this->saved);
$this->assertSame('doctor', $subscription->getEntityType());
$this->assertSame(7, $subscription->getEntityId());
$this->assertNull($subscription->getPayment());
$this->assertSame($admin, $subscription->getGrantedBy());
$this->assertTrue($subscription->isGranted());
$this->assertEqualsWithDelta(time() + 30 * 86400, $subscription->getExpiresAt(), 5);
}
/** Granting must never burn an unused trial — `hasUsedTrial` reads this flag. */
public function testGrantIsNeverMarkedAsTrial(): void
{
$service = $this->service($this->period(1), null);
$subscription = $service->grant('clinic', 3, 'period-uuid', $this->createMock(User::class));
$this->assertFalse($subscription->isTrial());
$this->assertTrue($subscription->toArray()['is_granted']);
}
public function testUnknownPeriodIsRejected(): void
{
$service = $this->service(null, null);
try {
$service->grant('doctor', 1, 'missing-uuid', $this->createMock(User::class));
$this->fail('expected AppException');
} catch (AppException $e) {
$this->assertSame(ErrorCodes::ERR_NOT_FOUND_001, $e->getErrorCode());
$this->assertSame(404, $e->getHttpStatus());
}
}
/** Boundary: an active subscription is extended from its own expiry, not from today. */
public function testGrantExtendsAnActiveSubscriptionInsteadOfRestartingIt(): void
{
$currentExpiry = time() + 20 * 86400;
$active = $this->createMock(ClinicSubscription::class);
$active->method('getExpiresAt')->willReturn($currentExpiry);
$service = $this->service($this->period(1), $active);
$subscription = $service->grant('doctor', 7, 'period-uuid', $this->createMock(User::class));
$this->assertEqualsWithDelta($currentExpiry + 30 * 86400, $subscription->getExpiresAt(), 5);
}
}