diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/bun.js/body-stream.test.ts | 9 | ||||
-rw-r--r-- | test/bun.js/ffi.test.js | 507 | ||||
-rw-r--r-- | test/bun.js/inline.macro.js | 10 | ||||
-rw-r--r-- | test/bun.js/node-timers.test.ts | 15 | ||||
-rw-r--r-- | test/bun.js/serve.test.ts | 880 |
5 files changed, 771 insertions, 650 deletions
diff --git a/test/bun.js/body-stream.test.ts b/test/bun.js/body-stream.test.ts index 0a44246b0..5f505143c 100644 --- a/test/bun.js/body-stream.test.ts +++ b/test/bun.js/body-stream.test.ts @@ -1,10 +1,8 @@ // @ts-nocheck import { file, gc, serve, ServeOptions } from "bun"; -import { afterEach, describe, expect, it, test } from "bun:test"; +import { afterAll, afterEach, describe, expect, it, test } from "bun:test"; import { readFileSync } from "fs"; -// afterEach(() => Bun.gc(true)); - var port = 4021; { @@ -215,6 +213,11 @@ async function runInServer( } } +afterAll(() => { + existingServer && existingServer.close(); + existingServer = null; +}); + function fillRepeating(dstBuffer, start, end) { let len = dstBuffer.length, sLen = end - start, diff --git a/test/bun.js/ffi.test.js b/test/bun.js/ffi.test.js index e3e858b1f..4adf67990 100644 --- a/test/bun.js/ffi.test.js +++ b/test/bun.js/ffi.test.js @@ -1,4 +1,4 @@ -import { expect, it } from "bun:test"; +import { afterAll, describe, expect, it } from "bun:test"; // import { CFunction, @@ -310,186 +310,191 @@ function getTypes(fast) { } function ffiRunner(fast) { - const types = getTypes(fast); - const { - symbols: { - returns_true, - returns_false, - return_a_function_ptr_to_function_that_returns_true, - returns_42_char, - returns_42_float, - returns_42_double, - returns_42_uint8_t, - returns_neg_42_int8_t, - returns_42_uint16_t, - returns_42_uint32_t, - returns_42_uint64_t, - returns_neg_42_int16_t, - returns_neg_42_int32_t, - returns_neg_42_int64_t, - identity_char, - identity_float, - identity_bool, - identity_double, - identity_int8_t, - identity_int16_t, - identity_int32_t, - identity_int64_t, - identity_uint8_t, - identity_uint16_t, - identity_uint32_t, - identity_uint64_t, - add_char, - add_float, - add_double, - add_int8_t, - add_int16_t, - add_int32_t, - add_int64_t, - add_uint8_t, - add_uint16_t, - identity_ptr, - add_uint32_t, - add_uint64_t, - is_null, - does_pointer_equal_42_as_int32_t, - ptr_should_point_to_42_as_int32_t, - cb_identity_true, - cb_identity_false, - cb_identity_42_char, - cb_identity_42_float, - cb_identity_42_double, - cb_identity_42_uint8_t, - cb_identity_neg_42_int8_t, - cb_identity_42_uint16_t, - cb_identity_42_uint32_t, - cb_identity_42_uint64_t, - cb_identity_neg_42_int16_t, - cb_identity_neg_42_int32_t, - cb_identity_neg_42_int64_t, - getDeallocatorCalledCount, - getDeallocatorCallback, - getDeallocatorBuffer, - }, - close, - } = dlopen("/tmp/bun-ffi-test.dylib", types); - - Bun.gc(true); - expect(returns_true()).toBe(true); - Bun.gc(true); - expect(returns_false()).toBe(false); - - expect(returns_42_char()).toBe(42); - if (fast) expect(returns_42_uint64_t().valueOf()).toBe(42); - else expect(returns_42_uint64_t().valueOf()).toBe(42n); - Bun.gc(true); - expect(Math.fround(returns_42_float())).toBe(Math.fround(42.41999804973602)); - expect(returns_42_double()).toBe(42.42); - expect(returns_42_uint8_t()).toBe(42); - expect(returns_neg_42_int8_t()).toBe(-42); - expect(returns_42_uint16_t()).toBe(42); - expect(returns_42_uint32_t()).toBe(42); - if (fast) expect(returns_42_uint64_t()).toBe(42); - else expect(returns_42_uint64_t()).toBe(42n); - expect(returns_neg_42_int16_t()).toBe(-42); - expect(returns_neg_42_int32_t()).toBe(-42); - expect(identity_int32_t(10)).toBe(10); - Bun.gc(true); - if (fast) expect(returns_neg_42_int64_t()).toBe(-42); - else expect(returns_neg_42_int64_t()).toBe(-42n); - - expect(identity_char(10)).toBe(10); - - expect(identity_float(10.199999809265137)).toBe(10.199999809265137); - - expect(identity_bool(true)).toBe(true); - - expect(identity_bool(false)).toBe(false); - expect(identity_double(10.100000000000364)).toBe(10.100000000000364); - - expect(identity_int8_t(10)).toBe(10); - expect(identity_int16_t(10)).toBe(10); - - if (fast) expect(identity_int64_t(10)).toBe(10); - else expect(identity_int64_t(10)).toBe(10n); - expect(identity_uint8_t(10)).toBe(10); - expect(identity_uint16_t(10)).toBe(10); - expect(identity_uint32_t(10)).toBe(10); - if (fast) expect(identity_uint64_t(10)).toBe(10); - else expect(identity_uint64_t(10)).toBe(10n); - Bun.gc(true); - var bigArray = new BigUint64Array(8); - new Uint8Array(bigArray.buffer).fill(255); - var bigIntArray = new BigInt64Array(bigArray.buffer); - expect(identity_uint64_t(bigArray[0])).toBe(bigArray[0]); - expect(identity_uint64_t(bigArray[0] - BigInt(1))).toBe( - bigArray[0] - BigInt(1), - ); - if (fast) { - expect(add_uint64_t(BigInt(-1) * bigArray[0], bigArray[0])).toBe(0); - expect( - add_uint64_t(BigInt(-1) * bigArray[0] + BigInt(10), bigArray[0]), - ).toBe(10); - } else { - expect(add_uint64_t(BigInt(-1) * bigArray[0], bigArray[0])).toBe(0n); - expect( - add_uint64_t(BigInt(-1) * bigArray[0] + BigInt(10), bigArray[0]), - ).toBe(10n); - } - if (fast) { - expect(identity_uint64_t(0)).toBe(0); - expect(identity_uint64_t(100)).toBe(100); - expect(identity_uint64_t(BigInt(100))).toBe(100); - - expect(identity_int64_t(bigIntArray[0])).toBe(-1); - expect(identity_int64_t(bigIntArray[0] - BigInt(1))).toBe(-2); - } else { - expect(identity_uint64_t(0)).toBe(0n); - expect(identity_uint64_t(100)).toBe(100n); - expect(identity_uint64_t(BigInt(100))).toBe(100n); - - expect(identity_int64_t(bigIntArray[0])).toBe(bigIntArray[0]); - expect(identity_int64_t(bigIntArray[0] - BigInt(1))).toBe( - bigIntArray[0] - BigInt(1), - ); - } - Bun.gc(true); - expect(add_char.native(1, 1)).toBe(2); - - expect(add_float(2.4, 2.8)).toBe(Math.fround(5.2)); - expect(add_double(4.2, 0.1)).toBe(4.3); - expect(add_int8_t(1, 1)).toBe(2); - expect(add_int16_t(1, 1)).toBe(2); - expect(add_int32_t(1, 1)).toBe(2); - if (fast) expect(add_int64_t(1, 1)).toBe(2); - else expect(add_int64_t(1n, 1n)).toBe(2n); - expect(add_uint8_t(1, 1)).toBe(2); - expect(add_uint16_t(1, 1)).toBe(2); - expect(add_uint32_t(1, 1)).toBe(2); - Bun.gc(true); - expect(is_null(null)).toBe(true); - const cptr = ptr_should_point_to_42_as_int32_t(); - expect(cptr != 0).toBe(true); - expect(typeof cptr === "number").toBe(true); - expect(does_pointer_equal_42_as_int32_t(cptr)).toBe(true); - const buffer = toBuffer(cptr, 0, 4); - expect(buffer.readInt32(0)).toBe(42); - expect(new DataView(toArrayBuffer(cptr, 0, 4), 0, 4).getInt32(0, true)).toBe( - 42, - ); - expect(ptr(buffer)).toBe(cptr); - expect(new CString(cptr, 0, 1).toString()).toBe("*"); - expect(identity_ptr(cptr)).toBe(cptr); - const second_ptr = ptr(new Buffer(8)); - expect(identity_ptr(second_ptr)).toBe(second_ptr); - - var myCFunction = new CFunction({ - ptr: return_a_function_ptr_to_function_that_returns_true(), - returns: "bool", - }); - expect(myCFunction()).toBe(true); + describe("FFI runner" + (fast ? " (fast int)" : ""), () => { + const types = getTypes(fast); + const { + symbols: { + returns_true, + returns_false, + return_a_function_ptr_to_function_that_returns_true, + returns_42_char, + returns_42_float, + returns_42_double, + returns_42_uint8_t, + returns_neg_42_int8_t, + returns_42_uint16_t, + returns_42_uint32_t, + returns_42_uint64_t, + returns_neg_42_int16_t, + returns_neg_42_int32_t, + returns_neg_42_int64_t, + identity_char, + identity_float, + identity_bool, + identity_double, + identity_int8_t, + identity_int16_t, + identity_int32_t, + identity_int64_t, + identity_uint8_t, + identity_uint16_t, + identity_uint32_t, + identity_uint64_t, + add_char, + add_float, + add_double, + add_int8_t, + add_int16_t, + add_int32_t, + add_int64_t, + add_uint8_t, + add_uint16_t, + identity_ptr, + add_uint32_t, + add_uint64_t, + is_null, + does_pointer_equal_42_as_int32_t, + ptr_should_point_to_42_as_int32_t, + cb_identity_true, + cb_identity_false, + cb_identity_42_char, + cb_identity_42_float, + cb_identity_42_double, + cb_identity_42_uint8_t, + cb_identity_neg_42_int8_t, + cb_identity_42_uint16_t, + cb_identity_42_uint32_t, + cb_identity_42_uint64_t, + cb_identity_neg_42_int16_t, + cb_identity_neg_42_int32_t, + cb_identity_neg_42_int64_t, + getDeallocatorCalledCount, + getDeallocatorCallback, + getDeallocatorBuffer, + }, + close, + } = dlopen("/tmp/bun-ffi-test.dylib", types); + it("primitives", () => { + Bun.gc(true); + expect(returns_true()).toBe(true); + Bun.gc(true); + expect(returns_false()).toBe(false); + + expect(returns_42_char()).toBe(42); + if (fast) expect(returns_42_uint64_t().valueOf()).toBe(42); + else expect(returns_42_uint64_t().valueOf()).toBe(42n); + Bun.gc(true); + expect(Math.fround(returns_42_float())).toBe( + Math.fround(42.41999804973602), + ); + expect(returns_42_double()).toBe(42.42); + expect(returns_42_uint8_t()).toBe(42); + expect(returns_neg_42_int8_t()).toBe(-42); + expect(returns_42_uint16_t()).toBe(42); + expect(returns_42_uint32_t()).toBe(42); + if (fast) expect(returns_42_uint64_t()).toBe(42); + else expect(returns_42_uint64_t()).toBe(42n); + expect(returns_neg_42_int16_t()).toBe(-42); + expect(returns_neg_42_int32_t()).toBe(-42); + expect(identity_int32_t(10)).toBe(10); + Bun.gc(true); + if (fast) expect(returns_neg_42_int64_t()).toBe(-42); + else expect(returns_neg_42_int64_t()).toBe(-42n); + + expect(identity_char(10)).toBe(10); + + expect(identity_float(10.199999809265137)).toBe(10.199999809265137); + + expect(identity_bool(true)).toBe(true); + + expect(identity_bool(false)).toBe(false); + expect(identity_double(10.100000000000364)).toBe(10.100000000000364); + + expect(identity_int8_t(10)).toBe(10); + expect(identity_int16_t(10)).toBe(10); + + if (fast) expect(identity_int64_t(10)).toBe(10); + else expect(identity_int64_t(10)).toBe(10n); + expect(identity_uint8_t(10)).toBe(10); + expect(identity_uint16_t(10)).toBe(10); + expect(identity_uint32_t(10)).toBe(10); + if (fast) expect(identity_uint64_t(10)).toBe(10); + else expect(identity_uint64_t(10)).toBe(10n); + Bun.gc(true); + var bigArray = new BigUint64Array(8); + new Uint8Array(bigArray.buffer).fill(255); + var bigIntArray = new BigInt64Array(bigArray.buffer); + expect(identity_uint64_t(bigArray[0])).toBe(bigArray[0]); + expect(identity_uint64_t(bigArray[0] - BigInt(1))).toBe( + bigArray[0] - BigInt(1), + ); + if (fast) { + expect(add_uint64_t(BigInt(-1) * bigArray[0], bigArray[0])).toBe(0); + expect( + add_uint64_t(BigInt(-1) * bigArray[0] + BigInt(10), bigArray[0]), + ).toBe(10); + } else { + expect(add_uint64_t(BigInt(-1) * bigArray[0], bigArray[0])).toBe(0n); + expect( + add_uint64_t(BigInt(-1) * bigArray[0] + BigInt(10), bigArray[0]), + ).toBe(10n); + } + if (fast) { + expect(identity_uint64_t(0)).toBe(0); + expect(identity_uint64_t(100)).toBe(100); + expect(identity_uint64_t(BigInt(100))).toBe(100); + + expect(identity_int64_t(bigIntArray[0])).toBe(-1); + expect(identity_int64_t(bigIntArray[0] - BigInt(1))).toBe(-2); + } else { + expect(identity_uint64_t(0)).toBe(0n); + expect(identity_uint64_t(100)).toBe(100n); + expect(identity_uint64_t(BigInt(100))).toBe(100n); + + expect(identity_int64_t(bigIntArray[0])).toBe(bigIntArray[0]); + expect(identity_int64_t(bigIntArray[0] - BigInt(1))).toBe( + bigIntArray[0] - BigInt(1), + ); + } + Bun.gc(true); + expect(add_char.native(1, 1)).toBe(2); + + expect(add_float(2.4, 2.8)).toBe(Math.fround(5.2)); + expect(add_double(4.2, 0.1)).toBe(4.3); + expect(add_int8_t(1, 1)).toBe(2); + expect(add_int16_t(1, 1)).toBe(2); + expect(add_int32_t(1, 1)).toBe(2); + if (fast) expect(add_int64_t(1, 1)).toBe(2); + else expect(add_int64_t(1n, 1n)).toBe(2n); + expect(add_uint8_t(1, 1)).toBe(2); + expect(add_uint16_t(1, 1)).toBe(2); + expect(add_uint32_t(1, 1)).toBe(2); + Bun.gc(true); + expect(is_null(null)).toBe(true); + const cptr = ptr_should_point_to_42_as_int32_t(); + expect(cptr != 0).toBe(true); + expect(typeof cptr === "number").toBe(true); + expect(does_pointer_equal_42_as_int32_t(cptr)).toBe(true); + const buffer = toBuffer(cptr, 0, 4); + expect(buffer.readInt32(0)).toBe(42); + expect( + new DataView(toArrayBuffer(cptr, 0, 4), 0, 4).getInt32(0, true), + ).toBe(42); + expect(ptr(buffer)).toBe(cptr); + expect(new CString(cptr, 0, 1).toString()).toBe("*"); + expect(identity_ptr(cptr)).toBe(cptr); + const second_ptr = ptr(new Buffer(8)); + expect(identity_ptr(second_ptr)).toBe(second_ptr); + }); + + it("CFunction", () => { + var myCFunction = new CFunction({ + ptr: return_a_function_ptr_to_function_that_returns_true(), + returns: "bool", + }); + expect(myCFunction()).toBe(true); + }); - { const typeMap = { int8_t: -8, int16_t: -16, @@ -505,25 +510,7 @@ function ffiRunner(fast) { "void*": null, }; - // Return types, 1 argument - for (let [returnName, returnValue] of Object.entries(typeMap)) { - var roundtripFunction = new CFunction({ - ptr: new JSCallback( - (input) => { - return input; - }, - { - returns: returnName, - args: [returnName], - }, - ).ptr, - returns: returnName, - args: [returnName], - }); - expect(roundtripFunction(returnValue)).toBe(returnValue); - } - - { + it("JSCallback", () => { var toClose = new JSCallback( (input) => { return input; @@ -536,65 +523,72 @@ function ffiRunner(fast) { expect(toClose.ptr > 0).toBe(true); toClose.close(); expect(toClose.ptr === null).toBe(true); - } - - // Return types, no args - for (let [name, value] of Object.entries(typeMap)) { - var roundtripFunction = new CFunction({ - ptr: new JSCallback(() => value, { - returns: name, - }).ptr, - returns: name, - }); - expect(roundtripFunction()).toBe(value); - } - - // 1 arg, threadsafe - for (let [name, value] of Object.entries(typeMap)) { - var roundtripFunction = new CFunction({ - ptr: new JSCallback( - (arg1) => { - expect(arg1).toBe(value); - }, - { + }); + + describe("callbacks", () => { + // Return types, 1 argument + for (let [returnName, returnValue] of Object.entries(typeMap)) { + it("fn(" + returnName + ") " + returnName, () => { + var roundtripFunction = new CFunction({ + ptr: new JSCallback( + (input) => { + return input; + }, + { + returns: returnName, + args: [returnName], + }, + ).ptr, + returns: returnName, + args: [returnName], + }); + expect(roundtripFunction(returnValue)).toBe(returnValue); + }); + } + // Return types, no args + for (let [name, value] of Object.entries(typeMap)) { + it("fn() " + name, () => { + var roundtripFunction = new CFunction({ + ptr: new JSCallback(() => value, { + returns: name, + }).ptr, + returns: name, + }); + expect(roundtripFunction()).toBe(value); + }); + } + }); + + describe("threadsafe callback", (done) => { + // 1 arg, threadsafe + for (let [name, value] of Object.entries(typeMap)) { + it("fn(" + name + ") " + name, async () => { + const cb = new JSCallback( + (arg1) => { + expect(arg1).toBe(value); + }, + { + args: [name], + threadsafe: true, + }, + ); + var roundtripFunction = new CFunction({ + ptr: cb.ptr, + returns: "void", args: [name], - threadsafe: true, - }, - ).ptr, - returns: "void", - args: [name], - }); - roundtripFunction(value); - } - } - - // check deallocator is called - - // for (let constructor of [toArrayBuffer, toBuffer]) { - // Bun.gc(true); - - // var bufferPtr = getDeallocatorBuffer(); - - // for (let i = 0; i < 100; i++) { - // // callback, no userData - // constructor(bufferPtr, 0, 128, getDeallocatorCallback()); - - // // callback, userData; - // constructor(bufferPtr, 0, 128, bufferPtr, getDeallocatorCallback()); - // } - - // Bun.gc(true); - // expect(getDeallocatorCalledCount() >= 190).toBe(true); - // Bun.gc(true); - // } - close(); + }); + roundtripFunction(value); + await 1; + }); + } + }); + + afterAll(() => { + close(); + }); + }); } -// TODO: There is a crash when dlopen() two times the same library in quick succession -// it("run ffi fast", () => { -// ffiRunner(true); -// }); - it("read", () => { const buffer = new BigInt64Array(16); const dataView = new DataView(buffer.buffer); @@ -629,6 +623,7 @@ it("read", () => { } }); -it("run ffi", () => { +describe("run ffi", () => { ffiRunner(false); + ffiRunner(true); }); diff --git a/test/bun.js/inline.macro.js b/test/bun.js/inline.macro.js index 0fd7491b9..082dbeaf4 100644 --- a/test/bun.js/inline.macro.js +++ b/test/bun.js/inline.macro.js @@ -12,8 +12,12 @@ export function promiseReturningFunction(expr, ctx) { export function promiseReturningCtx(expr, ctx) { return new Promise((resolve, reject) => { - setTimeout(() => { - resolve(ctx); - }, 1); + setTimeout( + (ctx) => { + resolve(ctx); + }, + 1, + ctx, + ); }); } diff --git a/test/bun.js/node-timers.test.ts b/test/bun.js/node-timers.test.ts index a1f2d08a4..f1a8ae802 100644 --- a/test/bun.js/node-timers.test.ts +++ b/test/bun.js/node-timers.test.ts @@ -1,10 +1,11 @@ import { test } from "bun:test"; import { setTimeout } from "node:timers"; -test("unref is possible", () => { - const timer = setTimeout(() => { - throw new Error("should not be called"); - }, 1000); - timer.unref(); - clearTimeout(timer); -}); +// not implemented yet +// test("unref is possible", () => { +// const timer = setTimeout(() => { +// throw new Error("should not be called"); +// }, 1000); +// timer.unref(); +// clearTimeout(timer); +// }); diff --git a/test/bun.js/serve.test.ts b/test/bun.js/serve.test.ts index becddc678..61740748d 100644 --- a/test/bun.js/serve.test.ts +++ b/test/bun.js/serve.test.ts @@ -1,5 +1,5 @@ import { file, gc, serve } from "bun"; -import { afterEach, describe, it, expect } from "bun:test"; +import { afterEach, describe, it, expect, beforeAll, afterAll } from "bun:test"; import { readFile, readFileSync, writeFileSync } from "fs"; import { resolve } from "path"; @@ -7,509 +7,613 @@ afterEach(() => Bun.gc(true)); var port = 10000; var count = 200; +var server; async function runTest(serverOptions, test) { - var server; - while (true) { - try { - serverOptions.port = port++; - server = serve(serverOptions); - break; - } catch (e: any) { - if (e?.message !== `Failed to start server. Is port ${serverOptions.port} in use?`) { - throw e; + if (server) { + server.reload(serverOptions); + } else { + while (!server) { + try { + serverOptions.port = port++; + server = serve(serverOptions); + break; + } catch (e: any) { + if ( + e?.message !== + `Failed to start server. Is port ${serverOptions.port} in use?` + ) { + throw e; + } } } } - try { - await test(server); - } finally { - server?.stop(); - server = null; - } + + await test(server); } +afterAll(() => { + if (server) { + server.stop(); + server = undefined; + } +}); + it("should work for a file", async () => { const fixture = resolve(import.meta.dir, "./fetch.js.txt"); const textToExpect = readFileSync(fixture, "utf-8"); - await runTest({ - fetch(req) { - return new Response(file(fixture)); + await runTest( + { + fetch(req) { + return new Response(file(fixture)); + }, }, - }, async (server) => { - const response = await fetch(`http://${server.hostname}:${server.port}`); - expect(await response.text()).toBe(textToExpect); - }); + async (server) => { + const response = await fetch(`http://${server.hostname}:${server.port}`); + expect(await response.text()).toBe(textToExpect); + }, + ); }); it("request.url should log successfully", async () => { const fixture = resolve(import.meta.dir, "./fetch.js.txt"); const textToExpect = readFileSync(fixture, "utf-8"); var expected; - await runTest({ - fetch(req) { - expect(Bun.inspect(req).includes(expected)).toBe(true); - return new Response(file(fixture)); + await runTest( + { + fetch(req) { + expect(Bun.inspect(req).includes(expected)).toBe(true); + return new Response(file(fixture)); + }, }, - }, async (server) => { - expected = `http://localhost:${server.port}/helloooo`; - const response = await fetch(expected); - expect(response.url).toBe(expected); - expect(await response.text()).toBe(textToExpect); - }); + async (server) => { + expected = `http://localhost:${server.port}/helloooo`; + const response = await fetch(expected); + expect(response.url).toBe(expected); + expect(await response.text()).toBe(textToExpect); + }, + ); }); it("request.url should be based on the Host header", async () => { const fixture = resolve(import.meta.dir, "./fetch.js.txt"); const textToExpect = readFileSync(fixture, "utf-8"); - await runTest({ - fetch(req) { - expect(req.url).toBe("http://example.com/helloooo"); - return new Response(file(fixture)); - }, - }, async (server) => { - const expected = `http://${server.hostname}:${server.port}/helloooo`; - const response = await fetch(expected, { - headers: { - Host: "example.com", + await runTest( + { + fetch(req) { + expect(req.url).toBe("http://example.com/helloooo"); + return new Response(file(fixture)); }, - }); - expect(response.url).toBe(expected); - expect(await response.text()).toBe(textToExpect); - }); + }, + async (server) => { + const expected = `http://${server.hostname}:${server.port}/helloooo`; + const response = await fetch(expected, { + headers: { + Host: "example.com", + }, + }); + expect(response.url).toBe(expected); + expect(await response.text()).toBe(textToExpect); + }, + ); }); describe("streaming", () => { describe("error handler", () => { it("throw on pull reports an error and close the connection", async () => { var pass = false; - await runTest({ - development: false, - error(e) { - pass = true; - return new Response("PASS", { status: 555 }); + await runTest( + { + development: false, + error(e) { + pass = true; + return new Response("PASS", { status: 555 }); + }, + fetch(req) { + return new Response( + new ReadableStream({ + pull(controller) { + throw new Error("FAIL"); + }, + }), + ); + }, }, - fetch(req) { - return new Response( - new ReadableStream({ - pull(controller) { - throw new Error("FAIL"); - }, - }), + async (server) => { + const response = await fetch( + `http://${server.hostname}:${server.port}`, ); + if (response.status > 0) { + expect(response.status).toBe(555); + expect(await response.text()).toBe("PASS"); + } + expect(pass).toBe(true); }, - }, async (server) => { - const response = await fetch( - `http://${server.hostname}:${server.port}`, - ); - if (response.status > 0) { - expect(response.status).toBe(555); - expect(await response.text()).toBe("PASS"); - } - expect(pass).toBe(true); - }); + ); }); it("throw on pull after writing should not call the error handler", async () => { var pass = true; - await runTest({ - development: false, - error(e) { - pass = true; - return new Response("FAIL", { status: 555 }); + await runTest( + { + development: false, + error(e) { + pass = true; + return new Response("FAIL", { status: 555 }); + }, + fetch(req) { + return new Response( + new ReadableStream({ + pull(controller) { + controller.enqueue("PASS"); + throw new Error("error"); + }, + }), + ); + }, }, + async (server) => { + const response = await fetch( + `http://${server.hostname}:${server.port}`, + ); + // connection terminated + expect(response.status).toBe(500); + expect(await response.text()).toBe("PASS"); + expect(pass).toBe(true); + }, + ); + }); + }); + + it("text from JS, one chunk", async () => { + const relative = new URL("./fetch.js.txt", import.meta.url); + const textToExpect = readFileSync(relative, "utf-8"); + await runTest( + { fetch(req) { return new Response( new ReadableStream({ - pull(controller) { - controller.enqueue("PASS"); - throw new Error("error"); + start(controller) { + controller.enqueue(textToExpect); + controller.close(); }, }), ); }, - }, async (server) => { + }, + async (server) => { const response = await fetch( `http://${server.hostname}:${server.port}`, ); - // connection terminated - expect(response.status).toBe(500); - expect(await response.text()).toBe("PASS"); - expect(pass).toBe(true); - }); - }); - }); - - it("text from JS, one chunk", async () => { - const relative = new URL("./fetch.js.txt", import.meta.url); - const textToExpect = readFileSync(relative, "utf-8"); - await runTest({ - fetch(req) { - return new Response( - new ReadableStream({ - start(controller) { - controller.enqueue(textToExpect); - controller.close(); - }, - }), - ); + const text = await response.text(); + expect(text.length).toBe(textToExpect.length); + expect(text).toBe(textToExpect); }, - }, async (server) => { - const response = await fetch(`http://${server.hostname}:${server.port}`); - const text = await response.text(); - expect(text.length).toBe(textToExpect.length); - expect(text).toBe(textToExpect); - }); + ); }); it("text from JS, two chunks", async () => { const fixture = resolve(import.meta.dir, "./fetch.js.txt"); - const textToExpect = readFileSync(fixture, "utf-8") - await runTest({ - fetch(req) { - return new Response( - new ReadableStream({ - start(controller) { - controller.enqueue(textToExpect.substring(0, 100)); - controller.enqueue(textToExpect.substring(100)); - controller.close(); - }, - }), + const textToExpect = readFileSync(fixture, "utf-8"); + await runTest( + { + fetch(req) { + return new Response( + new ReadableStream({ + start(controller) { + controller.enqueue(textToExpect.substring(0, 100)); + controller.enqueue(textToExpect.substring(100)); + controller.close(); + }, + }), + ); + }, + }, + async (server) => { + const response = await fetch( + `http://${server.hostname}:${server.port}`, ); + expect(await response.text()).toBe(textToExpect); }, - }, async (server) => { - const response = await fetch(`http://${server.hostname}:${server.port}`); - expect(await response.text()).toBe(textToExpect); - }); + ); }); it("text from JS throws on start no error handler", async () => { - await runTest({ - port: port++, - development: false, - fetch(req) { - return new Response( - new ReadableStream({ - start(controller) { - throw new Error("Test Passed"); - }, - }), + await runTest( + { + port: port++, + development: false, + fetch(req) { + return new Response( + new ReadableStream({ + start(controller) { + throw new Error("Test Passed"); + }, + }), + ); + }, + }, + async (server) => { + const response = await fetch( + `http://${server.hostname}:${server.port}`, ); + expect(response.status).toBe(500); }, - }, async (server) => { - const response = await fetch(`http://${server.hostname}:${server.port}`); - expect(response.status).toBe(500); - }); + ); }); it("text from JS throws on start has error handler", async () => { var pass = false; var err; - await runTest({ - development: false, - error(e) { - pass = true; - err = e; - return new Response("Fail", { status: 500 }); + await runTest( + { + development: false, + error(e) { + pass = true; + err = e; + return new Response("Fail", { status: 500 }); + }, + fetch(req) { + return new Response( + new ReadableStream({ + start(controller) { + throw new TypeError("error"); + }, + }), + ); + }, }, - fetch(req) { - return new Response( - new ReadableStream({ - start(controller) { - throw new TypeError("error"); - }, - }), + async (server) => { + const response = await fetch( + `http://${server.hostname}:${server.port}`, ); + expect(response.status).toBe(500); + expect(await response.text()).toBe("Fail"); + expect(pass).toBe(true); + expect(err?.name).toBe("TypeError"); + expect(err?.message).toBe("error"); }, - }, async (server) => { - const response = await fetch(`http://${server.hostname}:${server.port}`); - expect(response.status).toBe(500); - expect(await response.text()).toBe("Fail"); - expect(pass).toBe(true); - expect(err?.name).toBe("TypeError"); - expect(err?.message).toBe("error"); - }); + ); }); it("text from JS, 2 chunks, with delay", async () => { const fixture = resolve(import.meta.dir, "./fetch.js.txt"); const textToExpect = readFileSync(fixture, "utf-8"); - await runTest({ - fetch(req) { - return new Response( - new ReadableStream({ - start(controller) { - controller.enqueue(textToExpect.substring(0, 100)); - queueMicrotask(() => { - controller.enqueue(textToExpect.substring(100)); - controller.close(); - }); - }, - }), + await runTest( + { + fetch(req) { + return new Response( + new ReadableStream({ + start(controller) { + controller.enqueue(textToExpect.substring(0, 100)); + queueMicrotask(() => { + controller.enqueue(textToExpect.substring(100)); + controller.close(); + }); + }, + }), + ); + }, + }, + async (server) => { + const response = await fetch( + `http://${server.hostname}:${server.port}`, ); + expect(await response.text()).toBe(textToExpect); }, - }, async (server) => { - const response = await fetch(`http://${server.hostname}:${server.port}`); - expect(await response.text()).toBe(textToExpect); - }); + ); }); it("text from JS, 1 chunk via pull()", async () => { const fixture = resolve(import.meta.dir, "./fetch.js.txt"); const textToExpect = readFileSync(fixture, "utf-8"); - await runTest({ - fetch(req) { - return new Response( - new ReadableStream({ - pull(controller) { - controller.enqueue(textToExpect); - controller.close(); - }, - }), + await runTest( + { + fetch(req) { + return new Response( + new ReadableStream({ + pull(controller) { + controller.enqueue(textToExpect); + controller.close(); + }, + }), + ); + }, + }, + async (server) => { + const response = await fetch( + `http://${server.hostname}:${server.port}`, ); + const text = await response.text(); + expect(text).toBe(textToExpect); }, - }, async (server) => { - const response = await fetch(`http://${server.hostname}:${server.port}`); - const text = await response.text(); - expect(text).toBe(textToExpect); - }); + ); }); it("text from JS, 2 chunks, with delay in pull", async () => { const fixture = resolve(import.meta.dir, "./fetch.js.txt"); const textToExpect = readFileSync(fixture, "utf-8"); - await runTest({ - fetch(req) { - return new Response( - new ReadableStream({ - pull(controller) { - controller.enqueue(textToExpect.substring(0, 100)); - queueMicrotask(() => { - controller.enqueue(textToExpect.substring(100)); - controller.close(); - }); - }, - }), + await runTest( + { + fetch(req) { + return new Response( + new ReadableStream({ + pull(controller) { + controller.enqueue(textToExpect.substring(0, 100)); + queueMicrotask(() => { + controller.enqueue(textToExpect.substring(100)); + controller.close(); + }); + }, + }), + ); + }, + }, + async (server) => { + const response = await fetch( + `http://${server.hostname}:${server.port}`, ); + expect(await response.text()).toBe(textToExpect); }, - }, async (server) => { - const response = await fetch(`http://${server.hostname}:${server.port}`); - expect(await response.text()).toBe(textToExpect); - }); + ); }); it("text from JS, 2 chunks, with async pull", async () => { const fixture = resolve(import.meta.dir, "./fetch.js.txt"); const textToExpect = readFileSync(fixture, "utf-8"); - await runTest({ - fetch(req) { - return new Response( - new ReadableStream({ - async pull(controller) { - controller.enqueue(textToExpect.substring(0, 100)); - await Promise.resolve(); - controller.enqueue(textToExpect.substring(100)); - await Promise.resolve(); - controller.close(); - }, - }), + await runTest( + { + fetch(req) { + return new Response( + new ReadableStream({ + async pull(controller) { + controller.enqueue(textToExpect.substring(0, 100)); + await Promise.resolve(); + controller.enqueue(textToExpect.substring(100)); + await Promise.resolve(); + controller.close(); + }, + }), + ); + }, + }, + async (server) => { + const response = await fetch( + `http://${server.hostname}:${server.port}`, ); + expect(await response.text()).toBe(textToExpect); }, - }, async (server) => { - const response = await fetch(`http://${server.hostname}:${server.port}`); - expect(await response.text()).toBe(textToExpect); - }); + ); }); it("text from JS, 10 chunks, with async pull", async () => { const fixture = resolve(import.meta.dir, "./fetch.js.txt"); const textToExpect = readFileSync(fixture, "utf-8"); - await runTest({ - fetch(req) { - return new Response( - new ReadableStream({ - async pull(controller) { - var remain = textToExpect; - for (let i = 0; i < 10 && remain.length > 0; i++) { - controller.enqueue(remain.substring(0, 100)); - remain = remain.substring(100); - await new Promise((resolve) => queueMicrotask(resolve)); - } - - controller.enqueue(remain); - controller.close(); - }, - }), + await runTest( + { + fetch(req) { + return new Response( + new ReadableStream({ + async pull(controller) { + var remain = textToExpect; + for (let i = 0; i < 10 && remain.length > 0; i++) { + controller.enqueue(remain.substring(0, 100)); + remain = remain.substring(100); + await new Promise((resolve) => queueMicrotask(resolve)); + } + + controller.enqueue(remain); + controller.close(); + }, + }), + ); + }, + }, + async (server) => { + const response = await fetch( + `http://${server.hostname}:${server.port}`, ); + expect(await response.text()).toBe(textToExpect); }, - }, async (server) => { - const response = await fetch(`http://${server.hostname}:${server.port}`); - expect(await response.text()).toBe(textToExpect); - }); + ); }); }); it("should work for a hello world", async () => { - await runTest({ - fetch(req) { - return new Response(`Hello, world!`); + await runTest( + { + fetch(req) { + return new Response(`Hello, world!`); + }, }, - }, async (server) => { - const response = await fetch(`http://${server.hostname}:${server.port}`); - expect(await response.text()).toBe("Hello, world!"); - }); + async (server) => { + const response = await fetch(`http://${server.hostname}:${server.port}`); + expect(await response.text()).toBe("Hello, world!"); + }, + ); }); it("should work for a blob", async () => { const fixture = resolve(import.meta.dir, "./fetch.js.txt"); const textToExpect = readFileSync(fixture, "utf-8"); - await runTest({ - fetch(req) { - return new Response(new Blob([textToExpect])); + await runTest( + { + fetch(req) { + return new Response(new Blob([textToExpect])); + }, }, - }, async (server) => { - const response = await fetch(`http://${server.hostname}:${server.port}`); - expect(await response.text()).toBe(textToExpect); - }); + async (server) => { + const response = await fetch(`http://${server.hostname}:${server.port}`); + expect(await response.text()).toBe(textToExpect); + }, + ); }); it("should work for a blob stream", async () => { const fixture = resolve(import.meta.dir, "./fetch.js.txt"); const textToExpect = readFileSync(fixture, "utf-8"); - await runTest({ - fetch(req) { - return new Response(new Blob([textToExpect]).stream()); + await runTest( + { + fetch(req) { + return new Response(new Blob([textToExpect]).stream()); + }, }, - }, async (server) => { - const response = await fetch(`http://${server.hostname}:${server.port}`); - expect(await response.text()).toBe(textToExpect); - }); + async (server) => { + const response = await fetch(`http://${server.hostname}:${server.port}`); + expect(await response.text()).toBe(textToExpect); + }, + ); }); it("should work for a file stream", async () => { const fixture = resolve(import.meta.dir, "./fetch.js.txt"); const textToExpect = readFileSync(fixture, "utf-8"); - await runTest({ - fetch(req) { - return new Response(file(fixture).stream()); + await runTest( + { + fetch(req) { + return new Response(file(fixture).stream()); + }, }, - }, async (server) => { - const response = await fetch(`http://${server.hostname}:${server.port}`); - expect(await response.text()).toBe(textToExpect); - }); + async (server) => { + const response = await fetch(`http://${server.hostname}:${server.port}`); + expect(await response.text()).toBe(textToExpect); + }, + ); }); it("fetch should work with headers", async () => { const fixture = resolve(import.meta.dir, "./fetch.js.txt"); - await runTest({ - fetch(req) { - if (req.headers.get("X-Foo") !== "bar") { - return new Response("X-Foo header not set", { status: 500 }); - } - return new Response(file(fixture), { - headers: { "X-Both-Ways": "1" }, + await runTest( + { + fetch(req) { + if (req.headers.get("X-Foo") !== "bar") { + return new Response("X-Foo header not set", { status: 500 }); + } + return new Response(file(fixture), { + headers: { "X-Both-Ways": "1" }, + }); + }, + }, + async (server) => { + const response = await fetch(`http://${server.hostname}:${server.port}`, { + headers: { + "X-Foo": "bar", + }, }); + expect(response.status).toBe(200); + expect(response.headers.get("X-Both-Ways")).toBe("1"); }, - }, async (server) => { - const response = await fetch(`http://${server.hostname}:${server.port}`, { - headers: { - "X-Foo": "bar", - }, - }); - expect(response.status).toBe(200); - expect(response.headers.get("X-Both-Ways")).toBe("1"); - }); + ); }); it(`should work for a file ${count} times serial`, async () => { const fixture = resolve(import.meta.dir, "./fetch.js.txt"); const textToExpect = readFileSync(fixture, "utf-8"); - await runTest({ - async fetch(req) { - return new Response(file(fixture)); + await runTest( + { + async fetch(req) { + return new Response(file(fixture)); + }, }, - }, async (server) => { - for (let i = 0; i < count; i++) { - const response = await fetch(`http://${server.hostname}:${server.port}`); - expect(await response.text()).toBe(textToExpect); - } - }); + async (server) => { + for (let i = 0; i < count; i++) { + const response = await fetch( + `http://${server.hostname}:${server.port}`, + ); + expect(await response.text()).toBe(textToExpect); + } + }, + ); }); it(`should work for ArrayBuffer ${count} times serial`, async () => { const textToExpect = "hello"; - await runTest({ - fetch(req) { - return new Response(new TextEncoder().encode(textToExpect)); + await runTest( + { + fetch(req) { + return new Response(new TextEncoder().encode(textToExpect)); + }, }, - }, async (server) => { - for (let i = 0; i < count; i++) { - const response = await fetch(`http://${server.hostname}:${server.port}`); - expect(await response.text()).toBe(textToExpect); - } - }); + async (server) => { + for (let i = 0; i < count; i++) { + const response = await fetch( + `http://${server.hostname}:${server.port}`, + ); + expect(await response.text()).toBe(textToExpect); + } + }, + ); }); describe("parallel", () => { it(`should work for text ${count} times in batches of 5`, async () => { const textToExpect = "hello"; - await runTest({ - fetch(req) { - return new Response(textToExpect); - }, - }, async (server) => { - for (let i = 0; i < count; ) { - let responses = await Promise.all([ - fetch(`http://${server.hostname}:${server.port}`), - fetch(`http://${server.hostname}:${server.port}`), - fetch(`http://${server.hostname}:${server.port}`), - fetch(`http://${server.hostname}:${server.port}`), - fetch(`http://${server.hostname}:${server.port}`), - ]); - - for (let response of responses) { - expect(await response.text()).toBe(textToExpect); + await runTest( + { + fetch(req) { + return new Response(textToExpect); + }, + }, + async (server) => { + for (let i = 0; i < count; ) { + let responses = await Promise.all([ + fetch(`http://${server.hostname}:${server.port}`), + fetch(`http://${server.hostname}:${server.port}`), + fetch(`http://${server.hostname}:${server.port}`), + fetch(`http://${server.hostname}:${server.port}`), + fetch(`http://${server.hostname}:${server.port}`), + ]); + + for (let response of responses) { + expect(await response.text()).toBe(textToExpect); + } + i += responses.length; } - i += responses.length; - } - }); + }, + ); }); it(`should work for Uint8Array ${count} times in batches of 5`, async () => { const textToExpect = "hello"; - await runTest({ - fetch(req) { - return new Response(new TextEncoder().encode(textToExpect)); + await runTest( + { + fetch(req) { + return new Response(new TextEncoder().encode(textToExpect)); + }, }, - }, async (server) => { - for (let i = 0; i < count; ) { - let responses = await Promise.all([ - fetch(`http://${server.hostname}:${server.port}`), - fetch(`http://${server.hostname}:${server.port}`), - fetch(`http://${server.hostname}:${server.port}`), - fetch(`http://${server.hostname}:${server.port}`), - fetch(`http://${server.hostname}:${server.port}`), - ]); - - for (let response of responses) { - expect(await response.text()).toBe(textToExpect); + async (server) => { + for (let i = 0; i < count; ) { + let responses = await Promise.all([ + fetch(`http://${server.hostname}:${server.port}`), + fetch(`http://${server.hostname}:${server.port}`), + fetch(`http://${server.hostname}:${server.port}`), + fetch(`http://${server.hostname}:${server.port}`), + fetch(`http://${server.hostname}:${server.port}`), + ]); + + for (let response of responses) { + expect(await response.text()).toBe(textToExpect); + } + i += responses.length; } - i += responses.length; - } - }); + }, + ); }); }); it("should support reloading", async () => { const first = (req) => new Response("first"); const second = (req) => new Response("second"); - await runTest({ - fetch: first, - }, async (server) => { - const response = await fetch(`http://${server.hostname}:${server.port}`); - expect(await response.text()).toBe("first"); - server.reload({ fetch: second }); - const response2 = await fetch(`http://${server.hostname}:${server.port}`); - expect(await response2.text()).toBe("second"); - }); + await runTest( + { + fetch: first, + }, + async (server) => { + const response = await fetch(`http://${server.hostname}:${server.port}`); + expect(await response.text()).toBe("first"); + server.reload({ fetch: second }); + const response2 = await fetch(`http://${server.hostname}:${server.port}`); + expect(await response2.text()).toBe("second"); + }, + ); }); describe("status code text", () => { @@ -577,54 +681,68 @@ describe("status code text", () => { for (let code in fixture) { it(`should return ${code} ${fixture[code]}`, async () => { - await runTest({ - fetch(req) { - return new Response("hey", { status: +code }); + await runTest( + { + fetch(req) { + return new Response("hey", { status: +code }); + }, }, - }, async (server) => { - const response = await fetch(`http://${server.hostname}:${server.port}`); - expect(response.status).toBe(parseInt(code)); - expect(response.statusText).toBe(fixture[code]); - }); + async (server) => { + const response = await fetch( + `http://${server.hostname}:${server.port}`, + ); + expect(response.status).toBe(parseInt(code)); + expect(response.statusText).toBe(fixture[code]); + }, + ); }); } }); it("should support multiple Set-Cookie headers", async () => { - await runTest({ - fetch(req) { - return new Response("hello", { - headers: [ - ["Another-Header", "1"], - ["Set-Cookie", "foo=bar"], - ["Set-Cookie", "baz=qux"], - ], - }); + await runTest( + { + fetch(req) { + return new Response("hello", { + headers: [ + ["Another-Header", "1"], + ["Set-Cookie", "foo=bar"], + ["Set-Cookie", "baz=qux"], + ], + }); + }, }, - }, async (server) => { - const response = await fetch(`http://${server.hostname}:${server.port}`); - expect(response.headers.getAll("Set-Cookie")).toEqual(["foo=bar", "baz=qux"]); - expect(response.headers.get("Set-Cookie")).toEqual("foo=bar, baz=qux"); - - const cloned = response.clone().headers; - expect(response.headers.getAll("Set-Cookie")).toEqual(["foo=bar", "baz=qux"]); - - response.headers.delete("Set-Cookie"); - expect(response.headers.getAll("Set-Cookie")).toEqual([]); - response.headers.delete("Set-Cookie"); - expect(cloned.getAll("Set-Cookie")).toEqual(["foo=bar", "baz=qux"]); - expect(new Headers(cloned).getAll("Set-Cookie")).toEqual([ - "foo=bar", - "baz=qux", - ]); - }); + async (server) => { + const response = await fetch(`http://${server.hostname}:${server.port}`); + expect(response.headers.getAll("Set-Cookie")).toEqual([ + "foo=bar", + "baz=qux", + ]); + expect(response.headers.get("Set-Cookie")).toEqual("foo=bar, baz=qux"); + + const cloned = response.clone().headers; + expect(response.headers.getAll("Set-Cookie")).toEqual([ + "foo=bar", + "baz=qux", + ]); + + response.headers.delete("Set-Cookie"); + expect(response.headers.getAll("Set-Cookie")).toEqual([]); + response.headers.delete("Set-Cookie"); + expect(cloned.getAll("Set-Cookie")).toEqual(["foo=bar", "baz=qux"]); + expect(new Headers(cloned).getAll("Set-Cookie")).toEqual([ + "foo=bar", + "baz=qux", + ]); + }, + ); }); describe("should support Content-Range with Bun.file()", () => { var server; // this must be a big file so we can test potentially multiple chunks // more than 65 KB - const full = function() { + const full = (function () { const fixture = resolve(import.meta.dir + "/fetch.js.txt"); const chunk = readFileSync(fixture); var whole = new Uint8Array(chunk.byteLength * 128); @@ -633,7 +751,7 @@ describe("should support Content-Range with Bun.file()", () => { } writeFileSync(fixture + ".big", whole); return whole; - }(); + })(); const fixture = resolve(import.meta.dir + "/fetch.js.txt") + ".big"; const getServer = runTest.bind(null, { fetch(req) { |