trypost/tests/Feature
Paulo Castellano ca1e346227
feat: capture ad click IDs for ad-platform conversion attribution (#276)
* feat: capture ad click IDs for Meta/Google/LinkedIn/TikTok/Reddit/Pinterest attribution

Adds gclid, fbclid, li_fat_id, ttclid, rdt_cid, and epik columns to users,
captured the same way UTM parameters already are (query string -> session
-> persisted on signup, surviving the OAuth redirect round-trip via the
new PreservesClickIds trait).

Forwards them as first-touch ($set_once) PostHog person properties in
SyncUser, so PostHog's native ad-platform destinations (Meta Ads
Conversions API, Google Ads Conversions, LinkedIn Ads, TikTok Ads, Reddit
Ads, Pinterest) have first-party click IDs to match conversions back to
the originating ad click.

* refactor: unify PreservesUtmParameters and PreservesClickIds into one trait

Both traits captured a set of query-string keys into the session and
retrieved them at signup, with identical extract/store/retrieve logic and
every call site always using both together — the split added no real
separation, just duplicated the same mechanism twice.

PreservesAttributionParameters replaces both with a single ATTRIBUTION_KEYS
list and one session key. Adding a future ad network's click ID is now one
line in that list instead of a second trait.

* refactor: split UTM_KEYS and CLICK_ID_KEYS into separate constants

Same single trait, single session key, single extract/store/retrieve
mechanism — just two named arrays instead of one merged list, so it's
clear at a glance which key belongs to which category.

* fix: don't truncate ad click IDs to 255 chars, only UTM parameters

Ad platforms explicitly warn against assuming a fixed max length for
click IDs (Google: gclid has already grown from 26 to 100+ chars, and
their docs say never truncate or validate against a fixed length).
Truncating would silently corrupt the value into something that no
longer matches the real click ID, which is worse than not capturing it
at all.

Widens the click-id columns from string (VARCHAR 255) to text — safe to
edit the migration in place since it hasn't shipped to production yet.
UTM parameters still get truncated to 255, since those are ours (our own
campaign URLs) and the column stays VARCHAR(255).

* refactor: use Laravel collection/Str helpers, forward UTMs to PostHog too

- extractAttributionParameters now reads through collect()/Str::limit()
  instead of raw array_filter/array_map/mb_substr; storeAttributionParameters
  drops its now-redundant emptiness check since retrieveAttributionParameters
  already treats "absent" and "present-but-empty" the same via pull()'s
  default.
- SyncUser forwards utm_source/medium/campaign/term/content alongside the
  click ids as first-touch ($set_once) PostHog person properties. UTMs were
  never sent to PostHog before this, on any prior code — now that PostHog is
  the source of truth for ad-platform attribution, it should have the full
  picture, not just click ids.
- Adds the missing GitHub-existing-user click-id session test, mirroring
  the Google one (parity with the existing UTM coverage).

* fix: 3 issues found by review — empty-string leak, duplicated key list, comment style

- extractAttributionParameters no longer keeps an empty-string value (e.g.
  ?utm_source=&gclid=, which some ad/email templates always append even
  for unfilled slots). The refactor to collect()/Str::limit() a few
  commits back dropped the outer array_filter() that used to strip these,
  so they were slipping into User::create() as '' instead of staying
  null. Restored via a trailing ->filter() on the merged result, and
  extended the same protection to click ids (which never had it, even
  before that refactor).
- New App\Support\AttributionKeys centralizes the UTM_KEYS/CLICK_ID_KEYS
  lists that PreservesAttributionParameters and SyncUser each maintained
  independently. SyncUser previously hand-listed the same 11 field names
  as a second array with no shared source of truth — a future ad network
  added to the trait would silently never reach PostHog unless someone
  remembered to update this second copy too.
- Removed the // comment block from the click-id migration explaining the
  text-column rationale — CLAUDE.md's PHP rules reserve inline comments
  for exceptionally complex logic; the rationale already lives in the
  commit message that introduced it.
2026-08-11 15:07:23 -03:00
..
Actions Activation checklist + MCP OAuth authorize UX (#239) (#250) 2026-08-07 20:34:43 -03:00
Ai Fix OpenRouter via laravel/ai default provider (revises #216) (#220) 2026-08-10 21:38:50 -03:00
Api fix: sanitize invalid UTF-8 bytes in uploaded filenames (#265) 2026-08-09 14:29:29 -03:00
App/Post refactor(posts): return the card display domain from the backend 2026-07-17 16:24:23 -03:00
Auth feat: capture ad click IDs for ad-platform conversion attribution (#276) 2026-08-11 15:07:23 -03:00
Automation Enforce carousel min slides in Generate and expose min_media_count on API/MCP. 2026-07-24 23:51:10 -03:00
Billing Make Stripe Checkout configurable via billing env knobs (#252) 2026-08-07 21:25:32 -03:00
Brand Structure brand voice and make generated copy platform-aware 2026-06-12 17:09:39 -03:00
Commands chore: remove LinkedIn Page post URL backfill command (#273) 2026-08-11 14:09:14 -03:00
Database test(auth): cover remaining surfaces of the registration gate 2026-05-19 12:03:41 -03:00
Discord fix(channels): restrict Discord picker to postable channels the bot can use 2026-06-16 16:34:02 -03:00
Image Harden automations module: full-post generation, reliable runs, editor UX 2026-06-10 20:45:01 -03:00
Jobs feat: capture ad click IDs for ad-platform conversion attribution (#276) 2026-08-11 15:07:23 -03:00
Listeners Capture post.created to PostHog with created_via. 2026-07-24 11:44:47 -03:00
Mail feat: proactive connection check for at-risk posts + SocialAccount name centralization (#256) 2026-08-09 11:10:39 -03:00
Mcp Activation checklist + MCP OAuth authorize UX (#239) (#250) 2026-08-07 20:34:43 -03:00
Middleware Activation checklist + MCP OAuth authorize UX (#239) (#250) 2026-08-07 20:34:43 -03:00
Migrations Activation checklist + MCP OAuth authorize UX (#239) (#250) 2026-08-07 20:34:43 -03:00
Models refactor(billing): replace MonthlyCreditsLimit Pennant feature with BillingCycle 2026-06-22 09:55:03 -03:00
Observers fix: eager-load workspace.account in SocialAccountObserver to prevent lazy loading crash (#259) 2026-08-09 11:39:30 -03:00
Onboarding Activation checklist + MCP OAuth authorize UX (#239) (#250) 2026-08-07 20:34:43 -03:00
Passport fix: stop reporting client-error OAuth exceptions to Nightwatch (#261) 2026-08-09 12:01:12 -03:00
Permissions Allow account owners to delete workspaces (#208) 2026-08-01 13:00:38 -04:00
Services fix: LinkedIn company page post URL missing the post ID (#272) 2026-08-11 12:36:14 -03:00
Settings Add Ukrainian as a supported platform language (#219) 2026-08-05 19:45:30 -03:00
Social feat: proactive connection check for at-risk posts + SocialAccount name centralization (#256) 2026-08-09 11:10:39 -03:00
SocialAccount fix: merge-readiness — close two billing/network bugs, harden tests 2026-06-22 09:26:31 -03:00
Welcome Activation checklist + MCP OAuth authorize UX (#239) (#250) 2026-08-07 20:34:43 -03:00
Workspace feat: @mentions in comments, AI Action layer + MCP tools, settings tabs 2026-05-01 20:59:03 -03:00
AcceptInviteControllerTest.php Allow account owners to delete workspaces (#208) 2026-08-01 13:00:38 -04:00
AccountControllerTest.php refactor: reorganize settings UI, migrate post templates to a file-based registry, and remove legacy video generation features 2026-05-03 13:44:13 -03:00
AiUsageLogTest.php feat: per-workspace pricing, onboarding, and billing overhaul 2026-06-21 20:40:03 -03:00
ApiKeyControllerTest.php Scope MCP OAuth tokens to user + workspace (#222) (#245) 2026-08-06 21:59:34 -03:00
AssetControllerTest.php fix: chunked upload session collision + workspace name i18n (#263) 2026-08-09 14:06:47 -03:00
BackfillMcpOAuthWorkspaceTest.php Scope MCP OAuth tokens to user + workspace (#222) (#245) 2026-08-06 21:59:34 -03:00
BillingControllerTest.php Activation checklist + MCP OAuth authorize UX (#239) (#250) 2026-08-07 20:34:43 -03:00
ChunkedAssetReceiverTest.php fix: chunked upload session collision + workspace name i18n (#263) 2026-08-09 14:06:47 -03:00
ChunkedCloudUploadTest.php fix: chunked upload session collision + workspace name i18n (#263) 2026-08-09 14:06:47 -03:00
ChunkedUploadFilenameEncodingTest.php fix: chunked upload session collision + workspace name i18n (#263) 2026-08-09 14:06:47 -03:00
ContentTypeMediaRulesShareTest.php Restore media-rule parity and release failed MCP upload tokens. 2026-07-24 22:37:57 -03:00
ExampleTest.php refactor: settings redesign, Spanish translations, language system, strict_types 2026-03-30 00:20:43 -03:00
GitHubAuthToggleTest.php feat(auth): self-hosted registration gate + admin seeder (closes #46) 2026-05-19 11:45:16 -03:00
GoogleAuthToggleTest.php feat(auth): self-hosted registration gate + admin seeder (closes #46) 2026-05-19 11:45:16 -03:00
InstagramFacebookIntegrationTest.php feat: add PostCommentCreated broadcast event 2026-04-15 20:11:36 -03:00
InvitePermissionTest.php refactor: address code review for per-workspace pricing 2026-06-21 21:28:47 -03:00
LocalizationParityTest.php Allow account owners to delete workspaces (#208) 2026-08-01 13:00:38 -04:00
McpOAuthWorkspaceBindingTest.php Activation checklist + MCP OAuth authorize UX (#239) (#250) 2026-08-07 20:34:43 -03:00
McpSettingsControllerTest.php Scope MCP OAuth tokens to user + workspace (#222) (#245) 2026-08-06 21:59:34 -03:00
NotificationControllerTest.php refactor: remove onboarding flow, implement brand analysis services, and replace setup middleware with account readiness checks 2026-04-16 23:05:51 -03:00
PlanSeederTest.php feat: per-workspace pricing, onboarding, and billing overhaul 2026-06-21 20:40:03 -03:00
PlanTest.php chore: remove legacy plan tiers (starter/plus/pro/max) 2026-06-22 15:31:32 -03:00
PostCommentControllerTest.php feat: @mentions in comments, AI Action layer + MCP tools, settings tabs 2026-05-01 20:59:03 -03:00
PostControllerTest.php fix: keep post drafts unscheduled by default (#209) 2026-08-01 17:39:18 -03:00
PostMediaAltTextValidationTest.php Harden per-image alt text across publishers, validation, and attach paths 2026-07-16 13:55:33 -03:00
PostSearchTest.php refactor: remove onboarding flow, implement brand analysis services, and replace setup middleware with account readiness checks 2026-04-16 23:05:51 -03:00
PostTemplateControllerTest.php fix: keep post drafts unscheduled by default (#209) 2026-08-01 17:39:18 -03:00
SignupSuccessControllerTest.php refactor: code review fixes — policies, enums, data_get, tests 2026-03-31 00:40:18 -03:00
SocialAccountModelTest.php Keep Instagram/Threads tokens extended while still valid 2026-07-03 09:18:45 -03:00
SocialAccountToggleTest.php refactor: remove onboarding flow, implement brand analysis services, and replace setup middleware with account readiness checks 2026-04-16 23:05:51 -03:00
SocialControllerTest.php Fix Facebook Page connect pagination (#212) (#253) 2026-08-08 12:01:46 -03:00
SocialLoginControllerTest.php refactor: address third-pass review nits 2026-06-22 08:39:25 -03:00
TikTokI18nStringsTest.php fix(tiktok): align UX strings and render processing notice for app review 2026-05-29 10:16:04 -03:00
UpdatePostRequestTest.php Add optional Pinterest pin title and destination link (#232) 2026-08-05 18:15:50 -03:00
UsageControllerTest.php refactor: reorganize settings UI, migrate post templates to a file-based registry, and remove legacy video generation features 2026-05-03 13:44:13 -03:00
VerifyWorkspaceConnectionsTest.php fix(social): move Mastodon default instance to config + cleanup 2026-05-19 08:27:46 -03:00
WorkspaceBillingTest.php Welcome: pre-subscription funnel and member subscription-required screen (#243) 2026-08-06 11:34:50 -03:00
WorkspaceControllerTest.php Fix OpenRouter via laravel/ai default provider (revises #216) (#220) 2026-08-10 21:38:50 -03:00
WorkspaceInviteControllerTest.php Scope MCP OAuth tokens to user + workspace (#222) (#245) 2026-08-06 21:59:34 -03:00
WorkspaceInviteGuardTest.php Allow account owners to delete workspaces (#208) 2026-08-01 13:00:38 -04:00
WorkspaceLabelControllerTest.php refactor: remove onboarding flow, implement brand analysis services, and replace setup middleware with account readiness checks 2026-04-16 23:05:51 -03:00
WorkspaceSignatureControllerTest.php chore: remove deprecated localization files 2026-05-03 15:23:25 -03:00
YouTubeAnalyticsTest.php feat: proactive connection check for at-risk posts + SocialAccount name centralization (#256) 2026-08-09 11:10:39 -03:00