Skip to content

Commit 1972c3f

Browse files
dcramerclaude
andcommitted
fix(cloudflare): fix mock interceptor ordering in fetch-mock-setup
Fixed undici MockAgent interceptor ordering issues where general patterns were registered before specific handlers, causing wrong fixtures to be returned. Changes: - Move PERF-N1-001/events/latest handler before general /events/latest handler so performance events return the correct fixture - Add PERF-N1 and PEATED to org issues exclusion pattern to prevent incorrect list responses for these specific issues - Add section comments throughout file for maintainability - Add documentation about undici's first-match-wins behavior Co-Authored-By: Claude Code <noreply@anthropic.com>
1 parent 4dade87 commit 1972c3f

1 file changed

Lines changed: 34 additions & 24 deletions

File tree

packages/mcp-cloudflare/src/test-utils/fetch-mock-setup.ts

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -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
*/
5458
export 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

Comments
 (0)