@mastra/server-adapters-test-suite 0.1.3-alpha.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
2
+ import { handleError } from "@mastra/server/handlers/error";
1
3
  import { HTTPException, SERVER_ROUTES, WorkflowRegistry, createRoute } from "@mastra/server/server-adapter";
2
4
  import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
3
5
  import * as zod from "zod";
@@ -57,6 +59,7 @@ function getRouteSpecificSchemaDefaults(route) {
57
59
  predicateScope: "trace",
58
60
  path: "entityName"
59
61
  } };
62
+ if (route.path === "/agent-controller/:controllerId/sessions" && route.method === "POST") return { body: { resourceId: "test-resource" } };
60
63
  if (route.path === "/datasets/:datasetId/compare") return { body: {
61
64
  experimentIds: ["test-experiment-a", "test-experiment-b"],
62
65
  baselineId: "test-experiment-a"
@@ -134,8 +137,10 @@ function generateValidDataFromSchema(schema, fieldName) {
134
137
  def = getZodDef(schema);
135
138
  }
136
139
  if (typeName === "ZodOptional" || typeName === "ZodNullable") return generateValidDataFromSchema(def.innerType, fieldName);
137
- if (typeName === "ZodDefault") if ("_zod" in schema) return def.defaultValue;
138
- else return def.defaultValue();
140
+ if (typeName === "ZodDefault") {
141
+ if ("_zod" in schema) return def.defaultValue;
142
+ else return def.defaultValue();
143
+ }
139
144
  if (typeName === "ZodString") {
140
145
  if ((def.checks ?? []).some((check) => {
141
146
  const checkDef = check._zod?.def ?? check.def ?? check;
@@ -233,13 +238,19 @@ function generateValidDataFromSchema(schema, fieldName) {
233
238
  if (typeName === "ZodUndefined") return void 0;
234
239
  if (typeName === "ZodDate") return /* @__PURE__ */ new Date();
235
240
  if (typeName === "ZodBigInt") return BigInt(0);
236
- if (typeName === "ZodLiteral") if ("_zod" in schema) return def.values?.[0];
237
- else return def.value;
238
- if (typeName === "ZodEnum") if ("_zod" in schema) return Object.values(def.entries)[0];
239
- else return def.values[0];
241
+ if (typeName === "ZodLiteral") {
242
+ if ("_zod" in schema) return def.values?.[0];
243
+ else return def.value;
244
+ }
245
+ if (typeName === "ZodEnum") {
246
+ if ("_zod" in schema) return Object.values(def.entries)[0];
247
+ else return def.values[0];
248
+ }
240
249
  if (typeName === "ZodNativeEnum") return Object.values(def.values)[0];
241
- if (typeName === "ZodArray") if ("_zod" in schema) return [generateValidDataFromSchema(def.element, fieldName)];
242
- else return [generateValidDataFromSchema(def.type, fieldName)];
250
+ if (typeName === "ZodArray") {
251
+ if ("_zod" in schema) return [generateValidDataFromSchema(def.element, fieldName)];
252
+ else return [generateValidDataFromSchema(def.type, fieldName)];
253
+ }
243
254
  if (typeName === "ZodObject") {
244
255
  const shape = typeof def.shape === "function" ? def.shape() : def.shape;
245
256
  const obj = {};
@@ -307,6 +318,7 @@ function getDefaultValidPathParams(route) {
307
318
  if (route.path.includes(":controllerId")) params.controllerId = "test-controller";
308
319
  if (route.path.includes(":responseId")) params.responseId = "test-response";
309
320
  if (route.path.includes(":resourceId")) params.resourceId = "test-resource";
321
+ if (route.path.includes(":sessionId")) params.sessionId = "test-session";
310
322
  if (route.path.includes(":modelConfigId")) params.modelConfigId = "id1";
311
323
  if (route.path.includes(":scorerId") && route.path.includes("/stored/scorers/")) params.scorerId = "test-stored-scorer";
312
324
  else if (route.path.includes(":scorerId")) params.scorerId = "test-scorer";
@@ -430,7 +442,8 @@ function createRouteTestSuite(config) {
430
442
  });
431
443
  });
432
444
  routes.forEach((route) => {
433
- describe(`${route.method} ${route.path}`, () => {
445
+ const routeKey = `${route.method} ${route.path}`;
446
+ describe(routeKey, () => {
434
447
  if (route.deprecated) {
435
448
  it("should be marked as deprecated", () => {
436
449
  expect(route.deprecated).toBe(true);
@@ -782,9 +795,7 @@ async function createDefaultTestContext() {
782
795
  case "multiply":
783
796
  result = a * b;
784
797
  break;
785
- case "divide":
786
- result = b !== 0 ? a / b : Infinity;
787
- break;
798
+ case "divide": result = b !== 0 ? a / b : Infinity;
788
799
  }
789
800
  return { result };
790
801
  }
@@ -1336,9 +1347,10 @@ Follow these instructions for the test skill.
1336
1347
  installedAt: (/* @__PURE__ */ new Date("2024-01-01T00:00:00.000Z")).toISOString()
1337
1348
  };
1338
1349
  fs.writeFileSync(path.join(agentSkillsDir, ".meta.json"), JSON.stringify(metaJson, null, 2));
1350
+ const filesystem = new LocalFilesystem({ basePath: tempDir });
1339
1351
  const workspace = new Workspace({
1340
1352
  id: "test-workspace",
1341
- filesystem: new LocalFilesystem({ basePath: tempDir }),
1353
+ filesystem,
1342
1354
  skills: ["skills"],
1343
1355
  bm25: true
1344
1356
  });
@@ -1362,7 +1374,8 @@ function createTestTool(overrides = {}) {
1362
1374
  * Following the pattern from handler tests - uses actual MockMemory implementation
1363
1375
  */
1364
1376
  function createMockMemory() {
1365
- const mockMemory = new MockMemory({ storage: new InMemoryStore() });
1377
+ const storage = new InMemoryStore();
1378
+ const mockMemory = new MockMemory({ storage });
1366
1379
  mockMemory.__registerMastra = vi.fn();
1367
1380
  return mockMemory;
1368
1381
  }
@@ -1831,7 +1844,8 @@ function createRouteAdapterTestSuite(config) {
1831
1844
  expect(response.status).toBe(200);
1832
1845
  const data = response.data;
1833
1846
  expect(data.filter).toEqual({ status: "active" });
1834
- expect(Array.isArray(data.tag) ? data.tag : [data.tag]).toContain("b");
1847
+ const tags = Array.isArray(data.tag) ? data.tag : [data.tag];
1848
+ expect(tags).toContain("b");
1835
1849
  expect(data.keys).not.toContain("__proto__");
1836
1850
  expect(data.keys).not.toContain("constructor");
1837
1851
  expect(data.keys).not.toContain("prototype");
@@ -2102,7 +2116,8 @@ function createRouteAdapterTestSuite(config) {
2102
2116
  Object.entries(deprecatedByCategory).forEach(([category, routes]) => {
2103
2117
  describe(category, () => {
2104
2118
  routes.forEach((route) => {
2105
- describe(`${route.method} ${route.path}`, () => {
2119
+ const testName = `${route.method} ${route.path}`;
2120
+ describe(testName, () => {
2106
2121
  it("should be marked as deprecated", () => {
2107
2122
  expect(route.deprecated).toBe(true);
2108
2123
  expect(route.openapi?.deprecated).toBe(true);
@@ -2162,7 +2177,8 @@ function createRouteAdapterTestSuite(config) {
2162
2177
  Object.entries(routesByCategory).forEach(([category, routes]) => {
2163
2178
  describe(category, () => {
2164
2179
  routes.forEach((route) => {
2165
- describe(`${route.method} ${route.path}`, () => {
2180
+ const testName = `${route.method} ${route.path}`;
2181
+ describe(testName, () => {
2166
2182
  it("should execute with valid request", async () => {
2167
2183
  const request = buildRouteRequest(route);
2168
2184
  const httpRequest = {
@@ -2197,7 +2213,8 @@ function createRouteAdapterTestSuite(config) {
2197
2213
  query: request.query,
2198
2214
  body: request.body
2199
2215
  };
2200
- expect((await executeHttpRequest(app, httpRequest)).status).toBe(404);
2216
+ const response = await executeHttpRequest(app, httpRequest);
2217
+ expect(response.status).toBe(404);
2201
2218
  });
2202
2219
  if (route.path.includes(":workflowId")) it("should return 404 when workflow not found", async () => {
2203
2220
  const request = buildRouteRequest(route, { pathParams: { workflowId: "non-existent-workflow" } });
@@ -2207,7 +2224,8 @@ function createRouteAdapterTestSuite(config) {
2207
2224
  query: request.query,
2208
2225
  body: request.body
2209
2226
  };
2210
- expect((await executeHttpRequest(app, httpRequest)).status).toBe(404);
2227
+ const response = await executeHttpRequest(app, httpRequest);
2228
+ expect(response.status).toBe(404);
2211
2229
  });
2212
2230
  if (route.path.includes(":backgroundTaskId")) it("should return 404 when background task not found", async () => {
2213
2231
  const request = buildRouteRequest(route, { pathParams: { backgroundTaskId: "non-existent-background-task" } });
@@ -2217,7 +2235,8 @@ function createRouteAdapterTestSuite(config) {
2217
2235
  query: request.query,
2218
2236
  body: request.body
2219
2237
  };
2220
- expect((await executeHttpRequest(app, httpRequest)).status).toBe(404);
2238
+ const response = await executeHttpRequest(app, httpRequest);
2239
+ expect(response.status).toBe(404);
2221
2240
  });
2222
2241
  if (route.path.includes(":serverId")) it("should return 404 when MCP server not found (via :serverId)", async () => {
2223
2242
  const request = buildRouteRequest(route, { pathParams: { serverId: "non-existent-server" } });
@@ -2227,7 +2246,8 @@ function createRouteAdapterTestSuite(config) {
2227
2246
  query: request.query,
2228
2247
  body: request.body
2229
2248
  };
2230
- expect((await executeHttpRequest(app, httpRequest)).status).toBe(404);
2249
+ const response = await executeHttpRequest(app, httpRequest);
2250
+ expect(response.status).toBe(404);
2231
2251
  });
2232
2252
  if (route.path.includes("/mcp/v0/servers/:id")) it("should return 404 when MCP server not found (via :id)", async () => {
2233
2253
  const request = buildRouteRequest(route, { pathParams: { id: "non-existent-server" } });
@@ -2237,7 +2257,8 @@ function createRouteAdapterTestSuite(config) {
2237
2257
  query: request.query,
2238
2258
  body: request.body
2239
2259
  };
2240
- expect((await executeHttpRequest(app, httpRequest)).status).toBe(404);
2260
+ const response = await executeHttpRequest(app, httpRequest);
2261
+ expect(response.status).toBe(404);
2241
2262
  });
2242
2263
  if (route.path.includes(":processorId")) it("should return 404 when processor not found", async () => {
2243
2264
  const request = buildRouteRequest(route, { pathParams: { processorId: "non-existent-processor" } });
@@ -2247,7 +2268,8 @@ function createRouteAdapterTestSuite(config) {
2247
2268
  query: request.query,
2248
2269
  body: request.body
2249
2270
  };
2250
- expect((await executeHttpRequest(app, httpRequest)).status).toBe(404);
2271
+ const response = await executeHttpRequest(app, httpRequest);
2272
+ expect(response.status).toBe(404);
2251
2273
  });
2252
2274
  if (route.responseType === "stream") it("should be consumable via stream reader", async () => {
2253
2275
  const request = buildRouteRequest(route);
@@ -2262,9 +2284,13 @@ function createRouteAdapterTestSuite(config) {
2262
2284
  expect(response.stream).toBeDefined();
2263
2285
  if (typeof response.stream.getReader === "function") {
2264
2286
  const reader = response.stream.getReader();
2265
- expect(await reader.read()).toBeDefined();
2287
+ const firstChunk = await reader.read();
2288
+ expect(firstChunk).toBeDefined();
2266
2289
  reader.releaseLock();
2267
- } else if (typeof response.stream[Symbol.asyncIterator] === "function") expect(await response.stream[Symbol.asyncIterator]().next()).toBeDefined();
2290
+ } else if (typeof response.stream[Symbol.asyncIterator] === "function") {
2291
+ const firstChunk = await response.stream[Symbol.asyncIterator]().next();
2292
+ expect(firstChunk).toBeDefined();
2293
+ }
2268
2294
  });
2269
2295
  if (route.queryParamSchema || route.bodySchema) it("should return 400 when schema validation fails", async () => {
2270
2296
  const request = buildRouteRequest(route);
@@ -2321,7 +2347,8 @@ function createRouteAdapterTestSuite(config) {
2321
2347
  }
2322
2348
  }
2323
2349
  };
2324
- expect((await executeHttpRequest(app, httpRequest)).status).toBeLessThan(500);
2350
+ const response = await executeHttpRequest(app, httpRequest);
2351
+ expect(response.status).toBeLessThan(500);
2325
2352
  });
2326
2353
  const bodySchema = route.bodySchema;
2327
2354
  if (["POST", "PUT"].includes(route.method) && bodySchema) it("should spread body fields to handler params", async () => {
@@ -2382,7 +2409,8 @@ function createRouteAdapterTestSuite(config) {
2382
2409
  expect(response.type).toBe("json");
2383
2410
  const errorData = response.data;
2384
2411
  expect(errorData).toBeDefined();
2385
- expect(errorData.error !== void 0 || errorData.message !== void 0 || errorData.details !== void 0 || errorData.statusCode !== void 0).toBe(true);
2412
+ const hasErrorField = errorData.error !== void 0 || errorData.message !== void 0 || errorData.details !== void 0 || errorData.statusCode !== void 0;
2413
+ expect(hasErrorField).toBe(true);
2386
2414
  });
2387
2415
  });
2388
2416
  SERVER_ROUTES.filter((r) => r.method === "POST" && r.bodySchema && !isExcluded(r)).forEach((route) => {
@@ -2498,41 +2526,88 @@ function createRouteAdapterTestSuite(config) {
2498
2526
  expect(custom.getHandlerCalls()).toBe(1);
2499
2527
  });
2500
2528
  });
2529
+ describe("Run claim conflict responses (#27638)", () => {
2530
+ it.each([
2531
+ "AGENT_RESUME_ALREADY_CLAIMED",
2532
+ "WORKFLOW_RESUME_ALREADY_CLAIMED",
2533
+ "AGENT_RESUME_RUN_NOT_SUSPENDED",
2534
+ "WORKFLOW_RUN_NOT_SUSPENDED",
2535
+ "WORKFLOW_TIME_TRAVEL_ALREADY_CLAIMED",
2536
+ "WORKFLOW_TIME_TRAVEL_RUN_RUNNING"
2537
+ ])("lets clients identify %s without matching message text", async (code) => {
2538
+ const error = new MastraError({
2539
+ id: code,
2540
+ domain: ErrorDomain.MASTRA_WORKFLOW,
2541
+ category: ErrorCategory.USER
2542
+ }, "Run conflict");
2543
+ const route = {
2544
+ method: "POST",
2545
+ path: "/test/run-claim-conflict",
2546
+ responseType: "json",
2547
+ handler: async () => handleError(error, "Unable to resume")
2548
+ };
2549
+ const mutableServerRoutes = SERVER_ROUTES;
2550
+ const originalLength = mutableServerRoutes.length;
2551
+ mutableServerRoutes.push(route);
2552
+ let conflictApp;
2553
+ try {
2554
+ ({app: conflictApp} = await setupAdapter(context));
2555
+ } finally {
2556
+ mutableServerRoutes.splice(originalLength);
2557
+ }
2558
+ const response = await executeHttpRequest(conflictApp, {
2559
+ method: "POST",
2560
+ path: "/api/test/run-claim-conflict",
2561
+ body: {}
2562
+ });
2563
+ expect(response.status).toBe(409);
2564
+ expect(response.headers["content-type"]).toContain("application/json");
2565
+ expect(response.data).toEqual({
2566
+ error: "Run conflict",
2567
+ code
2568
+ });
2569
+ });
2570
+ });
2501
2571
  describe("Route Prefix", () => {
2502
2572
  it("should register routes at prefixed paths without double /api", async () => {
2503
2573
  const prefixedApp = (await setupAdapter(context, { prefix: "/v2" })).app;
2504
- expect((await executeHttpRequest(prefixedApp, {
2574
+ const response = await executeHttpRequest(prefixedApp, {
2505
2575
  method: "GET",
2506
2576
  path: "/v2/agents"
2507
- })).status).toBeLessThan(400);
2577
+ });
2578
+ expect(response.status).toBeLessThan(400);
2508
2579
  });
2509
2580
  it("should not have routes at double /api path when prefix is set", async () => {
2510
2581
  const prefixedApp = (await setupAdapter(context, { prefix: "/v2" })).app;
2511
- expect((await executeHttpRequest(prefixedApp, {
2582
+ const response = await executeHttpRequest(prefixedApp, {
2512
2583
  method: "GET",
2513
2584
  path: "/v2/api/agents"
2514
- })).status).toBe(404);
2585
+ });
2586
+ expect(response.status).toBe(404);
2515
2587
  });
2516
2588
  it("should normalize prefix with trailing slash", async () => {
2517
2589
  const prefixedApp = (await setupAdapter(context, { prefix: "/mastra/" })).app;
2518
- expect((await executeHttpRequest(prefixedApp, {
2590
+ const response = await executeHttpRequest(prefixedApp, {
2519
2591
  method: "GET",
2520
2592
  path: "/mastra/agents"
2521
- })).status).toBeLessThan(400);
2593
+ });
2594
+ expect(response.status).toBeLessThan(400);
2522
2595
  });
2523
2596
  it("should normalize prefix without leading slash", async () => {
2524
2597
  const prefixedApp = (await setupAdapter(context, { prefix: "mastra" })).app;
2525
- expect((await executeHttpRequest(prefixedApp, {
2598
+ const response = await executeHttpRequest(prefixedApp, {
2526
2599
  method: "GET",
2527
2600
  path: "/mastra/agents"
2528
- })).status).toBeLessThan(400);
2601
+ });
2602
+ expect(response.status).toBeLessThan(400);
2529
2603
  });
2530
2604
  it("should not have routes at double-slash path when prefix has trailing slash", async () => {
2531
2605
  const prefixedApp = (await setupAdapter(context, { prefix: "/mastra/" })).app;
2532
- expect((await executeHttpRequest(prefixedApp, {
2606
+ const response = await executeHttpRequest(prefixedApp, {
2533
2607
  method: "GET",
2534
2608
  path: "/mastra//agents"
2535
- })).status).toBe(404);
2609
+ });
2610
+ expect(response.status).toBe(404);
2536
2611
  });
2537
2612
  });
2538
2613
  });
@@ -2696,10 +2771,11 @@ function createMCPRouteTestSuite(config) {
2696
2771
  expect(res.data.tools).toEqual(expect.arrayContaining([expect.objectContaining({ name: "getWeather" }), expect.objectContaining({ name: "calculate" })]));
2697
2772
  });
2698
2773
  it("should return 404 for non-existent server", async () => {
2699
- expect((await executeHttpRequest(app, {
2774
+ const res = await executeHttpRequest(app, {
2700
2775
  method: "GET",
2701
2776
  path: "/api/mcp/non-existent/tools"
2702
- })).status).toBe(404);
2777
+ });
2778
+ expect(res.status).toBe(404);
2703
2779
  });
2704
2780
  });
2705
2781
  describe("GET /api/mcp/:serverId/tools/:toolId", () => {
@@ -2716,10 +2792,11 @@ function createMCPRouteTestSuite(config) {
2716
2792
  });
2717
2793
  });
2718
2794
  it("should return 404 for non-existent tool", async () => {
2719
- expect((await executeHttpRequest(app, {
2795
+ const res = await executeHttpRequest(app, {
2720
2796
  method: "GET",
2721
2797
  path: `/api/mcp/${mcpServer1.id}/tools/non-existent`
2722
- })).status).toBe(404);
2798
+ });
2799
+ expect(res.status).toBe(404);
2723
2800
  });
2724
2801
  });
2725
2802
  describe("POST /api/mcp/:serverId/tools/:toolId/execute", () => {
@@ -2749,7 +2826,7 @@ function createMCPRouteTestSuite(config) {
2749
2826
  });
2750
2827
  });
2751
2828
  it("should return 404 for non-existent server", async () => {
2752
- expect((await executeHttpRequest(app, {
2829
+ const res = await executeHttpRequest(app, {
2753
2830
  method: "POST",
2754
2831
  path: "/api/mcp/non-existent/tools/calculate/execute",
2755
2832
  body: { data: {
@@ -2757,7 +2834,8 @@ function createMCPRouteTestSuite(config) {
2757
2834
  a: 1,
2758
2835
  b: 2
2759
2836
  } }
2760
- })).status).toBe(404);
2837
+ });
2838
+ expect(res.status).toBe(404);
2761
2839
  });
2762
2840
  it("should return error for non-existent tool", async () => {
2763
2841
  const res = await executeHttpRequest(app, {
@@ -3028,9 +3106,7 @@ function createMCPTransportTestSuite(config) {
3028
3106
  case "multiply":
3029
3107
  result = a * b;
3030
3108
  break;
3031
- case "divide":
3032
- result = a / b;
3033
- break;
3109
+ case "divide": result = a / b;
3034
3110
  }
3035
3111
  return { result };
3036
3112
  }
@@ -3092,7 +3168,8 @@ function createMCPTransportTestSuite(config) {
3092
3168
  path: "messages",
3093
3169
  method: "POST"
3094
3170
  }])("rejects the legacy $path route for a native server", async ({ path, method }) => {
3095
- expect((await fetch(`http://localhost:${port}/api/mcp/native-fixture/${path}`, { method })).status).toBe(404);
3171
+ const response = await fetch(`http://localhost:${port}/api/mcp/native-fixture/${path}`, { method });
3172
+ expect(response.status).toBe(404);
3096
3173
  });
3097
3174
  it("executes ordinary tools with the v2 request context and reports a suspended tool truthfully", async () => {
3098
3175
  const base = `http://localhost:${port}/api/mcp/native-fixture/tools`;
@@ -3144,7 +3221,7 @@ function createMCPTransportTestSuite(config) {
3144
3221
  describe("HTTP Transport (/api/mcp/:serverId/mcp)", () => {
3145
3222
  describe("Error handling (raw HTTP)", () => {
3146
3223
  it("should return 404 for non-existent server", async () => {
3147
- expect((await fetch(`http://localhost:${port}/api/mcp/non-existent/mcp`, {
3224
+ const res = await fetch(`http://localhost:${port}/api/mcp/non-existent/mcp`, {
3148
3225
  method: "POST",
3149
3226
  headers: {
3150
3227
  "Content-Type": "application/json",
@@ -3163,7 +3240,8 @@ function createMCPTransportTestSuite(config) {
3163
3240
  },
3164
3241
  id: 1
3165
3242
  })
3166
- })).status).toBe(404);
3243
+ });
3244
+ expect(res.status).toBe(404);
3167
3245
  });
3168
3246
  });
3169
3247
  describe("Protocol operations (MCPClient)", () => {
@@ -3199,7 +3277,8 @@ function createMCPTransportTestSuite(config) {
3199
3277
  const tools = await mcpClient.listTools();
3200
3278
  expect(tools["server1_getWeather"]).toBeDefined();
3201
3279
  expect(tools["server1_calculate"]).toBeDefined();
3202
- expect(Object.keys(tools).filter((k) => k.startsWith("server2_"))).toHaveLength(0);
3280
+ const server2Tools = Object.keys(tools).filter((k) => k.startsWith("server2_"));
3281
+ expect(server2Tools).toHaveLength(0);
3203
3282
  });
3204
3283
  });
3205
3284
  describe("Tool execution errors (MCPClient)", () => {
@@ -3208,17 +3287,18 @@ function createMCPTransportTestSuite(config) {
3208
3287
  let failingMastra;
3209
3288
  let failingHttpServer;
3210
3289
  beforeAll(async () => {
3290
+ const failingTool = createTool({
3291
+ id: "failingTool",
3292
+ description: "A tool that always throws an error",
3293
+ inputSchema: z.object({}),
3294
+ execute: async () => {
3295
+ throw new Error("Tool execution failed intentionally");
3296
+ }
3297
+ });
3211
3298
  failingServer = new MCPServer({
3212
3299
  name: "failingServer",
3213
3300
  version: "1.0.0",
3214
- tools: { failingTool: createTool({
3215
- id: "failingTool",
3216
- description: "A tool that always throws an error",
3217
- inputSchema: z.object({}),
3218
- execute: async () => {
3219
- throw new Error("Tool execution failed intentionally");
3220
- }
3221
- }) }
3301
+ tools: { failingTool }
3222
3302
  });
3223
3303
  failingMastra = new Mastra$1({ mcpServers: { "failing-server": failingServer } });
3224
3304
  const serverSetup = await createServer(failingMastra);
@@ -3253,10 +3333,11 @@ function createMCPTransportTestSuite(config) {
3253
3333
  describe("SSE Transport (/api/mcp/:serverId/sse)", () => {
3254
3334
  describe("Error handling (raw HTTP)", () => {
3255
3335
  it("should return 404 for non-existent server on GET /sse", async () => {
3256
- expect((await fetch(`http://localhost:${port}/api/mcp/non-existent/sse`)).status).toBe(404);
3336
+ const res = await fetch(`http://localhost:${port}/api/mcp/non-existent/sse`);
3337
+ expect(res.status).toBe(404);
3257
3338
  });
3258
3339
  it("should return 404 for non-existent server on POST /messages", async () => {
3259
- expect((await fetch(`http://localhost:${port}/api/mcp/non-existent/messages`, {
3340
+ const res = await fetch(`http://localhost:${port}/api/mcp/non-existent/messages`, {
3260
3341
  method: "POST",
3261
3342
  headers: { "Content-Type": "application/json" },
3262
3343
  body: JSON.stringify({
@@ -3264,14 +3345,16 @@ function createMCPTransportTestSuite(config) {
3264
3345
  method: "test",
3265
3346
  id: 1
3266
3347
  })
3267
- })).status).toBe(404);
3348
+ });
3349
+ expect(res.status).toBe(404);
3268
3350
  });
3269
3351
  });
3270
3352
  describe("MCP 2.x servers (MCPClient)", () => {
3271
3353
  it("does not serve the legacy SSE transport for a 2.x MCPServer", async (ctx) => {
3272
3354
  if (mcpServer1.mcpVersion !== 2) ctx.skip();
3273
- expect((await fetch(`http://localhost:${port}/api/mcp/${mcpServer1.id}/sse`)).status).toBe(404);
3274
- expect((await fetch(`http://localhost:${port}/api/mcp/${mcpServer1.id}/messages`, {
3355
+ const res = await fetch(`http://localhost:${port}/api/mcp/${mcpServer1.id}/sse`);
3356
+ expect(res.status).toBe(404);
3357
+ const messages = await fetch(`http://localhost:${port}/api/mcp/${mcpServer1.id}/messages`, {
3275
3358
  method: "POST",
3276
3359
  headers: { "Content-Type": "application/json" },
3277
3360
  body: JSON.stringify({
@@ -3279,7 +3362,8 @@ function createMCPTransportTestSuite(config) {
3279
3362
  method: "tools/list",
3280
3363
  id: 1
3281
3364
  })
3282
- })).status).toBe(404);
3365
+ });
3366
+ expect(messages.status).toBe(404);
3283
3367
  });
3284
3368
  it("fails to connect through the SSE URL without downgrading", async (ctx) => {
3285
3369
  if (mcpServer1.mcpVersion !== 2) ctx.skip();
@@ -3356,7 +3440,8 @@ function createMultipartTestSuite(config) {
3356
3440
  body: formData
3357
3441
  });
3358
3442
  expect(response.status).toBe(200);
3359
- expect(await response.json()).toMatchObject({
3443
+ const result = await response.json();
3444
+ expect(result).toMatchObject({
3360
3445
  success: true,
3361
3446
  hasAudio: true
3362
3447
  });
@@ -3391,10 +3476,11 @@ function createMultipartTestSuite(config) {
3391
3476
  language: "en",
3392
3477
  format: "mp3"
3393
3478
  }));
3394
- expect((await fetch(`${serverInfo.baseUrl}/test/upload-with-options`, {
3479
+ const response = await fetch(`${serverInfo.baseUrl}/test/upload-with-options`, {
3395
3480
  method: "POST",
3396
3481
  body: formData
3397
- })).status).toBe(200);
3482
+ });
3483
+ expect(response.status).toBe(200);
3398
3484
  expect(receivedBody.options).toBeDefined();
3399
3485
  expect(typeof receivedBody.options).toBe("object");
3400
3486
  expect(receivedBody.options.language).toBe("en");
@@ -3424,10 +3510,11 @@ function createMultipartTestSuite(config) {
3424
3510
  ])], { type: "audio/wav" });
3425
3511
  formData.append("audio", audioBlob, "test.wav");
3426
3512
  formData.append("name", "test-recording");
3427
- expect((await fetch(`${serverInfo.baseUrl}/test/upload-with-string`, {
3513
+ const response = await fetch(`${serverInfo.baseUrl}/test/upload-with-string`, {
3428
3514
  method: "POST",
3429
3515
  body: formData
3430
- })).status).toBe(200);
3516
+ });
3517
+ expect(response.status).toBe(200);
3431
3518
  expect(receivedBody.name).toBe("test-recording");
3432
3519
  });
3433
3520
  it("should reject files exceeding size limit", async () => {
@@ -3449,10 +3536,11 @@ function createMultipartTestSuite(config) {
3449
3536
  const formData = new FormData();
3450
3537
  const largeBlob = new Blob([/* @__PURE__ */ new Uint8Array(200)], { type: "audio/wav" });
3451
3538
  formData.append("audio", largeBlob, "large.wav");
3452
- expect((await fetch(`${serverInfo.baseUrl}/test/upload-limited`, {
3539
+ const response = await fetch(`${serverInfo.baseUrl}/test/upload-limited`, {
3453
3540
  method: "POST",
3454
3541
  body: formData
3455
- })).status).toBeGreaterThanOrEqual(400);
3542
+ });
3543
+ expect(response.status).toBeGreaterThanOrEqual(400);
3456
3544
  });
3457
3545
  it("should handle empty FormData gracefully", async () => {
3458
3546
  const { adapter, app } = await setupAdapter(context);
@@ -3485,7 +3573,8 @@ function createMultipartTestSuite(config) {
3485
3573
  body: formData
3486
3574
  });
3487
3575
  expect(response.status).toBe(200);
3488
- expect(await response.json()).toMatchObject({
3576
+ const result = await response.json();
3577
+ expect(result).toMatchObject({
3489
3578
  success: true,
3490
3579
  bodyKeys: []
3491
3580
  });
@@ -3527,7 +3616,8 @@ function createMultipartTestSuite(config) {
3527
3616
  body: formData
3528
3617
  });
3529
3618
  expect(response.status).toBe(200);
3530
- expect(await response.json()).toMatchObject({
3619
+ const result = await response.json();
3620
+ expect(result).toMatchObject({
3531
3621
  hasFile1: true,
3532
3622
  hasFile2: true
3533
3623
  });
@@ -3556,7 +3646,8 @@ function createMultipartTestSuite(config) {
3556
3646
  body: JSON.stringify({ message: "Hello, World!" })
3557
3647
  });
3558
3648
  expect(response.status).toBe(200);
3559
- expect(await response.json()).toMatchObject({
3649
+ const result = await response.json();
3650
+ expect(result).toMatchObject({
3560
3651
  success: true,
3561
3652
  message: "Hello, World!"
3562
3653
  });
@@ -3656,7 +3747,8 @@ function createHttpLoggingTestSuite(config) {
3656
3747
  logSpy = vi.spyOn(adapter.logger, "info");
3657
3748
  await adapter.init();
3658
3749
  await addRoute(app, "GET", "/test", () => ({ message: "success" }));
3659
- expect((await executeRequest(app, "GET", "http://localhost/test")).status).toBe(200);
3750
+ const response = await executeRequest(app, "GET", "http://localhost/test");
3751
+ expect(response.status).toBe(200);
3660
3752
  expect(logSpy).toHaveBeenCalledWith(expect.stringMatching(/GET \/test 200 \d+ms/), expect.objectContaining({
3661
3753
  method: "GET",
3662
3754
  path: "/test",
@@ -3834,10 +3926,11 @@ function createBodyLimitTestSuite(config) {
3834
3926
  return { receivedBody: body };
3835
3927
  }
3836
3928
  });
3837
- expect((await executeRequest(wiredApp, "POST", "http://localhost/test/route-body-limit", {
3929
+ const response = await executeRequest(wiredApp, "POST", "http://localhost/test/route-body-limit", {
3838
3930
  headers: { "Content-Type": "application/json" },
3839
3931
  body: oversizedPayload
3840
- })).status).toBe(413);
3932
+ });
3933
+ expect(response.status).toBe(413);
3841
3934
  expect(handlerCalled).toBe(false);
3842
3935
  await cleanupApp?.(wiredApp);
3843
3936
  });
@@ -3856,10 +3949,11 @@ function createBodyLimitTestSuite(config) {
3856
3949
  return { receivedBody: body };
3857
3950
  }
3858
3951
  });
3859
- expect((await executeRequestWithoutContentLength(wiredApp, "POST", "http://localhost/test/route-body-limit-no-content-length", {
3952
+ const response = await executeRequestWithoutContentLength(wiredApp, "POST", "http://localhost/test/route-body-limit-no-content-length", {
3860
3953
  headers: { "Content-Type": "application/json" },
3861
3954
  body: oversizedPayload
3862
- })).status).toBe(413);
3955
+ });
3956
+ expect(response.status).toBe(413);
3863
3957
  expect(handlerCalled).toBe(false);
3864
3958
  await cleanupApp?.(wiredApp);
3865
3959
  });
@@ -3876,10 +3970,11 @@ function createBodyLimitTestSuite(config) {
3876
3970
  responseType: "json",
3877
3971
  handler: async ({ body }) => ({ receivedBody: body })
3878
3972
  });
3879
- expect((await executeRequest(wiredApp, method, "http://localhost/test/body-limit", {
3973
+ const response = await executeRequest(wiredApp, method, "http://localhost/test/body-limit", {
3880
3974
  headers: { "Content-Type": "application/json" },
3881
3975
  body: oversizedPayload
3882
- })).status).toBe(413);
3976
+ });
3977
+ expect(response.status).toBe(413);
3883
3978
  await cleanupApp?.(wiredApp);
3884
3979
  });
3885
3980
  });