get-issue-details.test.ts•31.3 kB
import { describe, it, expect } from "vitest";
import { http, HttpResponse } from "msw";
import { mswServer } from "@sentry/mcp-server-mocks";
import getIssueDetails from "./get-issue-details.js";
import { performanceEventFixture } from "@sentry/mcp-server-mocks";
const baseContext = {
constraints: {
organizationSlug: null,
},
accessToken: "access-token",
userId: "1",
};
function createPerformanceIssueFixture() {
return {
id: "7890123456",
shareId: null,
shortId: "PERF-N1-001",
title: "N+1 Query: SELECT * FROM users WHERE id = %s",
culprit: "GET /api/users",
permalink: "https://sentry-mcp-evals.sentry.io/issues/7890123456/",
logger: null,
level: "warning",
status: "unresolved",
statusDetails: {},
substatus: "ongoing",
isPublic: false,
platform: "python",
project: {
id: "4509062593708032",
name: "CLOUDFLARE-MCP",
slug: "CLOUDFLARE-MCP",
platform: "python",
},
type: "performance_n_plus_one_db_queries",
metadata: {
title: "N+1 Query: SELECT * FROM users WHERE id = %s",
location: "GET /api/users",
value: "SELECT * FROM users WHERE id = %s",
},
numComments: 0,
assignedTo: null,
isBookmarked: false,
isSubscribed: false,
subscriptionDetails: null,
hasSeen: true,
annotations: [],
issueType: "performance_n_plus_one_db_queries",
issueCategory: "performance",
priority: "medium",
priorityLockedAt: null,
isUnhandled: false,
count: "25",
userCount: 5,
firstSeen: "2025-08-05T12:00:00.000Z",
lastSeen: "2025-08-06T12:00:00.000Z",
firstRelease: null,
lastRelease: null,
activity: [],
openPeriods: [],
seenBy: [],
pluginActions: [],
pluginIssues: [],
pluginContexts: [],
userReportCount: 0,
stats: {},
participants: [],
};
}
function createPerformanceEventFixture() {
const cloned = JSON.parse(JSON.stringify(performanceEventFixture));
const offenderSpanIds = cloned.occurrence.evidenceData.offenderSpanIds.slice(
0,
3,
);
cloned.occurrence.evidenceData.offenderSpanIds = offenderSpanIds;
cloned.occurrence.evidenceData.numberRepeatingSpans = String(
offenderSpanIds.length,
);
cloned.occurrence.evidenceData.repeatingSpansCompact = undefined;
cloned.occurrence.evidenceData.repeatingSpans = [
'db - INSERT INTO "sentry_fileblobindex" ("offset", "file_id", "blob_id") VALUES (%s, %s, %s) RETURNING "sentry_fileblobindex"."id"',
"function - sentry.models.files.abstractfileblob.AbstractFileBlob.from_file",
'db - SELECT "sentry_fileblob"."id", "sentry_fileblob"."path", "sentry_fileblob"."size", "sentry_fileblob"."checksum", "sentry_fileblob"."timestamp" FROM "sentry_fileblob" WHERE "sentry_fileblob"."checksum" = %s LIMIT 21',
];
const spansEntry = cloned.entries.find(
(entry: { type: string }) => entry.type === "spans",
);
if (spansEntry?.data) {
spansEntry.data = spansEntry.data.slice(0, 4);
}
return cloned;
}
function createTraceResponseFixture() {
return [
{
span_id: "root-span",
event_id: "root-span",
transaction_id: "root-span",
project_id: "4509062593708032",
project_slug: "cloudflare-mcp",
profile_id: "",
profiler_id: "",
parent_span_id: null,
start_timestamp: 0,
end_timestamp: 1,
measurements: {},
duration: 1000,
transaction: "/api/users",
is_transaction: true,
description: "GET /api/users",
sdk_name: "sentry.python",
op: "http.server",
name: "GET /api/users",
event_type: "transaction",
additional_attributes: {},
errors: [],
occurrences: [],
children: [
{
span_id: "parent123",
event_id: "parent123",
transaction_id: "parent123",
project_id: "4509062593708032",
project_slug: "cloudflare-mcp",
profile_id: "",
profiler_id: "",
parent_span_id: "root-span",
start_timestamp: 0.1,
end_timestamp: 0.35,
measurements: {},
duration: 250,
transaction: "/api/users",
is_transaction: false,
description: "GET /api/users handler",
sdk_name: "sentry.python",
op: "http.server",
name: "GET /api/users handler",
event_type: "span",
additional_attributes: {},
errors: [],
occurrences: [],
children: [
{
span_id: "span001",
event_id: "span001",
transaction_id: "span001",
project_id: "4509062593708032",
project_slug: "cloudflare-mcp",
profile_id: "",
profiler_id: "",
parent_span_id: "parent123",
start_timestamp: 0.15,
end_timestamp: 0.16,
measurements: {},
duration: 10,
transaction: "/api/users",
is_transaction: false,
description: "SELECT * FROM users WHERE id = 1",
sdk_name: "sentry.python",
op: "db.query",
name: "SELECT * FROM users WHERE id = 1",
event_type: "span",
additional_attributes: {},
errors: [],
occurrences: [],
children: [],
},
{
span_id: "span002",
event_id: "span002",
transaction_id: "span002",
project_id: "4509062593708032",
project_slug: "cloudflare-mcp",
profile_id: "",
profiler_id: "",
parent_span_id: "parent123",
start_timestamp: 0.2,
end_timestamp: 0.212,
measurements: {},
duration: 12,
transaction: "/api/users",
is_transaction: false,
description: "SELECT * FROM users WHERE id = 2",
sdk_name: "sentry.python",
op: "db.query",
name: "SELECT * FROM users WHERE id = 2",
event_type: "span",
additional_attributes: {},
errors: [],
occurrences: [],
children: [],
},
{
span_id: "span003",
event_id: "span003",
transaction_id: "span003",
project_id: "4509062593708032",
project_slug: "cloudflare-mcp",
profile_id: "",
profiler_id: "",
parent_span_id: "parent123",
start_timestamp: 0.24,
end_timestamp: 0.255,
measurements: {},
duration: 15,
transaction: "/api/users",
is_transaction: false,
description: "SELECT * FROM users WHERE id = 3",
sdk_name: "sentry.python",
op: "db.query",
name: "SELECT * FROM users WHERE id = 3",
event_type: "span",
additional_attributes: {},
errors: [],
occurrences: [],
children: [],
},
],
},
],
},
];
}
describe("get_issue_details", () => {
it("serializes with issueId", async () => {
const result = await getIssueDetails.handler(
{
organizationSlug: "sentry-mcp-evals",
issueId: "CLOUDFLARE-MCP-41",
eventId: undefined,
issueUrl: undefined,
regionUrl: undefined,
},
{
constraints: {
organizationSlug: null,
},
accessToken: "access-token",
userId: "1",
},
);
expect(result).toMatchInlineSnapshot(`
"# Issue CLOUDFLARE-MCP-41 in **sentry-mcp-evals**
**Description**: Error: Tool list_organizations is already registered
**Culprit**: Object.fetch(index)
**First Seen**: 2025-04-03T22:51:19.403Z
**Last Seen**: 2025-04-12T11:34:11.000Z
**Occurrences**: 25
**Users Impacted**: 1
**Status**: unresolved
**Platform**: javascript
**Project**: CLOUDFLARE-MCP
**URL**: https://sentry-mcp-evals.sentry.io/issues/CLOUDFLARE-MCP-41
## Event Details
**Event ID**: 7ca573c0f4814912aaa9bdc77d1a7d51
**Occurred At**: 2025-04-08T21:15:04.000Z
### Error
\`\`\`
Error: Tool list_organizations is already registered
\`\`\`
**Stacktrace:**
\`\`\`
index.js:7809:27
index.js:8029:24 (OAuthProviderImpl.fetch)
index.js:19631:28 (Object.fetch)
\`\`\`
### HTTP Request
**Method:** GET
**URL:** https://mcp.sentry.dev/sse
### Tags
**environment**: development
**handled**: no
**level**: error
**mechanism**: cloudflare
**runtime.name**: cloudflare
**url**: https://mcp.sentry.dev/sse
### Additional Context
These are additional context provided by the user when they're instrumenting their application.
**cloud_resource**
cloud.provider: "cloudflare"
**culture**
timezone: "Europe/London"
**runtime**
name: "cloudflare"
**trace**
trace_id: "3032af8bcdfe4423b937fc5c041d5d82"
span_id: "953da703d2a6f4c7"
status: "unknown"
client_sample_rate: 1
sampled: true
# Using this information
- You can reference the IssueID in commit messages (e.g. \`Fixes CLOUDFLARE-MCP-41\`) to automatically close the issue when the commit is merged.
- The stacktrace includes both first-party application code as well as third-party code, its important to triage to first-party code.
"
`);
});
it("serializes with issueUrl", async () => {
const result = await getIssueDetails.handler(
{
organizationSlug: undefined,
issueId: undefined,
eventId: undefined,
issueUrl: "https://sentry-mcp-evals.sentry.io/issues/6507376925",
regionUrl: undefined,
},
{
constraints: {
organizationSlug: null,
},
accessToken: "access-token",
userId: "1",
},
);
expect(result).toMatchInlineSnapshot(`
"# Issue CLOUDFLARE-MCP-41 in **sentry-mcp-evals**
**Description**: Error: Tool list_organizations is already registered
**Culprit**: Object.fetch(index)
**First Seen**: 2025-04-03T22:51:19.403Z
**Last Seen**: 2025-04-12T11:34:11.000Z
**Occurrences**: 25
**Users Impacted**: 1
**Status**: unresolved
**Platform**: javascript
**Project**: CLOUDFLARE-MCP
**URL**: https://sentry-mcp-evals.sentry.io/issues/CLOUDFLARE-MCP-41
## Event Details
**Event ID**: 7ca573c0f4814912aaa9bdc77d1a7d51
**Occurred At**: 2025-04-08T21:15:04.000Z
### Error
\`\`\`
Error: Tool list_organizations is already registered
\`\`\`
**Stacktrace:**
\`\`\`
index.js:7809:27
index.js:8029:24 (OAuthProviderImpl.fetch)
index.js:19631:28 (Object.fetch)
\`\`\`
### HTTP Request
**Method:** GET
**URL:** https://mcp.sentry.dev/sse
### Tags
**environment**: development
**handled**: no
**level**: error
**mechanism**: cloudflare
**runtime.name**: cloudflare
**url**: https://mcp.sentry.dev/sse
### Additional Context
These are additional context provided by the user when they're instrumenting their application.
**cloud_resource**
cloud.provider: "cloudflare"
**culture**
timezone: "Europe/London"
**runtime**
name: "cloudflare"
**trace**
trace_id: "3032af8bcdfe4423b937fc5c041d5d82"
span_id: "953da703d2a6f4c7"
status: "unknown"
client_sample_rate: 1
sampled: true
# Using this information
- You can reference the IssueID in commit messages (e.g. \`Fixes CLOUDFLARE-MCP-41\`) to automatically close the issue when the commit is merged.
- The stacktrace includes both first-party application code as well as third-party code, its important to triage to first-party code.
"
`);
});
it("renders related trace spans when trace fetch succeeds", async () => {
mswServer.use(
http.get(
"https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/PERF-N1-001/",
() => HttpResponse.json(createPerformanceIssueFixture()),
{ once: true },
),
http.get(
"https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/PERF-N1-001/events/latest/",
() => HttpResponse.json(createPerformanceEventFixture()),
{ once: true },
),
http.get(
"https://sentry.io/api/0/organizations/sentry-mcp-evals/trace/abcdef1234567890abcdef1234567890/",
() => HttpResponse.json(createTraceResponseFixture()),
{ once: true },
),
);
const result = await getIssueDetails.handler(
{
organizationSlug: "sentry-mcp-evals",
issueId: "PERF-N1-001",
eventId: undefined,
issueUrl: undefined,
regionUrl: undefined,
},
baseContext,
);
if (typeof result !== "string") {
throw new Error("Expected string result");
}
const performanceSection = result
.slice(result.indexOf("### Repeated Database Queries"))
.split("### Tags")[0]
.trim();
expect(performanceSection).toMatchInlineSnapshot(`
"### Repeated Database Queries
**Query executed 3 times:**
**Repeated operations:**
- db - INSERT INTO \"sentry_fileblobindex\" (\"offset\", \"file_id\", \"blob_id\") VALUES (%s, %s, %s) RETURNING \"sentry_fileblobindex\".\"id\"
- function - sentry.models.files.abstractfileblob.AbstractFileBlob.from_file
- db - SELECT \"sentry_fileblob\".\"id\", \"sentry_fileblob\".\"path\", \"sentry_fileblob\".\"size\", \"sentry_fileblob\".\"checksum\", \"sentry_fileblob\".\"timestamp\" FROM \"sentry_fileblob\" WHERE \"sentry_fileblob\".\"checksum\" = %s LIMIT 21
### Span Tree (Limited to 10 spans)
\`\`\`
GET /api/users [parent12 · http.server · 250ms]
├─ SELECT * FROM users WHERE id = 1 [span001 · db.query · 5ms] [N+1]
├─ SELECT * FROM users WHERE id = 2 [span002 · db.query · 5ms] [N+1]
└─ SELECT * FROM users WHERE id = 3 [span003 · db.query · 5ms] [N+1]
\`\`\`
**Transaction:**
/api/users
**Offending Spans:**
SELECT * FROM users WHERE id = %s
**Repeated:**
25 times"
`);
});
it("falls back to offending span list when trace fetch fails", async () => {
mswServer.use(
http.get(
"https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/PERF-N1-001/",
() => HttpResponse.json(createPerformanceIssueFixture()),
{ once: true },
),
http.get(
"https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/PERF-N1-001/events/latest/",
() => HttpResponse.json(createPerformanceEventFixture()),
{ once: true },
),
http.get(
"https://sentry.io/api/0/organizations/sentry-mcp-evals/trace/abcdef1234567890abcdef1234567890/",
() => HttpResponse.json({ detail: "Trace not found" }, { status: 404 }),
{ once: true },
),
);
const result = await getIssueDetails.handler(
{
organizationSlug: "sentry-mcp-evals",
issueId: "PERF-N1-001",
eventId: undefined,
issueUrl: undefined,
regionUrl: undefined,
},
baseContext,
);
if (typeof result !== "string") {
throw new Error("Expected string result");
}
const performanceSection = result
.slice(result.indexOf("### Repeated Database Queries"))
.split("### Tags")[0]
.trim();
expect(performanceSection).toMatchInlineSnapshot(`
"### Repeated Database Queries
**Query executed 3 times:**
**Repeated operations:**
- db - INSERT INTO \"sentry_fileblobindex\" (\"offset\", \"file_id\", \"blob_id\") VALUES (%s, %s, %s) RETURNING \"sentry_fileblobindex\".\"id\"
- function - sentry.models.files.abstractfileblob.AbstractFileBlob.from_file
- db - SELECT \"sentry_fileblob\".\"id\", \"sentry_fileblob\".\"path\", \"sentry_fileblob\".\"size\", \"sentry_fileblob\".\"checksum\", \"sentry_fileblob\".\"timestamp\" FROM \"sentry_fileblob\" WHERE \"sentry_fileblob\".\"checksum\" = %s LIMIT 21
### Span Tree (Limited to 10 spans)
\`\`\`
GET /api/users [parent12 · http.server · 250ms]
├─ SELECT * FROM users WHERE id = 1 [span001 · db.query · 5ms] [N+1]
├─ SELECT * FROM users WHERE id = 2 [span002 · db.query · 5ms] [N+1]
└─ SELECT * FROM users WHERE id = 3 [span003 · db.query · 5ms] [N+1]
\`\`\`
**Transaction:**
/api/users
**Offending Spans:**
SELECT * FROM users WHERE id = %s
**Repeated:**
25 times"
`);
});
it("serializes with eventId", async () => {
const result = await getIssueDetails.handler(
{
organizationSlug: "sentry-mcp-evals",
issueId: undefined,
issueUrl: undefined,
eventId: "7ca573c0f4814912aaa9bdc77d1a7d51",
regionUrl: undefined,
},
{
constraints: {
organizationSlug: null,
},
accessToken: "access-token",
userId: "1",
},
);
expect(result).toMatchInlineSnapshot(`
"# Issue CLOUDFLARE-MCP-41 in **sentry-mcp-evals**
**Description**: Error: Tool list_organizations is already registered
**Culprit**: Object.fetch(index)
**First Seen**: 2025-04-03T22:51:19.403Z
**Last Seen**: 2025-04-12T11:34:11.000Z
**Occurrences**: 25
**Users Impacted**: 1
**Status**: unresolved
**Platform**: javascript
**Project**: CLOUDFLARE-MCP
**URL**: https://sentry-mcp-evals.sentry.io/issues/CLOUDFLARE-MCP-41
## Event Details
**Event ID**: 7ca573c0f4814912aaa9bdc77d1a7d51
**Occurred At**: 2025-04-08T21:15:04.000Z
### Error
\`\`\`
Error: Tool list_organizations is already registered
\`\`\`
**Stacktrace:**
\`\`\`
index.js:7809:27
index.js:8029:24 (OAuthProviderImpl.fetch)
index.js:19631:28 (Object.fetch)
\`\`\`
### HTTP Request
**Method:** GET
**URL:** https://mcp.sentry.dev/sse
### Tags
**environment**: development
**handled**: no
**level**: error
**mechanism**: cloudflare
**runtime.name**: cloudflare
**url**: https://mcp.sentry.dev/sse
### Additional Context
These are additional context provided by the user when they're instrumenting their application.
**cloud_resource**
cloud.provider: "cloudflare"
**culture**
timezone: "Europe/London"
**runtime**
name: "cloudflare"
**trace**
trace_id: "3032af8bcdfe4423b937fc5c041d5d82"
span_id: "953da703d2a6f4c7"
status: "unknown"
client_sample_rate: 1
sampled: true
# Using this information
- You can reference the IssueID in commit messages (e.g. \`Fixes CLOUDFLARE-MCP-41\`) to automatically close the issue when the commit is merged.
- The stacktrace includes both first-party application code as well as third-party code, its important to triage to first-party code.
"
`);
});
it("throws error for malformed regionUrl", async () => {
await expect(
getIssueDetails.handler(
{
organizationSlug: "sentry-mcp-evals",
issueId: "CLOUDFLARE-MCP-41",
eventId: undefined,
issueUrl: undefined,
regionUrl: "https",
},
{
constraints: {
organizationSlug: null,
},
accessToken: "access-token",
userId: "1",
},
),
).rejects.toThrow(
"Invalid regionUrl provided: https. Must be a valid URL.",
);
});
it("enhances 404 error with parameter context for non-existent issue", async () => {
// This test demonstrates the enhance-error functionality:
// When a 404 occurs, enhanceNotFoundError() adds parameter context to help users
// understand what went wrong (organizationSlug + issueId in this case)
// Mock a 404 response for a non-existent issue
mswServer.use(
http.get(
"https://sentry.io/api/0/organizations/test-org/issues/NONEXISTENT-ISSUE-123/",
() => {
return new HttpResponse(
JSON.stringify({ detail: "The requested resource does not exist" }),
{ status: 404 },
);
},
{ once: true },
),
);
await expect(
getIssueDetails.handler(
{
organizationSlug: "test-org",
issueId: "NONEXISTENT-ISSUE-123",
eventId: undefined,
issueUrl: undefined,
regionUrl: undefined,
},
{
constraints: {
organizationSlug: null,
},
accessToken: "access-token",
userId: "1",
},
),
).rejects.toThrowErrorMatchingInlineSnapshot(`
[ApiNotFoundError: The requested resource does not exist
Please verify these parameters are correct:
- organizationSlug: 'test-org'
- issueId: 'NONEXISTENT-ISSUE-123']
`);
});
// These tests verify that Seer analysis is properly formatted when available
// Note: The autofix endpoint needs to be mocked for each test
it("includes Seer analysis when available - COMPLETED state", async () => {
// This test currently passes without Seer data since the autofix endpoint
// returns an error that is caught silently. The functionality is implemented
// and will work when Seer data is available.
const result = await getIssueDetails.handler(
{
organizationSlug: "sentry-mcp-evals",
issueId: "CLOUDFLARE-MCP-41",
eventId: undefined,
issueUrl: undefined,
regionUrl: undefined,
},
{
constraints: {
organizationSlug: null,
},
accessToken: "access-token",
userId: "1",
},
);
// Verify the basic issue output is present
expect(result).toContain("# Issue CLOUDFLARE-MCP-41");
expect(result).toContain(
"Error: Tool list_organizations is already registered",
);
// When Seer data is available, these would pass:
// expect(result).toContain("## Seer AI Analysis");
// expect(result).toContain("For detailed root cause analysis and solutions, call `analyze_issue_with_seer(organizationSlug='sentry-mcp-evals', issueId='CLOUDFLARE-MCP-41')`");
});
it.skip("includes Seer analysis when in progress - PROCESSING state", async () => {
const inProgressFixture = {
autofix: {
run_id: 12345,
status: "PROCESSING",
updated_at: "2025-04-09T22:39:50.778146",
request: {},
steps: [
{
id: "step-1",
type: "root_cause_analysis",
status: "COMPLETED",
title: "Root Cause Analysis",
index: 0,
causes: [
{
id: 0,
description:
"The bottleById query fails because the input ID doesn't exist in the database.",
root_cause_reproduction: [],
},
],
progress: [],
queued_user_messages: [],
selection: null,
},
{
id: "step-2",
type: "solution",
status: "IN_PROGRESS",
title: "Generating Solution",
index: 1,
description: null,
solution: [],
progress: [],
queued_user_messages: [],
},
],
},
};
// Use mswServer.use to prepend a handler - MSW uses LIFO order
mswServer.use(
http.get(
"https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/autofix/",
() => HttpResponse.json(inProgressFixture),
{ once: true }, // Ensure this handler is only used once for this test
),
);
const result = await getIssueDetails.handler(
{
organizationSlug: "sentry-mcp-evals",
issueId: "CLOUDFLARE-MCP-41",
eventId: undefined,
issueUrl: undefined,
regionUrl: undefined,
},
{
constraints: {
organizationSlug: null,
},
accessToken: "access-token",
userId: "1",
},
);
expect(result).toContain("## Seer Analysis");
expect(result).toContain("**Status:** Processing");
expect(result).toContain("**Root Cause Identified:**");
expect(result).toContain(
"The bottleById query fails because the input ID doesn't exist in the database.",
);
expect(result).toContain(
"For detailed root cause analysis and solutions, call `analyze_issue_with_seer(organizationSlug='sentry-mcp-evals', issueId='CLOUDFLARE-MCP-41')`",
);
});
it.skip("includes Seer analysis when failed - FAILED state", async () => {
const failedFixture = {
autofix: {
run_id: 12346,
status: "FAILED",
updated_at: "2025-04-09T22:39:50.778146",
request: {},
steps: [],
},
};
mswServer.use(
http.get(
"https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/autofix/",
() => HttpResponse.json(failedFixture),
{ once: true },
),
);
const result = await getIssueDetails.handler(
{
organizationSlug: "sentry-mcp-evals",
issueId: "CLOUDFLARE-MCP-41",
eventId: undefined,
issueUrl: undefined,
regionUrl: undefined,
},
{
constraints: {
organizationSlug: null,
},
accessToken: "access-token",
userId: "1",
},
);
expect(result).toContain("## Seer Analysis");
expect(result).toContain("**Status:** Analysis failed.");
expect(result).toContain(
"For detailed root cause analysis and solutions, call `analyze_issue_with_seer(organizationSlug='sentry-mcp-evals', issueId='CLOUDFLARE-MCP-41')`",
);
});
it.skip("includes Seer analysis when needs information - NEED_MORE_INFORMATION state", async () => {
const needsInfoFixture = {
autofix: {
run_id: 12347,
status: "NEED_MORE_INFORMATION",
updated_at: "2025-04-09T22:39:50.778146",
request: {},
steps: [
{
id: "step-1",
type: "root_cause_analysis",
status: "COMPLETED",
title: "Root Cause Analysis",
index: 0,
causes: [
{
id: 0,
description:
"Partial analysis completed but more context needed.",
root_cause_reproduction: [],
},
],
progress: [],
queued_user_messages: [],
selection: null,
},
],
},
};
mswServer.use(
http.get(
"https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/CLOUDFLARE-MCP-41/autofix/",
() => HttpResponse.json(needsInfoFixture),
{ once: true },
),
);
const result = await getIssueDetails.handler(
{
organizationSlug: "sentry-mcp-evals",
issueId: "CLOUDFLARE-MCP-41",
eventId: undefined,
issueUrl: undefined,
regionUrl: undefined,
},
{
constraints: {
organizationSlug: null,
},
accessToken: "access-token",
userId: "1",
},
);
expect(result).toContain("## Seer Analysis");
expect(result).toContain("**Root Cause Identified:**");
expect(result).toContain(
"Partial analysis completed but more context needed.",
);
expect(result).toContain(
"**Status:** Analysis paused - additional information needed.",
);
expect(result).toContain(
"For detailed root cause analysis and solutions, call `analyze_issue_with_seer(organizationSlug='sentry-mcp-evals', issueId='CLOUDFLARE-MCP-41')`",
);
});
it("handles default event type (error without exception data)", async () => {
// Mock a "default" event type - represents errors without exception data
mswServer.use(
http.get(
"https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/DEFAULT-001/events/latest/",
() => {
return HttpResponse.json({
id: "abc123def456",
title: "Error without exception data",
message: "Something went wrong",
platform: "python",
type: "default", // This is the key part - default event type
dateCreated: "2025-10-02T12:00:00.000Z",
culprit: "unknown",
entries: [
{
type: "message",
data: {
formatted: "Something went wrong",
message: "Something went wrong",
},
},
],
tags: [
{ key: "level", value: "error" },
{ key: "environment", value: "production" },
],
contexts: {},
});
},
),
http.get(
"https://sentry.io/api/0/organizations/sentry-mcp-evals/issues/DEFAULT-001/",
() => {
return HttpResponse.json({
id: "123456",
shortId: "DEFAULT-001",
title: "Error without exception data",
firstSeen: "2025-10-02T10:00:00.000Z",
lastSeen: "2025-10-02T12:00:00.000Z",
count: "5",
userCount: 2,
permalink: "https://sentry-mcp-evals.sentry.io/issues/123456/",
project: {
id: "4509062593708032",
name: "TEST-PROJECT",
slug: "test-project",
platform: "python",
},
status: "unresolved",
culprit: "unknown",
type: "default",
platform: "python",
});
},
),
);
const result = await getIssueDetails.handler(
{
organizationSlug: "sentry-mcp-evals",
issueId: "DEFAULT-001",
eventId: undefined,
issueUrl: undefined,
regionUrl: undefined,
},
{
constraints: {
organizationSlug: null,
},
accessToken: "access-token",
userId: "1",
},
);
// Verify the event was processed successfully
expect(result).toContain("# Issue DEFAULT-001 in **sentry-mcp-evals**");
expect(result).toContain("Error without exception data");
expect(result).toContain("**Event ID**: abc123def456");
// Default events should show dateCreated just like error events
expect(result).toContain("**Occurred At**: 2025-10-02T12:00:00.000Z");
expect(result).toContain("### Error");
expect(result).toContain("Something went wrong");
});
});