Skip to content

Commit

Permalink
Merge pull request #33 from codefortulsa/CourtbotError-Wrapping
Browse files Browse the repository at this point in the history
CourtbotError Functionality Expansion
  • Loading branch information
pipakin committed May 4, 2017
2 parents a03802e + 8fb439b commit ef44a7e
Show file tree
Hide file tree
Showing 2 changed files with 217 additions and 16 deletions.
45 changes: 36 additions & 9 deletions src/courtbotError.js
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
188 changes: 181 additions & 7 deletions test/courtbotError.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand All @@ -39,7 +39,7 @@ describe(`courtbotError`, () => {
let testSettings = {
type: `test`,
message: `message`,
case: `case`,
casenumber: `casenumber`,
api: `api`,
timestamp: Date(),
initialError: `a`
Expand All @@ -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);
Expand All @@ -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) {
Expand All @@ -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);
})
});
});

0 comments on commit ef44a7e

Please sign in to comment.