index.js 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. // Load modules
  2. var Code = require('code');
  3. var Boom = require('../lib');
  4. var Lab = require('lab');
  5. // Declare internals
  6. var internals = {};
  7. // Test shortcuts
  8. var lab = exports.lab = Lab.script();
  9. var describe = lab.describe;
  10. var it = lab.it;
  11. var expect = Code.expect;
  12. it('returns the same object when already boom', function (done) {
  13. var error = Boom.badRequest();
  14. var wrapped = Boom.wrap(error);
  15. expect(error).to.equal(wrapped);
  16. done();
  17. });
  18. it('returns an error with info when constructed using another error', function (done) {
  19. var error = new Error('ka-boom');
  20. error.xyz = 123;
  21. var err = Boom.wrap(error);
  22. expect(err.xyz).to.equal(123);
  23. expect(err.message).to.equal('ka-boom');
  24. expect(err.output).to.deep.equal({
  25. statusCode: 500,
  26. payload: {
  27. statusCode: 500,
  28. error: 'Internal Server Error',
  29. message: 'An internal server error occurred'
  30. },
  31. headers: {}
  32. });
  33. expect(err.data).to.equal(null);
  34. done();
  35. });
  36. it('does not override data when constructed using another error', function (done) {
  37. var error = new Error('ka-boom');
  38. error.data = { useful: 'data' };
  39. var err = Boom.wrap(error);
  40. expect(err.data).to.equal(error.data);
  41. done();
  42. });
  43. it('sets new message when none exists', function (done) {
  44. var error = new Error();
  45. var wrapped = Boom.wrap(error, 400, 'something bad');
  46. expect(wrapped.message).to.equal('something bad');
  47. done();
  48. });
  49. it('throws when statusCode is not a number', function (done) {
  50. expect(function () {
  51. Boom.create('x');
  52. }).to.throw('First argument must be a number (400+): x');
  53. done();
  54. });
  55. it('will cast a number-string to an integer', function (done) {
  56. var codes = [
  57. { input: '404', result: 404 },
  58. { input: '404.1', result: 404 },
  59. { input: 400, result: 400 },
  60. { input: 400.123, result: 400 }];
  61. for (var i = 0, il = codes.length; i < il; ++i) {
  62. var code = codes[i];
  63. var err = Boom.create(code.input);
  64. expect(err.output.statusCode).to.equal(code.result);
  65. }
  66. done();
  67. });
  68. it('throws when statusCode is not finite', function (done) {
  69. expect(function () {
  70. Boom.create(1 / 0);
  71. }).to.throw('First argument must be a number (400+): null');
  72. done();
  73. });
  74. it('sets error code to unknown', function (done) {
  75. var err = Boom.create(999);
  76. expect(err.output.payload.error).to.equal('Unknown');
  77. done();
  78. });
  79. describe('create()', function () {
  80. it('does not sets null message', function (done) {
  81. var error = Boom.unauthorized(null);
  82. expect(error.output.payload.message).to.not.exist();
  83. expect(error.isServer).to.be.false();
  84. done();
  85. });
  86. it('sets message and data', function (done) {
  87. var error = Boom.badRequest('Missing data', { type: 'user' });
  88. expect(error.data.type).to.equal('user');
  89. expect(error.output.payload.message).to.equal('Missing data');
  90. done();
  91. });
  92. });
  93. describe('isBoom()', function () {
  94. it('returns true for Boom object', function (done) {
  95. expect(Boom.badRequest().isBoom).to.equal(true);
  96. done();
  97. });
  98. it('returns false for Error object', function (done) {
  99. expect((new Error()).isBoom).to.not.exist();
  100. done();
  101. });
  102. });
  103. describe('badRequest()', function () {
  104. it('returns a 400 error statusCode', function (done) {
  105. var error = Boom.badRequest();
  106. expect(error.output.statusCode).to.equal(400);
  107. expect(error.isServer).to.be.false();
  108. done();
  109. });
  110. it('sets the message with the passed in message', function (done) {
  111. expect(Boom.badRequest('my message').message).to.equal('my message');
  112. done();
  113. });
  114. it('sets the message to HTTP status if none provided', function (done) {
  115. expect(Boom.badRequest().message).to.equal('Bad Request');
  116. done();
  117. });
  118. });
  119. describe('unauthorized()', function () {
  120. it('returns a 401 error statusCode', function (done) {
  121. var err = Boom.unauthorized();
  122. expect(err.output.statusCode).to.equal(401);
  123. expect(err.output.headers).to.deep.equal({});
  124. done();
  125. });
  126. it('sets the message with the passed in message', function (done) {
  127. expect(Boom.unauthorized('my message').message).to.equal('my message');
  128. done();
  129. });
  130. it('returns a WWW-Authenticate header when passed a scheme', function (done) {
  131. var err = Boom.unauthorized('boom', 'Test');
  132. expect(err.output.statusCode).to.equal(401);
  133. expect(err.output.headers['WWW-Authenticate']).to.equal('Test error="boom"');
  134. done();
  135. });
  136. it('returns a WWW-Authenticate header set to the schema array value', function (done) {
  137. var err = Boom.unauthorized(null, ['Test','one','two']);
  138. expect(err.output.statusCode).to.equal(401);
  139. expect(err.output.headers['WWW-Authenticate']).to.equal('Test, one, two');
  140. done();
  141. });
  142. it('returns a WWW-Authenticate header when passed a scheme and attributes', function (done) {
  143. var err = Boom.unauthorized('boom', 'Test', { a: 1, b: 'something', c: null, d: 0 });
  144. expect(err.output.statusCode).to.equal(401);
  145. expect(err.output.headers['WWW-Authenticate']).to.equal('Test a="1", b="something", c="", d="0", error="boom"');
  146. expect(err.output.payload.attributes).to.deep.equal({ a: 1, b: 'something', c: '', d: 0, error: 'boom' });
  147. done();
  148. });
  149. it('returns a WWW-Authenticate header when passed attributes, missing error', function (done) {
  150. var err = Boom.unauthorized(null, 'Test', { a: 1, b: 'something', c: null, d: 0 });
  151. expect(err.output.statusCode).to.equal(401);
  152. expect(err.output.headers['WWW-Authenticate']).to.equal('Test a="1", b="something", c="", d="0"');
  153. expect(err.isMissing).to.equal(true);
  154. done();
  155. });
  156. it('sets the isMissing flag when error message is empty', function (done) {
  157. var err = Boom.unauthorized('', 'Basic');
  158. expect(err.isMissing).to.equal(true);
  159. done();
  160. });
  161. it('does not set the isMissing flag when error message is not empty', function (done) {
  162. var err = Boom.unauthorized('message', 'Basic');
  163. expect(err.isMissing).to.equal(undefined);
  164. done();
  165. });
  166. it('sets a WWW-Authenticate when passed as an array', function (done) {
  167. var err = Boom.unauthorized('message', ['Basic', 'Example e="1"', 'Another x="3", y="4"']);
  168. expect(err.output.headers['WWW-Authenticate']).to.equal('Basic, Example e="1", Another x="3", y="4"');
  169. done();
  170. });
  171. });
  172. describe('methodNotAllowed()', function () {
  173. it('returns a 405 error statusCode', function (done) {
  174. expect(Boom.methodNotAllowed().output.statusCode).to.equal(405);
  175. done();
  176. });
  177. it('sets the message with the passed in message', function (done) {
  178. expect(Boom.methodNotAllowed('my message').message).to.equal('my message');
  179. done();
  180. });
  181. });
  182. describe('notAcceptable()', function () {
  183. it('returns a 406 error statusCode', function (done) {
  184. expect(Boom.notAcceptable().output.statusCode).to.equal(406);
  185. done();
  186. });
  187. it('sets the message with the passed in message', function (done) {
  188. expect(Boom.notAcceptable('my message').message).to.equal('my message');
  189. done();
  190. });
  191. });
  192. describe('proxyAuthRequired()', function () {
  193. it('returns a 407 error statusCode', function (done) {
  194. expect(Boom.proxyAuthRequired().output.statusCode).to.equal(407);
  195. done();
  196. });
  197. it('sets the message with the passed in message', function (done) {
  198. expect(Boom.proxyAuthRequired('my message').message).to.equal('my message');
  199. done();
  200. });
  201. });
  202. describe('clientTimeout()', function () {
  203. it('returns a 408 error statusCode', function (done) {
  204. expect(Boom.clientTimeout().output.statusCode).to.equal(408);
  205. done();
  206. });
  207. it('sets the message with the passed in message', function (done) {
  208. expect(Boom.clientTimeout('my message').message).to.equal('my message');
  209. done();
  210. });
  211. });
  212. describe('conflict()', function () {
  213. it('returns a 409 error statusCode', function (done) {
  214. expect(Boom.conflict().output.statusCode).to.equal(409);
  215. done();
  216. });
  217. it('sets the message with the passed in message', function (done) {
  218. expect(Boom.conflict('my message').message).to.equal('my message');
  219. done();
  220. });
  221. });
  222. describe('resourceGone()', function () {
  223. it('returns a 410 error statusCode', function (done) {
  224. expect(Boom.resourceGone().output.statusCode).to.equal(410);
  225. done();
  226. });
  227. it('sets the message with the passed in message', function (done) {
  228. expect(Boom.resourceGone('my message').message).to.equal('my message');
  229. done();
  230. });
  231. });
  232. describe('lengthRequired()', function () {
  233. it('returns a 411 error statusCode', function (done) {
  234. expect(Boom.lengthRequired().output.statusCode).to.equal(411);
  235. done();
  236. });
  237. it('sets the message with the passed in message', function (done) {
  238. expect(Boom.lengthRequired('my message').message).to.equal('my message');
  239. done();
  240. });
  241. });
  242. describe('preconditionFailed()', function () {
  243. it('returns a 412 error statusCode', function (done) {
  244. expect(Boom.preconditionFailed().output.statusCode).to.equal(412);
  245. done();
  246. });
  247. it('sets the message with the passed in message', function (done) {
  248. expect(Boom.preconditionFailed('my message').message).to.equal('my message');
  249. done();
  250. });
  251. });
  252. describe('entityTooLarge()', function () {
  253. it('returns a 413 error statusCode', function (done) {
  254. expect(Boom.entityTooLarge().output.statusCode).to.equal(413);
  255. done();
  256. });
  257. it('sets the message with the passed in message', function (done) {
  258. expect(Boom.entityTooLarge('my message').message).to.equal('my message');
  259. done();
  260. });
  261. });
  262. describe('uriTooLong()', function () {
  263. it('returns a 414 error statusCode', function (done) {
  264. expect(Boom.uriTooLong().output.statusCode).to.equal(414);
  265. done();
  266. });
  267. it('sets the message with the passed in message', function (done) {
  268. expect(Boom.uriTooLong('my message').message).to.equal('my message');
  269. done();
  270. });
  271. });
  272. describe('unsupportedMediaType()', function () {
  273. it('returns a 415 error statusCode', function (done) {
  274. expect(Boom.unsupportedMediaType().output.statusCode).to.equal(415);
  275. done();
  276. });
  277. it('sets the message with the passed in message', function (done) {
  278. expect(Boom.unsupportedMediaType('my message').message).to.equal('my message');
  279. done();
  280. });
  281. });
  282. describe('rangeNotSatisfiable()', function () {
  283. it('returns a 416 error statusCode', function (done) {
  284. expect(Boom.rangeNotSatisfiable().output.statusCode).to.equal(416);
  285. done();
  286. });
  287. it('sets the message with the passed in message', function (done) {
  288. expect(Boom.rangeNotSatisfiable('my message').message).to.equal('my message');
  289. done();
  290. });
  291. });
  292. describe('expectationFailed()', function () {
  293. it('returns a 417 error statusCode', function (done) {
  294. expect(Boom.expectationFailed().output.statusCode).to.equal(417);
  295. done();
  296. });
  297. it('sets the message with the passed in message', function (done) {
  298. expect(Boom.expectationFailed('my message').message).to.equal('my message');
  299. done();
  300. });
  301. });
  302. describe('badData()', function () {
  303. it('returns a 422 error statusCode', function (done) {
  304. expect(Boom.badData().output.statusCode).to.equal(422);
  305. done();
  306. });
  307. it('sets the message with the passed in message', function (done) {
  308. expect(Boom.badData('my message').message).to.equal('my message');
  309. done();
  310. });
  311. });
  312. describe('preconditionRequired()', function () {
  313. it('returns a 428 error statusCode', function (done) {
  314. expect(Boom.preconditionRequired().output.statusCode).to.equal(428);
  315. done();
  316. });
  317. it('sets the message with the passed in message', function (done) {
  318. expect(Boom.preconditionRequired('my message').message).to.equal('my message');
  319. done();
  320. });
  321. });
  322. describe('tooManyRequests()', function () {
  323. it('returns a 429 error statusCode', function (done) {
  324. expect(Boom.tooManyRequests().output.statusCode).to.equal(429);
  325. done();
  326. });
  327. it('sets the message with the passed-in message', function (done) {
  328. expect(Boom.tooManyRequests('my message').message).to.equal('my message');
  329. done();
  330. });
  331. });
  332. describe('serverTimeout()', function () {
  333. it('returns a 503 error statusCode', function (done) {
  334. expect(Boom.serverTimeout().output.statusCode).to.equal(503);
  335. done();
  336. });
  337. it('sets the message with the passed in message', function (done) {
  338. expect(Boom.serverTimeout('my message').message).to.equal('my message');
  339. done();
  340. });
  341. });
  342. describe('forbidden()', function () {
  343. it('returns a 403 error statusCode', function (done) {
  344. expect(Boom.forbidden().output.statusCode).to.equal(403);
  345. done();
  346. });
  347. it('sets the message with the passed in message', function (done) {
  348. expect(Boom.forbidden('my message').message).to.equal('my message');
  349. done();
  350. });
  351. });
  352. describe('notFound()', function () {
  353. it('returns a 404 error statusCode', function (done) {
  354. expect(Boom.notFound().output.statusCode).to.equal(404);
  355. done();
  356. });
  357. it('sets the message with the passed in message', function (done) {
  358. expect(Boom.notFound('my message').message).to.equal('my message');
  359. done();
  360. });
  361. });
  362. describe('internal()', function () {
  363. it('returns a 500 error statusCode', function (done) {
  364. expect(Boom.internal().output.statusCode).to.equal(500);
  365. done();
  366. });
  367. it('sets the message with the passed in message', function (done) {
  368. var err = Boom.internal('my message');
  369. expect(err.message).to.equal('my message');
  370. expect(err.isServer).to.true();
  371. expect(err.output.payload.message).to.equal('An internal server error occurred');
  372. done();
  373. });
  374. it('passes data on the callback if its passed in', function (done) {
  375. expect(Boom.internal('my message', { my: 'data' }).data.my).to.equal('data');
  376. done();
  377. });
  378. it('returns an error with composite message', function (done) {
  379. try {
  380. JSON.parse('{');
  381. }
  382. catch (err) {
  383. var boom = Boom.internal('Someting bad', err);
  384. expect(boom.message).to.equal('Someting bad: Unexpected end of input');
  385. expect(boom.isServer).to.be.true();
  386. done();
  387. }
  388. });
  389. });
  390. describe('notImplemented()', function () {
  391. it('returns a 501 error statusCode', function (done) {
  392. expect(Boom.notImplemented().output.statusCode).to.equal(501);
  393. done();
  394. });
  395. it('sets the message with the passed in message', function (done) {
  396. expect(Boom.notImplemented('my message').message).to.equal('my message');
  397. done();
  398. });
  399. });
  400. describe('badGateway()', function () {
  401. it('returns a 502 error statusCode', function (done) {
  402. expect(Boom.badGateway().output.statusCode).to.equal(502);
  403. done();
  404. });
  405. it('sets the message with the passed in message', function (done) {
  406. expect(Boom.badGateway('my message').message).to.equal('my message');
  407. done();
  408. });
  409. });
  410. describe('gatewayTimeout()', function () {
  411. it('returns a 504 error statusCode', function (done) {
  412. expect(Boom.gatewayTimeout().output.statusCode).to.equal(504);
  413. done();
  414. });
  415. it('sets the message with the passed in message', function (done) {
  416. expect(Boom.gatewayTimeout('my message').message).to.equal('my message');
  417. done();
  418. });
  419. });
  420. describe('badImplementation()', function () {
  421. it('returns a 500 error statusCode', function (done) {
  422. var err = Boom.badImplementation();
  423. expect(err.output.statusCode).to.equal(500);
  424. expect(err.isDeveloperError).to.equal(true);
  425. expect(err.isServer).to.be.true();
  426. done();
  427. });
  428. });
  429. describe('stack trace', function () {
  430. it('should omit lib', function (done) {
  431. ['badRequest', 'unauthorized', 'forbidden', 'notFound', 'methodNotAllowed',
  432. 'notAcceptable', 'proxyAuthRequired', 'clientTimeout', 'conflict',
  433. 'resourceGone', 'lengthRequired', 'preconditionFailed', 'entityTooLarge',
  434. 'uriTooLong', 'unsupportedMediaType', 'rangeNotSatisfiable', 'expectationFailed',
  435. 'badData', 'preconditionRequired', 'tooManyRequests',
  436. // 500s
  437. 'internal', 'notImplemented', 'badGateway', 'serverTimeout', 'gatewayTimeout',
  438. 'badImplementation'
  439. ].forEach(function (name) {
  440. var err = Boom[name]();
  441. expect(err.stack).to.not.match(/\/lib\/index\.js/);
  442. });
  443. done();
  444. });
  445. });