├── .github └── workflows │ └── ci-module.yml ├── .gitignore ├── API.md ├── LICENSE.md ├── README.md ├── lib └── index.js ├── package.json └── test └── index.js /.github/workflows/ci-module.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | jobs: 11 | test: 12 | uses: hapijs/.github/.github/workflows/ci-module.yml@master 13 | with: 14 | min-node-version: 14 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/package-lock.json 3 | 4 | coverage.* 5 | 6 | **/.DS_Store 7 | **/._* 8 | 9 | **/*.pem 10 | 11 | **/.vs 12 | **/.vscode 13 | **/.idea 14 | -------------------------------------------------------------------------------- /API.md: -------------------------------------------------------------------------------- 1 | 2 | ## Introduction 3 | 4 | Working with `async`/`await` introduces a new challenge in handling errors. Unlike callbacks, which 5 | provide a dual mechanism for passing application errors via the callback `err` argument and 6 | developer errors via exceptions, `await` combines these two channels into one. 7 | 8 | It is common practice to ignore application errors in background processing or when there is no 9 | useful fallback. In those cases, it is still imperative to allow developer errors to surface and 10 | not get swallowed. 11 | 12 | For more information read: 13 | - [Learning to Throw Again](https://medium.com/@eranhammer/learning-to-throw-again-79b498504d28) 14 | - [Catching without Awaiting](https://medium.com/@eranhammer/catching-without-awaiting-b2cb7df45790) 15 | 16 | For example: 17 | 18 | ```js 19 | async function email(user) { 20 | 21 | if (!user.address) { 22 | throw new Error('User has no email address'); 23 | } 24 | 25 | const message = 'Welcome!'; 26 | if (user.name) { 27 | message = `Welcome ${user.name}!`; 28 | } 29 | 30 | await mailer.send(user.address, message); 31 | } 32 | 33 | async function register(address, name) { 34 | 35 | const user = { address, name }; 36 | const id = await db.user.insert(user); 37 | user.id = id; 38 | 39 | try { 40 | await email(user); 41 | } 42 | catch (err) { } // Ignore errors 43 | 44 | return user; 45 | } 46 | ``` 47 | 48 | This will fail silently every time the user has a `name` because it is reassigning a value to a 49 | `const` variable. However, because `email()` errors are ignored, system errors are ignored as well. 50 | The idea is that `email()` can be used in both critical and non-critical paths. In the critical 51 | paths, errors are checked and addressed, but in the non-critical paths, errors are simply ignored. 52 | 53 | This can be solved by adding a `rethrow()` statement: 54 | 55 | ```js 56 | const Bounce = require('@hapi/bounce'); 57 | 58 | async function register(address, name) { 59 | 60 | const user = { address, name }; 61 | const id = await db.user.insert(user); 62 | user.id = id; 63 | 64 | try { 65 | await email(user); 66 | } 67 | catch (err) { 68 | Bounce.rethrow(err, 'system'); // Rethrows system errors and ignores application errors 69 | } 70 | 71 | return user; 72 | } 73 | ``` 74 | 75 | ## Usage 76 | 77 | ### `rethrow(err, types, [options])` 78 | 79 | Throws the error passed if it matches any of the specified rules where: 80 | - `err` - the error. 81 | - `type` - a single item or an array of items of: 82 | - An error constructor (e.g. `SyntaxError`). 83 | - `'system'` - matches any languange native error or node assertions. 84 | - `'boom'` - matches [**boom**](https://github.com/hapijs/boom) errors. 85 | - an object where each property is compared with the error and must match the error property 86 | value. All the properties in the object must match the error but do not need to include all 87 | the error properties. 88 | - `options` - optional object where: 89 | - `decorate` - an object which is assigned to the `err`, copying the properties onto the error. 90 | - `override` - an error used to override `err` when `err` matches. If used with `decorate`, 91 | the `override` object is modified. 92 | - `return` - if `true`, the error is returned instead of thrown. Defaults to `false`. 93 | 94 | ### `ignore(err, types, [options])` 95 | 96 | The opposite action of `rethrow()`. Ignores any errors matching the specified `types`. Any error 97 | not matching is thrown after applying the `options`. 98 | 99 | ### `background(operation, [action], [types], [options])` 100 | 101 | Awaits for the value to resolve in the background and then apply either the `rethrow()` or `ignore()` 102 | actions where: 103 | - `operation` - a function, promise, or value that is `await`ed on inside a `try...catch` and any 104 | error thrown processed by the `action` rule. 105 | - `action` - one of `'rethrow'` or `'ignore'`. Defaults to `'rethrow'`. 106 | - `types` - same as the `types` argument passed to `rethrow()` or `ignore()`. Defaults to `'system'`. 107 | - `options` - same as the `options` argument passed to `rethrow()` or `ignore()`. 108 | 109 | ### `isBoom(err)` 110 | 111 | Returns `true` when `err` is a [**boom**](https://github.com/hapijs/boom) error. 112 | 113 | ### `isError(err)` 114 | 115 | Returns `true` when `err` is an error. 116 | 117 | ### `isSystem(err)` 118 | 119 | Return `true` when `err` is one of: 120 | - `EvalError` 121 | - `RangeError` 122 | - `ReferenceError` 123 | - `SyntaxError` 124 | - `TypeError` 125 | - `URIError` 126 | - Node's `AssertionError` 127 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2022, Project contributors 2 | Copyright (c) 2017-2020, Sideway Inc 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | * The names of any contributors may not be used to endorse or promote products derived from this software without specific prior written permission. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS OFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # @hapi/bounce 4 | 5 | #### Selective error catching and rewrite rules. 6 | 7 | **bounce** is part of the **hapi** ecosystem and was designed to work seamlessly with the [hapi web framework](https://hapi.dev) and its other components (but works great on its own or with other frameworks). If you are using a different web framework and find this module useful, check out [hapi](https://hapi.dev) – they work even better together. 8 | 9 | ### Visit the [hapi.dev](https://hapi.dev) Developer Portal for tutorials, documentation, and support 10 | 11 | ## Useful resources 12 | 13 | - [Documentation and API](https://hapi.dev/family/bounce/) 14 | - [Versions status](https://hapi.dev/resources/status/#bounce) (builds, dependencies, node versions, licenses, eol) 15 | - [Changelog](https://hapi.dev/family/bounce/changelog/) 16 | - [Project policies](https://hapi.dev/policies/) 17 | - [Free and commercial support options](https://hapi.dev/support/) 18 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Assert = require('assert'); 4 | 5 | const Boom = require('@hapi/boom'); 6 | const Hoek = require('@hapi/hoek'); 7 | 8 | 9 | const internals = { 10 | system: [ 11 | 12 | // JavaScript 13 | 14 | EvalError, 15 | RangeError, 16 | ReferenceError, 17 | SyntaxError, 18 | TypeError, 19 | URIError, 20 | 21 | // Node 22 | 23 | Assert.AssertionError, 24 | 25 | // Hoek 26 | 27 | Hoek.AssertError 28 | ] 29 | }; 30 | 31 | 32 | exports.rethrow = function (err, types, options = {}) { 33 | 34 | return internals.catch(err, types, options, true); 35 | }; 36 | 37 | 38 | exports.ignore = function (err, types, options = {}) { 39 | 40 | return internals.catch(err, types, options, false); 41 | }; 42 | 43 | 44 | internals.catch = function (err, types, options, match) { 45 | 46 | if (internals.match(err, types) !== match) { 47 | return; 48 | } 49 | 50 | // Error replacement 51 | 52 | if (options.override) { 53 | err = options.override; 54 | } 55 | 56 | // Error decorations 57 | 58 | if (options.decorate) { 59 | Object.assign(err, options.decorate); 60 | } 61 | 62 | if (options.return) { 63 | return err; 64 | } 65 | 66 | throw err; 67 | }; 68 | 69 | 70 | exports.background = async function (operation, action = 'rethrow', types = 'system', options = {}) { 71 | 72 | try { 73 | if (typeof operation === 'function') { 74 | await operation(); 75 | } 76 | else { 77 | await operation; 78 | } 79 | } 80 | catch (err) { 81 | return exports[action](err, types, options); 82 | } 83 | }; 84 | 85 | 86 | exports.isBoom = function (err) { 87 | 88 | return Boom.isBoom(err); 89 | }; 90 | 91 | 92 | exports.isError = function (err) { 93 | 94 | return err instanceof Error; 95 | }; 96 | 97 | 98 | exports.isSystem = function (err) { 99 | 100 | if (!err) { 101 | return false; 102 | } 103 | 104 | if (err.isBoom) { 105 | return false; 106 | } 107 | 108 | for (const system of internals.system) { 109 | if (err instanceof system) { 110 | return true; 111 | } 112 | } 113 | 114 | return false; 115 | }; 116 | 117 | 118 | internals.rules = { 119 | system: exports.isSystem, 120 | boom: exports.isBoom 121 | }; 122 | 123 | 124 | internals.match = function (err, types) { 125 | 126 | if (!types) { 127 | return true; 128 | } 129 | 130 | types = Array.isArray(types) ? types : [types]; 131 | for (const type of types) { 132 | if (typeof type === 'string') { 133 | if (internals.rules[type](err)) { 134 | return true; 135 | } 136 | } 137 | else if (typeof type === 'object') { 138 | if (Hoek.contain(err, type, { deep: true, part: true })) { 139 | return true; 140 | } 141 | } 142 | else if (err instanceof type) { 143 | return true; 144 | } 145 | } 146 | 147 | return false; 148 | }; 149 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hapi/bounce", 3 | "description": "Selective error catching and rewrite rules", 4 | "version": "3.0.2", 5 | "repository": "git://github.com/hapijs/bounce", 6 | "main": "lib/index.js", 7 | "files": [ 8 | "lib" 9 | ], 10 | "keywords": [ 11 | "error", 12 | "catch" 13 | ], 14 | "eslintConfig": { 15 | "extends": [ 16 | "plugin:@hapi/module" 17 | ] 18 | }, 19 | "dependencies": { 20 | "@hapi/boom": "^10.0.1", 21 | "@hapi/hoek": "^11.0.2" 22 | }, 23 | "devDependencies": { 24 | "@hapi/code": "^9.0.0", 25 | "@hapi/eslint-plugin": "^6.0.0", 26 | "@hapi/lab": "^25.1.0" 27 | }, 28 | "scripts": { 29 | "test": "lab -a @hapi/code -t 100 -L", 30 | "test-cov-html": "lab -a @hapi/code -r html -o coverage.html -L" 31 | }, 32 | "license": "BSD-3-Clause" 33 | } 34 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Assert = require('assert'); 4 | 5 | const Code = require('@hapi/code'); 6 | const Boom = require('@hapi/boom'); 7 | const Bounce = require('..'); 8 | const Hoek = require('@hapi/hoek'); 9 | const Lab = require('@hapi/lab'); 10 | 11 | 12 | const internals = {}; 13 | 14 | 15 | const { describe, it } = exports.lab = Lab.script(); 16 | const expect = Code.expect; 17 | 18 | 19 | describe('Bounce', () => { 20 | 21 | describe('rethrow()', () => { 22 | 23 | it('rethrows all errors', () => { 24 | 25 | const orig = new Error('Something'); 26 | 27 | try { 28 | Bounce.rethrow(orig); 29 | } 30 | catch (err) { 31 | var error = err; 32 | } 33 | 34 | expect(error).to.shallow.equal(orig); 35 | expect(error).to.be.an.error('Something'); 36 | }); 37 | 38 | it('rethrows only system errors', () => { 39 | 40 | try { 41 | Bounce.rethrow(new Error('Something'), 'system'); 42 | } 43 | catch (err) { 44 | var error1 = err; 45 | } 46 | 47 | expect(error1).to.not.exist(); 48 | 49 | try { 50 | Bounce.rethrow(new URIError('Something'), 'system'); 51 | } 52 | catch (err) { 53 | var error2 = err; 54 | } 55 | 56 | expect(error2).to.be.an.error('Something', URIError); 57 | }); 58 | 59 | it('rethrows only boom errors', () => { 60 | 61 | try { 62 | Bounce.rethrow(new Error('Something'), 'boom'); 63 | } 64 | catch (err) { 65 | var error1 = err; 66 | } 67 | 68 | expect(error1).to.not.exist(); 69 | 70 | try { 71 | Bounce.rethrow(Boom.badRequest('Something'), 'boom'); 72 | } 73 | catch (err) { 74 | var error2 = err; 75 | } 76 | 77 | expect(error2).to.be.an.error('Something'); 78 | }); 79 | 80 | it('rethrows only boom/system errors', () => { 81 | 82 | try { 83 | Bounce.rethrow(new Error('Something'), ['boom', 'system']); 84 | } 85 | catch (err) { 86 | var error1 = err; 87 | } 88 | 89 | expect(error1).to.not.exist(); 90 | 91 | try { 92 | Bounce.rethrow(Boom.badRequest('Something'), ['boom', 'system']); 93 | } 94 | catch (err) { 95 | var error2 = err; 96 | } 97 | 98 | expect(error2).to.be.an.error('Something'); 99 | 100 | try { 101 | Bounce.rethrow(new SyntaxError('Something'), ['boom', 'system']); 102 | } 103 | catch (err) { 104 | var error3 = err; 105 | } 106 | 107 | expect(error3).to.be.an.error('Something', SyntaxError); 108 | }); 109 | 110 | it('rethrows only specified errors', () => { 111 | 112 | try { 113 | Bounce.rethrow(new Error('Something'), URIError); 114 | } 115 | catch (err) { 116 | var error1 = err; 117 | } 118 | 119 | expect(error1).to.not.exist(); 120 | 121 | try { 122 | Bounce.rethrow(new URIError('Something'), URIError); 123 | } 124 | catch (err) { 125 | var error2 = err; 126 | } 127 | 128 | expect(error2).to.be.an.error('Something', URIError); 129 | }); 130 | 131 | it('rethrows only specified errors', () => { 132 | 133 | try { 134 | Bounce.rethrow(new Error('Something'), URIError); 135 | } 136 | catch (err) { 137 | var error1 = err; 138 | } 139 | 140 | expect(error1).to.not.exist(); 141 | 142 | try { 143 | Bounce.rethrow(new URIError('Something'), URIError); 144 | } 145 | catch (err) { 146 | var error2 = err; 147 | } 148 | 149 | expect(error2).to.be.an.error('Something', URIError); 150 | }); 151 | 152 | it('rethrows only errors matching a pattern', () => { 153 | 154 | try { 155 | Bounce.rethrow(new Error('Something'), { x: 1 }); 156 | } 157 | catch (err) { 158 | var error1 = err; 159 | } 160 | 161 | expect(error1).to.not.exist(); 162 | 163 | const xErr = new Error('Something'); 164 | xErr.x = 1; 165 | 166 | try { 167 | Bounce.rethrow(xErr, { x: 1 }); 168 | } 169 | catch (err) { 170 | var error2 = err; 171 | } 172 | 173 | expect(error2).to.be.an.error('Something'); 174 | }); 175 | 176 | it('rethrows only errors matching a pattern (deep)', () => { 177 | 178 | try { 179 | Bounce.rethrow(new Error('Something'), { x: { y: 2 } }); 180 | } 181 | catch (err) { 182 | var error1 = err; 183 | } 184 | 185 | expect(error1).to.not.exist(); 186 | 187 | const xErr = new Error('Something'); 188 | xErr.x = { y: 2, z: 4 }; 189 | 190 | try { 191 | Bounce.rethrow(xErr, { x: { y: 2 } }); 192 | } 193 | catch (err) { 194 | var error2 = err; 195 | } 196 | 197 | expect(error2).to.be.an.error('Something'); 198 | }); 199 | 200 | it('rethrows a decorated error', () => { 201 | 202 | const orig = new Error('Something'); 203 | const decorate = { x: 1, y: 'z' }; 204 | 205 | try { 206 | Bounce.rethrow(orig, Error, { decorate }); 207 | } 208 | catch (err) { 209 | var error = err; 210 | } 211 | 212 | expect(error).to.shallow.equal(orig); 213 | expect(error).to.be.an.error('Something'); 214 | expect(error.x).to.equal(1); 215 | expect(error.y).to.equal('z'); 216 | }); 217 | 218 | it('throws a different error', () => { 219 | 220 | const orig = new Error('Something'); 221 | 222 | try { 223 | Bounce.rethrow(orig, Error, { override: new Error('Else') }); 224 | } 225 | catch (err) { 226 | var error = err; 227 | } 228 | 229 | expect(error).to.not.shallow.equal(orig); 230 | expect(error).to.be.an.error('Else'); 231 | }); 232 | 233 | it('returns error instead of throwing', () => { 234 | 235 | const orig = new Error('Something'); 236 | 237 | expect(() => Bounce.rethrow(orig, Error, { return: true })).to.not.throw(); 238 | 239 | const error = Bounce.rethrow(orig, Error, { return: true }); 240 | expect(error).to.shallow.equal(orig); 241 | expect(error).to.be.an.error('Something'); 242 | }); 243 | }); 244 | 245 | describe('ignore()', () => { 246 | 247 | it('ignores system errors', () => { 248 | 249 | try { 250 | Bounce.ignore(new Error('Something'), 'system'); 251 | } 252 | catch (err) { 253 | var error1 = err; 254 | } 255 | 256 | expect(error1).to.be.an.error('Something', Error); 257 | 258 | try { 259 | Bounce.ignore(new URIError('Something'), 'system'); 260 | } 261 | catch (err) { 262 | var error2 = err; 263 | } 264 | 265 | expect(error2).to.not.exist(); 266 | }); 267 | 268 | it('ignores boom errors', () => { 269 | 270 | try { 271 | Bounce.ignore(new Error('Something'), 'boom'); 272 | } 273 | catch (err) { 274 | var error1 = err; 275 | } 276 | 277 | expect(error1).to.be.an.error('Something', Error); 278 | 279 | try { 280 | Bounce.ignore(Boom.badRequest('Something'), 'boom'); 281 | } 282 | catch (err) { 283 | var error2 = err; 284 | } 285 | 286 | expect(error2).to.not.exist(); 287 | }); 288 | 289 | it('ignores boom/system errors', () => { 290 | 291 | try { 292 | Bounce.ignore(new Error('Something'), ['boom', 'system']); 293 | } 294 | catch (err) { 295 | var error1 = err; 296 | } 297 | 298 | expect(error1).to.be.an.error('Something', Error); 299 | 300 | try { 301 | Bounce.ignore(Boom.badRequest('Something'), ['boom', 'system']); 302 | } 303 | catch (err) { 304 | var error2 = err; 305 | } 306 | 307 | expect(error2).to.not.exist(); 308 | 309 | try { 310 | Bounce.ignore(new ReferenceError('Something'), ['boom', 'system']); 311 | } 312 | catch (err) { 313 | var error3 = err; 314 | } 315 | 316 | expect(error3).to.not.exist(); 317 | }); 318 | }); 319 | 320 | describe('background()', () => { 321 | 322 | it('rethrows system errors', async () => { 323 | 324 | const test = async () => { 325 | 326 | await Hoek.wait(10); 327 | throw new SyntaxError('Something'); 328 | }; 329 | 330 | try { 331 | await Bounce.background(test(), 'rethrow', 'system'); 332 | } 333 | catch (err) { 334 | var error = err; 335 | } 336 | 337 | expect(error).to.exist(); 338 | }); 339 | 340 | it('rethrows system errors (defaults)', async () => { 341 | 342 | const test = async () => { 343 | 344 | await Hoek.wait(10); 345 | throw new SyntaxError('Something'); 346 | }; 347 | 348 | try { 349 | await Bounce.background(test()); 350 | } 351 | catch (err) { 352 | var error = err; 353 | } 354 | 355 | expect(error).to.exist(); 356 | }); 357 | 358 | it('ignores system errors', async () => { 359 | 360 | const test = async () => { 361 | 362 | await Hoek.wait(10); 363 | throw new Error('Something'); 364 | }; 365 | 366 | try { 367 | await Bounce.background(test(), 'rethrow', 'system'); 368 | } 369 | catch (err) { 370 | var error = err; 371 | } 372 | 373 | expect(error).to.not.exist(); 374 | }); 375 | 376 | it('ignores system errors (background)', () => { 377 | 378 | const test = async () => { 379 | 380 | await Hoek.wait(10); 381 | throw new Error('Something'); 382 | }; 383 | 384 | Bounce.background(test(), 'rethrow', 'system'); 385 | }); 386 | 387 | it('rethrows system errors (sync)', async () => { 388 | 389 | const test = () => { 390 | 391 | throw new SyntaxError('Something'); 392 | }; 393 | 394 | try { 395 | await Bounce.background(test, 'rethrow', 'system'); 396 | } 397 | catch (err) { 398 | var error = err; 399 | } 400 | 401 | expect(error).to.exist(); 402 | }); 403 | 404 | it('ignores system errors (sync)', async () => { 405 | 406 | const test = () => { 407 | 408 | throw new Error('Something'); 409 | }; 410 | 411 | try { 412 | await Bounce.background(test, 'rethrow', 'system'); 413 | } 414 | catch (err) { 415 | var error = err; 416 | } 417 | 418 | expect(error).to.not.exist(); 419 | }); 420 | 421 | it('ignores system errors (background sync)', () => { 422 | 423 | const test = () => { 424 | 425 | throw new Error('Something'); 426 | }; 427 | 428 | Bounce.background(test, 'rethrow', 'system'); 429 | }); 430 | 431 | it('supports the return option', async () => { 432 | 433 | const test = async () => { 434 | 435 | await Hoek.wait(1); 436 | throw new SyntaxError('Something'); 437 | }; 438 | 439 | const res = await Bounce.background(test(), 'rethrow', 'system', { return: true }); 440 | expect(res).to.exist(); 441 | expect(res).to.be.an.error(SyntaxError); 442 | }); 443 | }); 444 | 445 | describe('isBoom()', () => { 446 | 447 | it('identifies Boom as Boom', () => { 448 | 449 | expect(Bounce.isBoom(Boom.badRequest())).to.be.true(); 450 | }); 451 | 452 | it('identifies EvalError as non-boom', () => { 453 | 454 | expect(Bounce.isBoom(new EvalError())).to.be.false(); 455 | }); 456 | 457 | it('identifies object as non-boom', () => { 458 | 459 | expect(Bounce.isBoom({})).to.be.false(); 460 | }); 461 | 462 | it('identifies object with isBoom as non-boom', () => { 463 | 464 | expect(Bounce.isBoom({ isBoom: true })).to.be.false(); 465 | }); 466 | }); 467 | 468 | describe('isError()', () => { 469 | 470 | it('identifies Error as error', () => { 471 | 472 | expect(Bounce.isError(new Error())).to.be.true(); 473 | }); 474 | 475 | it('identifies Boom as error', () => { 476 | 477 | expect(Bounce.isError(Boom.badRequest())).to.be.true(); 478 | }); 479 | 480 | it('identifies object as non-error', () => { 481 | 482 | expect(Bounce.isBoom({})).to.be.false(); 483 | }); 484 | }); 485 | 486 | describe('isSystem()', () => { 487 | 488 | it('identifies EvalError as system', () => { 489 | 490 | expect(Bounce.isSystem(new EvalError())).to.be.true(); 491 | }); 492 | 493 | it('identifies RangeError as system', () => { 494 | 495 | expect(Bounce.isSystem(new RangeError())).to.be.true(); 496 | }); 497 | 498 | it('identifies ReferenceError as system', () => { 499 | 500 | expect(Bounce.isSystem(new ReferenceError())).to.be.true(); 501 | }); 502 | 503 | it('identifies SyntaxError as system', () => { 504 | 505 | expect(Bounce.isSystem(new SyntaxError())).to.be.true(); 506 | }); 507 | 508 | it('identifies TypeError as system', () => { 509 | 510 | expect(Bounce.isSystem(new TypeError())).to.be.true(); 511 | }); 512 | 513 | it('identifies URIError as system', () => { 514 | 515 | expect(Bounce.isSystem(new URIError())).to.be.true(); 516 | }); 517 | 518 | it('identifies node AssertionError as system', () => { 519 | 520 | expect(Bounce.isSystem(new Assert.AssertionError({}))).to.be.true(); 521 | }); 522 | 523 | it('identifies hoek Error as system', () => { 524 | 525 | expect(Bounce.isSystem(new Hoek.AssertError([]))).to.be.true(); 526 | }); 527 | 528 | it('identifies Error as non-system', () => { 529 | 530 | expect(Bounce.isSystem(new Error())).to.be.false(); 531 | }); 532 | 533 | it('identifies Boom as non-system', () => { 534 | 535 | expect(Bounce.isSystem(Boom.badRequest())).to.be.false(); 536 | }); 537 | 538 | it('identifies object as non-system', () => { 539 | 540 | expect(Bounce.isSystem({})).to.be.false(); 541 | }); 542 | 543 | it('identifies null as non-system', () => { 544 | 545 | expect(Bounce.isSystem(null)).to.be.false(); 546 | }); 547 | 548 | it('identifies boomified system as non-system', () => { 549 | 550 | expect(Bounce.isSystem(Boom.boomify(new TypeError()))).to.be.false(); 551 | }); 552 | }); 553 | }); 554 | --------------------------------------------------------------------------------