Skip to content
File

Blob: src/workerd/api/node/tests/bound-als-test.js

javascript79 lines
1// Copyright (c) 2025 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4import { AsyncLocalStorage } from 'node:async_hooks';
5import { strictEqual } from 'node:assert';
6 
7// This test verifies that the AsyncLocalStorage snapshot and bind APIs appropriately
8// throw an error when called from outside the request context in which they were created.
9// This is a workers specific limitation and is not present in Node.js.
10 
11// The test will only work if the associated compatibility flag is enabled.
12strictEqual(
13 Cloudflare.compatibilityFlags.bind_asynclocalstorage_snapshot_to_request,
14 true
15);
16 
17const kErr =
18 'Cannot call this AsyncLocalStorage bound function outside of the ' +
19 'request in which it was created.';
20 
21export const test = {
22 async test(_, env) {
23 const resp = await env.subrequest.fetch(
24 'http://AsyncLocalStorage/snapshot'
25 );
26 strictEqual(resp.status, 200);
27 strictEqual(await resp.text(), 'ok');
28 const resp2 = await env.subrequest.fetch(
29 'http://AsyncLocalStorage/snapshot'
30 );
31 strictEqual(resp2.status, 500);
32 strictEqual(await resp2.text(), kErr);
33 
34 const resp3 = await env.subrequest.fetch('http://AsyncLocalStorage/bind');
35 strictEqual(resp3.status, 200);
36 strictEqual(await resp3.text(), 'ok');
37 const resp4 = await env.subrequest.fetch('http://AsyncLocalStorage/bind');
38 strictEqual(resp4.status, 500);
39 strictEqual(await resp4.text(), kErr);
40 },
41};
42 
43let snapshot;
44let boundFn;
45 
46const als = new AsyncLocalStorage();
47const noRequestSnapshot = als.run(123, () => AsyncLocalStorage.snapshot());
48 
49export default {
50 async fetch(req) {
51 // A snapshot created outside of a request can still be used, and can
52 // perform i/o operation...
53 strictEqual(
54 noRequestSnapshot(() => {
55 // Works if no error is thrown. We don't care about the actual
56 // callback here.
57 setTimeout(() => {}, 0);
58 return als.getStore();
59 }),
60 123
61 );
62 
63 try {
64 if (req.url.endsWith('/snapshot')) {
65 snapshot ??= AsyncLocalStorage.snapshot();
66 snapshot(() => {});
67 return new Response('ok');
68 } else if (req.url.endsWith('/bind')) {
69 boundFn ??= AsyncLocalStorage.bind(() => {});
70 boundFn();
71 return new Response('ok');
72 }
73 throw new Error('Unknown request');
74 } catch (err) {
75 return new Response(err.message, { status: 500 });
76 }
77 },
78};