From 7a29a2161800ab19a47e02be9ffdd6fe3003245a Mon Sep 17 00:00:00 2001 From: cliffhall Date: Sun, 16 Aug 2026 20:47:01 -0400 Subject: [PATCH] test(web): drop the double cast from the CRLF SSE stream mocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both new #2006 regressions faked the events response with an object cast `as unknown as Response`, which AGENTS.md rejects — it erases the check that the mock actually satisfies the API the hook consumes. Each now builds a real `ReadableStream` inside a real `Response`. `pull` runs once per chunk the hook's reader consumes, so awaiting a promise inside it gates delivery exactly the way the hand-rolled reader did, while the mock stays type-checked against the Response contract. The chunk counter replaces the read counter as the "the reader has consumed the previous chunk" signal. Both still fail against the pre-fix parser, verified. Raised by Copilot on PR #2036. Signed-off-by: cliffhall --- .../src/test/core/react/useServers.test.tsx | 72 +++++++++---------- 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/clients/web/src/test/core/react/useServers.test.tsx b/clients/web/src/test/core/react/useServers.test.tsx index e7c2fbf3..c0d5260c 100644 --- a/clients/web/src/test/core/react/useServers.test.tsx +++ b/clients/web/src/test/core/react/useServers.test.tsx @@ -987,7 +987,6 @@ describe("useServers", () => { const framed = new Promise((r) => { releaseFrame = r; }); - let reads = 0; const encoder = new TextEncoder(); // Referentially stable: an inline arrow re-subscribes on every render, and // the re-subscription's own mount refresh would pick the edit up on its @@ -995,22 +994,18 @@ describe("useServers", () => { const fetchFn: typeof fetch = async (input, init) => { const url = input instanceof Request ? input.url : String(input); if (url.endsWith("/api/servers/events")) { - const body = { - getReader: () => ({ - read: async () => { - reads += 1; - if (reads === 1) { - await framed; - return { - done: false, - value: encoder.encode("event: change\r\n\r\n"), - }; - } - return { done: true, value: undefined }; - }, - }), - }; - return { ok: true, body } as unknown as Response; + // A real ReadableStream in a real Response — `pull` is only invoked + // when the hook's reader asks for a chunk, so awaiting inside it + // gates delivery exactly the way a hand-rolled reader double would, + // while keeping the mock type-checked against the Response API. + const stream = new ReadableStream({ + async pull(controller) { + await framed; + controller.enqueue(encoder.encode("event: change\r\n\r\n")); + controller.close(); + }, + }); + return new Response(stream, { status: 200 }); } return h.fetchFn(url, init); }; @@ -1057,31 +1052,30 @@ describe("useServers", () => { releaseSecondHalf = r; }); let listGets = 0; - let reads = 0; + let chunks = 0; const encoder = new TextEncoder(); const fetchFn: typeof fetch = async (input, init) => { const url = input instanceof Request ? input.url : String(input); if (url.endsWith("/api/servers/events")) { - const body = { - getReader: () => ({ - read: async () => { - reads += 1; - if (reads === 1) { - await firstHalf; - return { - done: false, - value: encoder.encode("event: change\r\n\r"), - }; - } - if (reads === 2) { - await secondHalf; - return { done: false, value: encoder.encode("\n") }; - } - return { done: true, value: undefined }; - }, - }), - }; - return { ok: true, body } as unknown as Response; + // `pull` runs once per chunk the hook's reader consumes, so `chunks` + // reaching 2 proves the half-separator chunk was read and parsed. + const stream = new ReadableStream({ + async pull(controller) { + chunks += 1; + if (chunks === 1) { + await firstHalf; + controller.enqueue(encoder.encode("event: change\r\n\r")); + return; + } + if (chunks === 2) { + await secondHalf; + controller.enqueue(encoder.encode("\n")); + return; + } + controller.close(); + }, + }); + return new Response(stream, { status: 200 }); } if (url.endsWith("/api/servers")) listGets += 1; return h.fetchFn(url, init); @@ -1094,7 +1088,7 @@ describe("useServers", () => { // Half a separator is not a frame: no refresh yet. releaseFirstHalf?.(); - await waitFor(() => expect(reads).toBeGreaterThanOrEqual(2)); + await waitFor(() => expect(chunks).toBeGreaterThanOrEqual(2)); await new Promise((r) => setTimeout(r, 100)); expect(listGets).toBe(1);