@@ -42,6 +42,11 @@ jest.mock('@/lib/credit-campaigns', () => ({
4242
4343import { getAffiliateAttribution } from '@/lib/affiliate-attribution' ;
4444import { recordAffiliateAttributionAndQueueParentEvent } from '@/lib/impact/affiliate-events' ;
45+ import {
46+ queueImpactAdvocateParticipantRegistration ,
47+ recordImpactAffiliateTouch ,
48+ recordImpactReferralTouch ,
49+ } from '@/lib/impact/referral' ;
4550import { getUserFromAuth } from '@/lib/user/server' ;
4651import { GET } from './route' ;
4752
@@ -50,6 +55,11 @@ const mockRecordAffiliateAttributionAndQueueParentEvent = jest.mocked(
5055 recordAffiliateAttributionAndQueueParentEvent
5156) ;
5257const mockGetUserFromAuth = jest . mocked ( getUserFromAuth ) ;
58+ const mockQueueImpactAdvocateParticipantRegistration = jest . mocked (
59+ queueImpactAdvocateParticipantRegistration
60+ ) ;
61+ const mockRecordImpactAffiliateTouch = jest . mocked ( recordImpactAffiliateTouch ) ;
62+ const mockRecordImpactReferralTouch = jest . mocked ( recordImpactReferralTouch ) ;
5363
5464describe ( 'GET /users/after-sign-in' , ( ) => {
5565 beforeEach ( ( ) => {
@@ -64,6 +74,81 @@ describe('GET /users/after-sign-in', () => {
6474 } as Awaited < ReturnType < typeof getUserFromAuth > > ) ;
6575 } ) ;
6676
77+ it ( 'records and queues Kilo Pass referral touches from Kilo Pass referral-page callback paths' , async ( ) => {
78+ const response = await GET (
79+ new NextRequest (
80+ 'http://localhost:3000/users/after-sign-in?callbackPath=%2Fsubscriptions%2Fkilo-pass%2Frefer&_saasquatch=pass-cookie&rsCode=PASSCODE'
81+ )
82+ ) ;
83+
84+ expect ( response . status ) . toBe ( 307 ) ;
85+ expect ( response . headers . get ( 'location' ) ) . toBe (
86+ 'http://localhost:3000/subscriptions/kilo-pass/refer'
87+ ) ;
88+ expect ( mockRecordImpactReferralTouch ) . toHaveBeenCalledWith (
89+ expect . objectContaining ( {
90+ userId : 'user-after-sign-in' ,
91+ touch : expect . objectContaining ( {
92+ product : 'kilo_pass' ,
93+ programKey : 'kilo_pass' ,
94+ opaqueTrackingValue : 'pass-cookie' ,
95+ } ) ,
96+ } )
97+ ) ;
98+ expect ( mockQueueImpactAdvocateParticipantRegistration ) . toHaveBeenCalledWith (
99+ expect . objectContaining ( {
100+ user : expect . objectContaining ( { id : 'user-after-sign-in' } ) ,
101+ referralTouch : expect . objectContaining ( {
102+ product : 'kilo_pass' ,
103+ programKey : 'kilo_pass' ,
104+ opaqueTrackingValue : 'pass-cookie' ,
105+ } ) ,
106+ } )
107+ ) ;
108+ } ) ;
109+
110+ it ( 'records Kilo Pass affiliate touches from Kilo Pass callback paths' , async ( ) => {
111+ const response = await GET (
112+ new NextRequest (
113+ 'http://localhost:3000/users/after-sign-in?callbackPath=%2Fsubscriptions%2Fkilo-pass&im_ref=impact-click'
114+ )
115+ ) ;
116+
117+ expect ( response . status ) . toBe ( 307 ) ;
118+ expect ( mockRecordImpactAffiliateTouch ) . toHaveBeenCalledWith (
119+ expect . objectContaining ( {
120+ userId : 'user-after-sign-in' ,
121+ product : 'kilo_pass' ,
122+ touch : expect . objectContaining ( {
123+ product : 'kilo_pass' ,
124+ trackingId : 'impact-click' ,
125+ } ) ,
126+ } )
127+ ) ;
128+ } ) ;
129+
130+ it ( 'preserves Impact tracking parameters through unauthenticated OAuth redirects' , async ( ) => {
131+ mockGetUserFromAuth . mockResolvedValueOnce ( { user : null } as Awaited <
132+ ReturnType < typeof getUserFromAuth >
133+ > ) ;
134+
135+ const response = await GET (
136+ new NextRequest (
137+ 'http://localhost:3000/users/after-sign-in?callbackPath=%2Fsubscriptions%2Fkilo-pass%2Frefer&signup=true&_saasquatch=pass-cookie&rsCode=PASSCODE&im_ref=impact-click&utm_campaign=launch'
138+ )
139+ ) ;
140+
141+ expect ( response . status ) . toBe ( 307 ) ;
142+ const location = new URL ( response . headers . get ( 'location' ) ?? '' ) ;
143+ expect ( location . pathname ) . toBe ( '/users/sign_in' ) ;
144+ expect ( location . searchParams . get ( 'callbackPath' ) ) . toBe ( '/subscriptions/kilo-pass/refer' ) ;
145+ expect ( location . searchParams . get ( 'signup' ) ) . toBe ( 'true' ) ;
146+ expect ( location . searchParams . get ( '_saasquatch' ) ) . toBe ( 'pass-cookie' ) ;
147+ expect ( location . searchParams . get ( 'rsCode' ) ) . toBe ( 'PASSCODE' ) ;
148+ expect ( location . searchParams . get ( 'im_ref' ) ) . toBe ( 'impact-click' ) ;
149+ expect ( location . searchParams . get ( 'utm_campaign' ) ) . toBe ( 'launch' ) ;
150+ } ) ;
151+
67152 it ( 'continues redirect flow when affiliate attribution lookup fails' , async ( ) => {
68153 const consoleError = jest . spyOn ( console , 'error' ) . mockImplementation ( ( ) => { } ) ;
69154 mockGetAffiliateAttribution . mockRejectedValueOnce ( new Error ( 'affiliate lookup unavailable' ) ) ;
0 commit comments