@@ -7,6 +7,17 @@ import * as SentryCore from '@sentry/core';
7
7
import { beforeEach , describe , expect , test , vi } from 'vitest' ;
8
8
import { CloudflareClient } from '../src/client' ;
9
9
import { 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
+ } ;
10
21
11
22
const MOCK_ENV = {
12
23
SENTRY_DSN :
'https://[email protected] /1337' ,
@@ -931,6 +942,86 @@ describe('withSentry', () => {
931
942
} ) ;
932
943
} ) ;
933
944
} ) ;
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
+ } ) ;
934
1025
} ) ;
935
1026
936
1027
function createMockExecutionContext ( ) : ExecutionContext {
0 commit comments