Skip to content
File

Blob: src/workerd/api/tests/scheduler-test.js

javascript104 lines
1// Copyright (c) 2023 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 { deepStrictEqual, strictEqual, ok } from 'node:assert';
5 
6// Test for the Event and EventTarget standard Web API implementations.
7// The implementation for these are in api/basics.{h|c++}
8 
9export const wait = {
10 async test(ctrl, env, ctx) {
11 strictEqual(typeof scheduler, 'object');
12 strictEqual(typeof scheduler.wait, 'function');
13 
14 // regular wait...
15 await scheduler.wait(10);
16 
17 // get's coerced to a number value (in this case 0)
18 await scheduler.wait('foo');
19 
20 // waiting can be canceled
21 try {
22 await scheduler.wait(100000, { signal: AbortSignal.timeout(100) });
23 throw new Error('should have thrown');
24 } catch (err) {
25 strictEqual(err.message, 'The operation was aborted due to timeout');
26 }
27 
28 try {
29 await scheduler.wait(100000, { signal: AbortSignal.abort() });
30 throw new Error('should have thrown');
31 } catch (err) {
32 strictEqual(err.message, 'The operation was aborted');
33 }
34 
35 try {
36 const ac = new AbortController();
37 const promise = scheduler.wait(10000, { signal: ac.signal });
38 ac.abort();
39 await promise;
40 throw new Error('should have thrown');
41 } catch (err) {
42 strictEqual(err.message, 'The operation was aborted');
43 }
44 
45 // Verify that ties are broken in the order the timeouts are scheduled
46 let order = [];
47 await Promise.all([
48 scheduler.wait(100).then(() => order.push(1)),
49 scheduler.wait(100).then(() => order.push(2)),
50 scheduler.wait(50).then(() => order.push(0)),
51 scheduler.wait(200).then(() => order.push(3)),
52 ]);
53 deepStrictEqual(order, [0, 1, 2, 3]);
54 
55 // Verify that timeouts are properly canceled when IoContext is destroyed.
56 await env.subrequest.fetch('http://example.com');
57 await env.subrequest.fetch('http://example.com');
58 
59 // globalThis.scheduler can be monkeypatched over...
60 scheduler = 'foo';
61 strictEqual(globalThis.scheduler, 'foo');
62 },
63};
64 
65export const sequentialAbort = {
66 async test(ctrl, env, ctx) {
67 // Sequentially wait a very long time 11k times, each time aborting via an
68 // AbortController. This exercises the abort path under heavy repetition.
69 // Test ensures that the underlying timer quota is not exceeded when the
70 // timeout is cancelled with an AbortSignal
71 const iterations = 11_000;
72 let completed = 0;
73 for (let i = 0; i < iterations; i++) {
74 const ac = new AbortController();
75 const promise = scheduler.wait(1_000_000, { signal: ac.signal });
76 ac.abort();
77 try {
78 await promise;
79 throw new Error('should have thrown');
80 } catch (err) {
81 strictEqual(err.message, 'The operation was aborted');
82 }
83 completed++;
84 }
85 strictEqual(
86 completed,
87 11_000,
88 `Expected 11000 iterations but only completed ${completed}`
89 );
90 },
91};
92 
93export default {
94 async fetch() {
95 // If globalThis.longWait exists, that means the timer fired after the
96 // IoContext was destroyed. Boo!
97 ok(!globalThis.longWait);
98 scheduler.wait(10).then(() => {
99 globalThis.longWait = true;
100 });
101 return new Response('not waiting');
102 },
103};