Files
bun.sh/test/js/node/async_hooks/AsyncLocalStorage.test.ts
dave caruso c2a77cf7ec Rewrite built-in modules to use CommonJS over ESM (#3814)
* stfdsafsd

sadffdsa

stuff

finish commonjs stuff

asdf

not done but work

not done but work

not done yet but this is how far i am

remove files

lol

update built files

uncomment everything in events lol

export default

stuff

* afdsafsd

* its not perfect but almost done

* okay

* cool

* remove temp file

* finish rebase

* revert settings.json

* a

* ch-ch-ch-ch-changes

* okay

* remove this check in release for now

* sxdcfghnjm,

* lkjhgf

* fmt

* filename can be null

* Update NodeModuleModule.h

* weee

* fmt

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-08-02 16:27:36 -07:00

563 lines
16 KiB
TypeScript

import { AsyncLocalStorage, AsyncResource } from "async_hooks";
import { describe, expect, test } from "bun:test";
describe("AsyncLocalStorage", () => {
test("throw inside of AsyncLocalStorage.run() will be passed out", () => {
const s = new AsyncLocalStorage();
expect(() => {
s.run(1, () => {
throw new Error("error");
});
}).toThrow("error");
});
});
test("AsyncResource", () => {
const resource = new AsyncResource("prisma-client-request");
var called = false;
resource.runInAsyncScope(
() => {
called = true;
},
null,
"foo",
"bar",
);
expect(called).toBe(true);
});
describe("async context passes through", () => {
test("syncronously", () => {
const s = new AsyncLocalStorage();
s.run("value", () => {
expect(s.getStore()).toBe("value");
});
expect(s.getStore()).toBe(undefined);
s.run("value", () => {
s.run("second", () => {
expect(s.getStore()).toBe("second");
});
expect(s.getStore()).toBe("value");
});
expect(s.getStore()).toBe(undefined);
});
test("promise.then", async () => {
const s = new AsyncLocalStorage<string>();
let resolve!: () => void;
const promise = new Promise<void>(r => (resolve = r));
let v!: string;
s.run("value", () => {
promise.then(() => {
v = s.getStore()!;
});
});
resolve();
await promise;
expect(v).toBe("value");
expect(s.getStore()).toBe(undefined);
});
test("nested promises", async () => {
const s = new AsyncLocalStorage<string>();
let resolve!: () => void;
let resolve2!: () => void;
const promise = new Promise<void>(r => (resolve = r));
const promise2 = new Promise<void>(r => (resolve2 = r));
let v!: string;
const resolved = Promise.resolve(5);
// console.log(1);
s.run("value", () => {
// console.log(2);
promise.then(() => {
// console.log(3);
new Promise<void>(resolve => {
// console.log(4);
setTimeout(() => {
// console.log(5);
resolve();
}, 1);
}).then(() => {
// console.log(6);
resolved.then(() => {
// console.log(7);
v = s.getStore()!;
resolve2();
});
});
});
});
resolve();
await promise2;
expect(v).toBe("value");
expect(s.getStore()).toBe(undefined);
});
test("await 1", async () => {
const s = new AsyncLocalStorage<string>();
await s.run("value", async () => {
expect(s.getStore()).toBe("value");
await 1;
expect(s.getStore()).toBe("value");
});
expect(s.getStore()).toBe(undefined);
});
test("await an actual promise", async () => {
const s = new AsyncLocalStorage<string>();
await s.run("value", async () => {
expect(s.getStore()).toBe("value");
await Promise.resolve(1);
expect(s.getStore()).toBe("value");
await Bun.sleep(2);
expect(s.getStore()).toBe("value");
});
expect(s.getStore()).toBe(undefined);
});
test("setTimeout", async () => {
let resolve: (x: string) => void;
const promise = new Promise<string>(r => (resolve = r));
const s = new AsyncLocalStorage<string>();
s.run("value", () => {
expect(s.getStore()).toBe("value");
setTimeout(() => {
resolve(s.getStore()!);
}, 2);
});
expect(s.getStore()).toBe(undefined);
expect(await promise).toBe("value");
});
test("setInterval", async () => {
let resolve: (x: string[]) => void;
const promise = new Promise<string[]>(r => (resolve = r));
const s = new AsyncLocalStorage<string>();
await s.run("value", () => {
expect(s.getStore()).toBe("value");
const array: string[] = [];
const interval = setInterval(() => {
array.push(s.getStore()!);
if (array.length === 3) {
clearInterval(interval);
resolve(array);
}
}, 5);
});
expect(s.getStore()).toBe(undefined);
expect(await promise).toEqual(["value", "value", "value"]);
});
test("setImmediate", async () => {
let resolve: (x: string) => void;
const promise = new Promise<string>(r => (resolve = r));
const s = new AsyncLocalStorage<string>();
await s.run("value", () => {
expect(s.getStore()).toBe("value");
setImmediate(() => {
resolve(s.getStore()!);
});
});
expect(s.getStore()).toBe(undefined);
expect(await promise).toBe("value");
});
test("process.nextTick", async () => {
let resolve: (x: string) => void;
const promise = new Promise<string>(r => (resolve = r));
const s = new AsyncLocalStorage<string>();
await s.run("value", () => {
expect(s.getStore()).toBe("value");
process.nextTick(() => {
resolve(s.getStore()!);
});
});
expect(s.getStore()).toBe(undefined);
expect(await promise).toBe("value");
});
test("queueMicrotask", async () => {
let resolve: (x: string) => void;
const promise = new Promise<string>(r => (resolve = r));
const s = new AsyncLocalStorage<string>();
await s.run("value", () => {
expect(s.getStore()).toBe("value");
queueMicrotask(() => {
resolve(s.getStore()!);
});
});
expect(s.getStore()).toBe(undefined);
expect(await promise).toBe("value");
});
test("promise catch", async () => {
const s = new AsyncLocalStorage<string>();
let reject!: () => void;
let promise = new Promise<void>((_, r) => (reject = r));
let v!: string;
s.run("value", () => {
promise = promise.catch(() => {
v = s.getStore()!;
});
});
reject();
await promise;
expect(v).toBe("value");
expect(s.getStore()).toBe(undefined);
});
test("promise finally", async () => {
const s = new AsyncLocalStorage<string>();
let resolve!: () => void;
let promise = new Promise<void>(r => (resolve = r));
let v!: string;
s.run("value", () => {
promise = promise.finally(() => {
v = s.getStore()!;
});
});
resolve();
await promise;
expect(v).toBe("value");
expect(s.getStore()).toBe(undefined);
});
test("fetch", async () => {
const s = new AsyncLocalStorage<string>();
await s.run("value", async () => {
expect(s.getStore()).toBe("value");
const response = await fetch("https://bun.sh") //
.then(r => {
expect(s.getStore()).toBe("value");
return true;
});
expect(s.getStore()).toBe("value");
expect(response).toBe(true);
});
expect(s.getStore()).toBe(undefined);
});
test("Bun.spawn() onExit", async () => {
const s = new AsyncLocalStorage<string>();
let value: string | undefined;
let resolve!: () => void;
const promise = new Promise<void>(r => (resolve = r));
await s.run("value", () => {
expect(s.getStore()).toBe("value");
const x = Bun.spawn({
cmd: ["echo", "hello"],
onExit(subprocess, exitCode, signalCode, error) {
value = s.getStore()!;
resolve();
},
});
expect(s.getStore()).toBe("value");
});
expect(s.getStore()).toBe(undefined);
await promise;
expect(value).toBe("value");
});
test("Bun.serve", async () => {
const s = new AsyncLocalStorage<string>();
await s.run("value", async () => {
expect(s.getStore()).toBe("value");
const server = Bun.serve({
port: 0,
fetch(request, server) {
return new Response(s.getStore()!);
},
});
const response = await fetch("http://" + server.hostname + ":" + server.port);
expect(await response.text()).toBe("value");
expect(s.getStore()).toBe("value");
});
expect(s.getStore()).toBe(undefined);
});
test("readable stream .start", async () => {
const s = new AsyncLocalStorage<string>();
let stream!: ReadableStream;
s.run("value", async () => {
stream = new ReadableStream({
start(controller) {
controller.enqueue(s.getStore()!);
controller.close();
},
});
});
const reader = stream.getReader();
const result = await reader.read();
expect(result.value).toBe("value");
const result2 = await reader.read();
expect(result2.done).toBe(true);
expect(s.getStore()).toBe(undefined);
});
test("readable stream .pull", async () => {
const s = new AsyncLocalStorage<string>();
let stream!: ReadableStream;
s.run("value", async () => {
stream = new ReadableStream(
{
start(controller) {
controller.enqueue(new Uint8Array(500));
},
pull(controller) {
controller.enqueue(s.getStore()!);
controller.close();
},
},
{
highWaterMark: 1,
size() {
return 500;
},
},
);
});
const reader = stream.getReader();
const result = await reader.read();
const result2 = await reader.read();
expect(result2.value).toBe("value");
const result3 = await reader.read();
expect(result3.done).toBe(true);
expect(s.getStore()).toBe(undefined);
});
test("readable stream .pull 2", async () => {
const s = new AsyncLocalStorage<string>();
let stream!: ReadableStream;
let n = 0;
s.run("value", async () => {
stream = new ReadableStream(
{
start(controller) {
controller.enqueue(new Uint8Array(500));
},
async pull(controller) {
controller.enqueue(s.getStore()!);
n++;
if (n < 5) {
await new Promise(r => setTimeout(r, 1));
} else {
controller.close();
}
},
},
{
highWaterMark: 1,
size() {
return 500;
},
},
);
});
expect(s.getStore()).toBe(undefined);
const reader = stream.getReader();
const result = await reader.read();
const result2 = await reader.read();
expect(result2.value).toBe("value");
const result3 = await reader.read();
expect(result3.value).toBe("value");
const result4 = await reader.read();
expect(result4.value).toBe("value");
const result5 = await reader.read();
expect(result5.value).toBe("value");
const result6 = await reader.read();
expect(result6.value).toBe("value");
const result7 = await reader.read();
expect(result7.done).toBe(true);
expect(s.getStore()).toBe(undefined);
});
test("readable stream .cancel", async () => {
const s = new AsyncLocalStorage<string>();
let stream!: ReadableStream;
let value: string | undefined;
let resolve!: () => void;
let promise = new Promise<void>(r => (resolve = r));
s.run("value", async () => {
stream = new ReadableStream({
start(controller) {},
cancel(reason) {
value = s.getStore();
resolve();
},
});
});
expect(s.getStore()).toBe(undefined);
const reader = stream.getReader();
reader.cancel();
await promise;
expect(value).toBe("value");
});
test("readable stream direct .pull", async () => {
const s = new AsyncLocalStorage<string>();
let stream!: ReadableStream;
let value: string | undefined;
let value2: string | undefined;
let resolve!: () => void;
let promise = new Promise<void>(r => (resolve = r));
s.run("value", async () => {
stream = new ReadableStream({
type: "direct",
pull(controller) {
value = s.getStore();
controller.write("hello");
controller.close();
resolve();
},
cancel(reason) {},
});
});
expect(s.getStore()).toBe(undefined);
const reader = stream.getReader();
await reader.read();
await promise;
expect(value).toBe("value");
});
// blocked by a bug with .cancel
test.todo("readable stream direct .cancel", async () => {
const s = new AsyncLocalStorage<string>();
let stream!: ReadableStream;
let value: string | undefined;
let value2: string | undefined;
let resolve!: () => void;
let promise = new Promise<void>(r => (resolve = r));
s.run("value", async () => {
stream = new ReadableStream({
type: "direct",
pull(controller) {
value = s.getStore();
controller.write("hello");
},
cancel(reason) {
console.log("1");
value2 = s.getStore();
resolve();
},
});
});
expect(s.getStore()).toBe(undefined);
const reader = stream.getReader();
await reader.read();
await reader.cancel();
await stream.cancel();
await promise;
expect(value).toBe("value");
expect(value2).toBe("value");
});
test("Websocket Server", async () => {
const s = new AsyncLocalStorage<string>();
let values_server: string[] = [];
let resolve: () => void;
const promise = new Promise<void>(r => (resolve = r));
await s.run("value", async () => {
expect(s.getStore()).toBe("value");
const server = Bun.serve({
port: 0,
fetch(request, server) {
if (server.upgrade(request)) return null as any;
return new Response(s.getStore()!);
},
websocket: {
open(ws) {
values_server.push("open:" + s.getStore());
},
message(ws, message) {
values_server.push("message:" + s.getStore());
ws.close();
},
close(ws, code, message) {
values_server.push("close:" + s.getStore());
},
},
});
const ws = new WebSocket("ws://" + server.hostname + ":" + server.port);
ws.addEventListener("open", () => {
ws.send("hello");
});
ws.addEventListener("close", () => {
resolve();
});
});
expect(s.getStore()).toBe(undefined);
await promise;
expect(values_server).toEqual(["open:value", "message:value", "close:value"]);
});
test.todo("WebSocket client", async () => {
const s = new AsyncLocalStorage<string>();
let values_client: string[] = [];
let resolve: () => void;
const promise = new Promise<void>(r => (resolve = r));
await s.run("value", async () => {
expect(s.getStore()).toBe("value");
const server = Bun.serve({
port: 0,
fetch(request, server) {
if (server.upgrade(request)) return null as any;
return new Response(s.getStore()!);
},
websocket: {
open(ws) {},
message(ws, message) {
ws.close();
},
close(ws, code, message) {},
},
});
const ws = new WebSocket("ws://" + server.hostname + ":" + server.port);
ws.addEventListener("open", () => {
ws.send("hello");
values_client.push("open:" + s.getStore());
});
ws.addEventListener("close", () => {
resolve();
values_client.push("close:" + s.getStore());
});
});
expect(s.getStore()).toBe(undefined);
await promise;
expect(values_client).toEqual(["open:value", "close:value"]);
});
test("node:fs callback", async () => {
const fs = require("fs");
const s = new AsyncLocalStorage<string>();
let resolve: (x: string) => void;
const promise = new Promise<string>(r => (resolve = r));
await s.run("value", async () => {
expect(s.getStore()).toBe("value");
fs.readFile(import.meta.path, () => {
resolve(s.getStore()!);
});
expect(s.getStore()).toBe("value");
});
expect(s.getStore()).toBe(undefined);
expect(await promise).toBe("value");
});
test("node:fs/promises", async () => {
const fs = require("fs").promises;
const s = new AsyncLocalStorage<string>();
let v!: string;
await s.run("value", async () => {
expect(s.getStore()).toBe("value");
await fs.readFile(import.meta.path).then(() => {
v = s.getStore()!;
});
expect(s.getStore()).toBe("value");
});
expect(s.getStore()).toBe(undefined);
expect(v).toBe("value");
});
test("Bun.build plugin", async () => {
const s = new AsyncLocalStorage<string>();
let a = undefined;
await s.run("value", async () => {
Bun.build({
entrypoints: [import.meta.path],
plugins: [
{
name: "test",
setup(build) {
a = s.getStore();
},
},
],
});
});
expect(a).toBe("value");
});
});