main
  1// Copyright 2024 Google LLC
  2//
  3// Licensed under the Apache License, Version 2.0 (the "License");
  4// you may not use this file except in compliance with the License.
  5// You may obtain a copy of the License at
  6//
  7//     https://www.apache.org/licenses/LICENSE-2.0
  8//
  9// Unless required by applicable law or agreed to in writing, software
 10// distributed under the License is distributed on an "AS IS" BASIS,
 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12// See the License for the specific language governing permissions and
 13// limitations under the License.
 14
 15import assert from 'node:assert'
 16import { test, describe } from 'node:test'
 17import { Fail } from '../src/error.ts'
 18
 19const {
 20  getErrnoMessage,
 21  getExitCodeInfo,
 22  getCallerLocation,
 23  getCallerLocationFromString,
 24  formatExitMessage,
 25  formatErrorMessage,
 26  formatErrorDetails,
 27} = Fail
 28
 29describe('error', () => {
 30  test('getExitCodeInfo()', () => {
 31    assert.equal(getExitCodeInfo(2), 'Misuse of shell builtins')
 32  })
 33
 34  test('getErrnoMessage()', () => {
 35    assert.equal(getErrnoMessage(-2), 'No such file or directory')
 36    assert.equal(getErrnoMessage(1e9), 'Unknown error')
 37    assert.equal(getErrnoMessage(undefined), 'Unknown error')
 38  })
 39
 40  test('getCallerLocation()', () => {
 41    assert.match(
 42      getCallerLocation(new Error('Foo')),
 43      /TestContext\.<anonymous>/
 44    )
 45  })
 46
 47  describe('getCallerLocationFromString()', () => {
 48    test('empty', () => {
 49      assert.equal(getCallerLocationFromString(), 'unknown')
 50    })
 51
 52    test('no-match', () => {
 53      assert.equal(
 54        getCallerLocationFromString('stack\nstring'),
 55        'stack\nstring'
 56      )
 57    })
 58
 59    test(`getCallerLocationFromString-v8`, () => {
 60      const stack = `
 61    Error
 62      at getCallerLocation (/Users/user/test.js:22:17)
 63      at Proxy.set (/Users/user/test.js:40:10)
 64      at e (/Users/user/test.js:34:13)
 65      at d (/Users/user/test.js:11:5)
 66      at c (/Users/user/test.js:8:5)
 67      at b (/Users/user/test.js:5:5)
 68      at a (/Users/user/test.js:2:5)
 69      at Object.<anonymous> (/Users/user/test.js:37:1)
 70      at Module._compile (node:internal/modules/cjs/loader:1254:14)
 71      at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
 72      at Module.load (node:internal/modules/cjs/loader:1117:32)
 73      at Module._load (node:internal/modules/cjs/loader:958:12)
 74    `
 75      assert.match(getCallerLocationFromString(stack), /^.*:11:5.*$/)
 76    })
 77
 78    test(`getCallerLocationFromString-JSC`, () => {
 79      const stack = `
 80    getCallerLocation@/Users/user/test.js:22:17
 81    Proxy.set@/Users/user/test.js:40:10)
 82    e@/Users/user/test.js:34:13
 83    d@/Users/user/test.js:11:5
 84    c@/Users/user/test.js:8:5
 85    b@/Users/user/test.js:5:5
 86    a@/Users/user/test.js:2:5
 87    module code@/Users/user/test.js:37:1
 88    evaluate@[native code]
 89    moduleEvaluation@[native code]
 90    moduleEvaluation@[native code]
 91    @[native code]
 92    asyncFunctionResume@[native code]
 93    promiseReactionJobWithoutPromise@[native code]
 94    promiseReactionJob@[native code]
 95    d@/Users/user/test.js:11:5
 96  `
 97      assert.match(getCallerLocationFromString(stack), /^.*:11:5.*$/)
 98    })
 99  })
100
101  // prettier-ignore
102  test('getExitMessage()', () => {
103    assert.match(formatExitMessage(2, null, '', ''), /Misuse of shell builtins/)
104    assert.equal(formatExitMessage(1, 'SIGKILL', '', '', 'data'), `\n    at \n    exit code: 1\n    signal: SIGKILL\n    details: \ndata`)
105    assert.equal(formatExitMessage(0, null, '', ''), 'exit code: 0')
106  })
107
108  test('getErrorMessage()', () => {
109    assert.match(
110      formatErrorMessage({ errno: -2 } as NodeJS.ErrnoException, ''),
111      /No such file or directory/
112    )
113    assert.match(
114      formatErrorMessage({ errno: -1e9 } as NodeJS.ErrnoException, ''),
115      /Unknown error/
116    )
117    assert.match(
118      formatErrorMessage({} as NodeJS.ErrnoException, ''),
119      /Unknown error/
120    )
121  })
122
123  test('findErrors()', () => {
124    const lines = [...Array(40).keys()].map((v) => v + '')
125
126    assert.equal(formatErrorDetails([]), '', 'empty returns empty')
127    assert.equal(
128      formatErrorDetails(['foo', 'bar']),
129      'foo\nbar',
130      'squashes a few'
131    )
132    assert.equal(
133      formatErrorDetails(['failure: foo', 'NOT OK smth', ...lines]),
134      'failure: foo\nNOT OK smth',
135      'extracts errors'
136    )
137    assert.equal(
138      formatErrorDetails(lines),
139      '0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n...',
140      'shows a sample'
141    )
142  })
143})