feat(core): carry the request context in an AsyncLocalStorage
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import {
|
||||
getRequestContext,
|
||||
requireRequestContext,
|
||||
runWithRequestContext,
|
||||
} from "../src/request-context.ts";
|
||||
|
||||
const ctx = { marker: "the-request" } as never;
|
||||
|
||||
test("the context is visible inside the run", () => {
|
||||
runWithRequestContext(ctx, () => {
|
||||
expect(getRequestContext()).toBe(ctx);
|
||||
});
|
||||
});
|
||||
|
||||
test("the context is visible across an await", async () => {
|
||||
// The whole point is that it survives async boundaries a caller cannot see.
|
||||
await runWithRequestContext(ctx, async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1));
|
||||
expect(getRequestContext()).toBe(ctx);
|
||||
});
|
||||
});
|
||||
|
||||
test("there is no context outside a run", () => {
|
||||
expect(getRequestContext()).toBeUndefined();
|
||||
});
|
||||
|
||||
test("requireRequestContext throws a message naming the caller", () => {
|
||||
expect(() => requireRequestContext("api.searchUsers")).toThrow(/api\.searchUsers/);
|
||||
});
|
||||
|
||||
test("concurrent runs do not see each other's context", async () => {
|
||||
const first = { id: 1 } as never;
|
||||
const second = { id: 2 } as never;
|
||||
const seen: unknown[] = [];
|
||||
|
||||
await Promise.all([
|
||||
runWithRequestContext(first, async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 5));
|
||||
seen.push(getRequestContext());
|
||||
}),
|
||||
runWithRequestContext(second, async () => {
|
||||
seen.push(getRequestContext());
|
||||
}),
|
||||
]);
|
||||
|
||||
expect(seen).toContain(first);
|
||||
expect(seen).toContain(second);
|
||||
});
|
||||
Reference in New Issue
Block a user