From 53ee6421b30d5020dd6ef09d260548cb81ed91d2 Mon Sep 17 00:00:00 2001 From: Brandon Keepers Date: Sun, 4 Jun 2017 06:30:33 -0500 Subject: [PATCH] Catch and log errors from plugins --- index.js | 17 +++++++---------- lib/robot.js | 8 ++++++-- test/robot.js | 35 +++++++++++++++++++++++++++++------ 3 files changed, 42 insertions(+), 18 deletions(-) diff --git a/index.js b/index.js index d038a36..e1b1c60 100644 --- a/index.js +++ b/index.js @@ -30,25 +30,22 @@ module.exports = options => { const server = createServer(webhook); const robot = createRobot({integration, webhook, cache, logger}); + // Log all webhook errors + webhook.on('error', logger.error.bind(logger)); + + // Log all unhandled rejections + process.on('unhandledRejection', logger.error.bind(logger)); + + // If sentry is configured, report all logged errors if (process.env.SENTRY_URL) { Raven.disableConsoleAlerts(); Raven.config(process.env.SENTRY_URL, { - captureUnhandledRejections: true, autoBreadcrumbs: true }).install({}); logger.addStream(sentryStream(Raven)); - } else { - process.on('unhandledRejection', reason => { - robot.log.error(reason); - }); } - // Handle case when webhook creation fails - webhook.on('error', err => { - logger.error(err); - }); - return { server, robot, diff --git a/lib/robot.js b/lib/robot.js index 73fe189..b10bd40 100644 --- a/lib/robot.js +++ b/lib/robot.js @@ -49,8 +49,12 @@ class Robot { return this.webhook.on(name, async event => { if (!action || action === event.payload.action) { - const github = await this.auth(event.payload.installation.id); - return callback(event, new Context(event, github)); + try { + const github = await this.auth(event.payload.installation.id); + return callback(event, new Context(event, github)); + } catch (err) { + this.log.error(err); + } } }); } diff --git a/test/robot.js b/test/robot.js index 55da635..16a4e10 100644 --- a/test/robot.js +++ b/test/robot.js @@ -2,10 +2,15 @@ const expect = require('expect'); const Context = require('../lib/context'); const createRobot = require('../lib/robot'); -const nullLogger = {}; -['trace', 'debug', 'info', 'warn', 'error', 'fatal'].forEach(level => { - nullLogger[level] = function () { }; -}); +function createNullLogger() { + const logger = expect.createSpy(); + + ['trace', 'debug', 'info', 'warn', 'error', 'fatal'].forEach(level => { + logger[level] = expect.createSpy(); + }); + + return logger; +} describe('Robot', function () { let webhook; @@ -13,6 +18,7 @@ describe('Robot', function () { let event; let callbacks; let spy; + let logger; beforeEach(function () { callbacks = {}; @@ -21,11 +27,14 @@ describe('Robot', function () { callbacks[name] = callback; }, emit: (name, event) => { - return callbacks[name](event); + if (callbacks[name]) { + return callbacks[name](event); + } } }; - robot = createRobot({webhook, logger: nullLogger}); + logger = createNullLogger(); + robot = createRobot({webhook, logger}); robot.auth = () => {}; event = { @@ -63,4 +72,18 @@ describe('Robot', function () { expect(spy).toNotHaveBeenCalled(); }); }); + + describe('error handling', () => { + it('logs errors throw from handlers', async () => { + const error = new Error('testing'); + + robot.on('test', () => { + throw error; + }); + + await webhook.emit('test', event); + + expect(logger.error).toHaveBeenCalledWith(error); + }); + }); });