2022-11-21 22:10:20 +03:00
|
|
|
const path = require('path');
|
2023-06-21 11:56:59 +03:00
|
|
|
const assert = require('assert/strict');
|
2024-05-06 12:30:44 +03:00
|
|
|
const sinon = require('sinon');
|
|
|
|
|
2022-09-20 12:47:59 +03:00
|
|
|
const {InternalServerError, NotFoundError} = require('@tryghost/errors');
|
|
|
|
const {cacheControlValues} = require('@tryghost/http-cache-utils');
|
2022-04-08 06:24:03 +03:00
|
|
|
const {
|
|
|
|
prepareError,
|
2022-09-20 12:47:59 +03:00
|
|
|
jsonErrorRenderer,
|
2022-04-08 06:24:03 +03:00
|
|
|
handleHTMLResponse,
|
2022-09-20 12:47:59 +03:00
|
|
|
handleJSONResponse,
|
|
|
|
prepareErrorCacheControl,
|
2022-04-08 07:40:37 +03:00
|
|
|
prepareStack,
|
2022-05-06 17:42:58 +03:00
|
|
|
resourceNotFound,
|
|
|
|
pageNotFound
|
2022-11-23 19:03:46 +03:00
|
|
|
} = require('..');
|
2021-12-07 21:40:46 +03:00
|
|
|
|
2021-12-14 15:23:17 +03:00
|
|
|
describe('Prepare Error', function () {
|
2022-11-20 15:42:21 +03:00
|
|
|
it('Correctly prepares a non-Ghost error', function (done) {
|
2021-12-14 15:23:17 +03:00
|
|
|
prepareError(new Error('test!'), {}, {
|
|
|
|
set: () => {}
|
|
|
|
}, (err) => {
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.equal(err.statusCode, 500);
|
|
|
|
assert.equal(err.name, 'InternalServerError');
|
|
|
|
assert.equal(err.message, 'An unexpected error occurred, please try again.');
|
|
|
|
assert.equal(err.context, 'test!');
|
|
|
|
assert.equal(err.code, 'UNEXPECTED_ERROR');
|
|
|
|
assert.ok(err.stack.startsWith('Error: test!'));
|
2021-12-14 15:23:17 +03:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
2022-05-06 17:42:58 +03:00
|
|
|
|
2022-11-20 15:42:21 +03:00
|
|
|
it('Correctly prepares a Ghost error', function (done) {
|
|
|
|
prepareError(new InternalServerError({message: 'Handled Error', context: 'Details'}), {}, {
|
|
|
|
set: () => {}
|
|
|
|
}, (err) => {
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.equal(err.statusCode, 500);
|
|
|
|
assert.equal(err.name, 'InternalServerError');
|
|
|
|
assert.equal(err.message, 'Handled Error');
|
|
|
|
assert.equal(err.context, 'Details');
|
|
|
|
assert.ok(err.stack.startsWith('InternalServerError: Handled Error'));
|
2022-11-20 15:42:21 +03:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2022-05-06 17:42:58 +03:00
|
|
|
it('Correctly prepares a 404 error', function (done) {
|
|
|
|
let error = {message: 'Oh dear', statusCode: 404};
|
|
|
|
|
|
|
|
prepareError(error, {}, {
|
|
|
|
set: () => {}
|
|
|
|
}, (err) => {
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.equal(err.statusCode, 404);
|
|
|
|
assert.equal(err.name, 'NotFoundError');
|
|
|
|
assert.ok(err.stack.startsWith('NotFoundError: Resource could not be found'));
|
|
|
|
assert.equal(err.hideStack, true);
|
2022-05-06 17:42:58 +03:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Correctly prepares an error array', function (done) {
|
|
|
|
prepareError([new Error('test!')], {}, {
|
|
|
|
set: () => {}
|
|
|
|
}, (err) => {
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.equal(err.statusCode, 500);
|
|
|
|
assert.equal(err.name, 'InternalServerError');
|
|
|
|
assert.ok(err.stack.startsWith('Error: test!'));
|
2022-05-06 17:42:58 +03:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2022-11-21 22:10:20 +03:00
|
|
|
it('Correctly prepares a handlebars error', function (done) {
|
2022-05-06 17:42:58 +03:00
|
|
|
let error = new Error('obscure handlebars message!');
|
2022-11-21 22:10:20 +03:00
|
|
|
|
|
|
|
error.stack += '\n';
|
|
|
|
error.stack += path.join('node_modules', 'handlebars', 'something');
|
2022-05-06 17:42:58 +03:00
|
|
|
|
|
|
|
prepareError(error, {}, {
|
|
|
|
set: () => {}
|
|
|
|
}, (err) => {
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.equal(err.statusCode, 400);
|
|
|
|
assert.equal(err.name, 'IncorrectUsageError');
|
2022-11-22 21:22:52 +03:00
|
|
|
// TODO: consider if the message should be trusted here
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.equal(err.message, 'obscure handlebars message!');
|
|
|
|
assert.ok(err.stack.startsWith('Error: obscure handlebars message!'));
|
2022-05-06 17:42:58 +03:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
2022-11-21 22:10:20 +03:00
|
|
|
|
|
|
|
it('Correctly prepares an express-hbs error', function (done) {
|
|
|
|
let error = new Error('obscure express-hbs message!');
|
|
|
|
|
|
|
|
error.stack += '\n';
|
|
|
|
error.stack += path.join('node_modules', 'express-hbs', 'lib');
|
|
|
|
|
|
|
|
prepareError(error, {}, {
|
|
|
|
set: () => {}
|
|
|
|
}, (err) => {
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.equal(err.statusCode, 400);
|
|
|
|
assert.equal(err.name, 'IncorrectUsageError');
|
|
|
|
assert.equal(err.message, 'obscure express-hbs message!');
|
|
|
|
assert.ok(err.stack.startsWith('Error: obscure express-hbs message!'));
|
2022-11-21 22:10:20 +03:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
Improved error handling for SQL errors (#18797)
refs TryGhost/Product#4083
- In the vast majority of cases, we shouldn't have SQL errors in our
code. Due to some limitations with validating e.g. nql filters passed to
the API, sometimes we don't catch these errors and they bubble up to the
user.
- In these rare cases, Ghost was returning the raw SQL error from mysql
which is not very user friendly and also exposes information about the
database, which generally is not a good practice.
- To make things worse, Sentry was treating every instance of these
errors as a unique issue, even when it was exactly the same query
failing over and over.
- This change improves the error message returned from the API, and also
makes sure that Sentry will group all these errors together, so we can
easily see how many times they are happening and where.
- It also adds more specific context to the event that is sent to
Sentry, including the mysql error number, code, and the SQL query
itself.
2023-11-01 23:47:41 +03:00
|
|
|
|
2024-05-07 18:42:14 +03:00
|
|
|
it('Correctly prepares a known ER_WRONG_VALUE mysql2 error', function (done) {
|
Improved error handling for SQL errors (#18797)
refs TryGhost/Product#4083
- In the vast majority of cases, we shouldn't have SQL errors in our
code. Due to some limitations with validating e.g. nql filters passed to
the API, sometimes we don't catch these errors and they bubble up to the
user.
- In these rare cases, Ghost was returning the raw SQL error from mysql
which is not very user friendly and also exposes information about the
database, which generally is not a good practice.
- To make things worse, Sentry was treating every instance of these
errors as a unique issue, even when it was exactly the same query
failing over and over.
- This change improves the error message returned from the API, and also
makes sure that Sentry will group all these errors together, so we can
easily see how many times they are happening and where.
- It also adds more specific context to the event that is sent to
Sentry, including the mysql error number, code, and the SQL query
itself.
2023-11-01 23:47:41 +03:00
|
|
|
let error = new Error('select anything from anywhere where something = anything;');
|
|
|
|
|
|
|
|
error.stack += '\n';
|
|
|
|
error.stack += path.join('node_modules', 'mysql2', 'lib');
|
|
|
|
error.code = 'ER_WRONG_VALUE';
|
|
|
|
error.sql = 'select anything from anywhere where something = anything;';
|
|
|
|
error.sqlMessage = 'Incorrect DATETIME value: 3234234234';
|
|
|
|
|
2024-05-07 18:42:14 +03:00
|
|
|
prepareError(error, {}, {
|
|
|
|
set: () => {}
|
|
|
|
}, (err) => {
|
|
|
|
assert.equal(err.statusCode, 422);
|
|
|
|
assert.equal(err.name, 'ValidationError');
|
|
|
|
assert.equal(err.message, 'Invalid value');
|
|
|
|
assert.equal(err.code, 'ER_WRONG_VALUE');
|
|
|
|
assert.equal(err.sqlErrorCode, 'ER_WRONG_VALUE');
|
|
|
|
assert.equal(err.sql, 'select anything from anywhere where something = anything;');
|
|
|
|
assert.equal(err.sqlMessage, 'Incorrect DATETIME value: 3234234234');
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Correctly prepares an unknown mysql2 error', function (done) {
|
|
|
|
let error = new Error('select anything from anywhere where something = anything;');
|
|
|
|
|
|
|
|
error.stack += '\n';
|
|
|
|
error.stack += path.join('node_modules', 'mysql2', 'lib');
|
|
|
|
error.code = 'ER_BAD_FIELD_ERROR';
|
|
|
|
error.sql = 'select anything from anywhere where something = anything;';
|
|
|
|
error.sqlMessage = 'Incorrect value: erororoor';
|
|
|
|
|
Improved error handling for SQL errors (#18797)
refs TryGhost/Product#4083
- In the vast majority of cases, we shouldn't have SQL errors in our
code. Due to some limitations with validating e.g. nql filters passed to
the API, sometimes we don't catch these errors and they bubble up to the
user.
- In these rare cases, Ghost was returning the raw SQL error from mysql
which is not very user friendly and also exposes information about the
database, which generally is not a good practice.
- To make things worse, Sentry was treating every instance of these
errors as a unique issue, even when it was exactly the same query
failing over and over.
- This change improves the error message returned from the API, and also
makes sure that Sentry will group all these errors together, so we can
easily see how many times they are happening and where.
- It also adds more specific context to the event that is sent to
Sentry, including the mysql error number, code, and the SQL query
itself.
2023-11-01 23:47:41 +03:00
|
|
|
prepareError(error, {}, {
|
|
|
|
set: () => {}
|
|
|
|
}, (err) => {
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.equal(err.statusCode, 500);
|
|
|
|
assert.equal(err.name, 'InternalServerError');
|
|
|
|
assert.equal(err.message, 'An unexpected error occurred, please try again.');
|
|
|
|
assert.equal(err.code, 'UNEXPECTED_ERROR');
|
2024-05-07 18:42:14 +03:00
|
|
|
assert.equal(err.sqlErrorCode, 'ER_BAD_FIELD_ERROR');
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.equal(err.sql, 'select anything from anywhere where something = anything;');
|
2024-05-07 18:42:14 +03:00
|
|
|
assert.equal(err.sqlMessage, 'Incorrect value: erororoor');
|
Improved error handling for SQL errors (#18797)
refs TryGhost/Product#4083
- In the vast majority of cases, we shouldn't have SQL errors in our
code. Due to some limitations with validating e.g. nql filters passed to
the API, sometimes we don't catch these errors and they bubble up to the
user.
- In these rare cases, Ghost was returning the raw SQL error from mysql
which is not very user friendly and also exposes information about the
database, which generally is not a good practice.
- To make things worse, Sentry was treating every instance of these
errors as a unique issue, even when it was exactly the same query
failing over and over.
- This change improves the error message returned from the API, and also
makes sure that Sentry will group all these errors together, so we can
easily see how many times they are happening and where.
- It also adds more specific context to the event that is sent to
Sentry, including the mysql error number, code, and the SQL query
itself.
2023-11-01 23:47:41 +03:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
2021-12-14 15:23:17 +03:00
|
|
|
});
|
|
|
|
|
2022-03-21 12:33:54 +03:00
|
|
|
describe('Prepare Stack', function () {
|
|
|
|
it('Correctly prepares the stack for an error', function (done) {
|
|
|
|
prepareStack(new Error('test!'), {}, {}, (err) => {
|
|
|
|
// Includes "Stack Trace" text prepending human readable trace
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.ok(err.stack.startsWith('Error: test!\nStack Trace:'));
|
2022-03-21 12:33:54 +03:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2022-09-20 12:47:59 +03:00
|
|
|
describe('Prepare Error Cache Control', function () {
|
|
|
|
it('Sets private cache control by default', function (done) {
|
|
|
|
const res = {
|
|
|
|
set: sinon.spy()
|
|
|
|
};
|
|
|
|
prepareErrorCacheControl()(new Error('generic error'), {}, res, () => {
|
|
|
|
assert(res.set.calledOnce);
|
|
|
|
assert(res.set.calledWith({
|
|
|
|
'Cache-Control': cacheControlValues.private
|
|
|
|
}));
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Sets private cache-control header for user-specific 404 responses', function (done) {
|
|
|
|
const req = {
|
|
|
|
method: 'GET',
|
|
|
|
get: (header) => {
|
|
|
|
if (header === 'authorization') {
|
|
|
|
return 'Basic YWxhZGRpbjpvcGVuc2VzYW1l';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const res = {
|
|
|
|
set: sinon.spy()
|
|
|
|
};
|
|
|
|
prepareErrorCacheControl()(new NotFoundError(), req, res, () => {
|
|
|
|
assert(res.set.calledOnce);
|
|
|
|
assert(res.set.calledWith({
|
|
|
|
'Cache-Control': cacheControlValues.private
|
|
|
|
}));
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Sets noCache cache-control header for non-user-specific 404 responses', function (done) {
|
|
|
|
const req = {
|
|
|
|
method: 'GET',
|
|
|
|
get: () => {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const res = {
|
|
|
|
set: sinon.spy(),
|
|
|
|
get: () => {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
prepareErrorCacheControl()(new NotFoundError(), req, res, () => {
|
|
|
|
assert(res.set.calledOnce);
|
|
|
|
assert(res.set.calledWith({
|
|
|
|
'Cache-Control': cacheControlValues.noCacheDynamic
|
|
|
|
}));
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2021-12-14 15:23:17 +03:00
|
|
|
describe('Error renderers', function () {
|
|
|
|
it('Renders JSON', function (done) {
|
2022-09-20 12:47:59 +03:00
|
|
|
jsonErrorRenderer(new Error('test!'), {}, {
|
2021-12-14 15:23:17 +03:00
|
|
|
json: (data) => {
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.equal(data.errors.length, 1);
|
|
|
|
assert.equal(data.errors[0].message, 'test!');
|
2021-12-14 15:23:17 +03:00
|
|
|
done();
|
|
|
|
}
|
|
|
|
}, () => {});
|
|
|
|
});
|
|
|
|
|
2022-03-24 13:06:55 +03:00
|
|
|
it('Handles unknown errors when preparing user message', function (done) {
|
2022-09-20 12:47:59 +03:00
|
|
|
jsonErrorRenderer(new RangeError('test!'), {
|
2022-03-24 13:06:55 +03:00
|
|
|
frameOptions: {
|
|
|
|
docName: 'oembed',
|
|
|
|
method: 'read'
|
|
|
|
}
|
|
|
|
}, {
|
|
|
|
json: (data) => {
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.equal(data.errors.length, 1);
|
|
|
|
assert.equal(data.errors[0].message, 'Unknown error - RangeError, cannot read oembed.');
|
|
|
|
assert.equal(data.errors[0].context, 'test!');
|
2022-03-24 13:06:55 +03:00
|
|
|
done();
|
|
|
|
}
|
|
|
|
}, () => {});
|
|
|
|
});
|
|
|
|
|
2021-12-14 15:23:17 +03:00
|
|
|
it('Uses templates when required', function (done) {
|
2022-09-20 12:47:59 +03:00
|
|
|
jsonErrorRenderer(new InternalServerError({
|
2021-12-14 15:23:17 +03:00
|
|
|
message: 'test!'
|
|
|
|
}), {
|
|
|
|
frameOptions: {
|
|
|
|
docName: 'blog',
|
|
|
|
method: 'browse'
|
|
|
|
}
|
|
|
|
}, {
|
|
|
|
json: (data) => {
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.equal(data.errors.length, 1);
|
|
|
|
assert.equal(data.errors[0].message, 'Internal server error, cannot list blog.');
|
|
|
|
assert.equal(data.errors[0].context, 'test!');
|
2022-05-06 17:42:58 +03:00
|
|
|
done();
|
|
|
|
}
|
|
|
|
}, () => {});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Uses defined message + context when available', function (done) {
|
2022-09-20 12:47:59 +03:00
|
|
|
jsonErrorRenderer(new InternalServerError({
|
2022-05-06 17:42:58 +03:00
|
|
|
message: 'test!',
|
|
|
|
context: 'Image was too large.'
|
|
|
|
}), {
|
|
|
|
frameOptions: {
|
|
|
|
docName: 'images',
|
|
|
|
method: 'upload'
|
|
|
|
}
|
|
|
|
}, {
|
|
|
|
json: (data) => {
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.equal(data.errors.length, 1);
|
|
|
|
assert.equal(data.errors[0].message, 'Internal server error, cannot upload image.');
|
|
|
|
assert.equal(data.errors[0].context, 'test! Image was too large.');
|
2021-12-14 15:23:17 +03:00
|
|
|
done();
|
|
|
|
}
|
|
|
|
}, () => {});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Exports the HTML renderer', function () {
|
|
|
|
const renderer = handleHTMLResponse({
|
|
|
|
errorHandler: () => {}
|
|
|
|
});
|
|
|
|
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.equal(renderer.length, 4);
|
2022-09-20 12:47:59 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
it('Exports the JSON renderer', function () {
|
|
|
|
const renderer = handleJSONResponse({
|
|
|
|
errorHandler: () => {}
|
|
|
|
});
|
|
|
|
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.equal(renderer.length, 5);
|
2021-12-07 21:40:46 +03:00
|
|
|
});
|
|
|
|
});
|
2022-04-08 07:40:37 +03:00
|
|
|
|
|
|
|
describe('Resource Not Found', function () {
|
|
|
|
it('Returns 404 Not Found Error for a generic case', function (done) {
|
|
|
|
resourceNotFound({}, {}, (error) => {
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.equal(error.statusCode, 404);
|
|
|
|
assert.equal(error.message, 'Resource not found');
|
2022-04-08 07:40:37 +03:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2024-05-06 12:44:53 +03:00
|
|
|
it('Returns 406 Request Not Acceptable Error for invalid version', function (done) {
|
|
|
|
const req = {
|
|
|
|
headers: {
|
|
|
|
'accept-version': 'foo'
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const res = {
|
|
|
|
locals: {
|
|
|
|
safeVersion: '4.3'
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
resourceNotFound(req, res, (error) => {
|
|
|
|
assert.equal(error.statusCode, 400);
|
|
|
|
assert.equal(error.message, 'Requested version is not supported.');
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2022-04-08 07:40:37 +03:00
|
|
|
it('Returns 406 Request Not Acceptable Error for when requested version is behind current version', function (done) {
|
|
|
|
const req = {
|
|
|
|
headers: {
|
|
|
|
'accept-version': 'v3.9'
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const res = {
|
|
|
|
locals: {
|
|
|
|
safeVersion: '4.3'
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
resourceNotFound(req, res, (error) => {
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.equal(error.statusCode, 406);
|
|
|
|
assert.equal(error.message, 'Request could not be served, the endpoint was not found.');
|
|
|
|
assert.equal(error.context, 'Provided client accept-version v3.9 is behind current Ghost version v4.3.');
|
|
|
|
assert.equal(error.help, 'Try upgrading your Ghost API client.');
|
2022-04-08 07:40:37 +03:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2022-04-08 07:52:41 +03:00
|
|
|
it('Returns 406 Request Not Acceptable Error for when requested version is ahead current version', function (done) {
|
|
|
|
const req = {
|
|
|
|
headers: {
|
|
|
|
'accept-version': 'v4.8'
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const res = {
|
|
|
|
locals: {
|
|
|
|
safeVersion: '4.3'
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
resourceNotFound(req, res, (error) => {
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.equal(error.statusCode, 406);
|
|
|
|
assert.equal(error.message, 'Request could not be served, the endpoint was not found.');
|
|
|
|
assert.equal(error.context, 'Provided client accept-version v4.8 is ahead of current Ghost version v4.3.');
|
|
|
|
assert.equal(error.help, 'Try upgrading your Ghost install.');
|
2022-04-08 07:52:41 +03:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2022-04-08 07:40:37 +03:00
|
|
|
it('Returns 404 Not Found Error for when requested version is the same as current version', function (done) {
|
|
|
|
const req = {
|
|
|
|
headers: {
|
|
|
|
'accept-version': 'v4.3'
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const res = {
|
|
|
|
locals: {
|
|
|
|
safeVersion: '4.3'
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
resourceNotFound(req, res, (error) => {
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.equal(error.statusCode, 404);
|
|
|
|
assert.equal(error.message, 'Resource not found');
|
2022-04-08 07:40:37 +03:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
2022-05-06 17:42:58 +03:00
|
|
|
|
|
|
|
describe('pageNotFound', function () {
|
|
|
|
it('returns 404 with special message when message not set', function (done) {
|
|
|
|
pageNotFound({}, {}, (error) => {
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.equal(error.statusCode, 404);
|
|
|
|
assert.equal(error.message, 'Page not found');
|
2022-05-06 17:42:58 +03:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('returns 404 with special message even if message is set', function (done) {
|
|
|
|
pageNotFound({message: 'uh oh'}, {}, (error) => {
|
2024-05-06 12:30:44 +03:00
|
|
|
assert.equal(error.statusCode, 404);
|
|
|
|
assert.equal(error.message, 'Page not found');
|
2022-05-06 17:42:58 +03:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2022-04-08 07:40:37 +03:00
|
|
|
});
|