| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- const { expect } = require('chai')
- const { promisify } = require('util')
- const OError = require('..')
- const {
- expectError,
- expectFullStackWithoutStackFramesToEqual,
- } = require('./support')
- describe('OError.tag', function () {
- it('tags errors thrown from an async function', async function () {
- const delay = promisify(setTimeout)
- async function foo() {
- await delay(10)
- throw new Error('foo error')
- }
- async function bar() {
- try {
- await foo()
- } catch (error) {
- throw OError.tag(error, 'failed to bar', { bar: 'baz' })
- }
- }
- async function baz() {
- try {
- await bar()
- } catch (error) {
- throw OError.tag(error, 'failed to baz', { baz: 'bat' })
- }
- }
- try {
- await baz()
- expect.fail('should have thrown')
- } catch (error) {
- expectError(error, {
- name: 'Error',
- klass: Error,
- message: 'Error: foo error',
- firstFrameRx: /at foo/,
- })
- expectFullStackWithoutStackFramesToEqual(error, [
- 'Error: foo error',
- 'TaggedError: failed to bar',
- 'TaggedError: failed to baz',
- ])
- expect(OError.getFullInfo(error)).to.eql({
- bar: 'baz',
- baz: 'bat',
- })
- }
- })
- it('tags errors thrown from a promise rejection', async function () {
- function foo() {
- return new Promise((resolve, reject) => {
- setTimeout(function () {
- reject(new Error('foo error'))
- }, 10)
- })
- }
- async function bar() {
- try {
- await foo()
- } catch (error) {
- throw OError.tag(error, 'failed to bar', { bar: 'baz' })
- }
- }
- async function baz() {
- try {
- await bar()
- } catch (error) {
- throw OError.tag(error, 'failed to baz', { baz: 'bat' })
- }
- }
- try {
- await baz()
- expect.fail('should have thrown')
- } catch (error) {
- expectError(error, {
- name: 'Error',
- klass: Error,
- message: 'Error: foo error',
- firstFrameRx: /_onTimeout/,
- })
- expectFullStackWithoutStackFramesToEqual(error, [
- 'Error: foo error',
- 'TaggedError: failed to bar',
- 'TaggedError: failed to baz',
- ])
- expect(OError.getFullInfo(error)).to.eql({
- bar: 'baz',
- baz: 'bat',
- })
- }
- })
- it('tags errors yielded through callbacks', function (done) {
- function foo(cb) {
- setTimeout(function () {
- cb(new Error('foo error'))
- }, 10)
- }
- function bar(cb) {
- foo(function (err) {
- if (err) {
- return cb(OError.tag(err, 'failed to bar', { bar: 'baz' }))
- }
- cb()
- })
- }
- function baz(cb) {
- bar(function (err) {
- if (err) {
- return cb(OError.tag(err, 'failed to baz', { baz: 'bat' }))
- }
- cb()
- })
- }
- baz(function (err) {
- if (err) {
- expectError(err, {
- name: 'Error',
- klass: Error,
- message: 'Error: foo error',
- firstFrameRx: /_onTimeout/,
- })
- expectFullStackWithoutStackFramesToEqual(err, [
- 'Error: foo error',
- 'TaggedError: failed to bar',
- 'TaggedError: failed to baz',
- ])
- expect(OError.getFullInfo(err)).to.eql({
- bar: 'baz',
- baz: 'bat',
- })
- return done()
- }
- expect.fail('should have yielded an error')
- })
- })
- })
- describe('OError.getFullInfo', function () {
- it('works on a normal error', function () {
- const err = new Error('foo')
- expect(OError.getFullInfo(err)).to.deep.equal({})
- })
- it('works on an error with tags', function () {
- const err = OError.tag(new Error('foo'), 'bar', { userId: 123 })
- expect(OError.getFullInfo(err)).to.deep.equal({ userId: 123 })
- })
- it('merges info from an error and its tags', function () {
- const err = new OError('foo').withInfo({ projectId: 456 })
- OError.tag(err, 'failed to foo', { userId: 123 })
- expect(OError.getFullInfo(err)).to.deep.equal({
- projectId: 456,
- userId: 123,
- })
- })
- it('does not merge info from a cause', function () {
- const err1 = new Error('foo')
- const err2 = new Error('bar')
- err1.cause = err2
- err2.info = { userId: 123 }
- expect(OError.getFullInfo(err1)).to.deep.equal({})
- })
- it('merges info from tags with duplicate keys', function () {
- const err1 = OError.tag(new Error('foo'), 'bar', { userId: 123 })
- const err2 = OError.tag(err1, 'bat', { userId: 456 })
- expect(OError.getFullInfo(err2)).to.deep.equal({ userId: 456 })
- })
- it('works on an error with .info set to a string', function () {
- const err = new Error('foo')
- err.info = 'test'
- expect(OError.getFullInfo(err)).to.deep.equal({})
- })
- })
- describe('OError.getFullStack', function () {
- it('works on a normal error', function () {
- const err = new Error('foo')
- const fullStack = OError.getFullStack(err)
- expect(fullStack).to.match(/^Error: foo$/m)
- expect(fullStack).to.match(/^\s+at /m)
- })
- it('works on an error with a cause', function () {
- const err1 = new Error('foo')
- const err2 = new Error('bar')
- err1.cause = err2
- const fullStack = OError.getFullStack(err1)
- expect(fullStack).to.match(/^Error: foo$/m)
- expect(fullStack).to.match(/^\s+at /m)
- expect(fullStack).to.match(/^caused by:\n\s+Error: bar$/m)
- })
- it('works on both tags and causes', async function () {
- // Here's the actual error.
- function tryToFoo() {
- try {
- throw Error('foo')
- } catch (error) {
- throw OError.tag(error, 'failed to foo', { foo: 1 })
- }
- }
- // Inside another function that wraps it.
- function tryToBar() {
- try {
- tryToFoo()
- } catch (error) {
- throw new OError('failed to bar').withCause(error)
- }
- }
- // And it is in another try.
- try {
- try {
- tryToBar()
- expect.fail('should have thrown')
- } catch (error) {
- throw OError.tag(error, 'failed to bat', { bat: 1 })
- }
- } catch (error) {
- // We catch the wrapping error.
- expectError(error, {
- name: 'OError',
- klass: OError,
- message: 'OError: failed to bar',
- firstFrameRx: /tryToBar/,
- })
- // But the stack contains all of the errors and tags.
- expectFullStackWithoutStackFramesToEqual(error, [
- 'OError: failed to bar',
- 'TaggedError: failed to bat',
- 'caused by:',
- ' Error: foo',
- ' TaggedError: failed to foo',
- ])
- // The info from the wrapped cause should not leak out.
- expect(OError.getFullInfo(error)).to.eql({ bat: 1 })
- // But it should still be recorded.
- expect(OError.getFullInfo(error.cause)).to.eql({ foo: 1 })
- }
- })
- })
|