Implement academy analytics, billing, and web stories updates
This commit is contained in:
161
tests/Feature/Academy/AcademyBillingAccessTest.php
Normal file
161
tests/Feature/Academy/AcademyBillingAccessTest.php
Normal file
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Academy;
|
||||
|
||||
use App\Http\Middleware\ConditionalValidateCsrfToken;
|
||||
use App\Models\AcademyLesson;
|
||||
use App\Models\AcademyPromptTemplate;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Inertia\Testing\AssertableInertia;
|
||||
use Laravel\Cashier\Subscription;
|
||||
use Tests\TestCase;
|
||||
|
||||
final class AcademyBillingAccessTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->withoutMiddleware(ConditionalValidateCsrfToken::class);
|
||||
$this->configureBilling();
|
||||
}
|
||||
|
||||
public function test_success_page_does_not_grant_access_by_itself(): void
|
||||
{
|
||||
$prompt = AcademyPromptTemplate::query()->create([
|
||||
'title' => 'Creator Prompt',
|
||||
'slug' => 'billing-success-does-not-unlock',
|
||||
'excerpt' => 'Locked creator prompt.',
|
||||
'prompt' => 'SECRET CREATOR PROMPT',
|
||||
'difficulty' => 'beginner',
|
||||
'access_level' => 'creator',
|
||||
'active' => true,
|
||||
'published_at' => now()->subMinute(),
|
||||
]);
|
||||
|
||||
$user = User::factory()->create(['email_verified_at' => now()]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->get(route('academy.billing.success', ['session_id' => 'cs_test_only']))
|
||||
->assertOk()
|
||||
->assertInertia(fn (AssertableInertia $page) => $page
|
||||
->where('currentTier', 'free')
|
||||
->where('isSubscribed', false));
|
||||
|
||||
$this->actingAs($user)
|
||||
->get(route('academy.prompts.show', ['slug' => $prompt->slug]))
|
||||
->assertOk()
|
||||
->assertDontSee('SECRET CREATOR PROMPT')
|
||||
->assertInertia(fn (AssertableInertia $page) => $page
|
||||
->where('item.locked', true)
|
||||
->where('item.prompt', null));
|
||||
}
|
||||
|
||||
public function test_canceled_subscription_on_grace_period_still_has_access(): void
|
||||
{
|
||||
$lesson = AcademyLesson::query()->create([
|
||||
'title' => 'Creator Grace Lesson',
|
||||
'slug' => 'creator-grace-lesson',
|
||||
'excerpt' => 'Should remain available in grace period.',
|
||||
'content' => 'VISIBLE DURING GRACE PERIOD',
|
||||
'difficulty' => 'beginner',
|
||||
'access_level' => 'creator',
|
||||
'lesson_type' => 'article',
|
||||
'active' => true,
|
||||
'published_at' => now()->subMinute(),
|
||||
]);
|
||||
|
||||
$user = User::factory()->create(['email_verified_at' => now()]);
|
||||
$this->attachSubscription($user, 'sub_grace', 'price_creator_month', now()->addDay());
|
||||
|
||||
$this->actingAs($user)
|
||||
->get(route('academy.lessons.show', ['slug' => $lesson->slug]))
|
||||
->assertOk()
|
||||
->assertSee('VISIBLE DURING GRACE PERIOD')
|
||||
->assertInertia(fn (AssertableInertia $page) => $page
|
||||
->where('item.locked', false)
|
||||
->where('item.content', 'VISIBLE DURING GRACE PERIOD'));
|
||||
}
|
||||
|
||||
public function test_ended_subscription_loses_paid_access(): void
|
||||
{
|
||||
$lesson = AcademyLesson::query()->create([
|
||||
'title' => 'Creator Ended Lesson',
|
||||
'slug' => 'creator-ended-lesson',
|
||||
'excerpt' => 'Should lock after grace period.',
|
||||
'content' => 'NO LONGER VISIBLE',
|
||||
'difficulty' => 'beginner',
|
||||
'access_level' => 'creator',
|
||||
'lesson_type' => 'article',
|
||||
'active' => true,
|
||||
'published_at' => now()->subMinute(),
|
||||
]);
|
||||
|
||||
$user = User::factory()->create(['email_verified_at' => now()]);
|
||||
$this->attachSubscription($user, 'sub_ended', 'price_creator_month', now()->subMinute());
|
||||
|
||||
$this->actingAs($user)
|
||||
->get(route('academy.lessons.show', ['slug' => $lesson->slug]))
|
||||
->assertOk()
|
||||
->assertDontSee('NO LONGER VISIBLE')
|
||||
->assertInertia(fn (AssertableInertia $page) => $page
|
||||
->where('item.locked', true)
|
||||
->where('item.content', null));
|
||||
}
|
||||
|
||||
public function test_billing_portal_route_requires_authentication(): void
|
||||
{
|
||||
$this->get(route('academy.billing.portal'))
|
||||
->assertRedirect(route('login'));
|
||||
}
|
||||
|
||||
private function attachSubscription(User $user, string $subscriptionId, string $priceId, ?\Illuminate\Support\Carbon $endsAt = null): Subscription
|
||||
{
|
||||
$subscription = $user->subscriptions()->create([
|
||||
'type' => 'academy',
|
||||
'stripe_id' => $subscriptionId,
|
||||
'stripe_status' => 'active',
|
||||
'stripe_price' => $priceId,
|
||||
'quantity' => 1,
|
||||
'ends_at' => $endsAt,
|
||||
]);
|
||||
|
||||
$subscription->items()->create([
|
||||
'stripe_id' => 'si_'.$subscriptionId,
|
||||
'stripe_product' => 'prod_'.($priceId === 'price_pro_month' ? 'pro' : 'creator'),
|
||||
'stripe_price' => $priceId,
|
||||
'quantity' => 1,
|
||||
]);
|
||||
|
||||
return $subscription;
|
||||
}
|
||||
|
||||
private function configureBilling(): void
|
||||
{
|
||||
config()->set('academy.enabled', true);
|
||||
config()->set('academy.payments_enabled', true);
|
||||
config()->set('academy_billing.enabled', true);
|
||||
config()->set('academy_billing.subscription_name', 'academy');
|
||||
config()->set('academy_billing.plans', [
|
||||
'creator_monthly' => [
|
||||
'label' => 'Creator Monthly',
|
||||
'tier' => 'creator',
|
||||
'interval' => 'monthly',
|
||||
'stripe_price_id' => 'price_creator_month',
|
||||
'featured' => false,
|
||||
],
|
||||
'pro_monthly' => [
|
||||
'label' => 'Pro Monthly',
|
||||
'tier' => 'pro',
|
||||
'interval' => 'monthly',
|
||||
'stripe_price_id' => 'price_pro_month',
|
||||
'featured' => true,
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user