import {prepareDatabase} from 'test/server/lib/helpers/PrepareDatabase'; import {TestServer} from 'test/server/lib/helpers/TestServer'; import {createTestDir, setTmpLogLevel} from 'test/server/testUtils'; import * as testUtils from 'test/server/testUtils'; import {waitForIt} from 'test/server/wait'; import {assert} from 'chai'; import fetch from 'node-fetch'; import {PassThrough} from 'stream'; /** * Grist sticks to the Node 18 default and recommendation to give up and exit on uncaught * exceptions, unhandled promise rejections, and unhandled 'error' events. But it makes an effort * to clean up before exiting, and to log the error in a better way. */ describe('UnhandledErrors', function() { this.timeout(30000); setTmpLogLevel('warn'); let testDir: string; let oldEnv: testUtils.EnvironmentSnapshot; before(async function() { oldEnv = new testUtils.EnvironmentSnapshot(); testDir = await createTestDir('UnhandledErrors'); await prepareDatabase(testDir); }); after(function() { oldEnv.restore(); }); for (const errType of ['exception', 'rejection', 'error-event']) { it(`should clean up on unhandled ${errType}`, async function() { // Capture server log output, so that we can look to see how the server coped. const output = new PassThrough(); const serverLogLines: string[] = []; output.on('data', (data) => serverLogLines.push(data.toString())); const server = await TestServer.startServer('home', testDir, errType, undefined, undefined, {output}); try { assert.equal((await fetch(`${server.serverUrl}/status`)).status, 200); serverLogLines.length = 0; // Trigger an unhandled error, and check that the server logged it and attempted cleanup. await server.testingHooks.tickleUnhandledErrors(errType); await waitForIt(() => { assert.isTrue(serverLogLines.some(line => new RegExp(`Fake ${errType}`).test(line))); assert.isTrue(serverLogLines.some(line => /Server .* cleaning up/.test(line))); }, 1000, 100); // We expect the server to be dead now. await assert.isRejected(fetch(`${server.serverUrl}/status`), /failed.*ECONNREFUSED/); } finally { await server.stop(); } }); } });