fix(test-servers): address review on the cancellation showcase

- `slow_task` sleeps through a helper that removes both listeners on
  whichever outcome wins and returns early on an already-aborted signal.
  `{ once: true }` detaches on the event firing, not on the waiter losing
  interest, so the old loop left one listener per second attached and a
  60s run tripped MaxListenersExceededWarning.
- Report the outcome on stderr. A successful cancel closes the stream the
  result would travel on, so the tool's return value is undeliverable by
  construction — the README now points at the server's terminal rather
  than at a result panel that cannot show it.
- Drop a double cast the file's own next hunk shows is unnecessary, and a
  floating observer promise whose assertion was false anyway: `postSend`
  rethrows before awaiting the SSE wait, so the send rejects on abort
  whether or not that wait was cancelled.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hzwzS8UvBsr4yZm7o7sev
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-28 01:11:30 -04:00
commit 56d3c04643

View file

@ -579,7 +579,7 @@ describe("RemoteClientTransport", () => {
it("advertises hasPerRequestStream only for streamable-http", () => {
const forType = (c: MCPServerConfig) =>
new RemoteClientTransport(
{ baseUrl, fetchFn: vi.fn() as unknown as typeof fetch },
{ baseUrl, fetchFn: vi.fn<typeof fetch>() },
c,
).hasPerRequestStream;
@ -640,7 +640,7 @@ describe("RemoteClientTransport", () => {
await transport.close();
});
it("aborting the requestSignal rejects the in-flight send and releases its response wait", async () => {
it("aborting the requestSignal rejects the in-flight send", async () => {
let sse = createPushableSseStream();
const fetchFn = vi
.fn<typeof fetch>()
@ -675,15 +675,13 @@ describe("RemoteClientTransport", () => {
{ jsonrpc: "2.0", id: 7, method: "tools/call" },
{ requestSignal: controller.signal },
);
const settled = vi.fn();
void sent.then(settled, settled);
controller.abort();
// The backend never answers an aborted send, so the rejection has to come
// from the fetch itself. `postSend` already cancels the SSE response wait
// it registered for this id on that path (its catch does, unchanged by
// #2140) — so the send surfaces the abort rather than the wait's timeout.
await expect(sent).rejects.toThrow(/Aborted/);
// The response wait registered for id 7 must have been cancelled too —
// otherwise this send would hang past the abort waiting for a JSON-RPC
// response the aborted request can never produce.
expect(settled).toHaveBeenCalledTimes(1);
await transport.close();
});