@@ -7,6 +7,17 @@ import * as SentryCore from '@sentry/core';
77import { beforeEach , describe , expect , test , vi } from 'vitest' ;
88import { CloudflareClient } from '../src/client' ;
99import { withSentry } from '../src/handler' ;
10+ import { markAsInstrumented } from '../src/instrument' ;
11+
12+ // Custom type for hono-like apps (cloudflare handlers) that include errorHandler and onError
13+ type HonoLikeApp < Env = unknown , QueueHandlerMessage = unknown , CfHostMetadata = unknown > = ExportedHandler <
14+ Env ,
15+ QueueHandlerMessage ,
16+ CfHostMetadata
17+ > & {
18+ onError ?: ( ) => void ;
19+ errorHandler ?: ( err : Error ) => Response ;
20+ } ;
1021
1122const MOCK_ENV = {
1223 SENTRY_DSN :
'https://[email protected] /1337' , @@ -931,6 +942,86 @@ describe('withSentry', () => {
931942 } ) ;
932943 } ) ;
933944 } ) ;
945+
946+ describe ( 'hono errorHandler' , ( ) => {
947+ test ( 'captures errors handled by the errorHandler' , async ( ) => {
948+ const captureExceptionSpy = vi . spyOn ( SentryCore , 'captureException' ) ;
949+ const error = new Error ( 'test hono error' ) ;
950+
951+ const honoApp = {
952+ fetch ( _request , _env , _context ) {
953+ return new Response ( 'test' ) ;
954+ } ,
955+ onError ( ) { } , // hono-like onError
956+ errorHandler ( err : Error ) {
957+ return new Response ( `Error: ${ err . message } ` , { status : 500 } ) ;
958+ } ,
959+ } satisfies HonoLikeApp < typeof MOCK_ENV > ;
960+
961+ withSentry ( env => ( { dsn : env . SENTRY_DSN } ) , honoApp ) ;
962+
963+ // simulates hono's error handling
964+ const errorHandlerResponse = honoApp . errorHandler ?.( error ) ;
965+
966+ expect ( captureExceptionSpy ) . toHaveBeenCalledTimes ( 1 ) ;
967+ expect ( captureExceptionSpy ) . toHaveBeenLastCalledWith ( error , {
968+ mechanism : { handled : false , type : 'cloudflare' } ,
969+ } ) ;
970+ expect ( errorHandlerResponse ?. status ) . toBe ( 500 ) ;
971+ } ) ;
972+
973+ test ( 'preserves the original errorHandler functionality' , async ( ) => {
974+ const originalErrorHandlerSpy = vi . fn ( ) . mockImplementation ( ( err : Error ) => {
975+ return new Response ( `Error: ${ err . message } ` , { status : 500 } ) ;
976+ } ) ;
977+
978+ const error = new Error ( 'test hono error' ) ;
979+
980+ const honoApp = {
981+ fetch ( _request , _env , _context ) {
982+ return new Response ( 'test' ) ;
983+ } ,
984+ onError ( ) { } , // hono-like onError
985+ errorHandler : originalErrorHandlerSpy ,
986+ } satisfies HonoLikeApp < typeof MOCK_ENV > ;
987+
988+ withSentry ( env => ( { dsn : env . SENTRY_DSN } ) , honoApp ) ;
989+
990+ // Call the errorHandler directly to simulate Hono's error handling
991+ const errorHandlerResponse = honoApp . errorHandler ?.( error ) ;
992+
993+ expect ( originalErrorHandlerSpy ) . toHaveBeenCalledTimes ( 1 ) ;
994+ expect ( originalErrorHandlerSpy ) . toHaveBeenLastCalledWith ( error ) ;
995+ expect ( errorHandlerResponse ?. status ) . toBe ( 500 ) ;
996+ } ) ;
997+
998+ test ( 'does not instrument an already instrumented errorHandler' , async ( ) => {
999+ const captureExceptionSpy = vi . spyOn ( SentryCore , 'captureException' ) ;
1000+ const error = new Error ( 'test hono error' ) ;
1001+
1002+ // Create a handler with an errorHandler that's already been instrumented
1003+ const originalErrorHandler = ( err : Error ) => {
1004+ return new Response ( `Error: ${ err . message } ` , { status : 500 } ) ;
1005+ } ;
1006+
1007+ // Mark as instrumented before wrapping
1008+ markAsInstrumented ( originalErrorHandler ) ;
1009+
1010+ const honoApp = {
1011+ fetch ( _request , _env , _context ) {
1012+ return new Response ( 'test' ) ;
1013+ } ,
1014+ onError ( ) { } , // hono-like onError
1015+ errorHandler : originalErrorHandler ,
1016+ } satisfies HonoLikeApp < typeof MOCK_ENV > ;
1017+
1018+ withSentry ( env => ( { dsn : env . SENTRY_DSN } ) , honoApp ) ;
1019+
1020+ // The errorHandler should not have been wrapped again
1021+ honoApp . errorHandler ?.( error ) ;
1022+ expect ( captureExceptionSpy ) . not . toHaveBeenCalled ( ) ;
1023+ } ) ;
1024+ } ) ;
9341025} ) ;
9351026
9361027function createMockExecutionContext ( ) : ExecutionContext {
0 commit comments