File: /home/xedaptot/be.naniguide.com/tests/Unit/Plans/QuotasServiceTest.php
<?php
/**
* QuotasServiceTest — covers Quota gating with counter delegation.
*
* See docs/payment-order-plan-subscription-saas/SUBSCRIPTION-COMPREHENSIVE-DESIGN.md §6.2.
*/
use App\Model\Customer;
use App\Model\MailList;
use App\Model\Plan;
use App\Model\PlanQuota;
use App\Model\Subscription;
use App\Services\Plans\Quotas\QuotaKey;
use App\Services\Plans\Quotas\QuotasService;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
uses(TestCase::class);
beforeEach(function () {
$this->customer = Customer::forceCreate([
'uid' => uniqid('test_c_'),
'timezone' => 'UTC',
'status' => 'active',
]);
$this->plan = Plan::forceCreate([
'uid' => uniqid('test_plan_'),
'name' => 'Test Plan',
'currency_id' => 1,
'frequency_amount' => 1,
'frequency_unit' => 'month',
'price' => 0,
'status' => 'active',
]);
$this->sub = Subscription::create([
'uid' => (string) \Illuminate\Support\Str::uuid(),
'customer_id' => $this->customer->id,
'plan_id' => $this->plan->id,
'status' => 'active',
'current_period_ends_at' => now()->addMonth(),
]);
PlanQuota::factory()->for($this->plan, 'plan')
->forKey(QuotaKey::MAX_LISTS)
->state(['limit_value' => 3])
->create();
$this->svc = app(QuotasService::class);
});
afterEach(function () {
DB::table('mail_lists')->where('customer_id', $this->customer->id)->delete();
DB::table('plan_quotas')->where('plan_id', $this->plan->id)->delete();
$this->sub->delete();
$this->plan->delete();
$this->customer->delete();
});
test('canCreate false when no active subscription', function () {
$this->sub->delete(); // orphan customer
expect($this->svc->canCreate($this->customer, QuotaKey::MAX_LISTS))->toBeFalse();
});
test('canCreate true when under limit', function () {
MailList::forceCreate([
'uid' => (string) \Illuminate\Support\Str::uuid(),
'customer_id' => $this->customer->id,
'name' => 'Test List ' . uniqid(),
]);
MailList::forceCreate([
'uid' => (string) \Illuminate\Support\Str::uuid(),
'customer_id' => $this->customer->id,
'name' => 'Test List ' . uniqid(),
]);
expect($this->svc->used($this->customer, QuotaKey::MAX_LISTS))->toBe(2);
expect($this->svc->limit($this->customer, QuotaKey::MAX_LISTS))->toBe(3);
expect($this->svc->canCreate($this->customer, QuotaKey::MAX_LISTS))->toBeTrue();
});
test('canCreate false when at limit', function () {
foreach (range(1, 3) as $_) {
MailList::forceCreate([
'uid' => (string) \Illuminate\Support\Str::uuid(),
'customer_id' => $this->customer->id,
'name' => 'Test List ' . uniqid(),
]);
}
expect($this->svc->canCreate($this->customer, QuotaKey::MAX_LISTS))->toBeFalse();
});
test('canCreate false when quota not configured for plan', function () {
expect($this->svc->canCreate($this->customer, QuotaKey::MAX_CAMPAIGNS))->toBeFalse();
expect($this->svc->limit($this->customer, QuotaKey::MAX_CAMPAIGNS))->toBeFalse();
});
test('canCreate true when limit is NULL (unlimited)', function () {
// Overwrite the default limit with NULL
\App\Model\PlanQuota::where('plan_id', $this->plan->id)
->where('quota_key', QuotaKey::MAX_LISTS->value)
->update(['limit_value' => null]);
foreach (range(1, 5) as $_) {
MailList::forceCreate([
'uid' => (string) \Illuminate\Support\Str::uuid(),
'customer_id' => $this->customer->id,
'name' => 'Test List ' . uniqid(),
]);
}
expect($this->svc->limit($this->customer, QuotaKey::MAX_LISTS))->toBeNull();
expect($this->svc->canCreate($this->customer, QuotaKey::MAX_LISTS))->toBeTrue();
});
test('per-list and storage quotas throw (no customer-scoped counter)', function () {
expect(fn () => $this->svc->used($this->customer, QuotaKey::MAX_SEGMENTS_PER_LIST))
->toThrow(RuntimeException::class);
expect(fn () => $this->svc->used($this->customer, QuotaKey::MAX_UPLOAD_SIZE_TOTAL))
->toThrow(RuntimeException::class);
});