Skip to content

Commit 2ea9a34

Browse files
authored
Add module loading tests for user function (#124)
* add built artifacts to .gitignore * add module loading tests * add instructions to not miss package-lock.json changes
1 parent db6a30d commit 2ea9a34

26 files changed

+323
-2
lines changed

.github/PULL_REQUEST_TEMPLATE.md

+3
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@ _Description of changes:_
44

55
_Target (OCI, Managed Runtime, both):_
66

7+
## Checklist
8+
- [ ] I have run `npm install` to generate the `package-lock.json` correctly and included it in the PR.
9+
710
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,7 @@ dist/
4848

4949
# Stores VSCode versions used for testing VSCode extensions
5050
.vscode-test
51+
52+
deps/artifacts/
53+
deps/aws-lambda-cpp*/
54+
deps/curl*/

README.md

+7
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,13 @@ Then,
165165
* to run integration tests: `make test-integ`
166166
* to run smoke tests: `make test-smoke`
167167

168+
### Raising a PR
169+
When modifying dependencies (`package.json`), make sure to:
170+
1. Run `npm install` to generate an updated `package-lock.json`
171+
2. Commit both `package.json` and `package-lock.json` together
172+
173+
We require package-lock.json to be checked in to ensure consistent installations across development environments.
174+
168175
### Troubleshooting
169176

170177
While running integration tests, you might encounter the Docker Hub rate limit error with the following body:

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/** Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
2+
3+
// This should fail because it's ESM syntax in a CJS context
4+
export const handler = async (event) => {
5+
return "This should fail";
6+
};

test/handlers/extensionless/index

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/** Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
2+
3+
'use strict';
4+
// This is a CommonJS module without file extension
5+
6+
module.exports.handler = async (event) => {
7+
return "Hello from extensionless CJS";
8+
};

test/handlers/pkg-less/cjsAndMjs.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/** Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
2+
3+
import { someESMFunction } from './esmModule.js'; // ESM import
4+
5+
module.exports.handler = async (event) => { // CJS export
6+
return someESMFunction(event);
7+
};
8+
9+
export const esm = 'This is ESM syntax'; // ESM export
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/** Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
2+
3+
'use strict';
4+
5+
const { getMessage } = require('./cjsModule.cjs')
6+
7+
exports.handler = async (_event) => {
8+
return getMessage();
9+
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/** Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
2+
3+
'use strict';
4+
5+
// This static import is not allowed in CJS
6+
import { getMessage } from './esmModule';
7+
8+
module.exports.handler = async () => {
9+
return getMessage();
10+
};

test/handlers/pkg-less/cjsInMjs.mjs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/** Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
2+
3+
'use strict';
4+
5+
// This should fail because it's CJS syntax in a ESM context
6+
module.exports.handler = async (_event) => {
7+
return 'This should fail';
8+
};

test/handlers/pkg-less/cjsModule.cjs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/** Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
2+
3+
'use strict';
4+
5+
module.exports.getMessage = () => {
6+
return "Hello from CJS!";
7+
};
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/** Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
2+
3+
import { getMessage } from './cjsModule.cjs';
4+
5+
export const handler = async (_event) => {
6+
return getMessage();
7+
};

test/handlers/pkg-less/esmInCjs.cjs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/** Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
2+
3+
// This should fail because it's ESM syntax in a CJS context
4+
export const handler = async (_event) => {
5+
return 'This should fail';
6+
};

test/handlers/pkg-less/esmModule.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/** Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
2+
3+
export const handler = async (_event) => {
4+
return 'Hello from ESM.js';
5+
};
6+
7+
export const getMessage = () => {
8+
return "Hello from ESM!";
9+
};
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/** Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
2+
3+
const { getMessage } = require('./cjsModule.cjs')
4+
5+
export const handler = async (_event) => {
6+
return getMessage();
7+
};

test/handlers/pkg/type-cjs/cjs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/** Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
2+
3+
'use strict';
4+
// This is a CommonJS module without file extension
5+
6+
module.exports.handler = async (event) => {
7+
return "Hello from extensionless CJS";
8+
};
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/** Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
2+
3+
'use strict';
4+
5+
module.exports.handler = async (_event) => {
6+
return 'Hello from CJS.js';
7+
};

test/handlers/pkg/type-cjs/esm

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/** Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
2+
3+
// This should fail because it's ESM syntax in a CJS context
4+
export const handler = async (event) => {
5+
return "This should fail";
6+
};
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/** Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
2+
3+
// This should fail because it's ESM syntax in a CJS context
4+
export const handler = async (_event) => {
5+
return 'This should fail';
6+
};
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"type": "commonjs"
3+
}

test/handlers/pkg/type-esm/cjs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/** Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
2+
3+
'use strict';
4+
// This is a CommonJS module without file extension
5+
6+
module.exports.handler = async (event) => {
7+
return "Hello from extensionless CJS";
8+
};
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/** Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
2+
3+
'use strict';
4+
5+
// This should fail because it's CJS syntax in a ESM context
6+
module.exports.handler = async (_event) => {
7+
return 'This should fail';
8+
};

test/handlers/pkg/type-esm/esm

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/** Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
2+
3+
// This should fail because it's ESM syntax in a CJS context
4+
export const handler = async (event) => {
5+
return "This should fail";
6+
};
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/** Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
2+
3+
export const handler = async (_event) => {
4+
return 'Hello from ESM.js';
5+
};
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"type": "module"
3+
}

test/unit/UserFunctionTest.js

+161
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const {
1010
HandlerNotFound,
1111
ImportModuleError,
1212
MalformedHandlerName,
13+
UserCodeSyntaxError,
1314
} = require('lambda-runtime/Errors.js');
1415
const UserFunction = require('lambda-runtime/UserFunction.js');
1516

@@ -250,6 +251,166 @@ describe('UserFunction.load method', () => {
250251

251252
response.should.be.resolvedWith('moon');
252253
});
254+
255+
it('should successfully load a CJS handler from extensionless file (no package.json)', async () => {
256+
const handler = await UserFunction.load(
257+
path.join(HANDLERS_ROOT, 'extensionless'),
258+
'index.handler',
259+
);
260+
const response = await handler('test event');
261+
262+
response.should.equal('Hello from extensionless CJS');
263+
});
264+
265+
it('should fail to load ESM syntax from extensionless file (no package.json)', async () => {
266+
await UserFunction.load(
267+
path.join(HANDLERS_ROOT, 'extensionless'),
268+
'esm-extensionless.handler',
269+
).should.be.rejectedWith(UserCodeSyntaxError);
270+
});
271+
272+
it('should load CJS handler from extensionless file with type:commonjs', async () => {
273+
// package.json is ignored in the case of extensionless
274+
const handler = await UserFunction.load(
275+
path.join(HANDLERS_ROOT, 'pkg', 'type-cjs'),
276+
'cjs.handler',
277+
);
278+
const response = await handler('test event');
279+
280+
response.should.equal('Hello from extensionless CJS');
281+
});
282+
283+
it('should fail to load ESM handler from extensionless file with type:commonjs', async () => {
284+
// package.json is ignored in the case of extensionless
285+
await UserFunction.load(
286+
path.join(HANDLERS_ROOT, 'pkg', 'type-cjs'),
287+
'esm.handler',
288+
).should.be.rejectedWith(UserCodeSyntaxError);
289+
});
290+
291+
it('should load CJS handler from extensionless file with type:module', async () => {
292+
// package.json is ignored in the case of extensionless
293+
const handler = await UserFunction.load(
294+
path.join(HANDLERS_ROOT, 'pkg', 'type-esm'),
295+
'cjs.handler',
296+
);
297+
const response = await handler('test event');
298+
299+
response.should.equal('Hello from extensionless CJS');
300+
});
301+
302+
it('should fail to load ESM handler from extensionless file with type:module', async () => {
303+
// package.json is ignored in the case of extensionless
304+
await UserFunction.load(
305+
path.join(HANDLERS_ROOT, 'pkg', 'type-esm'),
306+
'esm.handler',
307+
).should.be.rejectedWith(UserCodeSyntaxError);
308+
});
309+
310+
it('should load CJS handler from JS file with type:commonjs', async () => {
311+
const handler = await UserFunction.load(
312+
path.join(HANDLERS_ROOT, 'pkg', 'type-cjs'),
313+
'cjsModule.handler',
314+
);
315+
const response = await handler('test event');
316+
317+
response.should.equal('Hello from CJS.js');
318+
});
319+
320+
it('should fail to load ESM handler from JS file with type:commonjs', async () => {
321+
await UserFunction.load(
322+
path.join(HANDLERS_ROOT, 'pkg', 'type-cjs'),
323+
'esmModule.handler',
324+
).should.be.rejectedWith(UserCodeSyntaxError);
325+
});
326+
327+
it('should load ESM handler from JS file with type:module', async () => {
328+
const handler = await UserFunction.load(
329+
path.join(HANDLERS_ROOT, 'pkg', 'type-esm'),
330+
'esmModule.handler',
331+
);
332+
const response = await handler('test event');
333+
334+
response.should.equal('Hello from ESM.js');
335+
});
336+
337+
it('should fail to load CJS handler from JS file with type:module', async () => {
338+
await UserFunction.load(
339+
path.join(HANDLERS_ROOT, 'pkg', 'type-esm'),
340+
'cjsModule.handler',
341+
).should.be.rejectedWith(
342+
ReferenceError,
343+
/module is not defined in ES module scope/,
344+
);
345+
});
346+
347+
it('should fail to load ESM handler from JS file without type context', async () => {
348+
await UserFunction.load(
349+
path.join(HANDLERS_ROOT, 'pkg-less'),
350+
'esmModule.handler',
351+
).should.be.rejectedWith(UserCodeSyntaxError);
352+
});
353+
354+
it('should fail to load CJS handler from MJS file without type context', async () => {
355+
await UserFunction.load(
356+
path.join(HANDLERS_ROOT, 'pkg-less'),
357+
'cjsInMjs.handler',
358+
).should.be.rejectedWith(
359+
ReferenceError,
360+
/module is not defined in ES module scope/,
361+
);
362+
});
363+
364+
it('should fail to load ESM handler from CJS file without type context', async () => {
365+
await UserFunction.load(
366+
path.join(HANDLERS_ROOT, 'pkg-less'),
367+
'esmInCjs.handler',
368+
).should.be.rejectedWith(UserCodeSyntaxError);
369+
});
370+
371+
it('should fail to load mixed context handler from JS file without type context', async () => {
372+
await UserFunction.load(
373+
path.join(HANDLERS_ROOT, 'pkg-less'),
374+
'cjsAndMjs.handler',
375+
).should.be.rejectedWith(UserCodeSyntaxError);
376+
});
377+
378+
it('should successfully load ESM handler importing from CJS', async () => {
379+
const handler = await UserFunction.load(
380+
path.join(HANDLERS_ROOT, 'pkg-less'),
381+
'esmImportCjs.handler',
382+
);
383+
384+
const response = await handler();
385+
response.should.equal('Hello from CJS!');
386+
});
387+
388+
it('should fail when CJS tries to import from ESM using static import', async () => {
389+
await UserFunction.load(
390+
path.join(HANDLERS_ROOT, 'pkg-less'),
391+
'cjsImportESM.handler',
392+
).should.be.rejectedWith(UserCodeSyntaxError);
393+
});
394+
395+
it('should successfully load CJS handler importing from CJS', async () => {
396+
const handler = await UserFunction.load(
397+
path.join(HANDLERS_ROOT, 'pkg-less'),
398+
'cjsImportCjs.handler',
399+
);
400+
401+
const response = await handler();
402+
response.should.equal('Hello from CJS!');
403+
});
404+
405+
it('should fail when using require in .mjs', async () => {
406+
await UserFunction.load(
407+
path.join(HANDLERS_ROOT, 'pkg-less'),
408+
'esmRequireCjs.handler',
409+
).should.be.rejectedWith(
410+
ReferenceError,
411+
/require is not defined in ES module scope/,
412+
);
413+
});
253414
});
254415

255416
describe('type guards HandlerFunction', () => {

0 commit comments

Comments
 (0)