Fix @tryghost/error utils

refs: https://github.com/TryGhost/Toolbox/issues/147
This commit is contained in:
Sam Lord 2021-11-30 13:58:27 +00:00
parent 6f97bc9063
commit 4414857e4d
3 changed files with 182 additions and 13 deletions

View File

@ -294,8 +294,10 @@ const ghostErrors = {
};
module.exports = ghostErrors;
const ghostErrorsWithBase = Object.assign({}, ghostErrors, {GhostError});
module.exports.utils = {
serialize: utils.serialize.bind(ghostErrors),
deserialize: utils.deserialize.bind(ghostErrors),
isGhostError: utils.isGhostError.bind(ghostErrors)
serialize: utils.serialize.bind(ghostErrorsWithBase),
deserialize: utils.deserialize.bind(ghostErrorsWithBase),
isGhostError: utils.isGhostError.bind(ghostErrorsWithBase)
};

View File

@ -70,4 +70,153 @@ describe('Errors', function () {
ghostError.stack.should.match(/Error: string/);
});
});
});
describe('isGhostError', function () {
it('can determine non-Ghost errors', function () {
var isGhostError = errors.utils.isGhostError(new Error());
isGhostError.should.eql(false);
});
it('can determine standard GhostError errors', function () {
var isGhostError = errors.utils.isGhostError(new errors.NotFoundError());
isGhostError.should.eql(true);
});
it('can determine new non-GhostError errors', function () {
class NonGhostError extends Error {
constructor(options) {
super(options.message);
}
}
class CustomNonGhostError extends NonGhostError {
constructor(options) {
super(options);
}
}
const err = new CustomNonGhostError({
message: 'Doesn\'t inherit from GhostError'
});
var isGhostError = errors.utils.isGhostError(err);
isGhostError.should.eql(false);
});
});
describe('Serialization', function () {
it('serialize/deserialize error', function () {
var err = new errors.BadRequestError({
help: 'do you need help?',
context: 'i can\'t help',
property: 'email'
});
var serialized = errors.utils.serialize(err);
serialized.should.be.a.JSONErrorResponse({
status: 400,
code: 'BadRequestError',
title: 'BadRequestError',
detail: 'The request could not be understood.',
source: {
pointer: '/data/attributes/email'
},
meta: {
level: 'normal',
errorType: 'BadRequestError'
}
});
var deserialized = errors.utils.deserialize(serialized);
(deserialized instanceof Error).should.eql(true);
deserialized.id.should.eql(serialized.errors[0].id);
deserialized.message.should.eql(serialized.errors[0].detail);
deserialized.name.should.eql(serialized.errors[0].title);
deserialized.statusCode.should.eql(serialized.errors[0].status);
deserialized.level.should.eql(serialized.errors[0].meta.level);
deserialized.help.should.eql(serialized.errors[0].meta.help);
deserialized.context.should.eql(serialized.errors[0].meta.context);
deserialized.property.should.eql('email');
err = new errors.BadRequestError();
serialized = errors.utils.serialize(err);
serialized.should.be.a.JSONErrorResponse({
status: 400,
code: 'BadRequestError',
title: 'BadRequestError',
detail: 'The request could not be understood.',
meta: {
level: 'normal',
errorType: 'BadRequestError'
}
});
should.not.exist(serialized.errors[0].error);
should.not.exist(serialized.errors[0].error_description);
});
it('oauth serialize', function () {
var err = new errors.NoPermissionError({
message: 'Permissions you need to have.'
});
var serialized = errors.utils.serialize(err, {format: 'oauth'});
serialized.error.should.eql('access_denied');
serialized.error_description.should.eql('Permissions you need to have.');
serialized.status.should.eql(403);
serialized.title.should.eql('NoPermissionError');
serialized.meta.level.should.eql('normal');
should.not.exist(serialized.message);
should.not.exist(serialized.detail);
should.not.exist(serialized.code);
var deserialized = errors.utils.deserialize(serialized, {});
(deserialized instanceof errors.NoPermissionError).should.eql(true);
(deserialized instanceof Error).should.eql(true);
deserialized.id.should.eql(serialized.id);
deserialized.message.should.eql(serialized.error_description);
deserialized.name.should.eql(serialized.title);
deserialized.statusCode.should.eql(serialized.status);
deserialized.level.should.eql(serialized.meta.level);
});
it('[success] deserialize jsonapi, but target error name is unknown', function () {
var deserialized = errors.utils.deserialize({
errors: [{
name: 'UnknownError',
message: 'message'
}]
});
(deserialized instanceof errors.InternalServerError).should.eql(true);
(deserialized instanceof Error).should.eql(true);
deserialized.errorType.should.eql('UnknownError');
deserialized.message.should.eql('message');
});
it('[failure] deserialize jsonapi, but obj is empty', function () {
var deserialized = errors.utils.deserialize({});
(deserialized instanceof errors.InternalServerError).should.eql(true);
(deserialized instanceof Error).should.eql(true);
});
it('[failure] deserialize oauth, but obj is empty', function () {
var deserialized = errors.utils.deserialize({});
(deserialized instanceof errors.InternalServerError).should.eql(true);
(deserialized instanceof Error).should.eql(true);
});
it('[failure] serialize oauth, but obj is empty', function () {
var serialized = errors.utils.serialize({}, {format: 'oauth'});
serialized.error.should.eql('server_error');
});
});
});

View File

@ -1,11 +1,29 @@
/**
* Custom Should Assertions
*
* Add any custom assertions to this file.
*/
* Custom Should Assertions
*
* Add any custom assertions to this file.
*/
// Example Assertion
// should.Assertion.add('ExampleAssertion', function () {
// this.params = {operator: 'to be a valid Example Assertion'};
// this.obj.should.be.an.Object;
// });
var _ = require('lodash');
var errorProps = ['id', 'title', 'detail', 'status', 'code', 'meta'];
should.Assertion.add('JSONErrorObject', function () {
this.params = {operator: 'to be a valid JSON Error Object'};
this.obj.should.be.an.Object;
this.obj.should.have.properties(errorProps);
});
should.Assertion.add('JSONErrorResponse', function (match) {
this.params = {operator: 'to be a valid JSON Error Response'};
this.obj.should.have.property('errors').which.is.an.Array;
this.obj.errors.length.should.be.above(0);
this.obj.errors.forEach(function (err) {
err.should.be.a.JSONErrorObject();
});
if (match) {
_.some(this.obj.errors, match).should.be.true();
}
});