@@ -49,6 +49,10 @@ const JSON_HEADERS = { "Content-Type": "application/json" };
4949/**
5050 * Set up fetchMock for Sentry API mocking in Cloudflare Workers tests.
5151 *
52+ * IMPORTANT: undici MockAgent matches interceptors in registration order
53+ * (first match wins). Always register specific path handlers BEFORE
54+ * general pattern handlers to ensure correct matching.
55+ *
5256 * Call this in beforeAll() and call resetFetchMock() in afterEach().
5357 */
5458export function setupFetchMock ( ) {
@@ -58,7 +62,7 @@ export function setupFetchMock() {
5862 for ( const host of SENTRY_HOSTS ) {
5963 const pool = fetchMock . get ( host ) ;
6064
61- // Auth endpoints (control only - sentry.io only)
65+ // ===== Auth Endpoints (control only - sentry.io only) =====
6266 if ( host === "https://sentry.io" ) {
6367 pool
6468 . intercept ( { path : "/api/0/auth/" } )
@@ -75,7 +79,7 @@ export function setupFetchMock() {
7579 . persist ( ) ;
7680 }
7781
78- // Organizations
82+ // ===== Organizations =====
7983 pool
8084 . intercept ( { path : "/api/0/organizations/" } )
8185 . reply ( 200 , [ organizationFixture ] , { headers : JSON_HEADERS } )
@@ -95,7 +99,7 @@ export function setupFetchMock() {
9599 )
96100 . persist ( ) ;
97101
98- // Teams
102+ // ===== Teams =====
99103 pool
100104 . intercept ( { path : "/api/0/organizations/sentry-mcp-evals/teams/" } )
101105 . reply ( 200 , [ teamFixture ] , { headers : JSON_HEADERS } )
@@ -117,7 +121,7 @@ export function setupFetchMock() {
117121 )
118122 . persist ( ) ;
119123
120- // Projects
124+ // ===== Projects =====
121125 pool
122126 . intercept ( { path : "/api/0/organizations/sentry-mcp-evals/projects/" } )
123127 . reply ( 200 , [ { ...projectFixture , id : "4509106749636608" } ] , {
@@ -157,7 +161,7 @@ export function setupFetchMock() {
157161 )
158162 . persist ( ) ;
159163
160- // Client keys
164+ // ===== Client Keys =====
161165 pool
162166 . intercept ( {
163167 path : "/api/0/projects/sentry-mcp-evals/cloudflare-mcp/keys/" ,
@@ -173,7 +177,8 @@ export function setupFetchMock() {
173177 . reply ( 200 , [ clientKeyFixture ] , { headers : JSON_HEADERS } )
174178 . persist ( ) ;
175179
176- // Issues - project-scoped
180+ // ===== Issues =====
181+ // Project-scoped
177182 pool
178183 . intercept ( { path : "/api/0/projects/sentry-mcp-evals/foobar/issues/" } )
179184 . reply ( 200 , [ ] , { headers : JSON_HEADERS } )
@@ -189,18 +194,20 @@ export function setupFetchMock() {
189194 . reply ( 200 , [ issueFixture2 , issueFixture ] , { headers : JSON_HEADERS } )
190195 . persist ( ) ;
191196
192- // Issues - org- scoped
197+ // Org- scoped (excludes specific issue IDs that have their own handlers)
193198 pool
194199 . intercept ( {
195200 path : ( p : string ) =>
196201 p . startsWith ( "/api/0/organizations/sentry-mcp-evals/issues/" ) &&
197202 ! p . includes ( "CLOUDFLARE-MCP" ) &&
198- ! p . includes ( "6507376" ) ,
203+ ! p . includes ( "6507376" ) &&
204+ ! p . includes ( "PERF-N1" ) &&
205+ ! p . includes ( "PEATED" ) ,
199206 } )
200207 . reply ( 200 , [ issueFixture2 , issueFixture ] , { headers : JSON_HEADERS } )
201208 . persist ( ) ;
202209
203- // Specific issues
210+ // Specific issue details
204211 pool
205212 . intercept ( {
206213 path : "/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/" ,
@@ -229,7 +236,7 @@ export function setupFetchMock() {
229236 . reply ( 200 , issueFixture2 , { headers : JSON_HEADERS } )
230237 . persist ( ) ;
231238
232- // Issue updates
239+ // Issue updates (PUT)
233240 pool
234241 . intercept ( {
235242 path : "/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/" ,
@@ -246,25 +253,28 @@ export function setupFetchMock() {
246253 . reply ( 200 , issueFixture , { headers : JSON_HEADERS } )
247254 . persist ( ) ;
248255
249- // Events for issues
256+ // ===== Issue Events =====
257+ // IMPORTANT: Specific handlers must come before general patterns
258+
259+ // Performance issue events - specific handler
250260 pool
251261 . intercept ( {
252- path : ( p : string ) =>
253- p . includes ( "/events/7ca573c0f4814912aaa9bdc77d1a7d51" ) ||
254- p . includes ( "/events/latest" ) ,
262+ path : "/api/0/organizations/sentry-mcp-evals/issues/PERF-N1-001/events/latest/" ,
255263 } )
256- . reply ( 200 , eventsFixture , { headers : JSON_HEADERS } )
264+ . reply ( 200 , performanceEventFixture , { headers : JSON_HEADERS } )
257265 . persist ( ) ;
258266
259- // Performance events
267+ // General events - catch-all for remaining
260268 pool
261269 . intercept ( {
262- path : "/api/0/organizations/sentry-mcp-evals/issues/PERF-N1-001/events/latest/" ,
270+ path : ( p : string ) =>
271+ p . includes ( "/events/7ca573c0f4814912aaa9bdc77d1a7d51" ) ||
272+ p . includes ( "/events/latest" ) ,
263273 } )
264- . reply ( 200 , performanceEventFixture , { headers : JSON_HEADERS } )
274+ . reply ( 200 , eventsFixture , { headers : JSON_HEADERS } )
265275 . persist ( ) ;
266276
267- // Traces
277+ // ===== Traces =====
268278 pool
269279 . intercept ( {
270280 path : "/api/0/organizations/sentry-mcp-evals/trace-meta/a4d1aae7216b47ff8117cf4e09ce9d0a/" ,
@@ -279,7 +289,7 @@ export function setupFetchMock() {
279289 . reply ( 200 , traceFixture , { headers : JSON_HEADERS } )
280290 . persist ( ) ;
281291
282- // Releases
292+ // ===== Releases =====
283293 pool
284294 . intercept ( { path : "/api/0/organizations/sentry-mcp-evals/releases/" } )
285295 . reply ( 200 , [ releaseFixture ] , { headers : JSON_HEADERS } )
@@ -292,13 +302,13 @@ export function setupFetchMock() {
292302 . reply ( 200 , [ releaseFixture ] , { headers : JSON_HEADERS } )
293303 . persist ( ) ;
294304
295- // Tags
305+ // ===== Tags =====
296306 pool
297307 . intercept ( { path : "/api/0/organizations/sentry-mcp-evals/tags/" } )
298308 . reply ( 200 , tagsFixture , { headers : JSON_HEADERS } )
299309 . persist ( ) ;
300310
301- // Trace items attributes
311+ // ===== Trace Items Attributes =====
302312 pool
303313 . intercept ( {
304314 path : ( p : string ) =>
@@ -351,7 +361,7 @@ export function setupFetchMock() {
351361 } )
352362 . persist ( ) ;
353363
354- // Events endpoint (for search_events)
364+ // ===== Events Search (for search_events) =====
355365 pool
356366 . intercept ( {
357367 path : ( p : string ) =>
@@ -412,7 +422,7 @@ export function setupFetchMock() {
412422 } )
413423 . persist ( ) ;
414424
415- // Autofix
425+ // ===== Autofix =====
416426 pool
417427 . intercept ( {
418428 path : "/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/autofix/" ,
0 commit comments