Skip to content
File

Blob: src/workerd/api/node/tests/module-require-mutable-exports-test.js

javascript155 lines
1// Copyright (c) 2017-2026 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 { createRequire } from 'node:module';
5import { strictEqual, notStrictEqual, ok, doesNotThrow } from 'node:assert';
6 
7const require = createRequire('/');
8 
9export const testTimersPromisesMutable = {
10 async test() {
11 const timersPromises = require('node:timers/promises');
12 const originalSetImmediate = timersPromises.setImmediate;
13 ok(typeof originalSetImmediate === 'function');
14 
15 const patchedSetImmediate = async function patchedSetImmediate() {
16 return 'patched';
17 };
18 
19 doesNotThrow(() => {
20 timersPromises.setImmediate = patchedSetImmediate;
21 });
22 
23 strictEqual(timersPromises.setImmediate, patchedSetImmediate);
24 timersPromises.setImmediate = originalSetImmediate;
25 strictEqual(timersPromises.setImmediate, originalSetImmediate);
26 
27 // require() should return the same object as import { default }
28 const { default: timersPromises2 } = await import('node:timers/promises');
29 strictEqual(timersPromises, timersPromises2);
30 
31 // But import() returns the namespace object, not the default export
32 const timersPromisesNamespace = await import('node:timers/promises');
33 notStrictEqual(timersPromises, timersPromisesNamespace);
34 strictEqual(timersPromisesNamespace.default, timersPromises);
35 },
36};
37 
38// Test that patching the require()'d object doesn't affect the original named exports
39export const testPatchingDoesNotAffectNamedExports = {
40 async test() {
41 const timersPromises = require('node:timers/promises');
42 const originalSetTimeout = timersPromises.setTimeout;
43 
44 // Patch the require()'d object
45 const patchedSetTimeout = async function patched() {
46 return 'patched';
47 };
48 timersPromises.setTimeout = patchedSetTimeout;
49 
50 // The named export from the module namespace should be unaffected
51 const { setTimeout: namedSetTimeout } =
52 await import('node:timers/promises');
53 notStrictEqual(namedSetTimeout, patchedSetTimeout);
54 strictEqual(namedSetTimeout, originalSetTimeout);
55 
56 // Restore
57 timersPromises.setTimeout = originalSetTimeout;
58 },
59};
60 
61export const testTimersMutable = {
62 test() {
63 const timers = require('node:timers');
64 const originalSetTimeout = timers.setTimeout;
65 ok(typeof originalSetTimeout === 'function');
66 
67 const patchedSetTimeout = function patchedSetTimeout() {
68 return 'patched';
69 };
70 
71 doesNotThrow(() => {
72 timers.setTimeout = patchedSetTimeout;
73 });
74 
75 strictEqual(timers.setTimeout, patchedSetTimeout);
76 timers.setTimeout = originalSetTimeout;
77 },
78};
79 
80export const testBufferMutable = {
81 test() {
82 const buffer = require('node:buffer');
83 const originalBuffer = buffer.Buffer;
84 ok(typeof originalBuffer === 'function');
85 
86 const patchedBuffer = function PatchedBuffer() {
87 return 'patched';
88 };
89 
90 doesNotThrow(() => {
91 buffer.Buffer = patchedBuffer;
92 });
93 
94 strictEqual(buffer.Buffer, patchedBuffer);
95 buffer.Buffer = originalBuffer;
96 },
97};
98 
99export const testUtilMutable = {
100 test() {
101 const util = require('node:util');
102 const originalPromisify = util.promisify;
103 ok(typeof originalPromisify === 'function');
104 
105 const patchedPromisify = function patchedPromisify() {
106 return 'patched';
107 };
108 
109 doesNotThrow(() => {
110 util.promisify = patchedPromisify;
111 });
112 
113 strictEqual(util.promisify, patchedPromisify);
114 util.promisify = originalPromisify;
115 },
116};
117 
118export const testRequireCachesMutableObject = {
119 test() {
120 const timersPromises1 = require('node:timers/promises');
121 const timersPromises2 = require('node:timers/promises');
122 
123 strictEqual(timersPromises1, timersPromises2);
124 
125 const patchedSetImmediate = async function patched() {
126 return 'patched';
127 };
128 const original = timersPromises1.setImmediate;
129 
130 timersPromises1.setImmediate = patchedSetImmediate;
131 strictEqual(timersPromises2.setImmediate, patchedSetImmediate);
132 timersPromises1.setImmediate = original;
133 },
134};
135 
136// When require_returns_default_export is enabled, require() should return the
137// default export directly (which is the object with all the functions),
138// not the namespace wrapper with both `default` and named exports.
139export const testRequireReturnsDefaultExport = {
140 test() {
141 const timers = require('node:timers');
142 // With require_returns_default_export enabled, timers should be the
143 // default export object directly, not the namespace wrapper.
144 // The default export IS the object with setTimeout, setInterval, etc.
145 ok(typeof timers.setTimeout === 'function');
146 ok(typeof timers.setInterval === 'function');
147 ok(typeof timers.clearTimeout === 'function');
148 ok(typeof timers.clearInterval === 'function');
149 // The namespace wrapper would have a 'default' property, but when
150 // we return the default export directly, there's no 'default' property
151 // on the returned object (unless the default export itself has one).
152 strictEqual(timers.default, undefined);
153 },
154};