feat: create PlanSeeder with 4 tiered plans
This commit is contained in:
parent
d235e702cf
commit
5bc1548423
4 changed files with 154 additions and 18 deletions
|
|
@ -14,6 +14,8 @@ class DatabaseSeeder extends Seeder
|
|||
*/
|
||||
public function run(): void
|
||||
{
|
||||
//
|
||||
$this->call([
|
||||
PlanSeeder::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
88
database/seeders/PlanSeeder.php
Normal file
88
database/seeders/PlanSeeder.php
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Enums\Plan\Slug;
|
||||
use App\Models\Plan;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class PlanSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$plans = [
|
||||
[
|
||||
'slug' => Slug::Starter,
|
||||
'name' => 'Starter',
|
||||
'stripe_monthly_price_id' => env('STRIPE_STARTER_MONTHLY'),
|
||||
'stripe_yearly_price_id' => env('STRIPE_STARTER_YEARLY'),
|
||||
'monthly_price' => 1900,
|
||||
'yearly_price' => 19000,
|
||||
'social_account_limit' => 5,
|
||||
'member_limit' => 1,
|
||||
'brand_limit' => 0,
|
||||
'ai_images_limit' => 50,
|
||||
'ai_videos_limit' => 10,
|
||||
'data_retention_days' => 30,
|
||||
'sort' => 1,
|
||||
],
|
||||
[
|
||||
'slug' => Slug::Plus,
|
||||
'name' => 'Plus',
|
||||
'stripe_monthly_price_id' => env('STRIPE_PLUS_MONTHLY'),
|
||||
'stripe_yearly_price_id' => env('STRIPE_PLUS_YEARLY'),
|
||||
'monthly_price' => 2900,
|
||||
'yearly_price' => 29000,
|
||||
'social_account_limit' => 10,
|
||||
'member_limit' => 5,
|
||||
'brand_limit' => 5,
|
||||
'ai_images_limit' => 150,
|
||||
'ai_videos_limit' => 30,
|
||||
'data_retention_days' => 60,
|
||||
'sort' => 2,
|
||||
],
|
||||
[
|
||||
'slug' => Slug::Pro,
|
||||
'name' => 'Pro',
|
||||
'stripe_monthly_price_id' => env('STRIPE_PRO_MONTHLY'),
|
||||
'stripe_yearly_price_id' => env('STRIPE_PRO_YEARLY'),
|
||||
'monthly_price' => 4900,
|
||||
'yearly_price' => 49000,
|
||||
'social_account_limit' => 30,
|
||||
'member_limit' => 15,
|
||||
'brand_limit' => 15,
|
||||
'ai_images_limit' => 500,
|
||||
'ai_videos_limit' => 100,
|
||||
'data_retention_days' => 90,
|
||||
'sort' => 3,
|
||||
],
|
||||
[
|
||||
'slug' => Slug::Max,
|
||||
'name' => 'Max',
|
||||
'stripe_monthly_price_id' => env('STRIPE_MAX_MONTHLY'),
|
||||
'stripe_yearly_price_id' => env('STRIPE_MAX_YEARLY'),
|
||||
'monthly_price' => 9900,
|
||||
'yearly_price' => 99000,
|
||||
'social_account_limit' => 100,
|
||||
'member_limit' => 20,
|
||||
'brand_limit' => 50,
|
||||
'ai_images_limit' => 2000,
|
||||
'ai_videos_limit' => 500,
|
||||
'data_retention_days' => 730,
|
||||
'sort' => 4,
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($plans as $plan) {
|
||||
Plan::updateOrCreate(
|
||||
['slug' => $plan['slug']],
|
||||
$plan,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
53
tests/Feature/PlanSeederTest.php
Normal file
53
tests/Feature/PlanSeederTest.php
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Enums\Plan\Slug;
|
||||
use App\Models\Plan;
|
||||
use Database\Seeders\PlanSeeder;
|
||||
|
||||
test('seeder creates 4 plans', function () {
|
||||
expect(Plan::count())->toBe(4);
|
||||
});
|
||||
|
||||
test('seeder creates plans with correct limits', function () {
|
||||
$starter = Plan::where('slug', Slug::Starter)->first();
|
||||
|
||||
expect($starter->name)->toBe('Starter')
|
||||
->and($starter->monthly_price)->toBe(1900)
|
||||
->and($starter->yearly_price)->toBe(19000)
|
||||
->and($starter->social_account_limit)->toBe(5)
|
||||
->and($starter->member_limit)->toBe(1)
|
||||
->and($starter->brand_limit)->toBe(0)
|
||||
->and($starter->ai_images_limit)->toBe(50)
|
||||
->and($starter->ai_videos_limit)->toBe(10)
|
||||
->and($starter->data_retention_days)->toBe(30)
|
||||
->and($starter->sort)->toBe(1);
|
||||
|
||||
$max = Plan::where('slug', Slug::Max)->first();
|
||||
|
||||
expect($max->name)->toBe('Max')
|
||||
->and($max->monthly_price)->toBe(9900)
|
||||
->and($max->yearly_price)->toBe(99000)
|
||||
->and($max->social_account_limit)->toBe(100)
|
||||
->and($max->member_limit)->toBe(20)
|
||||
->and($max->brand_limit)->toBe(50)
|
||||
->and($max->ai_images_limit)->toBe(2000)
|
||||
->and($max->ai_videos_limit)->toBe(500)
|
||||
->and($max->data_retention_days)->toBe(730)
|
||||
->and($max->sort)->toBe(4);
|
||||
});
|
||||
|
||||
test('seeder is idempotent', function () {
|
||||
$this->seed(PlanSeeder::class);
|
||||
|
||||
expect(Plan::count())->toBe(4);
|
||||
});
|
||||
|
||||
test('active scope excludes archived plans', function () {
|
||||
$plan = Plan::where('slug', Slug::Starter)->first();
|
||||
$plan->update(['is_archived' => true]);
|
||||
|
||||
expect(Plan::active()->count())->toBe(3)
|
||||
->and(Plan::count())->toBe(4);
|
||||
});
|
||||
|
|
@ -6,6 +6,8 @@
|
|||
use App\Models\Plan;
|
||||
|
||||
test('plan can be created with factory', function () {
|
||||
Plan::where('slug', Slug::Pro)->delete();
|
||||
|
||||
$plan = Plan::factory()->create([
|
||||
'slug' => Slug::Pro,
|
||||
'name' => 'Pro',
|
||||
|
|
@ -22,7 +24,7 @@
|
|||
});
|
||||
|
||||
test('plan slug is cast to enum', function () {
|
||||
$plan = Plan::factory()->create(['slug' => Slug::Starter]);
|
||||
$plan = Plan::where('slug', Slug::Starter)->first();
|
||||
|
||||
expect($plan->slug)->toBeInstanceOf(Slug::class)
|
||||
->and($plan->slug)->toBe(Slug::Starter)
|
||||
|
|
@ -30,37 +32,28 @@
|
|||
});
|
||||
|
||||
test('active scope excludes archived plans', function () {
|
||||
Plan::factory()->create(['slug' => Slug::Starter, 'is_archived' => false]);
|
||||
Plan::factory()->create(['slug' => Slug::Plus, 'is_archived' => false]);
|
||||
Plan::factory()->archived()->create(['slug' => Slug::Pro]);
|
||||
$activeBefore = Plan::active()->count();
|
||||
|
||||
$activePlans = Plan::active()->get();
|
||||
$plan = Plan::where('slug', Slug::Pro)->first();
|
||||
$plan->update(['is_archived' => true]);
|
||||
|
||||
expect($activePlans)->toHaveCount(2);
|
||||
expect(Plan::active()->count())->toBe($activeBefore - 1);
|
||||
});
|
||||
|
||||
test('formatted monthly price returns dollar format', function () {
|
||||
$plan = Plan::factory()->create(['monthly_price' => 1900]);
|
||||
$plan = Plan::where('slug', Slug::Starter)->first();
|
||||
|
||||
expect($plan->formattedMonthlyPrice())->toBe('$19');
|
||||
});
|
||||
|
||||
test('formatted yearly price returns dollar format', function () {
|
||||
$plan = Plan::factory()->create(['yearly_price' => 19000]);
|
||||
$plan = Plan::where('slug', Slug::Starter)->first();
|
||||
|
||||
expect($plan->formattedYearlyPrice())->toBe('$190');
|
||||
});
|
||||
|
||||
test('integer fields are cast correctly', function () {
|
||||
$plan = Plan::factory()->create([
|
||||
'social_account_limit' => 10,
|
||||
'member_limit' => 3,
|
||||
'brand_limit' => 5,
|
||||
'ai_images_limit' => 50,
|
||||
'ai_videos_limit' => 25,
|
||||
'data_retention_days' => 365,
|
||||
'sort' => 2,
|
||||
]);
|
||||
$plan = Plan::where('slug', Slug::Starter)->first();
|
||||
|
||||
expect($plan->social_account_limit)->toBeInt()
|
||||
->and($plan->member_limit)->toBeInt()
|
||||
|
|
|
|||
Loading…
Reference in a new issue