Skip to content

Commit cccca96

Browse files
committed
test: Workaround for Jest dynamic import issues
Dynamic imports cause segmentation faults with Jest: nodejs/node#35889. We work around this by handling imports in IdentityProviderFactory differently when Jest is running. For unit tests we use a different tsconfig that transpiles dynamic imports differently, as those are also used in AppRunner.
1 parent bfa70a4 commit cccca96

File tree

4 files changed

+47
-4
lines changed

4 files changed

+47
-4
lines changed

jest.config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ const esModules = [
4646
module.exports = {
4747
transform: {
4848
'^.+\\.ts$': [ 'ts-jest', {
49-
tsconfig: '<rootDir>/tsconfig.json',
49+
tsconfig: '<rootDir>/test/tsconfig.json',
5050
diagnostics: false,
5151
isolatedModules: true,
5252
}],

src/identity/configuration/IdentityProviderFactory.ts

+16-2
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,23 @@ export class IdentityProviderFactory implements ProviderFactory {
145145
// Render errors with our own error handler
146146
this.configureErrors(config);
147147

148-
// Allow provider to interpret reverse proxy headers.
149148
// As oidc-provider is an ESM package and CSS is CJS, we have to use a dynamic import here.
150-
const provider = new (await import('oidc-provider')).default(this.baseUrl, config);
149+
// Unfortunately, there is a Node/Jest bug that causes segmentation faults when doing such an import in Jest:
150+
// https://github.com/nodejs/node/issues/35889
151+
// To work around that, we do the import differently, in case we are in a Jest test run.
152+
// This can be detected via the env variables: https://jestjs.io/docs/environment-variables.
153+
// There have been reports of `JEST_WORKER_ID` being undefined, so to be sure we check both.
154+
let ctr: { default: new(issuer: string, configuration?: Configuration) => Provider };
155+
// eslint-disable-next-line no-process-env
156+
if (process.env.JEST_WORKER_ID ?? process.env.NODE_ENV === 'test') {
157+
// eslint-disable-next-line no-undef
158+
ctr = jest.requireActual('oidc-provider');
159+
} else {
160+
ctr = await import('oidc-provider');
161+
}
162+
const provider = new ctr.default(this.baseUrl, config);
163+
164+
// Allow provider to interpret reverse proxy headers.
151165
provider.proxy = true;
152166

153167
this.captureErrorResponses(provider);

test/tsconfig.json

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
{
22
"extends": "../tsconfig.json",
3+
"compilerOptions": {
4+
"moduleResolution": "node"
5+
},
36
"include": [
47
"."
58
]

test/unit/identity/configuration/IdentityProviderFactory.test.ts

+27-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ const routes = {
3737
};
3838

3939
describe('An IdentityProviderFactory', (): void => {
40+
let jestWorkerId: string | undefined;
41+
let nodeEnv: string | undefined;
4042
let baseConfig: Configuration;
4143
const baseUrl = 'http://example.com/foo/';
4244
const oidcPath = '/oidc';
@@ -53,8 +55,24 @@ describe('An IdentityProviderFactory', (): void => {
5355
let responseWriter: jest.Mocked<ResponseWriter>;
5456
let factory: IdentityProviderFactory;
5557

58+
beforeAll(async(): Promise<void> => {
59+
// We need to fool the IDP factory into thinking we are not in a test run,
60+
// otherwise we can't mock the oidc-provider library due to the workaround in the code there.
61+
jestWorkerId = process.env.JEST_WORKER_ID;
62+
nodeEnv = process.env.NODE_ENV;
63+
delete process.env.JEST_WORKER_ID;
64+
delete process.env.NODE_ENV;
65+
});
66+
67+
afterAll(async(): Promise<void> => {
68+
process.env.JEST_WORKER_ID = jestWorkerId;
69+
process.env.NODE_ENV = nodeEnv;
70+
});
71+
5672
beforeEach(async(): Promise<void> => {
57-
baseConfig = { claims: { webid: [ 'webid', 'client_webid' ]}};
73+
// Disabling devInteractions to prevent warnings when testing the path
74+
// where we use the actual library instead of a mock.
75+
baseConfig = { claims: { webid: [ 'webid', 'client_webid' ]}, features: { devInteractions: { enabled: false }}};
5876

5977
ctx = {
6078
method: 'GET',
@@ -308,4 +326,12 @@ describe('An IdentityProviderFactory', (): void => {
308326
expect(oldAccept).toHaveBeenCalledTimes(1);
309327
expect(oldAccept).toHaveBeenLastCalledWith('something');
310328
});
329+
330+
it('avoids dynamic imports when testing with Jest.', async(): Promise<void> => {
331+
// Reset the env variable, so we can test the path where the dynamic import is not used
332+
process.env.JEST_WORKER_ID = jestWorkerId;
333+
const provider = await factory.getProvider() as any;
334+
// We don't define this in our mock
335+
expect(provider.app).toBeDefined();
336+
});
311337
});

0 commit comments

Comments
 (0)