diff --git a/src/baza.ts b/src/baza.ts index f24f7f9..0daf20c 100644 --- a/src/baza.ts +++ b/src/baza.ts @@ -24,10 +24,13 @@ export const baza = async function(path: string, method: string, let response: Response; try { response = await fetch(uri, meta); - } catch (e) { - throw new Error( - `Network failure for ${method} ${uri}: ${(e as Error).message}` + } catch (error: unknown) { + const reason = error instanceof Error ? error.message : String(error); + const failure = new Error( + `Network failure for ${method} ${uri}: ${reason}` ); + Object.defineProperty(failure, 'cause', { value: error }); + throw failure; } if (response.status != 200) { let error = `HTTP error ${response.status}`; diff --git a/test/baza.test.ts b/test/baza.test.ts index 0d3bae1..74e2f1a 100644 --- a/test/baza.test.ts +++ b/test/baza.test.ts @@ -41,22 +41,38 @@ describe('baza', () => { ).rejects.toThrow('HTTP error 404'); }); - test('does not follow redirects', async () => { - await expect( - baza('/redirect', 'PUT', {}, '') - ).rejects.toThrow('HTTP error 303'); + test('wraps network failures', async (): Promise => { + const failure = new TypeError('fetch failed'); + const fetch = jest.spyOn(globalThis, 'fetch').mockRejectedValue(failure); + let thrown: unknown; + try { + await baza('/robots.txt', 'GET', {}, ''); + } catch (error: unknown) { + thrown = error; + } finally { + fetch.mockRestore(); + } + expect(thrown).toBeInstanceOf(Error); + expect((thrown as Error).message).toBe( + `Network failure for GET ${host}/robots.txt?: fetch failed` + ); + expect(Object.getOwnPropertyDescriptor(thrown, 'cause')?.value).toBe(failure); }); - test('wraps network failures in typed Error', async () => { - const mock = jest.spyOn(globalThis, 'fetch'); - mock.mockRejectedValue(new TypeError('fetch failed')); + test('wraps non-error network failures', async (): Promise => { + const fetch = jest.spyOn(globalThis, 'fetch').mockRejectedValue('socket closed'); try { - await expect(baza('/test', 'GET', {}, '')).rejects.toThrow(Error); - await expect(baza('/test', 'GET', {}, '')).rejects.toThrow( - 'Network failure for GET' - ); + await expect( + baza('/robots.txt', 'GET', {}, '') + ).rejects.toThrow(`Network failure for GET ${host}/robots.txt?: socket closed`); } finally { - mock.mockRestore(); + fetch.mockRestore(); } }); + + test('does not follow redirects', async () => { + await expect( + baza('/redirect', 'PUT', {}, '') + ).rejects.toThrow('HTTP error 303'); + }); });