@@ -123,6 +123,31 @@ describe("sentry", () => {
123123 expect ( sentryRequests ?. length ) . toEqual ( 0 ) ;
124124 } ) ;
125125
126+ it ( "should not hit sentry (or even ask) after reportable error if WRANGLER_SEND_METRICS is explicitly false" , async ( ) => {
127+ // Trigger an API error
128+ msw . use (
129+ http . get (
130+ `https://api.cloudflare.com/client/v4/user` ,
131+ async ( ) => {
132+ return HttpResponse . error ( ) ;
133+ } ,
134+ { once : true }
135+ ) ,
136+ http . get ( "*/user/tokens/verify" , ( ) => {
137+ return HttpResponse . json ( createFetchResult ( [ ] ) ) ;
138+ } )
139+ ) ;
140+ await expect (
141+ runWrangler ( "whoami" , { WRANGLER_SEND_METRICS : "false" } )
142+ ) . rejects . toMatchInlineSnapshot ( `[TypeError: Failed to fetch]` ) ;
143+ expect ( std . out ) . toMatchInlineSnapshot ( `
144+ "Getting User settings...
145+
146+ [32mIf you think this is a bug then please create an issue at https://github.com/cloudflare/workers-sdk/issues/new/choose[0m"
147+ ` ) ;
148+ expect ( sentryRequests ?. length ) . toEqual ( 0 ) ;
149+ } ) ;
150+
126151 it ( "should hit sentry after reportable error when permission provided" , async ( ) => {
127152 // Trigger an API error
128153 msw . use (
@@ -428,6 +453,308 @@ describe("sentry", () => {
428453 } ,
429454 } ) ;
430455 } ) ;
456+
457+ it ( "should hit sentry after reportable error (without confirmation) if WRANGLER_SEND_METRICS is explicitly true" , async ( ) => {
458+ // Trigger an API error
459+ msw . use (
460+ http . get (
461+ `https://api.cloudflare.com/client/v4/user` ,
462+ async ( ) => {
463+ return HttpResponse . error ( ) ;
464+ } ,
465+ { once : true }
466+ ) ,
467+ http . get ( "*/user/tokens/verify" , ( ) => {
468+ return HttpResponse . json ( createFetchResult ( [ ] ) ) ;
469+ } )
470+ ) ;
471+ await expect (
472+ runWrangler ( "whoami" , { WRANGLER_SEND_METRICS : "true" } )
473+ ) . rejects . toMatchInlineSnapshot ( `[TypeError: Failed to fetch]` ) ;
474+ expect ( std . out ) . toMatchInlineSnapshot ( `
475+ "Getting User settings...
476+
477+ [32mIf you think this is a bug then please create an issue at https://github.com/cloudflare/workers-sdk/issues/new/choose[0m"
478+ ` ) ;
479+
480+ // Sentry sends multiple HTTP requests to capture breadcrumbs
481+ expect ( sentryRequests ?. length ) . toBeGreaterThan ( 0 ) ;
482+ assert ( sentryRequests !== undefined ) ;
483+
484+ // Check requests don't include PII
485+ const envelopes = sentryRequests . map ( ( { envelope } ) => {
486+ const parts = envelope . split ( "\n" ) . map ( ( line ) => JSON . parse ( line ) ) ;
487+ expect ( parts ) . toHaveLength ( 3 ) ;
488+ return { header : parts [ 0 ] , type : parts [ 1 ] , data : parts [ 2 ] } ;
489+ } ) ;
490+ const event = envelopes . find ( ( { type } ) => type . type === "event" ) ;
491+ assert ( event !== undefined ) ;
492+
493+ // Redact fields with random contents we know don't contain PII
494+ event . header . event_id = "" ;
495+ event . header . sent_at = "" ;
496+ event . header . trace . trace_id = "" ;
497+ event . header . trace . release = "" ;
498+ for ( const exception of event . data . exception . values ) {
499+ for ( const frame of exception . stacktrace . frames ) {
500+ if (
501+ frame . filename . startsWith ( "C:\\Project\\" ) ||
502+ frame . filename . startsWith ( "/project/" )
503+ ) {
504+ frame . filename = "/project/..." ;
505+ }
506+ frame . function = "" ;
507+ frame . lineno = 0 ;
508+ frame . colno = 0 ;
509+ frame . in_app = false ;
510+ frame . pre_context = [ ] ;
511+ frame . context_line = "" ;
512+ frame . post_context = [ ] ;
513+ }
514+ }
515+ event . data . event_id = "" ;
516+ event . data . contexts . trace . trace_id = "" ;
517+ event . data . contexts . trace . span_id = "" ;
518+ event . data . contexts . runtime . version = "" ;
519+ event . data . contexts . app . app_start_time = "" ;
520+ event . data . contexts . app . app_memory = 0 ;
521+ event . data . contexts . os = { } ;
522+ event . data . contexts . device = { } ;
523+ event . data . timestamp = 0 ;
524+ event . data . release = "" ;
525+ for ( const breadcrumb of event . data . breadcrumbs ) {
526+ breadcrumb . timestamp = 0 ;
527+ }
528+
529+ const fakeInstallPath = "/wrangler/" ;
530+ for ( const exception of event . data . exception ?. values ?? [ ] ) {
531+ for ( const frame of exception . stacktrace ?. frames ?? [ ] ) {
532+ if ( frame . module . startsWith ( "@mswjs" ) ) {
533+ frame . module =
534+ "@mswjs.interceptors.src.interceptors.fetch:index.ts" ;
535+ }
536+ if ( frame . filename === undefined ) {
537+ continue ;
538+ }
539+
540+ const wranglerPackageIndex = frame . filename . indexOf (
541+ path . join ( "packages" , "wrangler" , "src" )
542+ ) ;
543+ if ( wranglerPackageIndex === - 1 ) {
544+ continue ;
545+ }
546+ frame . filename =
547+ fakeInstallPath +
548+ frame . filename
549+ . substring ( wranglerPackageIndex )
550+ . replaceAll ( "\\" , "/" ) ;
551+ continue ;
552+ }
553+ }
554+
555+ // If more data is included in the Sentry request, we'll need to verify it
556+ // couldn't contain PII and update this snapshot
557+ expect ( event ) . toStrictEqual ( {
558+ data : {
559+ breadcrumbs : [
560+ {
561+ level : "log" ,
562+ message : "wrangler whoami" ,
563+ timestamp : 0 ,
564+ } ,
565+ ] ,
566+ contexts : {
567+ app : {
568+ app_memory : 0 ,
569+ app_start_time : "" ,
570+ } ,
571+ cloud_resource : { } ,
572+ device : { } ,
573+ os : { } ,
574+ runtime : {
575+ name : "node" ,
576+ version : "" ,
577+ } ,
578+ trace : {
579+ span_id : "" ,
580+ trace_id : "" ,
581+ } ,
582+ } ,
583+ environment : "production" ,
584+ event_id : "" ,
585+ exception : {
586+ values : [
587+ {
588+ mechanism : {
589+ handled : true ,
590+ type : "generic" ,
591+ } ,
592+ stacktrace : {
593+ frames : [
594+ {
595+ colno : 0 ,
596+ context_line : "" ,
597+ filename : expect . any ( String ) ,
598+ function : "" ,
599+ in_app : false ,
600+ lineno : 0 ,
601+ module : expect . any ( String ) ,
602+ post_context : [ ] ,
603+ pre_context : [ ] ,
604+ } ,
605+ {
606+ colno : 0 ,
607+ context_line : "" ,
608+ filename : expect . any ( String ) ,
609+ function : "" ,
610+ in_app : false ,
611+ lineno : 0 ,
612+ module : expect . any ( String ) ,
613+ post_context : [ ] ,
614+ pre_context : [ ] ,
615+ } ,
616+ {
617+ colno : 0 ,
618+ context_line : "" ,
619+ filename : expect . any ( String ) ,
620+ function : "" ,
621+ in_app : false ,
622+ lineno : 0 ,
623+ module : expect . any ( String ) ,
624+ post_context : [ ] ,
625+ pre_context : [ ] ,
626+ } ,
627+ {
628+ colno : 0 ,
629+ context_line : "" ,
630+ filename : expect . any ( String ) ,
631+ function : "" ,
632+ in_app : false ,
633+ lineno : 0 ,
634+ module : expect . any ( String ) ,
635+ post_context : [ ] ,
636+ pre_context : [ ] ,
637+ } ,
638+ {
639+ colno : 0 ,
640+ context_line : "" ,
641+ filename : expect . any ( String ) ,
642+ function : "" ,
643+ in_app : false ,
644+ lineno : 0 ,
645+ module : expect . any ( String ) ,
646+ post_context : [ ] ,
647+ pre_context : [ ] ,
648+ } ,
649+ {
650+ colno : 0 ,
651+ context_line : "" ,
652+ filename : expect . any ( String ) ,
653+ function : "" ,
654+ in_app : false ,
655+ lineno : 0 ,
656+ module : expect . any ( String ) ,
657+ post_context : [ ] ,
658+ pre_context : [ ] ,
659+ } ,
660+ {
661+ colno : 0 ,
662+ context_line : "" ,
663+ filename : expect . any ( String ) ,
664+ function : "" ,
665+ in_app : false ,
666+ lineno : 0 ,
667+ module : expect . any ( String ) ,
668+ post_context : [ ] ,
669+ pre_context : [ ] ,
670+ } ,
671+ {
672+ colno : 0 ,
673+ context_line : "" ,
674+ filename : expect . any ( String ) ,
675+ function : "" ,
676+ in_app : false ,
677+ lineno : 0 ,
678+ module : expect . any ( String ) ,
679+ post_context : [ ] ,
680+ pre_context : [ ] ,
681+ } ,
682+ {
683+ colno : 0 ,
684+ context_line : "" ,
685+ filename : "/project/..." ,
686+ function : "" ,
687+ in_app : false ,
688+ lineno : 0 ,
689+ module :
690+ "@mswjs.interceptors.src.interceptors.fetch:index.ts" ,
691+ post_context : [ ] ,
692+ pre_context : [ ] ,
693+ } ,
694+ {
695+ colno : 0 ,
696+ context_line : "" ,
697+ filename : "/project/..." ,
698+ function : "" ,
699+ in_app : false ,
700+ lineno : 0 ,
701+ module :
702+ "@mswjs.interceptors.src.interceptors.fetch:index.ts" ,
703+ post_context : [ ] ,
704+ pre_context : [ ] ,
705+ } ,
706+ ] ,
707+ } ,
708+ type : "TypeError" ,
709+ value : "Failed to fetch" ,
710+ } ,
711+ ] ,
712+ } ,
713+ modules : { } ,
714+ platform : "node" ,
715+ release : "" ,
716+ sdk : {
717+ integrations : [
718+ "InboundFilters" ,
719+ "FunctionToString" ,
720+ "LinkedErrors" ,
721+ "Console" ,
722+ "OnUncaughtException" ,
723+ "OnUnhandledRejection" ,
724+ "ContextLines" ,
725+ "Context" ,
726+ "Modules" ,
727+ ] ,
728+ name : "sentry.javascript.node" ,
729+ packages : [
730+ {
731+ name : "npm:@sentry/node" ,
732+ version : "7.87.0" ,
733+ } ,
734+ ] ,
735+ version : "7.87.0" ,
736+ } ,
737+ timestamp : 0 ,
738+ } ,
739+ header : {
740+ event_id : "" ,
741+ sdk : {
742+ name : "sentry.javascript.node" ,
743+ version : "7.87.0" ,
744+ } ,
745+ sent_at : "" ,
746+ trace : {
747+ environment : "production" ,
748+ public_key : "9edbb8417b284aa2bbead9b4c318918b" ,
749+ release : "" ,
750+ trace_id : "" ,
751+ } ,
752+ } ,
753+ type : {
754+ type : "event" ,
755+ } ,
756+ } ) ;
757+ } ) ;
431758 } ) ;
432759} ) ;
433760
0 commit comments