2026-03-31 00:18:07 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
refactor: namespace PostHog jobs + extract billing tracking, add tests
Reorganises PostHog plumbing under `App\Jobs\PostHog` and extracts the
Stripe billing capture out of `StripeEventListener` into its own job.
Adds the missing test coverage that was promised but not delivered in
the previous commit.
Code changes:
- Move `app/Jobs/SendPostHogEvent.php` → `app/Jobs/PostHog/SendEvent.php`
(low-level dispatcher).
- Move `app/Jobs/SyncUserToPostHog.php` → `app/Jobs/PostHog/SyncUser.php`
(high-level user/account/workspace sync).
- New `app/Jobs/PostHog/TrackBilling.php` that owns the
capture('subscription.*') + SyncUser re-dispatch flow. Receives
account id + event name + payload, runs on the `posthog` queue.
- `StripeEventListener` slims down to a switch table mapping Stripe
event types to PostHog event names and dispatches `TrackBilling`. No
more inline tracking logic in the listener.
- `resources/js/posthog.ts` now owns `syncPostHogContext(page)` and
`capturePageview()`. `resources/js/app.ts` imports them — no behaviour
inlined in the bootstrap.
- `app/Services/PostHogService.php` and `app/Actions/User/CreateUser.php`
updated to the new namespaces.
Tests added/updated:
- `tests/Feature/Jobs/PostHog/SyncUserTest.php` — identify/group payload
shape, account metrics, workspace skip when none, queue assignment,
no-op without api key.
- `tests/Feature/Jobs/PostHog/TrackBillingTest.php` — capture payload,
SyncUser re-dispatch, missing-account/owner handling, api key gate.
- `tests/Feature/Jobs/PostHog/SendEventTest.php` — moved from
`tests/Feature/SendPostHogEventTest.php` and updated to new namespace.
- `tests/Unit/PostHogServiceTest.php` — adds coverage for the
account-aware capture (auto-attached `\$groups.account`, `account_id`,
`plan`) and the no-account branch.
- `tests/Feature/Listeners/StripeEventListenerTest.php` — replaces the
old inline-PostHog assertions with `Bus::fake([TrackBilling::class])`
and verifies the listener dispatches TrackBilling with the right
account id + event name for each subscription type, and skips
non-subscription event types.
- `tests/Feature/Actions/User/CreateUserTest.php` — verifies signup
dispatches `SyncUser` with the new user id.
Suite: 1427 passed (+20 net new, including the previous round of
metrics-related tests).
2026-05-07 12:41:36 +00:00
|
|
|
use App\Jobs\PostHog\SendEvent;
|
|
|
|
|
use App\Models\Account;
|
|
|
|
|
use App\Models\Plan;
|
2026-03-31 00:18:07 +00:00
|
|
|
use App\Services\PostHogService;
|
refactor: namespace PostHog jobs + extract billing tracking, add tests
Reorganises PostHog plumbing under `App\Jobs\PostHog` and extracts the
Stripe billing capture out of `StripeEventListener` into its own job.
Adds the missing test coverage that was promised but not delivered in
the previous commit.
Code changes:
- Move `app/Jobs/SendPostHogEvent.php` → `app/Jobs/PostHog/SendEvent.php`
(low-level dispatcher).
- Move `app/Jobs/SyncUserToPostHog.php` → `app/Jobs/PostHog/SyncUser.php`
(high-level user/account/workspace sync).
- New `app/Jobs/PostHog/TrackBilling.php` that owns the
capture('subscription.*') + SyncUser re-dispatch flow. Receives
account id + event name + payload, runs on the `posthog` queue.
- `StripeEventListener` slims down to a switch table mapping Stripe
event types to PostHog event names and dispatches `TrackBilling`. No
more inline tracking logic in the listener.
- `resources/js/posthog.ts` now owns `syncPostHogContext(page)` and
`capturePageview()`. `resources/js/app.ts` imports them — no behaviour
inlined in the bootstrap.
- `app/Services/PostHogService.php` and `app/Actions/User/CreateUser.php`
updated to the new namespaces.
Tests added/updated:
- `tests/Feature/Jobs/PostHog/SyncUserTest.php` — identify/group payload
shape, account metrics, workspace skip when none, queue assignment,
no-op without api key.
- `tests/Feature/Jobs/PostHog/TrackBillingTest.php` — capture payload,
SyncUser re-dispatch, missing-account/owner handling, api key gate.
- `tests/Feature/Jobs/PostHog/SendEventTest.php` — moved from
`tests/Feature/SendPostHogEventTest.php` and updated to new namespace.
- `tests/Unit/PostHogServiceTest.php` — adds coverage for the
account-aware capture (auto-attached `\$groups.account`, `account_id`,
`plan`) and the no-account branch.
- `tests/Feature/Listeners/StripeEventListenerTest.php` — replaces the
old inline-PostHog assertions with `Bus::fake([TrackBilling::class])`
and verifies the listener dispatches TrackBilling with the right
account id + event name for each subscription type, and skips
non-subscription event types.
- `tests/Feature/Actions/User/CreateUserTest.php` — verifies signup
dispatches `SyncUser` with the new user id.
Suite: 1427 passed (+20 net new, including the previous round of
metrics-related tests).
2026-05-07 12:41:36 +00:00
|
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
2026-03-31 00:18:07 +00:00
|
|
|
use Illuminate\Support\Facades\Queue;
|
|
|
|
|
|
refactor: namespace PostHog jobs + extract billing tracking, add tests
Reorganises PostHog plumbing under `App\Jobs\PostHog` and extracts the
Stripe billing capture out of `StripeEventListener` into its own job.
Adds the missing test coverage that was promised but not delivered in
the previous commit.
Code changes:
- Move `app/Jobs/SendPostHogEvent.php` → `app/Jobs/PostHog/SendEvent.php`
(low-level dispatcher).
- Move `app/Jobs/SyncUserToPostHog.php` → `app/Jobs/PostHog/SyncUser.php`
(high-level user/account/workspace sync).
- New `app/Jobs/PostHog/TrackBilling.php` that owns the
capture('subscription.*') + SyncUser re-dispatch flow. Receives
account id + event name + payload, runs on the `posthog` queue.
- `StripeEventListener` slims down to a switch table mapping Stripe
event types to PostHog event names and dispatches `TrackBilling`. No
more inline tracking logic in the listener.
- `resources/js/posthog.ts` now owns `syncPostHogContext(page)` and
`capturePageview()`. `resources/js/app.ts` imports them — no behaviour
inlined in the bootstrap.
- `app/Services/PostHogService.php` and `app/Actions/User/CreateUser.php`
updated to the new namespaces.
Tests added/updated:
- `tests/Feature/Jobs/PostHog/SyncUserTest.php` — identify/group payload
shape, account metrics, workspace skip when none, queue assignment,
no-op without api key.
- `tests/Feature/Jobs/PostHog/TrackBillingTest.php` — capture payload,
SyncUser re-dispatch, missing-account/owner handling, api key gate.
- `tests/Feature/Jobs/PostHog/SendEventTest.php` — moved from
`tests/Feature/SendPostHogEventTest.php` and updated to new namespace.
- `tests/Unit/PostHogServiceTest.php` — adds coverage for the
account-aware capture (auto-attached `\$groups.account`, `account_id`,
`plan`) and the no-account branch.
- `tests/Feature/Listeners/StripeEventListenerTest.php` — replaces the
old inline-PostHog assertions with `Bus::fake([TrackBilling::class])`
and verifies the listener dispatches TrackBilling with the right
account id + event name for each subscription type, and skips
non-subscription event types.
- `tests/Feature/Actions/User/CreateUserTest.php` — verifies signup
dispatches `SyncUser` with the new user id.
Suite: 1427 passed (+20 net new, including the previous round of
metrics-related tests).
2026-05-07 12:41:36 +00:00
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
|
|
2026-03-31 00:18:07 +00:00
|
|
|
test('capture dispatches job when api key is configured', function () {
|
|
|
|
|
Queue::fake();
|
2026-05-07 15:42:35 +00:00
|
|
|
config(['services.posthog.enabled' => true, 'services.posthog.api_key' => 'phc_test_key']);
|
2026-03-31 00:18:07 +00:00
|
|
|
|
|
|
|
|
$service = new PostHogService;
|
|
|
|
|
$service->capture('user-123', 'test_event', ['foo' => 'bar']);
|
|
|
|
|
|
refactor: namespace PostHog jobs + extract billing tracking, add tests
Reorganises PostHog plumbing under `App\Jobs\PostHog` and extracts the
Stripe billing capture out of `StripeEventListener` into its own job.
Adds the missing test coverage that was promised but not delivered in
the previous commit.
Code changes:
- Move `app/Jobs/SendPostHogEvent.php` → `app/Jobs/PostHog/SendEvent.php`
(low-level dispatcher).
- Move `app/Jobs/SyncUserToPostHog.php` → `app/Jobs/PostHog/SyncUser.php`
(high-level user/account/workspace sync).
- New `app/Jobs/PostHog/TrackBilling.php` that owns the
capture('subscription.*') + SyncUser re-dispatch flow. Receives
account id + event name + payload, runs on the `posthog` queue.
- `StripeEventListener` slims down to a switch table mapping Stripe
event types to PostHog event names and dispatches `TrackBilling`. No
more inline tracking logic in the listener.
- `resources/js/posthog.ts` now owns `syncPostHogContext(page)` and
`capturePageview()`. `resources/js/app.ts` imports them — no behaviour
inlined in the bootstrap.
- `app/Services/PostHogService.php` and `app/Actions/User/CreateUser.php`
updated to the new namespaces.
Tests added/updated:
- `tests/Feature/Jobs/PostHog/SyncUserTest.php` — identify/group payload
shape, account metrics, workspace skip when none, queue assignment,
no-op without api key.
- `tests/Feature/Jobs/PostHog/TrackBillingTest.php` — capture payload,
SyncUser re-dispatch, missing-account/owner handling, api key gate.
- `tests/Feature/Jobs/PostHog/SendEventTest.php` — moved from
`tests/Feature/SendPostHogEventTest.php` and updated to new namespace.
- `tests/Unit/PostHogServiceTest.php` — adds coverage for the
account-aware capture (auto-attached `\$groups.account`, `account_id`,
`plan`) and the no-account branch.
- `tests/Feature/Listeners/StripeEventListenerTest.php` — replaces the
old inline-PostHog assertions with `Bus::fake([TrackBilling::class])`
and verifies the listener dispatches TrackBilling with the right
account id + event name for each subscription type, and skips
non-subscription event types.
- `tests/Feature/Actions/User/CreateUserTest.php` — verifies signup
dispatches `SyncUser` with the new user id.
Suite: 1427 passed (+20 net new, including the previous round of
metrics-related tests).
2026-05-07 12:41:36 +00:00
|
|
|
Queue::assertPushed(SendEvent::class, function ($job) {
|
2026-05-07 13:41:53 +00:00
|
|
|
return $job->method === 'capture'
|
|
|
|
|
&& $job->payload['event'] === 'test_event'
|
|
|
|
|
&& $job->payload['distinctId'] === 'user-123';
|
2026-03-31 00:18:07 +00:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('capture does not dispatch job when api key is missing', function () {
|
|
|
|
|
Queue::fake();
|
|
|
|
|
config(['services.posthog.api_key' => null]);
|
|
|
|
|
|
|
|
|
|
$service = new PostHogService;
|
|
|
|
|
$service->capture('user-123', 'test_event');
|
|
|
|
|
|
|
|
|
|
Queue::assertNothingPushed();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('identify dispatches job when api key is configured', function () {
|
|
|
|
|
Queue::fake();
|
2026-05-07 15:42:35 +00:00
|
|
|
config(['services.posthog.enabled' => true, 'services.posthog.api_key' => 'phc_test_key']);
|
2026-03-31 00:18:07 +00:00
|
|
|
|
|
|
|
|
$service = new PostHogService;
|
|
|
|
|
$service->identify('user-123', ['$email' => 'test@example.com']);
|
|
|
|
|
|
refactor: namespace PostHog jobs + extract billing tracking, add tests
Reorganises PostHog plumbing under `App\Jobs\PostHog` and extracts the
Stripe billing capture out of `StripeEventListener` into its own job.
Adds the missing test coverage that was promised but not delivered in
the previous commit.
Code changes:
- Move `app/Jobs/SendPostHogEvent.php` → `app/Jobs/PostHog/SendEvent.php`
(low-level dispatcher).
- Move `app/Jobs/SyncUserToPostHog.php` → `app/Jobs/PostHog/SyncUser.php`
(high-level user/account/workspace sync).
- New `app/Jobs/PostHog/TrackBilling.php` that owns the
capture('subscription.*') + SyncUser re-dispatch flow. Receives
account id + event name + payload, runs on the `posthog` queue.
- `StripeEventListener` slims down to a switch table mapping Stripe
event types to PostHog event names and dispatches `TrackBilling`. No
more inline tracking logic in the listener.
- `resources/js/posthog.ts` now owns `syncPostHogContext(page)` and
`capturePageview()`. `resources/js/app.ts` imports them — no behaviour
inlined in the bootstrap.
- `app/Services/PostHogService.php` and `app/Actions/User/CreateUser.php`
updated to the new namespaces.
Tests added/updated:
- `tests/Feature/Jobs/PostHog/SyncUserTest.php` — identify/group payload
shape, account metrics, workspace skip when none, queue assignment,
no-op without api key.
- `tests/Feature/Jobs/PostHog/TrackBillingTest.php` — capture payload,
SyncUser re-dispatch, missing-account/owner handling, api key gate.
- `tests/Feature/Jobs/PostHog/SendEventTest.php` — moved from
`tests/Feature/SendPostHogEventTest.php` and updated to new namespace.
- `tests/Unit/PostHogServiceTest.php` — adds coverage for the
account-aware capture (auto-attached `\$groups.account`, `account_id`,
`plan`) and the no-account branch.
- `tests/Feature/Listeners/StripeEventListenerTest.php` — replaces the
old inline-PostHog assertions with `Bus::fake([TrackBilling::class])`
and verifies the listener dispatches TrackBilling with the right
account id + event name for each subscription type, and skips
non-subscription event types.
- `tests/Feature/Actions/User/CreateUserTest.php` — verifies signup
dispatches `SyncUser` with the new user id.
Suite: 1427 passed (+20 net new, including the previous round of
metrics-related tests).
2026-05-07 12:41:36 +00:00
|
|
|
Queue::assertPushed(SendEvent::class, function ($job) {
|
2026-05-07 13:41:53 +00:00
|
|
|
return $job->method === 'identify'
|
|
|
|
|
&& $job->payload['distinctId'] === 'user-123';
|
2026-03-31 00:18:07 +00:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('identify does not dispatch job when api key is missing', function () {
|
|
|
|
|
Queue::fake();
|
|
|
|
|
config(['services.posthog.api_key' => null]);
|
|
|
|
|
|
|
|
|
|
$service = new PostHogService;
|
|
|
|
|
$service->identify('user-123', ['$email' => 'test@example.com']);
|
|
|
|
|
|
|
|
|
|
Queue::assertNothingPushed();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('group identify dispatches job when api key is configured', function () {
|
|
|
|
|
Queue::fake();
|
2026-05-07 15:42:35 +00:00
|
|
|
config(['services.posthog.enabled' => true, 'services.posthog.api_key' => 'phc_test_key']);
|
2026-03-31 00:18:07 +00:00
|
|
|
|
|
|
|
|
$service = new PostHogService;
|
|
|
|
|
$service->groupIdentify('workspace', 'ws-123', ['name' => 'Test Workspace']);
|
|
|
|
|
|
refactor: namespace PostHog jobs + extract billing tracking, add tests
Reorganises PostHog plumbing under `App\Jobs\PostHog` and extracts the
Stripe billing capture out of `StripeEventListener` into its own job.
Adds the missing test coverage that was promised but not delivered in
the previous commit.
Code changes:
- Move `app/Jobs/SendPostHogEvent.php` → `app/Jobs/PostHog/SendEvent.php`
(low-level dispatcher).
- Move `app/Jobs/SyncUserToPostHog.php` → `app/Jobs/PostHog/SyncUser.php`
(high-level user/account/workspace sync).
- New `app/Jobs/PostHog/TrackBilling.php` that owns the
capture('subscription.*') + SyncUser re-dispatch flow. Receives
account id + event name + payload, runs on the `posthog` queue.
- `StripeEventListener` slims down to a switch table mapping Stripe
event types to PostHog event names and dispatches `TrackBilling`. No
more inline tracking logic in the listener.
- `resources/js/posthog.ts` now owns `syncPostHogContext(page)` and
`capturePageview()`. `resources/js/app.ts` imports them — no behaviour
inlined in the bootstrap.
- `app/Services/PostHogService.php` and `app/Actions/User/CreateUser.php`
updated to the new namespaces.
Tests added/updated:
- `tests/Feature/Jobs/PostHog/SyncUserTest.php` — identify/group payload
shape, account metrics, workspace skip when none, queue assignment,
no-op without api key.
- `tests/Feature/Jobs/PostHog/TrackBillingTest.php` — capture payload,
SyncUser re-dispatch, missing-account/owner handling, api key gate.
- `tests/Feature/Jobs/PostHog/SendEventTest.php` — moved from
`tests/Feature/SendPostHogEventTest.php` and updated to new namespace.
- `tests/Unit/PostHogServiceTest.php` — adds coverage for the
account-aware capture (auto-attached `\$groups.account`, `account_id`,
`plan`) and the no-account branch.
- `tests/Feature/Listeners/StripeEventListenerTest.php` — replaces the
old inline-PostHog assertions with `Bus::fake([TrackBilling::class])`
and verifies the listener dispatches TrackBilling with the right
account id + event name for each subscription type, and skips
non-subscription event types.
- `tests/Feature/Actions/User/CreateUserTest.php` — verifies signup
dispatches `SyncUser` with the new user id.
Suite: 1427 passed (+20 net new, including the previous round of
metrics-related tests).
2026-05-07 12:41:36 +00:00
|
|
|
Queue::assertPushed(SendEvent::class, function ($job) {
|
2026-05-07 13:41:53 +00:00
|
|
|
return $job->method === 'groupIdentify'
|
|
|
|
|
&& $job->payload['groupType'] === 'workspace'
|
|
|
|
|
&& $job->payload['groupKey'] === 'ws-123';
|
2026-03-31 00:18:07 +00:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('group identify does not dispatch job when api key is missing', function () {
|
|
|
|
|
Queue::fake();
|
|
|
|
|
config(['services.posthog.api_key' => null]);
|
|
|
|
|
|
|
|
|
|
$service = new PostHogService;
|
|
|
|
|
$service->groupIdentify('workspace', 'ws-123', ['name' => 'Test']);
|
|
|
|
|
|
|
|
|
|
Queue::assertNothingPushed();
|
|
|
|
|
});
|
refactor: namespace PostHog jobs + extract billing tracking, add tests
Reorganises PostHog plumbing under `App\Jobs\PostHog` and extracts the
Stripe billing capture out of `StripeEventListener` into its own job.
Adds the missing test coverage that was promised but not delivered in
the previous commit.
Code changes:
- Move `app/Jobs/SendPostHogEvent.php` → `app/Jobs/PostHog/SendEvent.php`
(low-level dispatcher).
- Move `app/Jobs/SyncUserToPostHog.php` → `app/Jobs/PostHog/SyncUser.php`
(high-level user/account/workspace sync).
- New `app/Jobs/PostHog/TrackBilling.php` that owns the
capture('subscription.*') + SyncUser re-dispatch flow. Receives
account id + event name + payload, runs on the `posthog` queue.
- `StripeEventListener` slims down to a switch table mapping Stripe
event types to PostHog event names and dispatches `TrackBilling`. No
more inline tracking logic in the listener.
- `resources/js/posthog.ts` now owns `syncPostHogContext(page)` and
`capturePageview()`. `resources/js/app.ts` imports them — no behaviour
inlined in the bootstrap.
- `app/Services/PostHogService.php` and `app/Actions/User/CreateUser.php`
updated to the new namespaces.
Tests added/updated:
- `tests/Feature/Jobs/PostHog/SyncUserTest.php` — identify/group payload
shape, account metrics, workspace skip when none, queue assignment,
no-op without api key.
- `tests/Feature/Jobs/PostHog/TrackBillingTest.php` — capture payload,
SyncUser re-dispatch, missing-account/owner handling, api key gate.
- `tests/Feature/Jobs/PostHog/SendEventTest.php` — moved from
`tests/Feature/SendPostHogEventTest.php` and updated to new namespace.
- `tests/Unit/PostHogServiceTest.php` — adds coverage for the
account-aware capture (auto-attached `\$groups.account`, `account_id`,
`plan`) and the no-account branch.
- `tests/Feature/Listeners/StripeEventListenerTest.php` — replaces the
old inline-PostHog assertions with `Bus::fake([TrackBilling::class])`
and verifies the listener dispatches TrackBilling with the right
account id + event name for each subscription type, and skips
non-subscription event types.
- `tests/Feature/Actions/User/CreateUserTest.php` — verifies signup
dispatches `SyncUser` with the new user id.
Suite: 1427 passed (+20 net new, including the previous round of
metrics-related tests).
2026-05-07 12:41:36 +00:00
|
|
|
|
|
|
|
|
test('capture auto-attaches account groups when account is supplied', function () {
|
|
|
|
|
Queue::fake();
|
2026-05-07 15:42:35 +00:00
|
|
|
config(['services.posthog.enabled' => true, 'services.posthog.api_key' => 'phc_test_key']);
|
refactor: namespace PostHog jobs + extract billing tracking, add tests
Reorganises PostHog plumbing under `App\Jobs\PostHog` and extracts the
Stripe billing capture out of `StripeEventListener` into its own job.
Adds the missing test coverage that was promised but not delivered in
the previous commit.
Code changes:
- Move `app/Jobs/SendPostHogEvent.php` → `app/Jobs/PostHog/SendEvent.php`
(low-level dispatcher).
- Move `app/Jobs/SyncUserToPostHog.php` → `app/Jobs/PostHog/SyncUser.php`
(high-level user/account/workspace sync).
- New `app/Jobs/PostHog/TrackBilling.php` that owns the
capture('subscription.*') + SyncUser re-dispatch flow. Receives
account id + event name + payload, runs on the `posthog` queue.
- `StripeEventListener` slims down to a switch table mapping Stripe
event types to PostHog event names and dispatches `TrackBilling`. No
more inline tracking logic in the listener.
- `resources/js/posthog.ts` now owns `syncPostHogContext(page)` and
`capturePageview()`. `resources/js/app.ts` imports them — no behaviour
inlined in the bootstrap.
- `app/Services/PostHogService.php` and `app/Actions/User/CreateUser.php`
updated to the new namespaces.
Tests added/updated:
- `tests/Feature/Jobs/PostHog/SyncUserTest.php` — identify/group payload
shape, account metrics, workspace skip when none, queue assignment,
no-op without api key.
- `tests/Feature/Jobs/PostHog/TrackBillingTest.php` — capture payload,
SyncUser re-dispatch, missing-account/owner handling, api key gate.
- `tests/Feature/Jobs/PostHog/SendEventTest.php` — moved from
`tests/Feature/SendPostHogEventTest.php` and updated to new namespace.
- `tests/Unit/PostHogServiceTest.php` — adds coverage for the
account-aware capture (auto-attached `\$groups.account`, `account_id`,
`plan`) and the no-account branch.
- `tests/Feature/Listeners/StripeEventListenerTest.php` — replaces the
old inline-PostHog assertions with `Bus::fake([TrackBilling::class])`
and verifies the listener dispatches TrackBilling with the right
account id + event name for each subscription type, and skips
non-subscription event types.
- `tests/Feature/Actions/User/CreateUserTest.php` — verifies signup
dispatches `SyncUser` with the new user id.
Suite: 1427 passed (+20 net new, including the previous round of
metrics-related tests).
2026-05-07 12:41:36 +00:00
|
|
|
|
|
|
|
|
$plan = Plan::query()->where('slug', 'starter')->first();
|
|
|
|
|
$account = Account::factory()->create(['plan_id' => $plan?->id]);
|
|
|
|
|
|
|
|
|
|
$service = new PostHogService;
|
|
|
|
|
$service->capture('user-123', 'subscription.created', ['stripe_status' => 'active'], $account);
|
|
|
|
|
|
|
|
|
|
Queue::assertPushed(SendEvent::class, function ($job) use ($account, $plan) {
|
2026-05-07 13:41:53 +00:00
|
|
|
$payload = $job->payload;
|
refactor: namespace PostHog jobs + extract billing tracking, add tests
Reorganises PostHog plumbing under `App\Jobs\PostHog` and extracts the
Stripe billing capture out of `StripeEventListener` into its own job.
Adds the missing test coverage that was promised but not delivered in
the previous commit.
Code changes:
- Move `app/Jobs/SendPostHogEvent.php` → `app/Jobs/PostHog/SendEvent.php`
(low-level dispatcher).
- Move `app/Jobs/SyncUserToPostHog.php` → `app/Jobs/PostHog/SyncUser.php`
(high-level user/account/workspace sync).
- New `app/Jobs/PostHog/TrackBilling.php` that owns the
capture('subscription.*') + SyncUser re-dispatch flow. Receives
account id + event name + payload, runs on the `posthog` queue.
- `StripeEventListener` slims down to a switch table mapping Stripe
event types to PostHog event names and dispatches `TrackBilling`. No
more inline tracking logic in the listener.
- `resources/js/posthog.ts` now owns `syncPostHogContext(page)` and
`capturePageview()`. `resources/js/app.ts` imports them — no behaviour
inlined in the bootstrap.
- `app/Services/PostHogService.php` and `app/Actions/User/CreateUser.php`
updated to the new namespaces.
Tests added/updated:
- `tests/Feature/Jobs/PostHog/SyncUserTest.php` — identify/group payload
shape, account metrics, workspace skip when none, queue assignment,
no-op without api key.
- `tests/Feature/Jobs/PostHog/TrackBillingTest.php` — capture payload,
SyncUser re-dispatch, missing-account/owner handling, api key gate.
- `tests/Feature/Jobs/PostHog/SendEventTest.php` — moved from
`tests/Feature/SendPostHogEventTest.php` and updated to new namespace.
- `tests/Unit/PostHogServiceTest.php` — adds coverage for the
account-aware capture (auto-attached `\$groups.account`, `account_id`,
`plan`) and the no-account branch.
- `tests/Feature/Listeners/StripeEventListenerTest.php` — replaces the
old inline-PostHog assertions with `Bus::fake([TrackBilling::class])`
and verifies the listener dispatches TrackBilling with the right
account id + event name for each subscription type, and skips
non-subscription event types.
- `tests/Feature/Actions/User/CreateUserTest.php` — verifies signup
dispatches `SyncUser` with the new user id.
Suite: 1427 passed (+20 net new, including the previous round of
metrics-related tests).
2026-05-07 12:41:36 +00:00
|
|
|
|
|
|
|
|
return $payload['event'] === 'subscription.created'
|
|
|
|
|
&& $payload['properties']['$groups']['account'] === (string) $account->id
|
|
|
|
|
&& $payload['properties']['account_id'] === (string) $account->id
|
|
|
|
|
&& $payload['properties']['plan'] === $plan?->name
|
|
|
|
|
&& $payload['properties']['stripe_status'] === 'active';
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('capture without account does not attach group properties', function () {
|
|
|
|
|
Queue::fake();
|
2026-05-07 15:42:35 +00:00
|
|
|
config(['services.posthog.enabled' => true, 'services.posthog.api_key' => 'phc_test_key']);
|
refactor: namespace PostHog jobs + extract billing tracking, add tests
Reorganises PostHog plumbing under `App\Jobs\PostHog` and extracts the
Stripe billing capture out of `StripeEventListener` into its own job.
Adds the missing test coverage that was promised but not delivered in
the previous commit.
Code changes:
- Move `app/Jobs/SendPostHogEvent.php` → `app/Jobs/PostHog/SendEvent.php`
(low-level dispatcher).
- Move `app/Jobs/SyncUserToPostHog.php` → `app/Jobs/PostHog/SyncUser.php`
(high-level user/account/workspace sync).
- New `app/Jobs/PostHog/TrackBilling.php` that owns the
capture('subscription.*') + SyncUser re-dispatch flow. Receives
account id + event name + payload, runs on the `posthog` queue.
- `StripeEventListener` slims down to a switch table mapping Stripe
event types to PostHog event names and dispatches `TrackBilling`. No
more inline tracking logic in the listener.
- `resources/js/posthog.ts` now owns `syncPostHogContext(page)` and
`capturePageview()`. `resources/js/app.ts` imports them — no behaviour
inlined in the bootstrap.
- `app/Services/PostHogService.php` and `app/Actions/User/CreateUser.php`
updated to the new namespaces.
Tests added/updated:
- `tests/Feature/Jobs/PostHog/SyncUserTest.php` — identify/group payload
shape, account metrics, workspace skip when none, queue assignment,
no-op without api key.
- `tests/Feature/Jobs/PostHog/TrackBillingTest.php` — capture payload,
SyncUser re-dispatch, missing-account/owner handling, api key gate.
- `tests/Feature/Jobs/PostHog/SendEventTest.php` — moved from
`tests/Feature/SendPostHogEventTest.php` and updated to new namespace.
- `tests/Unit/PostHogServiceTest.php` — adds coverage for the
account-aware capture (auto-attached `\$groups.account`, `account_id`,
`plan`) and the no-account branch.
- `tests/Feature/Listeners/StripeEventListenerTest.php` — replaces the
old inline-PostHog assertions with `Bus::fake([TrackBilling::class])`
and verifies the listener dispatches TrackBilling with the right
account id + event name for each subscription type, and skips
non-subscription event types.
- `tests/Feature/Actions/User/CreateUserTest.php` — verifies signup
dispatches `SyncUser` with the new user id.
Suite: 1427 passed (+20 net new, including the previous round of
metrics-related tests).
2026-05-07 12:41:36 +00:00
|
|
|
|
|
|
|
|
$service = new PostHogService;
|
|
|
|
|
$service->capture('user-123', 'page.viewed', ['url' => '/dashboard']);
|
|
|
|
|
|
|
|
|
|
Queue::assertPushed(SendEvent::class, function ($job) {
|
2026-05-07 13:41:53 +00:00
|
|
|
$properties = $job->payload['properties'];
|
refactor: namespace PostHog jobs + extract billing tracking, add tests
Reorganises PostHog plumbing under `App\Jobs\PostHog` and extracts the
Stripe billing capture out of `StripeEventListener` into its own job.
Adds the missing test coverage that was promised but not delivered in
the previous commit.
Code changes:
- Move `app/Jobs/SendPostHogEvent.php` → `app/Jobs/PostHog/SendEvent.php`
(low-level dispatcher).
- Move `app/Jobs/SyncUserToPostHog.php` → `app/Jobs/PostHog/SyncUser.php`
(high-level user/account/workspace sync).
- New `app/Jobs/PostHog/TrackBilling.php` that owns the
capture('subscription.*') + SyncUser re-dispatch flow. Receives
account id + event name + payload, runs on the `posthog` queue.
- `StripeEventListener` slims down to a switch table mapping Stripe
event types to PostHog event names and dispatches `TrackBilling`. No
more inline tracking logic in the listener.
- `resources/js/posthog.ts` now owns `syncPostHogContext(page)` and
`capturePageview()`. `resources/js/app.ts` imports them — no behaviour
inlined in the bootstrap.
- `app/Services/PostHogService.php` and `app/Actions/User/CreateUser.php`
updated to the new namespaces.
Tests added/updated:
- `tests/Feature/Jobs/PostHog/SyncUserTest.php` — identify/group payload
shape, account metrics, workspace skip when none, queue assignment,
no-op without api key.
- `tests/Feature/Jobs/PostHog/TrackBillingTest.php` — capture payload,
SyncUser re-dispatch, missing-account/owner handling, api key gate.
- `tests/Feature/Jobs/PostHog/SendEventTest.php` — moved from
`tests/Feature/SendPostHogEventTest.php` and updated to new namespace.
- `tests/Unit/PostHogServiceTest.php` — adds coverage for the
account-aware capture (auto-attached `\$groups.account`, `account_id`,
`plan`) and the no-account branch.
- `tests/Feature/Listeners/StripeEventListenerTest.php` — replaces the
old inline-PostHog assertions with `Bus::fake([TrackBilling::class])`
and verifies the listener dispatches TrackBilling with the right
account id + event name for each subscription type, and skips
non-subscription event types.
- `tests/Feature/Actions/User/CreateUserTest.php` — verifies signup
dispatches `SyncUser` with the new user id.
Suite: 1427 passed (+20 net new, including the previous round of
metrics-related tests).
2026-05-07 12:41:36 +00:00
|
|
|
|
|
|
|
|
return ! array_key_exists('$groups', $properties)
|
|
|
|
|
&& ! array_key_exists('account_id', $properties)
|
|
|
|
|
&& ! array_key_exists('plan', $properties);
|
|
|
|
|
});
|
|
|
|
|
});
|
2026-05-07 16:21:58 +00:00
|
|
|
|
|
|
|
|
// ========================================
|
|
|
|
|
// Self-hosted contract: enabled=false dominates regardless of api key
|
|
|
|
|
// ========================================
|
|
|
|
|
//
|
|
|
|
|
// These cover the exact scenario the open-source gate exists to defend
|
|
|
|
|
// against: a self-hosted install that inherited POSTHOG_API_KEY from an
|
|
|
|
|
// example env file but did NOT explicitly opt in via POSTHOG_ENABLED.
|
|
|
|
|
|
|
|
|
|
test('capture is a no-op when enabled is false even with an api key set', function () {
|
|
|
|
|
Queue::fake();
|
|
|
|
|
config(['services.posthog.enabled' => false, 'services.posthog.api_key' => 'phc_inherited_key']);
|
|
|
|
|
|
|
|
|
|
$service = new PostHogService;
|
|
|
|
|
$service->capture('user-123', 'test_event');
|
|
|
|
|
|
|
|
|
|
Queue::assertNothingPushed();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('identify is a no-op when enabled is false even with an api key set', function () {
|
|
|
|
|
Queue::fake();
|
|
|
|
|
config(['services.posthog.enabled' => false, 'services.posthog.api_key' => 'phc_inherited_key']);
|
|
|
|
|
|
|
|
|
|
$service = new PostHogService;
|
|
|
|
|
$service->identify('user-123', ['$email' => 'test@example.com']);
|
|
|
|
|
|
|
|
|
|
Queue::assertNothingPushed();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('groupIdentify is a no-op when enabled is false even with an api key set', function () {
|
|
|
|
|
Queue::fake();
|
|
|
|
|
config(['services.posthog.enabled' => false, 'services.posthog.api_key' => 'phc_inherited_key']);
|
|
|
|
|
|
|
|
|
|
$service = new PostHogService;
|
|
|
|
|
$service->groupIdentify('account', 'acc-123', ['name' => 'Test']);
|
|
|
|
|
|
|
|
|
|
Queue::assertNothingPushed();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('isEnabled requires both enabled and api key', function () {
|
|
|
|
|
config(['services.posthog.enabled' => true, 'services.posthog.api_key' => null]);
|
|
|
|
|
expect(PostHogService::isEnabled())->toBeFalse();
|
|
|
|
|
|
|
|
|
|
config(['services.posthog.enabled' => false, 'services.posthog.api_key' => 'phc_x']);
|
|
|
|
|
expect(PostHogService::isEnabled())->toBeFalse();
|
|
|
|
|
|
|
|
|
|
config(['services.posthog.enabled' => true, 'services.posthog.api_key' => 'phc_x']);
|
|
|
|
|
expect(PostHogService::isEnabled())->toBeTrue();
|
|
|
|
|
});
|