All customers were migrated to the single Workspace plan and the old plan rows were deleted in production, so drop the now-dead legacy tiers from the code: reduce the Plan Slug enum to Workspace only and default the PlanFactory to it. Rework StripeEventListenerTest around the single Workspace plan (its monthly and yearly price ids still exercise plan-by-price mapping, trial clearing, deletion, and previous-plan propagation), and point the remaining tests that referenced the starter/pro slugs at the seeded Workspace plan.
123 lines
4.3 KiB
PHP
123 lines
4.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Enums\PostHog\BillingEvent;
|
|
use App\Enums\User\Persona;
|
|
use App\Jobs\PostHog\SendEvent;
|
|
use App\Jobs\PostHog\SyncUser;
|
|
use App\Jobs\PostHog\TrackBilling;
|
|
use App\Models\Account;
|
|
use App\Models\Plan;
|
|
use App\Models\User;
|
|
use App\Services\PostHogService;
|
|
use Illuminate\Support\Facades\Bus;
|
|
use Illuminate\Support\Facades\Queue;
|
|
|
|
beforeEach(function () {
|
|
config(['services.posthog.enabled' => true, 'services.posthog.api_key' => 'phc_test_key']);
|
|
|
|
$this->account = Account::factory()->create([
|
|
'plan_id' => Plan::query()->where('slug', 'workspace')->first()?->id,
|
|
]);
|
|
$this->user = User::factory()->create(['account_id' => $this->account->id]);
|
|
$this->account->update(['owner_id' => $this->user->id]);
|
|
|
|
$this->payload = [
|
|
'type' => 'customer.subscription.updated',
|
|
'data' => ['object' => ['customer' => 'cus_test', 'status' => 'active']],
|
|
];
|
|
});
|
|
|
|
test('job is queued on the posthog queue', function () {
|
|
$job = new TrackBilling((string) $this->account->id, BillingEvent::Updated, $this->payload);
|
|
|
|
expect($job->queue)->toBe('posthog');
|
|
});
|
|
|
|
test('handle captures event on the owner profile with account group attached', function () {
|
|
Queue::fake();
|
|
|
|
(new TrackBilling((string) $this->account->id, BillingEvent::Updated, $this->payload))
|
|
->handle(app(PostHogService::class));
|
|
|
|
Queue::assertPushed(SendEvent::class, function ($job) {
|
|
return $job->method === 'capture'
|
|
&& $job->payload['event'] === BillingEvent::Updated->value
|
|
&& $job->payload['distinctId'] === (string) $this->user->id
|
|
&& $job->payload['properties']['$groups']['account'] === (string) $this->account->id
|
|
&& $job->payload['properties']['stripe_status'] === 'active'
|
|
&& array_key_exists('previous_plan', $job->payload['properties']);
|
|
});
|
|
});
|
|
|
|
test('handle forwards the owner persona as an event property', function () {
|
|
$this->user->update(['persona' => Persona::Agency->value]);
|
|
Queue::fake();
|
|
|
|
(new TrackBilling((string) $this->account->id, BillingEvent::Created, $this->payload))
|
|
->handle(app(PostHogService::class));
|
|
|
|
Queue::assertPushed(
|
|
SendEvent::class,
|
|
fn ($job) => ($job->payload['properties']['persona'] ?? null) === Persona::Agency->value,
|
|
);
|
|
});
|
|
|
|
test('handle forwards previousPlan as a property when supplied', function () {
|
|
Queue::fake();
|
|
|
|
(new TrackBilling((string) $this->account->id, BillingEvent::Updated, $this->payload, 'Starter'))
|
|
->handle(app(PostHogService::class));
|
|
|
|
Queue::assertPushed(SendEvent::class, function ($job) {
|
|
return $job->payload['properties']['previous_plan'] === 'Starter';
|
|
});
|
|
});
|
|
|
|
test('handle dispatches SyncUser for the account owner', function () {
|
|
Bus::fake([SyncUser::class]);
|
|
|
|
(new TrackBilling((string) $this->account->id, BillingEvent::Cancelled, $this->payload))
|
|
->handle(app(PostHogService::class));
|
|
|
|
Bus::assertDispatched(
|
|
SyncUser::class,
|
|
fn ($job) => $job->userId === (string) $this->user->id,
|
|
);
|
|
});
|
|
|
|
test('handle returns silently when account does not exist', function () {
|
|
Queue::fake();
|
|
Bus::fake([SyncUser::class]);
|
|
|
|
(new TrackBilling('00000000-0000-0000-0000-000000000000', BillingEvent::Created, $this->payload))
|
|
->handle(app(PostHogService::class));
|
|
|
|
Queue::assertNothingPushed();
|
|
Bus::assertNotDispatched(SyncUser::class);
|
|
});
|
|
|
|
test('handle returns silently when account has no owner', function () {
|
|
$this->account->update(['owner_id' => null]);
|
|
Queue::fake();
|
|
Bus::fake([SyncUser::class]);
|
|
|
|
(new TrackBilling((string) $this->account->id, BillingEvent::Created, $this->payload))
|
|
->handle(app(PostHogService::class));
|
|
|
|
Queue::assertNothingPushed();
|
|
Bus::assertNotDispatched(SyncUser::class);
|
|
});
|
|
|
|
test('handle does not push a PostHog network call when api key is unset', function () {
|
|
config(['services.posthog.api_key' => null]);
|
|
Queue::fake();
|
|
|
|
(new TrackBilling((string) $this->account->id, BillingEvent::Created, $this->payload))
|
|
->handle(app(PostHogService::class));
|
|
|
|
// The contract of this job is: when PostHog is disabled, handle short-
|
|
// circuits before any DB query and no SendEvent reaches the queue.
|
|
Queue::assertNotPushed(SendEvent::class);
|
|
});
|