@@ -5,23 +5,30 @@ import {mockAndCaptureOutput} from './testing/output.js'
55import * as error from './error.js'
66import { hashString } from '../../public/node/crypto.js'
77import { isLocalEnvironment } from '../../private/node/context/service.js'
8+ import { getLastSeenUserIdAfterAuth } from '../../private/node/session.js'
89import { settings } from '@oclif/core'
910import { beforeEach , describe , expect , test , vi } from 'vitest'
1011
1112const onNotify = vi . fn ( )
13+ const capturedEventHandler = vi . fn ( )
1214let lastBugsnagEvent : { addMetadata : ReturnType < typeof vi . fn > } | undefined
1315
1416vi . mock ( 'process' )
1517vi . mock ( '@bugsnag/js' , ( ) => {
1618 return {
1719 default : {
18- notify : ( reportedError : any , args : any , callback : any ) => {
20+ notify : ( reportedError : any , eventHandler : any , callback : any ) => {
1921 onNotify ( reportedError )
20- if ( typeof args === 'function' ) {
21- const event = { addMetadata : vi . fn ( ) }
22- lastBugsnagEvent = event as any
23- args ( event )
22+ // Create a mock event to pass to the event handler
23+ const mockEvent = {
24+ severity : '' ,
25+ unhandled : false ,
26+ setUser : vi . fn ( ) ,
27+ addMetadata : vi . fn ( ) ,
2428 }
29+ eventHandler ( mockEvent )
30+ capturedEventHandler ( mockEvent )
31+ lastBugsnagEvent = mockEvent as any
2532 callback ( null )
2633 } ,
2734 isStarted : ( ) => true ,
@@ -33,6 +40,7 @@ vi.mock('./cli.js')
3340vi . mock ( './context/local.js' )
3441vi . mock ( '../../public/node/crypto.js' )
3542vi . mock ( '../../private/node/context/service.js' )
43+ vi . mock ( '../../private/node/session.js' )
3644vi . mock ( '@oclif/core' , ( ) => ( {
3745 settings : {
3846 debug : false ,
@@ -47,9 +55,11 @@ beforeEach(() => {
4755 vi . mocked ( hashString ) . mockReturnValue ( 'hashed-macaddress' )
4856 vi . mocked ( isUnitTest ) . mockReturnValue ( true )
4957 onNotify . mockClear ( )
58+ capturedEventHandler . mockClear ( )
5059 lastBugsnagEvent = undefined
5160 vi . mocked ( settings ) . debug = false
5261 vi . mocked ( isLocalEnvironment ) . mockReturnValue ( false )
62+ vi . mocked ( getLastSeenUserIdAfterAuth ) . mockResolvedValue ( 'test-user-id-123' )
5363} )
5464
5565describe ( 'errorHandler' , async ( ) => {
@@ -168,6 +178,8 @@ describe('skips sending errors to Bugsnag', () => {
168178describe ( 'sends errors to Bugsnag' , ( ) => {
169179 test ( 'processes Error instances as unhandled' , async ( ) => {
170180 const toThrow = new Error ( 'In test' )
181+ capturedEventHandler . mockClear ( )
182+
171183 const res = await sendErrorToBugsnag ( toThrow , 'unexpected_error' )
172184 expect ( res . reported ) . toEqual ( true )
173185 expect ( res . unhandled ) . toEqual ( true )
@@ -217,6 +229,43 @@ describe('sends errors to Bugsnag', () => {
217229 expect ( mockOutput . debug ( ) ) . toMatch ( 'Error reporting to Bugsnag: Error: Bugsnag is down' )
218230 } )
219231
232+ test ( 'sets user ID from getLastSeenUserIdAfterAuth when reporting to Bugsnag' , async ( ) => {
233+ // Given
234+ capturedEventHandler . mockClear ( )
235+ const testUserId = 'specific-test-user-id'
236+ vi . mocked ( getLastSeenUserIdAfterAuth ) . mockResolvedValue ( testUserId )
237+ const toThrow = new Error ( 'In test' )
238+
239+ // When
240+ const res = await sendErrorToBugsnag ( toThrow , 'unexpected_error' )
241+
242+ // Then
243+ expect ( res . reported ) . toEqual ( true )
244+ expect ( capturedEventHandler ) . toHaveBeenCalled ( )
245+
246+ const mockEvent = capturedEventHandler . mock . calls [ 0 ] ! [ 0 ]
247+ expect ( mockEvent . setUser ) . toHaveBeenCalledWith ( testUserId )
248+ expect ( mockEvent . severity ) . toEqual ( 'error' )
249+ expect ( mockEvent . unhandled ) . toEqual ( true )
250+ } )
251+
252+ test ( 'handles missing user ID gracefully' , async ( ) => {
253+ // Given
254+ capturedEventHandler . mockClear ( )
255+ vi . mocked ( getLastSeenUserIdAfterAuth ) . mockResolvedValue ( 'unknown' )
256+ const toThrow = new Error ( 'In test' )
257+
258+ // When
259+ const res = await sendErrorToBugsnag ( toThrow , 'unexpected_error' )
260+
261+ // Then
262+ expect ( res . reported ) . toEqual ( true )
263+ expect ( capturedEventHandler ) . toHaveBeenCalled ( )
264+
265+ const mockEvent = capturedEventHandler . mock . calls [ 0 ] ! [ 0 ]
266+ expect ( mockEvent . setUser ) . toHaveBeenCalledWith ( 'unknown' )
267+ } )
268+
220269 test ( 'attaches custom metadata with allowed slice_name when startCommand is present' , async ( ) => {
221270 await metadata . addSensitiveMetadata ( ( ) => ( {
222271 commandStartOptions : { startTime : Date . now ( ) , startCommand : 'app dev' , startArgs : [ ] } ,
0 commit comments