Skip to content

Commit 8ff9fbe

Browse files
authored
test: add test for null source in ESM loader
Add test cases to verify that loadCJSModule properly handles null, undefined, and empty string source values by throwing ERR_INVALID_RETURN_PROPERTY_VALUE instead of triggering ERR_INTERNAL_ASSERTION. Tests three scenarios: - Custom loader returning null source - Custom loader returning undefined source - Custom loader returning empty string source Refs: #60401
1 parent ea08d7f commit 8ff9fbe

File tree

1 file changed

+181
-0
lines changed

1 file changed

+181
-0
lines changed
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
'use strict';
2+
3+
// Test that ESM loader handles null/undefined source gracefully
4+
// and throws meaningful error instead of ERR_INTERNAL_ASSERTION.
5+
// Refs: https://github.com/nodejs/node/issues/60401
6+
7+
const common = require('../common');
8+
const assert = require('assert');
9+
const { spawnSync } = require('child_process');
10+
const fixtures = require('../common/fixtures');
11+
12+
// Test case: Loader returning null source for CommonJS module
13+
// This should throw ERR_INVALID_RETURN_PROPERTY_VALUE, not ERR_INTERNAL_ASSERTION
14+
{
15+
const result = spawnSync(
16+
process.execPath,
17+
[
18+
'--no-warnings',
19+
'--input-type=module',
20+
'--eval',
21+
`
22+
import { register } from 'node:module';
23+
24+
// Register a custom loader that returns null source
25+
const code = 'export function load(url, context, next) {' +
26+
' if (url.includes("test-null-source")) {' +
27+
' return { format: "commonjs", source: null, shortCircuit: true };' +
28+
' }' +
29+
' return next(url);' +
30+
'}';
31+
32+
register('data:text/javascript,' + encodeURIComponent(code));
33+
34+
try {
35+
await import('file:///test-null-source.js');
36+
console.log('ERROR: Should have thrown');
37+
process.exit(1);
38+
} catch (err) {
39+
// Should throw ERR_INVALID_RETURN_PROPERTY_VALUE, not ERR_INTERNAL_ASSERTION
40+
if (err.code === 'ERR_INTERNAL_ASSERTION') {
41+
console.log('FAIL: Got ERR_INTERNAL_ASSERTION');
42+
process.exit(1);
43+
}
44+
if (err.code === 'ERR_INVALID_RETURN_PROPERTY_VALUE') {
45+
console.log('PASS: Got expected error');
46+
process.exit(0);
47+
}
48+
console.log('ERROR: Got unexpected error:', err.code);
49+
process.exit(1);
50+
}
51+
`,
52+
],
53+
{ encoding: 'utf8' }
54+
);
55+
56+
const output = result.stdout + result.stderr;
57+
58+
// Verify test passed
59+
assert.ok(
60+
output.includes('PASS: Got expected error'),
61+
'Should pass with expected error. Output: ' + output
62+
);
63+
64+
assert.strictEqual(
65+
result.status,
66+
0,
67+
'Process should exit with code 0. Output: ' + output
68+
);
69+
}
70+
71+
// Test case: Loader returning undefined source
72+
{
73+
const result = spawnSync(
74+
process.execPath,
75+
[
76+
'--no-warnings',
77+
'--input-type=module',
78+
'--eval',
79+
`
80+
import { register } from 'node:module';
81+
82+
const code = 'export function load(url, context, next) {' +
83+
' if (url.includes("test-undefined-source")) {' +
84+
' return { format: "commonjs", source: undefined, shortCircuit: true };' +
85+
' }' +
86+
' return next(url);' +
87+
'}';
88+
89+
register('data:text/javascript,' + encodeURIComponent(code));
90+
91+
try {
92+
await import('file:///test-undefined-source.js');
93+
console.log('ERROR: Should have thrown');
94+
process.exit(1);
95+
} catch (err) {
96+
if (err.code === 'ERR_INTERNAL_ASSERTION') {
97+
console.log('FAIL: Got ERR_INTERNAL_ASSERTION');
98+
process.exit(1);
99+
}
100+
if (err.code === 'ERR_INVALID_RETURN_PROPERTY_VALUE') {
101+
console.log('PASS: Got expected error');
102+
process.exit(0);
103+
}
104+
console.log('ERROR: Got unexpected error:', err.code);
105+
process.exit(1);
106+
}
107+
`,
108+
],
109+
{ encoding: 'utf8' }
110+
);
111+
112+
const output = result.stdout + result.stderr;
113+
114+
assert.ok(
115+
output.includes('PASS: Got expected error'),
116+
'Should pass with expected error for undefined. Output: ' + output
117+
);
118+
119+
assert.strictEqual(
120+
result.status,
121+
0,
122+
'Process should exit with code 0. Output: ' + output
123+
);
124+
}
125+
126+
// Test case: Loader returning empty string source
127+
{
128+
const result = spawnSync(
129+
process.execPath,
130+
[
131+
'--no-warnings',
132+
'--input-type=module',
133+
'--eval',
134+
`
135+
import { register } from 'node:module';
136+
137+
const code = 'export function load(url, context, next) {' +
138+
' if (url.includes("test-empty-source")) {' +
139+
' return { format: "commonjs", source: "", shortCircuit: true };' +
140+
' }' +
141+
' return next(url);' +
142+
'}';
143+
144+
register('data:text/javascript,' + encodeURIComponent(code));
145+
146+
try {
147+
await import('file:///test-empty-source.js');
148+
console.log('ERROR: Should have thrown');
149+
process.exit(1);
150+
} catch (err) {
151+
if (err.code === 'ERR_INTERNAL_ASSERTION') {
152+
console.log('FAIL: Got ERR_INTERNAL_ASSERTION');
153+
process.exit(1);
154+
}
155+
if (err.code === 'ERR_INVALID_RETURN_PROPERTY_VALUE') {
156+
console.log('PASS: Got expected error');
157+
process.exit(0);
158+
}
159+
console.log('ERROR: Got unexpected error:', err.code);
160+
process.exit(1);
161+
}
162+
`,
163+
],
164+
{ encoding: 'utf8' }
165+
);
166+
167+
const output = result.stdout + result.stderr;
168+
169+
assert.ok(
170+
output.includes('PASS: Got expected error'),
171+
'Should pass with expected error for empty string. Output: ' + output
172+
);
173+
174+
assert.strictEqual(
175+
result.status,
176+
0,
177+
'Process should exit with code 0. Output: ' + output
178+
);
179+
}
180+
181+
console.log('All tests passed!');

0 commit comments

Comments
 (0)