diff --git a/src/narou-fetch.ts b/src/narou-fetch.ts index 425cdeb..71ebb47 100644 --- a/src/narou-fetch.ts +++ b/src/narou-fetch.ts @@ -39,6 +39,10 @@ export default class NarouNovelFetch extends NarouNovel { const res = await (this.fetch ?? fetch)(url, options?.fetchOptions); + if (!res.ok) { + throw new Error(`HTTP ${res.status}: ${res.statusText}`); + } + if (!query.gzip) { return (await res.json()) as T; } diff --git a/test/narou-fetch.test.ts b/test/narou-fetch.test.ts index 427b939..01af106 100644 --- a/test/narou-fetch.test.ts +++ b/test/narou-fetch.test.ts @@ -82,6 +82,7 @@ describe('NarouNovelFetch', () => { it('should use custom fetch function when provided', async () => { // カスタムフェッチ関数をモック const customFetchMock = vi.fn().mockResolvedValue({ + ok: true, json: vi.fn().mockResolvedValue(mockData) }); @@ -95,6 +96,33 @@ describe('NarouNovelFetch', () => { expect(result).toEqual(mockData); }); + it.each([403, 404, 429, 500])('should reject HTTP %i before reading the body', async (status) => { + for (const gzip of [0, 5] as const) { + const response = Response.json(mockData, { status }); + const jsonSpy = vi.spyOn(response, 'json'); + const bufferSpy = vi.spyOn(response, 'arrayBuffer'); + const narouFetch = new NarouNovelFetch(vi.fn().mockResolvedValue(response)); + + await expect(narouFetch.executeRanking({ rtype: '20260101-d', gzip })) + .rejects.toThrow(`HTTP ${status}:`); + expect(jsonSpy).not.toHaveBeenCalled(); + expect(bufferSpy).not.toHaveBeenCalled(); + expect(response.bodyUsed).toBe(false); + } + }); + + it.each([0, 5] as const)('should still reject plain API errors returned with HTTP 200 (gzip=%i)', async (gzip) => { + const response = new Response('Error: Invalid date.', { status: 200 }); + const narouFetch = new NarouNovelFetch(vi.fn().mockResolvedValue(response)); + const result = narouFetch.executeRanking({ rtype: '20260101-d', gzip }); + + if (gzip === 0) { + await expect(result).rejects.toBeInstanceOf(SyntaxError); + } else { + await expect(result).rejects.toBe('Error: Invalid date.'); + } + }); + it('should set gzip to 5 when undefined', async () => { // URLパラメータをキャプチャするモック const requestSpy = vi.fn(); @@ -185,4 +213,4 @@ describe('NarouNovelFetch', () => { { fetchOptions: { headers: { 'user-agent': 'node-narou' } } } ); }); -}); \ No newline at end of file +});