@mastra/server-adapters-test-suite 0.1.4-alpha.0 → 0.1.4-alpha.1
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/CHANGELOG.md +13 -0
- package/dist/durable-disconnect-test-suite.d.ts +23 -0
- package/dist/durable-disconnect-test-suite.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +275 -1
- package/dist/index.js.map +1 -1
- package/dist/route-adapter-test-suite.d.ts.map +1 -1
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @mastra/server-adapters-test-suite
|
|
2
2
|
|
|
3
|
+
## 0.1.4-alpha.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Add a reusable real-HTTP disconnect suite covering durable and evented stream starts, resumes, tool approvals, observer replay, and explicit cancellation, with ordinary agents as the cancellation control. ([#28162](https://github.com/mastra-ai/mastra/pull/28162))
|
|
8
|
+
|
|
9
|
+
- Skipped the run-scoped abort route (`POST /agents/:agentId/runs/:runId/abort`) in the generic adapter route suite, since it returns 501 for the suite's non-durable agent by design. Adapter suites no longer fail on this route. ([#28301](https://github.com/mastra-ai/mastra/pull/28301))
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [[`f396b43`](https://github.com/mastra-ai/mastra/commit/f396b435c72042fc6d9e82c361fa047675995812), [`f396b43`](https://github.com/mastra-ai/mastra/commit/f396b435c72042fc6d9e82c361fa047675995812), [`0d7d5cb`](https://github.com/mastra-ai/mastra/commit/0d7d5cbdccacc0b03697b3400579ea520e382061), [`0d7d5cb`](https://github.com/mastra-ai/mastra/commit/0d7d5cbdccacc0b03697b3400579ea520e382061), [`f5ebbf3`](https://github.com/mastra-ai/mastra/commit/f5ebbf3b2dc935b8fc068996b4db478815f05bd5), [`11c83d5`](https://github.com/mastra-ai/mastra/commit/11c83d53331588e06623d5d4a35baa606bbaa424), [`703b0a1`](https://github.com/mastra-ai/mastra/commit/703b0a1cb342d3a6fce72b5b07685fbb31695674), [`1283717`](https://github.com/mastra-ai/mastra/commit/1283717ecfc652da293aafe6ff582afa07ca84a5), [`3be96ae`](https://github.com/mastra-ai/mastra/commit/3be96ae2f65a2ff3b2189fb79fd03b7eedee3dbe)]:
|
|
12
|
+
- @mastra/core@1.80.0-alpha.1
|
|
13
|
+
- @mastra/server@1.80.0-alpha.1
|
|
14
|
+
- @mastra/mcp@2.6.0-alpha.0
|
|
15
|
+
|
|
3
16
|
## 0.1.4-alpha.0
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Agent } from '@mastra/core/agent';
|
|
2
|
+
import { Mastra } from '@mastra/core/mastra';
|
|
3
|
+
export interface DurableDisconnectTestConfig {
|
|
4
|
+
setupModel: () => Promise<{
|
|
5
|
+
model: ConstructorParameters<typeof Agent>[0]['model'];
|
|
6
|
+
addResponse: (sequenceIndex: number, response: {
|
|
7
|
+
content?: string;
|
|
8
|
+
toolCalls?: {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
arguments: Record<string, unknown>;
|
|
12
|
+
}[];
|
|
13
|
+
}) => void;
|
|
14
|
+
getRequestCount: () => number;
|
|
15
|
+
close: () => Promise<void>;
|
|
16
|
+
}>;
|
|
17
|
+
startServer: (mastra: Mastra, onDisconnect: (path: string) => void) => Promise<{
|
|
18
|
+
baseUrl: string;
|
|
19
|
+
close: () => Promise<void>;
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
export declare function createDurableDisconnectTestSuite(config: DurableDisconnectTestConfig): void;
|
|
23
|
+
//# sourceMappingURL=durable-disconnect-test-suite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"durable-disconnect-test-suite.d.ts","sourceRoot":"","sources":["../src/durable-disconnect-test-suite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAO7C,MAAM,WAAW,2BAA2B;IAC1C,UAAU,EAAE,MAAM,OAAO,CAAC;QACxB,KAAK,EAAE,qBAAqB,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACvD,WAAW,EAAE,CACX,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE;YACR,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,SAAS,CAAC,EAAE;gBAAE,EAAE,EAAE,MAAM,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;aAAE,EAAE,CAAC;SAChF,KACE,IAAI,CAAC;QACV,eAAe,EAAE,MAAM,MAAM,CAAC;QAC9B,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;KAC5B,CAAC,CAAC;IACH,WAAW,EAAE,CACX,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,KACjC,OAAO,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;KAC5B,CAAC,CAAC;CACJ;AAWD,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,2BAA2B,QAmPnF"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { createRouteAdapterTestSuite } from './route-adapter-test-suite.js';
|
|
2
|
+
export { createDurableDisconnectTestSuite, type DurableDisconnectTestConfig } from './durable-disconnect-test-suite.js';
|
|
2
3
|
export type { AdapterTestSuiteConfig, AdapterTestContext, AdapterSetupOptions, HttpRequest, HttpResponse, } from './test-helpers.js';
|
|
3
4
|
export { createMCPRouteTestSuite } from './mcp-route-test-suite.js';
|
|
4
5
|
export { createMCPTransportTestSuite, type MCPTransportTestConfig } from './mcp-transport-test-suite.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AACzE,YAAY,EACV,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACnB,WAAW,EACX,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,2BAA2B,EAAE,KAAK,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACtG,OAAO,EAAE,wBAAwB,EAAE,KAAK,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AACjG,OAAO,EAAE,0BAA0B,EAAE,KAAK,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACxG,OAAO,EAAE,wBAAwB,EAAE,KAAK,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAClG,OAAO,EAAE,6BAA6B,EAAE,KAAK,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AAElH,OAAO,EACL,wBAAwB,EACxB,6BAA6B,EAC7B,mCAAmC,EACnC,4BAA4B,EAC5B,gBAAgB,GACjB,MAAM,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,EAAE,gCAAgC,EAAE,KAAK,2BAA2B,EAAE,MAAM,iCAAiC,CAAC;AACrH,YAAY,EACV,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACnB,WAAW,EACX,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,2BAA2B,EAAE,KAAK,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACtG,OAAO,EAAE,wBAAwB,EAAE,KAAK,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AACjG,OAAO,EAAE,0BAA0B,EAAE,KAAK,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACxG,OAAO,EAAE,wBAAwB,EAAE,KAAK,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAClG,OAAO,EAAE,6BAA6B,EAAE,KAAK,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AAElH,OAAO,EACL,wBAAwB,EACxB,6BAA6B,EAC7B,mCAAmC,EACnC,4BAA4B,EAC5B,gBAAgB,GACjB,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -25,6 +25,7 @@ import { CompositeVoice } from "@mastra/core/voice";
|
|
|
25
25
|
import { createStep, createWorkflow } from "@mastra/core/workflows";
|
|
26
26
|
import { LocalFilesystem, Workspace } from "@mastra/core/workspace";
|
|
27
27
|
import { MCPClient, MCPServer, defineEvent } from "@mastra/mcp";
|
|
28
|
+
import { createDurableAgent, createEventedAgent } from "@mastra/core/agent/durable";
|
|
28
29
|
import { Mastra as Mastra$1 } from "@mastra/core/mastra";
|
|
29
30
|
import http from "http";
|
|
30
31
|
import { MCPServerBase } from "@mastra/core/mcp";
|
|
@@ -2171,6 +2172,7 @@ function createRouteAdapterTestSuite(config) {
|
|
|
2171
2172
|
"/agents/:agentId/observe",
|
|
2172
2173
|
"/mcp/:serverId/events/:eventName/stream",
|
|
2173
2174
|
"/agents/:agentId/recover",
|
|
2175
|
+
"/agents/:agentId/runs/:runId/abort",
|
|
2174
2176
|
"/agents/:agentId/plans/file",
|
|
2175
2177
|
"/tool-providers/:providerId/connections/:connectionId",
|
|
2176
2178
|
"/tool-providers/:providerId/connections/:connectionId/usage",
|
|
@@ -3012,6 +3014,278 @@ function createRouteAdapterTestSuite(config) {
|
|
|
3012
3014
|
});
|
|
3013
3015
|
}
|
|
3014
3016
|
//#endregion
|
|
3017
|
+
//#region src/durable-disconnect-test-suite.ts
|
|
3018
|
+
function deferred() {
|
|
3019
|
+
let resolve;
|
|
3020
|
+
return {
|
|
3021
|
+
promise: new Promise((r) => {
|
|
3022
|
+
resolve = r;
|
|
3023
|
+
}),
|
|
3024
|
+
resolve
|
|
3025
|
+
};
|
|
3026
|
+
}
|
|
3027
|
+
function createDurableDisconnectTestSuite(config) {
|
|
3028
|
+
describe.each([
|
|
3029
|
+
"plain",
|
|
3030
|
+
"durable",
|
|
3031
|
+
"evented"
|
|
3032
|
+
])("HTTP observer disconnect: %s (COR-1489)", (kind) => {
|
|
3033
|
+
let server;
|
|
3034
|
+
let fixture;
|
|
3035
|
+
let mastra;
|
|
3036
|
+
afterEach(async () => {
|
|
3037
|
+
if (mastra) await mastra.shutdown();
|
|
3038
|
+
if (server) await server.close();
|
|
3039
|
+
if (fixture) await fixture.close();
|
|
3040
|
+
server = void 0;
|
|
3041
|
+
mastra = void 0;
|
|
3042
|
+
fixture = void 0;
|
|
3043
|
+
});
|
|
3044
|
+
for (const route of [
|
|
3045
|
+
"stream",
|
|
3046
|
+
"stream-alias",
|
|
3047
|
+
"stream-until-idle",
|
|
3048
|
+
"resume-stream",
|
|
3049
|
+
"resume-alias",
|
|
3050
|
+
"resume-stream-until-idle",
|
|
3051
|
+
"approve-tool-call",
|
|
3052
|
+
"decline-tool-call"
|
|
3053
|
+
]) for (const explicitAbort of [
|
|
3054
|
+
false,
|
|
3055
|
+
true,
|
|
3056
|
+
"run",
|
|
3057
|
+
"threadless"
|
|
3058
|
+
]) {
|
|
3059
|
+
if (typeof explicitAbort === "string" && (kind === "plain" || !["stream", "stream-alias"].includes(route))) continue;
|
|
3060
|
+
const threadless = explicitAbort === "threadless";
|
|
3061
|
+
it(`${route}: disconnect ${explicitAbort ? `then explicit ${explicitAbort === true ? "thread" : explicitAbort} abort stops execution` : "preserves durable execution and replay"}`, async () => {
|
|
3062
|
+
fixture = await config.setupModel();
|
|
3063
|
+
const entered = deferred();
|
|
3064
|
+
const gate = deferred();
|
|
3065
|
+
const disconnected = deferred();
|
|
3066
|
+
const calls = [];
|
|
3067
|
+
let toolSignal;
|
|
3068
|
+
const storage = new InMemoryStore();
|
|
3069
|
+
const memory = new MockMemory({ storage });
|
|
3070
|
+
const slow = createTool({
|
|
3071
|
+
id: "slow",
|
|
3072
|
+
description: "Execute a numbered step",
|
|
3073
|
+
inputSchema: z.object({ n: z.number() }),
|
|
3074
|
+
execute: async ({ n }, context) => {
|
|
3075
|
+
calls.push(n);
|
|
3076
|
+
if (n === 1) {
|
|
3077
|
+
toolSignal = context?.abortSignal;
|
|
3078
|
+
entered.resolve();
|
|
3079
|
+
await Promise.race([gate.promise, new Promise((resolve) => {
|
|
3080
|
+
if (toolSignal?.aborted) return resolve();
|
|
3081
|
+
toolSignal?.addEventListener("abort", () => resolve(), { once: true });
|
|
3082
|
+
})]);
|
|
3083
|
+
}
|
|
3084
|
+
return { n };
|
|
3085
|
+
}
|
|
3086
|
+
});
|
|
3087
|
+
const research = createTool({
|
|
3088
|
+
id: "research",
|
|
3089
|
+
description: "Background research",
|
|
3090
|
+
inputSchema: z.object({}),
|
|
3091
|
+
background: { enabled: true },
|
|
3092
|
+
execute: async () => ({ done: true })
|
|
3093
|
+
});
|
|
3094
|
+
let authorizations = 0;
|
|
3095
|
+
const authorize = createTool({
|
|
3096
|
+
id: "authorize",
|
|
3097
|
+
description: "Authorize the steps",
|
|
3098
|
+
inputSchema: z.object({}),
|
|
3099
|
+
requireApproval: true,
|
|
3100
|
+
execute: async () => {
|
|
3101
|
+
authorizations++;
|
|
3102
|
+
return { approved: true };
|
|
3103
|
+
}
|
|
3104
|
+
});
|
|
3105
|
+
const resumed = route.startsWith("resume") || route.endsWith("tool-call");
|
|
3106
|
+
const offset = resumed ? 1 : 0;
|
|
3107
|
+
const inner = new Agent({
|
|
3108
|
+
id: "disconnect-agent",
|
|
3109
|
+
name: "Disconnect agent",
|
|
3110
|
+
instructions: "Execute three steps.",
|
|
3111
|
+
model: fixture.model,
|
|
3112
|
+
...threadless ? {} : { memory },
|
|
3113
|
+
tools: {
|
|
3114
|
+
slow,
|
|
3115
|
+
research,
|
|
3116
|
+
authorize
|
|
3117
|
+
},
|
|
3118
|
+
backgroundTasks: { tools: { research: true } }
|
|
3119
|
+
});
|
|
3120
|
+
const agent = kind === "durable" ? createDurableAgent({ agent: inner }) : kind === "evented" ? createEventedAgent({ agent: inner }) : inner;
|
|
3121
|
+
const otherAgent = createDurableAgent({ agent: new Agent({
|
|
3122
|
+
id: "other-agent",
|
|
3123
|
+
name: "Other agent",
|
|
3124
|
+
instructions: "Do not run.",
|
|
3125
|
+
model: fixture.model
|
|
3126
|
+
}) });
|
|
3127
|
+
mastra = new Mastra$1({
|
|
3128
|
+
agents: {
|
|
3129
|
+
agent,
|
|
3130
|
+
otherAgent
|
|
3131
|
+
},
|
|
3132
|
+
storage,
|
|
3133
|
+
logger: false,
|
|
3134
|
+
backgroundTasks: {
|
|
3135
|
+
enabled: true,
|
|
3136
|
+
globalConcurrency: 5,
|
|
3137
|
+
perAgentConcurrency: 3
|
|
3138
|
+
}
|
|
3139
|
+
});
|
|
3140
|
+
server = await config.startServer(mastra, (path) => {
|
|
3141
|
+
if (!resumed || !path.endsWith("/stream")) disconnected.resolve();
|
|
3142
|
+
});
|
|
3143
|
+
const base = `${server.baseUrl}/api/agents/agent`;
|
|
3144
|
+
if (resumed) fixture.addResponse(0, { toolCalls: [{
|
|
3145
|
+
id: "authorize-1",
|
|
3146
|
+
name: "authorize",
|
|
3147
|
+
arguments: {}
|
|
3148
|
+
}] });
|
|
3149
|
+
for (let n = 1; n <= 3; n++) fixture.addResponse(offset + n - 1, { toolCalls: [{
|
|
3150
|
+
id: `slow-${n}`,
|
|
3151
|
+
name: "slow",
|
|
3152
|
+
arguments: { n }
|
|
3153
|
+
}] });
|
|
3154
|
+
fixture.addResponse(offset + 3, { content: "done: 3 results" });
|
|
3155
|
+
const runId = `disconnect-${kind}-${route}-${explicitAbort}`;
|
|
3156
|
+
const thread = {
|
|
3157
|
+
thread: runId,
|
|
3158
|
+
resource: "disconnect-user"
|
|
3159
|
+
};
|
|
3160
|
+
if (resumed) {
|
|
3161
|
+
const parked = await fetch(`${base}/stream`, {
|
|
3162
|
+
method: "POST",
|
|
3163
|
+
headers: { "content-type": "application/json" },
|
|
3164
|
+
body: JSON.stringify({
|
|
3165
|
+
messages: "Start",
|
|
3166
|
+
runId,
|
|
3167
|
+
memory: thread,
|
|
3168
|
+
maxSteps: 6
|
|
3169
|
+
})
|
|
3170
|
+
});
|
|
3171
|
+
expect(parked.status).toBe(200);
|
|
3172
|
+
const reader = parked.body.getReader();
|
|
3173
|
+
let received = "";
|
|
3174
|
+
while (!received.includes("tool-call-approval")) {
|
|
3175
|
+
const { done, value } = await reader.read();
|
|
3176
|
+
expect(done).toBe(false);
|
|
3177
|
+
received += new TextDecoder().decode(value);
|
|
3178
|
+
}
|
|
3179
|
+
await reader.cancel();
|
|
3180
|
+
}
|
|
3181
|
+
const client = new AbortController();
|
|
3182
|
+
const request = fetch(`${base}/${route === "stream-alias" ? "stream" : route === "resume-alias" ? "resume-stream" : route}`, {
|
|
3183
|
+
method: "POST",
|
|
3184
|
+
headers: { "content-type": "application/json" },
|
|
3185
|
+
signal: client.signal,
|
|
3186
|
+
body: JSON.stringify({
|
|
3187
|
+
...resumed ? {
|
|
3188
|
+
resumeData: { approved: true },
|
|
3189
|
+
toolCallId: "authorize-1"
|
|
3190
|
+
} : { messages: "Start" },
|
|
3191
|
+
runId,
|
|
3192
|
+
...threadless ? {} : { memory: thread },
|
|
3193
|
+
maxSteps: 6,
|
|
3194
|
+
...["stream-alias", "resume-alias"].includes(route) ? { untilIdle: true } : {}
|
|
3195
|
+
})
|
|
3196
|
+
}).then(async (response) => {
|
|
3197
|
+
expect(response.status).toBe(200);
|
|
3198
|
+
await response.text();
|
|
3199
|
+
}).catch((error) => {
|
|
3200
|
+
if (error.name !== "AbortError") throw error;
|
|
3201
|
+
});
|
|
3202
|
+
try {
|
|
3203
|
+
await entered.promise;
|
|
3204
|
+
client.abort();
|
|
3205
|
+
await disconnected.promise;
|
|
3206
|
+
await request;
|
|
3207
|
+
if (kind !== "plain" && explicitAbort) {
|
|
3208
|
+
const runScoped = typeof explicitAbort === "string";
|
|
3209
|
+
if (runScoped) {
|
|
3210
|
+
const denied = await fetch(`${server.baseUrl}/api/agents/otherAgent/runs/${runId}/abort`, { method: "POST" });
|
|
3211
|
+
expect(denied.status).toBe(403);
|
|
3212
|
+
expect(toolSignal?.aborted).toBe(false);
|
|
3213
|
+
}
|
|
3214
|
+
const response = await fetch(`${base}/${runScoped ? `runs/${runId}/abort` : "threads/abort"}`, {
|
|
3215
|
+
method: "POST",
|
|
3216
|
+
headers: { "content-type": "application/json" },
|
|
3217
|
+
...runScoped ? {} : { body: JSON.stringify({
|
|
3218
|
+
threadId: runId,
|
|
3219
|
+
resourceId: thread.resource,
|
|
3220
|
+
expectedRunId: runId
|
|
3221
|
+
}) }
|
|
3222
|
+
});
|
|
3223
|
+
const result = await response.json();
|
|
3224
|
+
expect(response.status).toBe(200);
|
|
3225
|
+
expect(result).toEqual({ aborted: true });
|
|
3226
|
+
}
|
|
3227
|
+
const completes = kind !== "plain" && !explicitAbort;
|
|
3228
|
+
if (!completes) await vi.waitFor(() => expect(toolSignal?.aborted).toBe(true));
|
|
3229
|
+
else expect(toolSignal?.aborted).toBe(false);
|
|
3230
|
+
const observing = kind === "plain" ? void 0 : fetch(`${base}/observe`, {
|
|
3231
|
+
method: "POST",
|
|
3232
|
+
headers: { "content-type": "application/json" },
|
|
3233
|
+
body: JSON.stringify({ runId })
|
|
3234
|
+
});
|
|
3235
|
+
gate.resolve();
|
|
3236
|
+
if (observing) {
|
|
3237
|
+
const response = await observing;
|
|
3238
|
+
expect(response.status).toBe(200);
|
|
3239
|
+
const replay = await response.text();
|
|
3240
|
+
expect(replay).toContain("finish");
|
|
3241
|
+
if (completes) expect(replay).toContain("done: 3 results");
|
|
3242
|
+
else expect(replay).toContain("abort");
|
|
3243
|
+
}
|
|
3244
|
+
await vi.waitFor(() => expect(calls).toEqual(completes ? [
|
|
3245
|
+
1,
|
|
3246
|
+
2,
|
|
3247
|
+
3
|
|
3248
|
+
] : [1]));
|
|
3249
|
+
expect(fixture.getRequestCount()).toBe(offset + (completes ? 4 : 1));
|
|
3250
|
+
if (resumed) expect(authorizations).toBe(route === "decline-tool-call" ? 0 : 1);
|
|
3251
|
+
if (kind !== "plain") {
|
|
3252
|
+
const workflows = await storage.getStore("workflows");
|
|
3253
|
+
await vi.waitFor(async () => {
|
|
3254
|
+
for (const workflowName of ["durable-agentic-loop", "durable-agentic-execution"]) expect(await workflows.loadWorkflowSnapshot({
|
|
3255
|
+
workflowName,
|
|
3256
|
+
runId
|
|
3257
|
+
})).toBeFalsy();
|
|
3258
|
+
});
|
|
3259
|
+
}
|
|
3260
|
+
if (completes) {
|
|
3261
|
+
await vi.waitFor(async () => {
|
|
3262
|
+
const { messages } = await memory.recall({
|
|
3263
|
+
threadId: runId,
|
|
3264
|
+
resourceId: thread.resource
|
|
3265
|
+
});
|
|
3266
|
+
expect(messages.flatMap((m) => m.content.parts ?? []).some((p) => p.type === "text" && p.text === "done: 3 results")).toBe(true);
|
|
3267
|
+
});
|
|
3268
|
+
const replay = await fetch(`${base}/observe`, {
|
|
3269
|
+
method: "POST",
|
|
3270
|
+
headers: { "content-type": "application/json" },
|
|
3271
|
+
body: JSON.stringify({ runId })
|
|
3272
|
+
}).then((response) => response.text());
|
|
3273
|
+
expect(replay).toContain("done: 3 results");
|
|
3274
|
+
expect(replay).toContain("finish");
|
|
3275
|
+
const lateAbort = await fetch(`${base}/runs/${runId}/abort`, { method: "POST" });
|
|
3276
|
+
expect(lateAbort.status).toBe(200);
|
|
3277
|
+
expect(await lateAbort.json()).toEqual({ aborted: false });
|
|
3278
|
+
expect(fixture.getRequestCount()).toBe(offset + 4);
|
|
3279
|
+
}
|
|
3280
|
+
} finally {
|
|
3281
|
+
gate.resolve();
|
|
3282
|
+
client.abort();
|
|
3283
|
+
}
|
|
3284
|
+
}, 3e4);
|
|
3285
|
+
}
|
|
3286
|
+
});
|
|
3287
|
+
}
|
|
3288
|
+
//#endregion
|
|
3015
3289
|
//#region src/mcp-route-test-suite.ts
|
|
3016
3290
|
/**
|
|
3017
3291
|
* Creates a standardized integration test suite for MCP registry routes
|
|
@@ -4674,6 +4948,6 @@ function createCustomRouteFGATestSuite(config) {
|
|
|
4674
4948
|
});
|
|
4675
4949
|
}
|
|
4676
4950
|
//#endregion
|
|
4677
|
-
export { consumeSSEStream, createBodyLimitTestSuite, createCustomRouteFGATestSuite, createDefaultTestContext, createHttpLoggingTestSuite, createMCPRouteTestSuite, createMCPTransportTestSuite, createMultipartTestSuite, createRouteAdapterTestSuite, createStreamWithSensitiveData, createStreamWithUnserializableChunk, expectSerializedStreamChunks };
|
|
4951
|
+
export { consumeSSEStream, createBodyLimitTestSuite, createCustomRouteFGATestSuite, createDefaultTestContext, createDurableDisconnectTestSuite, createHttpLoggingTestSuite, createMCPRouteTestSuite, createMCPTransportTestSuite, createMultipartTestSuite, createRouteAdapterTestSuite, createStreamWithSensitiveData, createStreamWithUnserializableChunk, expectSerializedStreamChunks };
|
|
4678
4952
|
|
|
4679
4953
|
//# sourceMappingURL=index.js.map
|