diff options
Diffstat (limited to 'test/js')
-rw-r--r-- | test/js/web/abort/abort.signal.ts | 24 | ||||
-rw-r--r-- | test/js/web/abort/abort.test.ts | 21 | ||||
-rw-r--r-- | test/js/web/fetch/fetch.stream.test.ts | 136 |
3 files changed, 181 insertions, 0 deletions
diff --git a/test/js/web/abort/abort.signal.ts b/test/js/web/abort/abort.signal.ts new file mode 100644 index 000000000..da402f637 --- /dev/null +++ b/test/js/web/abort/abort.signal.ts @@ -0,0 +1,24 @@ +import type { Server } from "bun"; + +const server = Bun.serve({ + port: 0, + async fetch() { + const signal = AbortSignal.timeout(1); + return await fetch("https://bun.sh", { signal }); + }, +}); + +function hostname(server: Server) { + if (server.hostname.startsWith(":")) return `[${server.hostname}]`; + return server.hostname; +} + +let url = `http://${hostname(server)}:${server.port}/`; + +const responses: Response[] = []; +for (let i = 0; i < 10; i++) { + responses.push(await fetch(url)); +} +server.stop(true); +// we fail if any of the requests succeeded +process.exit(responses.every(res => res.status === 500) ? 0 : 1); diff --git a/test/js/web/abort/abort.test.ts b/test/js/web/abort/abort.test.ts index 4895e0d13..ef0b07a18 100644 --- a/test/js/web/abort/abort.test.ts +++ b/test/js/web/abort/abort.test.ts @@ -18,4 +18,25 @@ describe("AbortSignal", () => { expect(stderr?.toString()).not.toContain("✗"); }); + + test("AbortSignal.timeout(n) should not freeze the process", async () => { + const fileName = join(import.meta.dir, "abort.signal.ts"); + + const server = Bun.spawn({ + cmd: [bunExe(), fileName], + env: bunEnv, + cwd: tmpdir(), + }); + + const exitCode = await Promise.race([ + server.exited, + (async () => { + await Bun.sleep(5000); + server.kill(); + return 2; + })(), + ]); + + expect(exitCode).toBe(0); + }); }); diff --git a/test/js/web/fetch/fetch.stream.test.ts b/test/js/web/fetch/fetch.stream.test.ts index 98271ee79..82c63ba53 100644 --- a/test/js/web/fetch/fetch.stream.test.ts +++ b/test/js/web/fetch/fetch.stream.test.ts @@ -28,6 +28,142 @@ const smallText = Buffer.from("Hello".repeat(16)); const empty = Buffer.alloc(0); describe("fetch() with streaming", () => { + it(`should be able to fail properly when reading from readable stream`, async () => { + let server: Server | null = null; + try { + server = Bun.serve({ + port: 0, + async fetch(req) { + return new Response( + new ReadableStream({ + async start(controller) { + controller.enqueue("Hello, World!"); + await Bun.sleep(1000); + controller.enqueue("Hello, World!"); + controller.close(); + }, + }), + { + status: 200, + headers: { + "Content-Type": "text/plain", + }, + }, + ); + }, + }); + + const server_url = `http://${server.hostname}:${server.port}`; + try { + const res = await fetch(server_url, { signal: AbortSignal.timeout(20) }); + const reader = res.body?.getReader(); + while (true) { + const { done } = await reader?.read(); + if (done) break; + } + expect(true).toBe("unreachable"); + } catch (err: any) { + if (err.name !== "TimeoutError") throw err; + expect(err.message).toBe("The operation timed out."); + } + } finally { + server?.stop(); + } + }); + + it(`should be locked after start buffering`, async () => { + let server: Server | null = null; + try { + server = Bun.serve({ + port: 0, + fetch(req) { + return new Response( + new ReadableStream({ + async start(controller) { + controller.enqueue("Hello, World!"); + await Bun.sleep(10); + controller.enqueue("Hello, World!"); + await Bun.sleep(10); + controller.enqueue("Hello, World!"); + await Bun.sleep(10); + controller.enqueue("Hello, World!"); + await Bun.sleep(10); + controller.close(); + }, + }), + { + status: 200, + headers: { + "Content-Type": "text/plain", + }, + }, + ); + }, + }); + + const server_url = `http://${server.hostname}:${server.port}`; + const res = await fetch(server_url); + try { + const promise = res.text(); // start buffering + res.body?.getReader(); // get a reader + const result = await promise; // should throw the right error + expect(result).toBe("unreachable"); + } catch (err: any) { + if (err.name !== "TypeError") throw err; + expect(err.message).toBe("ReadableStream is locked"); + } + } finally { + server?.stop(); + } + }); + + it(`should be locked after start buffering when calling getReader`, async () => { + let server: Server | null = null; + try { + server = Bun.serve({ + port: 0, + fetch(req) { + return new Response( + new ReadableStream({ + async start(controller) { + controller.enqueue("Hello, World!"); + await Bun.sleep(10); + controller.enqueue("Hello, World!"); + await Bun.sleep(10); + controller.enqueue("Hello, World!"); + await Bun.sleep(10); + controller.enqueue("Hello, World!"); + await Bun.sleep(10); + controller.close(); + }, + }), + { + status: 200, + headers: { + "Content-Type": "text/plain", + }, + }, + ); + }, + }); + + const server_url = `http://${server.hostname}:${server.port}`; + const res = await fetch(server_url); + try { + const body = res.body as ReadableStream<Uint8Array>; + const promise = res.text(); // start buffering + body.getReader(); // get a reader + const result = await promise; // should throw the right error + expect(result).toBe("unreachable"); + } catch (err: any) { + if (err.name !== "TypeError") throw err; + expect(err.message).toBe("ReadableStream is locked"); + } + } finally { + server?.stop(); + } + }); + it("can deflate with and without headers #4478", async () => { let server: Server | null = null; try { |