diff --git a/jest.config.js b/jest.config.js index 771c8f206c..6e2e15944f 100644 --- a/jest.config.js +++ b/jest.config.js @@ -46,7 +46,7 @@ const esModules = [ module.exports = { transform: { '^.+\\.ts$': [ 'ts-jest', { - tsconfig: '/tsconfig.json', + tsconfig: '/test/tsconfig.json', diagnostics: false, isolatedModules: true, }], diff --git a/src/identity/configuration/IdentityProviderFactory.ts b/src/identity/configuration/IdentityProviderFactory.ts index 90425c6d77..f478642a64 100644 --- a/src/identity/configuration/IdentityProviderFactory.ts +++ b/src/identity/configuration/IdentityProviderFactory.ts @@ -144,9 +144,23 @@ export class IdentityProviderFactory implements ProviderFactory { // Render errors with our own error handler this.configureErrors(config); - // Allow provider to interpret reverse proxy headers. // As oidc-provider is an ESM package and CSS is CJS, we have to use a dynamic import here. - const provider = new (await import('oidc-provider')).default(this.baseUrl, config); + // Unfortunately, there is a Node/Jest bug that causes segmentation faults when doing such an import in Jest: + // https://github.com/nodejs/node/issues/35889 + // To work around that, we do the import differently, in case we are in a Jest test run. + // This can be detected via the env variables: https://jestjs.io/docs/environment-variables. + // There have been reports of `JEST_WORKER_ID` being undefined, so to be sure we check both. + let ctr: { default: new(issuer: string, configuration?: Configuration) => Provider }; + // eslint-disable-next-line no-process-env + if (process.env.JEST_WORKER_ID ?? process.env.NODE_ENV === 'test') { + // eslint-disable-next-line no-undef + ctr = jest.requireActual('oidc-provider'); + } else { + ctr = await import('oidc-provider'); + } + const provider = new ctr.default(this.baseUrl, config); + + // Allow provider to interpret reverse proxy headers. provider.proxy = true; this.captureErrorResponses(provider); diff --git a/test/tsconfig.json b/test/tsconfig.json index f75e3dba36..80d54095e5 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -1,5 +1,8 @@ { "extends": "../tsconfig.json", + "compilerOptions": { + "moduleResolution": "node" + }, "include": [ "." ] diff --git a/test/unit/identity/configuration/IdentityProviderFactory.test.ts b/test/unit/identity/configuration/IdentityProviderFactory.test.ts index b404bd917c..cc08a1895a 100644 --- a/test/unit/identity/configuration/IdentityProviderFactory.test.ts +++ b/test/unit/identity/configuration/IdentityProviderFactory.test.ts @@ -36,6 +36,8 @@ const routes = { }; describe('An IdentityProviderFactory', (): void => { + let jestWorkerId: string | undefined; + let nodeEnv: string | undefined; let baseConfig: Configuration; const baseUrl = 'http://example.com/foo/'; const oidcPath = '/oidc'; @@ -52,8 +54,24 @@ describe('An IdentityProviderFactory', (): void => { let responseWriter: jest.Mocked; let factory: IdentityProviderFactory; + beforeAll(async(): Promise => { + // We need to fool the IDP factory into thinking we are not in a test run, + // otherwise we can't mock the oidc-provider library due to the workaround in the code there. + jestWorkerId = process.env.JEST_WORKER_ID; + nodeEnv = process.env.NODE_ENV; + delete process.env.JEST_WORKER_ID; + delete process.env.NODE_ENV; + }); + + afterAll(async(): Promise => { + process.env.JEST_WORKER_ID = jestWorkerId; + process.env.NODE_ENV = nodeEnv; + }); + beforeEach(async(): Promise => { - baseConfig = { claims: { webid: [ 'webid', 'client_webid' ]}}; + // Disabling devInteractions to prevent warnings when testing the path + // where we use the actual library instead of a mock. + baseConfig = { claims: { webid: [ 'webid', 'client_webid' ]}, features: { devInteractions: { enabled: false }}}; ctx = { method: 'GET', @@ -307,4 +325,12 @@ describe('An IdentityProviderFactory', (): void => { expect(oldAccept).toHaveBeenCalledTimes(1); expect(oldAccept).toHaveBeenLastCalledWith('something'); }); + + it('avoids dynamic imports when testing with Jest.', async(): Promise => { + // Reset the env variable, so we can test the path where the dynamic import is not used + process.env.JEST_WORKER_ID = jestWorkerId; + const provider = await factory.getProvider() as any; + // We don't define this in our mock + expect(provider.app).toBeDefined(); + }); });