From 8fb439bdc4da09b6040be7b3cade7c9a1b5d91cc Mon Sep 17 00:00:00 2001 From: Chris Wong Sick Hong Date: Wed, 3 May 2017 19:02:55 -0500 Subject: [PATCH] Adding wrap() and such * Adding wrap() static method * using better coding practices --- src/courtbotError.js | 45 +++++++-- test/courtbotError.test.js | 188 +++++++++++++++++++++++++++++++++++-- 2 files changed, 217 insertions(+), 16 deletions(-) diff --git a/src/courtbotError.js b/src/courtbotError.js index bd76e24..38dab7f 100644 --- a/src/courtbotError.js +++ b/src/courtbotError.js @@ -1,27 +1,54 @@ -/* Module containing an extension of the Error class for courtbot-specific purposes */ -// See https://www.bennadel.com/blog/2828-creating-custom-error-objects-in-node-js-with-error-capturestacktrace.htm +/* Module containing an extension of the Error class for courtbot-specific purposes + * Extended from https://www.bennadel.com/blog/2828-creating-custom-error-objects-in-node-js-with-error-capturestacktrace.htm */ export const COURTBOT_ERROR_NAME = `Courtbot Error`; -const API = Object.freeze({ 'GENERAL': `api-error--general`, 'GET': `api-error--get`}); +const API = Object.freeze({ 'GENERAL': `api-error--general`, 'GET': `api-error--get`, 'UNKNOWN': `api-error--unknown`}); +const GENERAL = Object.freeze({ 'GENERAL': `general--general` }); -export const COURTBOT_ERROR_TYPES = Object.freeze({ 'API': API }); +export const COURTBOT_ERROR_TYPES = Object.freeze({ 'API': API, 'GENERAL': GENERAL }); -export default class courtbotError extends Error { +export default class CourtbotError extends Error { constructor(settings = {}, context) { super(); settings = settings || {}; this.name = COURTBOT_ERROR_NAME; - this.type = settings.type || `general`; + this.type = settings.type || COURTBOT_ERROR_TYPES.GENERAL.GENERAL; this.message = settings.message || `No message listed`; - this.case = settings.case || `No case listed`; + // Backwards support for CourtbotError.case + if (settings.casenumber !== undefined) { + this.casenumber = settings.casenumber; + } else { + this.casenumber = settings.case || `No casenumber listed`; + } this.api = settings.api || `No api listed`; this.timestamp = settings.timestamp || `No timestamp listed`; this.initialError = settings.initialError || null; this.isCourtbotError = true; // undefined values are falsy - Error.captureStackTrace(this, (context || courtbotError)); + // Backwards support for CourtbotError.case + Object.defineProperty(this, `case`, {get: () => this.casenumber, set: (val) => { this.casenumber = val; }}); + + Error.captureStackTrace(this, (context || CourtbotError)); + } + + // If I'm passed a non-courtbot error, I return a new courtbot error with the initial data in this.initialError + // Otherwise I just return the error. + static wrap(err, options = {}) { + // Backwards support for CourtbotError.case + if (options.case !== undefined && options.casenumber === undefined) options.casenumber = options.case; + + // Add a timestamp of now if one is not present. Otherwise, allow missing options to be set to undefined. The constructor will handle them + const {type, api, message, casenumber, timestamp} = + Object.assign( + { timestamp: Date() }, options + ); + + if(err.name !== COURTBOT_ERROR_NAME) { + return new CourtbotError({ type: type, casenumber: casenumber, api: api, message: message, timestamp: timestamp, initialError: typeof err === 'object' ? err : {data: err} }); + } + return err; } -} +} \ No newline at end of file diff --git a/test/courtbotError.test.js b/test/courtbotError.test.js index 2ac5c9d..0958ace 100644 --- a/test/courtbotError.test.js +++ b/test/courtbotError.test.js @@ -17,9 +17,9 @@ describe(`courtbotError`, () => { it(`a courtbotError should have the correct default settings`, () => { let testSettings = { - type: `general`, + type: testee.COURTBOT_ERROR_TYPES.GENERAL.GENERAL, message: `No message listed`, - case: `No case listed`, + casenumber: `No casenumber listed`, api: `No api listed`, timestamp: `No timestamp listed`, initialError: null @@ -29,7 +29,7 @@ describe(`courtbotError`, () => { expect(testError.type).to.equal(testSettings.type); expect(testError.message).to.equal(testSettings.message); - expect(testError.case).to.equal(testSettings.case); + expect(testError.casenumber).to.equal(testSettings.casenumber); expect(testError.api).to.equal(testSettings.api); expect(testError.timestamp).to.equal(testSettings.timestamp); expect(testError.initialError).to.deep.equal(testSettings.initialError); @@ -39,7 +39,7 @@ describe(`courtbotError`, () => { let testSettings = { type: `test`, message: `message`, - case: `case`, + casenumber: `casenumber`, api: `api`, timestamp: Date(), initialError: `a` @@ -49,7 +49,7 @@ describe(`courtbotError`, () => { expect(testError.type).to.equal(testSettings.type); expect(testError.message).to.equal(testSettings.message); - expect(testError.case).to.equal(testSettings.case); + expect(testError.casenumber).to.equal(testSettings.casenumber); expect(testError.api).to.equal(testSettings.api); expect(testError.timestamp).to.equal(testSettings.timestamp); expect(testError.initialError).to.deep.equal(testSettings.initialError); @@ -65,7 +65,7 @@ describe(`courtbotError`, () => { }); it(`a courtbotError should not include itself in a stack trace by default`, function stackTraceTest() { - try { + try { throw(new testee.default()); } catch (err) { @@ -81,4 +81,178 @@ describe(`courtbotError`, () => { expect(err.stack).to.not.contain(`stackTraceTest`); } }); -}); + + describe(`wrap()`, () => { + it(`should return a courtbot error unchanged`, () => { + let testError = new testee.default(); + expect (testee.default.wrap(testError)).to.deep.equal(testError); + }); + + it (`should wrap a non-courtbot Error object with the correct default values`, () => { + let initialError = new Error(`test`); + + let testSettings = { + type: testee.COURTBOT_ERROR_TYPES.GENERAL.GENERAL, + message: `No message listed`, + casenumber: `No casenumber listed`, + api: `No api listed`, + initialError: initialError + }; + + let testError = testee.default.wrap(initialError); + + expect(testError.type).to.equal(testSettings.type); + expect(testError.message).to.equal(testSettings.message); + expect(testError.casenumber).to.equal(testSettings.casenumber); + expect(testError.api).to.equal(testSettings.api); + expect(testError.initialError).to.deep.equal(testSettings.initialError); + }); + + it (`should wrap a non-courtbot object with the correct default values`, () => { + let initialError = {foo: `bar`}; + + let testSettings = { + type: testee.COURTBOT_ERROR_TYPES.GENERAL.GENERAL, + message: `No message listed`, + casenumber: `No casenumber listed`, + api: `No api listed`, + initialError: initialError + }; + + let testError = testee.default.wrap(initialError); + + expect(testError.type).to.equal(testSettings.type); + expect(testError.message).to.equal(testSettings.message); + expect(testError.casenumber).to.equal(testSettings.casenumber); + expect(testError.api).to.equal(testSettings.api); + expect(testError.initialError).to.deep.equal(testSettings.initialError); + }); + + it (`should wrap a non-courtbot primitive with the form { data: value } and the correct default values`, () => { + let initialError = false; + + let testSettings = { + type: testee.COURTBOT_ERROR_TYPES.GENERAL.GENERAL, + message: `No message listed`, + casenumber: `No casenumber listed`, + api: `No api listed`, + initialError: initialError + }; + + let testError = testee.default.wrap(initialError); + + expect(testError.type).to.equal(testSettings.type); + expect(testError.message).to.equal(testSettings.message); + expect(testError.casenumber).to.equal(testSettings.casenumber); + expect(testError.api).to.equal(testSettings.api); + expect(testError.initialError).to.deep.equal({ data: testSettings.initialError }); + }); + + it (`should wrap with the correct options, if set`, () => { + let initialError = new Error(`test`); + + let testSettings = { + type: testee.COURTBOT_ERROR_TYPES.API.GENERAL, + message: `test message`, + casenumber: `test casenumber`, + api: `test api`, + timestamp: `test timestamp`, + initialError: initialError + }; + + let testError = testee.default.wrap(initialError, testSettings); + expect(testError.type).to.equal(testSettings.type); + expect(testError.message).to.equal(testSettings.message); + expect(testError.casenumber).to.equal(testSettings.casenumber); + expect(testError.api).to.equal(testSettings.api); + expect(testError.timestamp).to.equal(testSettings.timestamp); + expect(testError.initialError).to.deep.equal(testSettings.initialError); + }); + }); + + describe(`backwards support for .case`, ()=> { + it (`should set CourtbotError.casenumber when passed case`, () => { + let testSettings = { + type: `test`, + message: `message`, + case: `casenumber`, + api: `api`, + timestamp: Date(), + initialError: `a` + } + + let testError = new testee.default(testSettings); + + expect(testError.type).to.equal(testSettings.type); + expect(testError.message).to.equal(testSettings.message); + expect(testError.casenumber).to.equal(testSettings.case); + expect(testError.api).to.equal(testSettings.api); + expect(testError.timestamp).to.equal(testSettings.timestamp); + expect(testError.initialError).to.deep.equal(testSettings.initialError); + }); + + it (`should preferentially use casenumber when passed both case and casenumber in settings object`, () => { + let testSettings = { + type: `test`, + message: `message`, + case: `casenumber`, + casenumber: `preferred casenumber`, + api: `api`, + timestamp: Date(), + initialError: `a` + } + + let testError = new testee.default(testSettings); + + expect(testError.type).to.equal(testSettings.type); + expect(testError.message).to.equal(testSettings.message); + expect(testError.casenumber).to.equal(testSettings.casenumber); + expect(testError.api).to.equal(testSettings.api); + expect(testError.timestamp).to.equal(testSettings.timestamp); + expect(testError.initialError).to.deep.equal(testSettings.initialError); + }); + + it ('should return CourtbotError.casenumber when accessing CourtbotError.case', () => { + let testSettings = { + type: `test`, + message: `message`, + casenumber: `casenumber`, + api: `api`, + timestamp: Date(), + initialError: `a` + } + + let testError = new testee.default(testSettings); + expect(testError.case).to.equal(testError.casenumber); + }); + + it ('should set CourtbotError.casenumber when writing to CourtbotError.case', () => { + let testError = new testee.default(); + testError.case = `testing`; + expect(testError.casenumber).to.equal(`testing`); + }); + + it ('should wrap with .case mapping to .casenumber, if set', () => { + let initialError = new Error(`test`); + + let testSettings = { + case: `test casenumber`, + }; + + let testError = testee.default.wrap(initialError, testSettings); + expect(testError.casenumber).to.equal(testSettings.case); + }); + + it ('should wrap .casenumber preferentially, if both .case and .casenumber are set', () => { + let initialError = new Error(`test`); + + let testSettings = { + case: `case`, + casenumber: `casenumber` + }; + + let testError = testee.default.wrap(initialError, testSettings); + expect(testError.casenumber).to.equal(testSettings.casenumber); + }) + }); +}); \ No newline at end of file