import { describe, expect, it } from "@suaveplan/testing/runner"; import { MCP_CODES } from "../../error-codes/error-codes.js"; import type { JsonRpcMessage } from "../../schemas/schemas.js"; import type { EventSourceCtor, EventSourceLike, FetchLike, } from "../../types/types.js"; import { createHttpSseTransport } from "./http-sse.js"; class FakeEventSource implements EventSourceLike { public static instances: FakeEventSource[] = []; private listeners: ((ev: { data: string }) => void)[] = []; private _closed = false; public constructor(public readonly url: string) { FakeEventSource.instances.push(this); } public addEventListener( _type: "message", handler: (ev: { data: string }) => void ): void { this.listeners.push(handler); } public close(): void { this._closed = true; } public get closed(): boolean { return this._closed; } public emit(data: string): void { for (const l of this.listeners) l({ data }); } } const FakeEventSourceCtor = FakeEventSource as unknown as EventSourceCtor; describe("HttpSseTransport", () => { it("posts requests via fetchImpl", { timeout: 5000 }, async () => { const calls: Array<{ url: string; body: string }> = []; const fetchImpl: FetchLike = async (url, init) => { calls.push({ url, body: init.body }); return { ok: true, status: 200, text: async () => "", }; }; FakeEventSource.instances = []; const t = createHttpSseTransport({ endpoint: "http://x", fetchImpl, eventSourceImpl: FakeEventSourceCtor, }); await t.send({ jsonrpc: "2.0", id: 1, method: "ping" }); expect(calls.length).toBe(1); expect(calls[0]?.url).toBe("http://x"); await t.close(); }); it("dispatches messages from eventSourceImpl", { timeout: 5000, }, async () => { FakeEventSource.instances = []; const t = createHttpSseTransport({ endpoint: "http://x", fetchImpl: async () => ({ ok: true, status: 200, text: async () => "", }), eventSourceImpl: FakeEventSourceCtor, }); const received: JsonRpcMessage[] = []; t.onMessage((m) => received.push(m)); const es = FakeEventSource.instances[0]; if (es === undefined) throw new Error("no es"); es.emit( JSON.stringify({ jsonrpc: "2.0", id: 1, result: { ok: true } }) ); es.emit("garbage-not-json"); // JSON-valid but schema-invalid — exercises validated.success false. es.emit(JSON.stringify({ not: "a jsonrpc message" })); await Promise.resolve(); expect(received.length).toBe(1); await t.close(); }); it("maps non-2xx responses to TRANSPORT_ERROR", { timeout: 5000, }, async () => { FakeEventSource.instances = []; const t = createHttpSseTransport({ endpoint: "http://x", fetchImpl: async () => ({ ok: false, status: 503, text: async () => "boom", }), eventSourceImpl: FakeEventSourceCtor, }); await expect( t.send({ jsonrpc: "2.0", id: 1, method: "ping" }) ).rejects.toMatchObject({ code: MCP_CODES.TRANSPORT_ERROR }); await t.close(); }); it("raises TRANSPORT_CLOSED after close", { timeout: 5000 }, async () => { FakeEventSource.instances = []; const t = createHttpSseTransport({ endpoint: "http://x", fetchImpl: async () => ({ ok: true, status: 200, text: async () => "", }), eventSourceImpl: FakeEventSourceCtor, }); await t.close(); await expect( t.send({ jsonrpc: "2.0", id: 1, method: "ping" }) ).rejects.toMatchObject({ code: MCP_CODES.TRANSPORT_CLOSED }); }); });