@@ -7,6 +7,21 @@ import type { ApiUser } from '@lib/api';
77jest . mock ( 'posthog-node' ) ;
88jest . mock ( 'uuid' ) ;
99
10+ // IS_PRODUCTION_BUILD is read live (property access) in the Analytics
11+ // constructor, so a getter backed by this mutable flag lets a test flip the
12+ // build type without re-importing the module. Defaults falsy → 'dev',
13+ // matching every other test. `var` (not `let`) so the hoisted jest.mock
14+ // factory can read it at import time without hitting the temporal dead zone;
15+ // the `mock` prefix satisfies jest's hoisting rule.
16+ // eslint-disable-next-line no-var
17+ var mockIsProductionBuild = false ;
18+ jest . mock ( '@env' , ( ) => ( {
19+ ...jest . requireActual ( '@env' ) ,
20+ get IS_PRODUCTION_BUILD ( ) {
21+ return mockIsProductionBuild ;
22+ } ,
23+ } ) ) ;
24+
1025const mockUuidv4 = uuidv4 as jest . MockedFunction < typeof uuidv4 > ;
1126const MockedPostHog = PostHog as jest . MockedClass < typeof PostHog > ;
1227
@@ -16,7 +31,18 @@ describe('Analytics', () => {
1631
1732 beforeEach ( ( ) => {
1833 jest . clearAllMocks ( ) ;
19- mockUuidv4 . mockReturnValue ( 'test-uuid' as any ) ;
34+ mockIsProductionBuild = false ;
35+ // Each run mints several distinct uuids; mock them to different values
36+ // so the tests reflect reality (run_id !== $session_id) rather than
37+ // collapsing them. Call order: anonymousId, runId (both in the
38+ // constructor), then sessionId (lazily, on first identify).
39+ let uuidCall = 0 ;
40+ mockUuidv4 . mockImplementation ( ( ( ) => {
41+ uuidCall += 1 ;
42+ if ( uuidCall === 1 ) return 'test-uuid' ; // anonymousId
43+ if ( uuidCall === 2 ) return 'run-uuid' ; // runId
44+ return 'session-uuid' ; // sessionId (first identify)
45+ } ) as any ) ;
2046
2147 mockPostHogInstance = {
2248 capture : jest . fn ( ) ,
@@ -45,6 +71,7 @@ describe('Analytics', () => {
4571 team : ANALYTICS_TEAM_TAG ,
4672 $app_name : 'wizard' ,
4773 build : 'dev' ,
74+ run_id : 'run-uuid' ,
4875 ...properties ,
4976 } ,
5077 ) ;
@@ -64,6 +91,7 @@ describe('Analytics', () => {
6491 team : ANALYTICS_TEAM_TAG ,
6592 $app_name : 'wizard' ,
6693 build : 'dev' ,
94+ run_id : 'run-uuid' ,
6795 testTag : 'testValue' ,
6896 ...properties ,
6997 } ,
@@ -84,6 +112,8 @@ describe('Analytics', () => {
84112 team : ANALYTICS_TEAM_TAG ,
85113 $app_name : 'wizard' ,
86114 build : 'dev' ,
115+ run_id : 'run-uuid' ,
116+ $session_id : 'session-uuid' ,
87117 } ,
88118 ) ;
89119 } ) ;
@@ -100,6 +130,7 @@ describe('Analytics', () => {
100130 team : ANALYTICS_TEAM_TAG ,
101131 $app_name : 'wizard' ,
102132 build : 'dev' ,
133+ run_id : 'run-uuid' ,
103134 } ,
104135 ) ;
105136 } ) ;
@@ -119,6 +150,7 @@ describe('Analytics', () => {
119150 team : ANALYTICS_TEAM_TAG ,
120151 $app_name : 'wizard' ,
121152 build : 'dev' ,
153+ run_id : 'run-uuid' ,
122154 environment : 'test' ,
123155 version : '1.0.0' ,
124156 integration : 'nextjs' ,
@@ -141,6 +173,7 @@ describe('Analytics', () => {
141173 team : ANALYTICS_TEAM_TAG ,
142174 $app_name : 'wizard' ,
143175 build : 'dev' ,
176+ run_id : 'run-uuid' ,
144177 integration : 'react' ,
145178 } ,
146179 ) ;
@@ -158,11 +191,37 @@ describe('Analytics', () => {
158191 team : ANALYTICS_TEAM_TAG ,
159192 $app_name : 'wizard' ,
160193 build : 'dev' ,
194+ run_id : 'run-uuid' ,
161195 } ,
162196 ) ;
163197 } ) ;
164198 } ) ;
165199
200+ describe ( 'build tag' , ( ) => {
201+ it ( "tags dev/test runs as 'dev'" , ( ) => {
202+ analytics . captureException ( new Error ( 'e' ) ) ;
203+
204+ expect (
205+ ( mockPostHogInstance . captureException as jest . Mock ) . mock . calls . at (
206+ - 1 ,
207+ ) ?. [ 2 ] ,
208+ ) . toMatchObject ( { build : 'dev' } ) ;
209+ } ) ;
210+
211+ it ( "tags production builds as 'prod'" , ( ) => {
212+ mockIsProductionBuild = true ;
213+ const prodAnalytics = new Analytics ( ) ;
214+
215+ prodAnalytics . captureException ( new Error ( 'e' ) ) ;
216+
217+ expect (
218+ ( mockPostHogInstance . captureException as jest . Mock ) . mock . calls . at (
219+ - 1 ,
220+ ) ?. [ 2 ] ,
221+ ) . toMatchObject ( { build : 'prod' } ) ;
222+ } ) ;
223+ } ) ;
224+
166225 describe ( 'identifyUser' , ( ) => {
167226 const user = {
168227 distinct_id : 'user-123' ,
@@ -209,6 +268,24 @@ describe('Analytics', () => {
209268 expect ( mockPostHogInstance . alias ) . not . toHaveBeenCalled ( ) ;
210269 } ) ;
211270
271+ it ( 'opens the session ($session_id) only once the user is identified' , ( ) => {
272+ const error = new Error ( 'e' ) ;
273+
274+ // Pre-login: run_id is present, $session_id is not.
275+ analytics . captureException ( error ) ;
276+ const beforeLogin = ( mockPostHogInstance . captureException as jest . Mock )
277+ . mock . calls [ 0 ] [ 2 ] ;
278+ expect ( beforeLogin ) . toMatchObject ( { run_id : 'run-uuid' } ) ;
279+ expect ( beforeLogin ) . not . toHaveProperty ( '$session_id' ) ;
280+
281+ // Post-login: both ids ride along.
282+ analytics . identifyUser ( { distinct_id : 'user-123' } as unknown as ApiUser ) ;
283+ analytics . captureException ( error ) ;
284+ expect (
285+ ( mockPostHogInstance . captureException as jest . Mock ) . mock . calls [ 1 ] [ 2 ] ,
286+ ) . toMatchObject ( { run_id : 'run-uuid' , $session_id : 'session-uuid' } ) ;
287+ } ) ;
288+
212289 it ( 'omits person properties the user does not have' , ( ) => {
213290 analytics . identifyUser ( {
214291 distinct_id : 'user-123' ,
@@ -249,6 +326,7 @@ describe('Analytics', () => {
249326 expect ( result ?. properties ) . toEqual ( {
250327 $app_name : 'wizard' ,
251328 build : 'dev' ,
329+ run_id : 'run-uuid' ,
252330 command : 'slack' ,
253331 $exception_list : [ { type : 'Error' } ] ,
254332 } ) ;
@@ -411,6 +489,7 @@ describe('Analytics', () => {
411489 team : ANALYTICS_TEAM_TAG ,
412490 $app_name : 'wizard' ,
413491 build : 'dev' ,
492+ run_id : 'run-uuid' ,
414493 integration : 'nextjs' ,
415494 localMcp : true ,
416495 debug : false ,
@@ -435,6 +514,8 @@ describe('Analytics', () => {
435514 team : ANALYTICS_TEAM_TAG ,
436515 $app_name : 'wizard' ,
437516 build : 'dev' ,
517+ run_id : 'run-uuid' ,
518+ $session_id : 'session-uuid' ,
438519 integration : 'svelte' ,
439520 } ,
440521 ) ;
0 commit comments