mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
4d9bbf6263
Summary: - Node has a strong recommendation to assume bad state and exit promptly on unhandled exceptions and rejections. We follow it, and only make an effort to clean up before exiting, and to log the error in a more standard way. - The only case seen in recent month of an unhandled rejection was for attempting to write overly large JSON to a Client websocket. Ensure that's handled, and add a test case that artificially reproduces this scenario. Test Plan: Added a test case for failing write to Client, and a test case that unhandled errors indeed kill the server but with an attempt at cleanup. Reviewers: georgegevoian Reviewed By: georgegevoian Differential Revision: https://phab.getgrist.com/D4124
55 lines
2.0 KiB
TypeScript
55 lines
2.0 KiB
TypeScript
import {prepareDatabase} from 'test/server/lib/helpers/PrepareDatabase';
|
|
import {TestServer} from 'test/server/lib/helpers/TestServer';
|
|
import {createTestDir, setTmpLogLevel} 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;
|
|
|
|
before(async function() {
|
|
testDir = await createTestDir('UnhandledErrors');
|
|
await prepareDatabase(testDir);
|
|
});
|
|
|
|
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();
|
|
}
|
|
});
|
|
}
|
|
});
|