From 2a45325f0aeacb0643bea80e5bbd7b11e295907b Mon Sep 17 00:00:00 2001 From: kan_shimizu Date: Thu, 17 Sep 2026 09:25:08 +0900 Subject: [PATCH] fix: prevent endless pagination when contents decrease --- src/createClient.ts | 8 +++---- tests/getAllContentIds.test.ts | 41 ++++++++++++++++++++++++++++++++++ tests/getAllContents.test.ts | 41 ++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 4 deletions(-) diff --git a/src/createClient.ts b/src/createClient.ts index 71034db..d567d85 100644 --- a/src/createClient.ts +++ b/src/createClient.ts @@ -263,7 +263,7 @@ export const createClient = ({ const isStringArray = (arr: unknown[]): arr is string[] => arr.every((item) => typeof item === 'string'); - while (contentIds.length < totalCount) { + while (offset < totalCount) { const { contents } = (await makeRequest({ endpoint, queries: { ...defaultQueries, offset }, @@ -281,7 +281,7 @@ export const createClient = ({ contentIds = [...contentIds, ...ids]; offset += limit; - if (contentIds.length < totalCount) { + if (offset < totalCount) { await sleep(1000); // sleep for 1 second before the next request } } @@ -311,7 +311,7 @@ export const createClient = ({ const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); - while (contents.length < totalCount) { + while (offset < totalCount) { const { contents: _contents } = (await makeRequest({ endpoint, queries: { ...queries, limit, offset }, @@ -321,7 +321,7 @@ export const createClient = ({ contents = contents.concat(_contents); offset += limit; - if (contents.length < totalCount) { + if (offset < totalCount) { await sleep(1000); // sleep for 1 second before the next request } } diff --git a/tests/getAllContentIds.test.ts b/tests/getAllContentIds.test.ts index 1c62e90..c8abe42 100644 --- a/tests/getAllContentIds.test.ts +++ b/tests/getAllContentIds.test.ts @@ -119,6 +119,47 @@ describe('getAllContentIds', () => { expect(result).toContain('id249'); }); + test('should stop fetching when contents decrease during pagination', async () => { + const requestedOffsets: number[] = []; + + server.use( + http.get(`${testBaseUrl}/getAllContentIds-list-type`, ({ request }) => { + const url = new URL(request.url); + const limit = Number(url.searchParams.get('limit')); + + if (limit === 0) { + return HttpResponse.json( + { contents: [], totalCount: 101 }, + { status: 200 }, + ); + } + + const offset = Number(url.searchParams.get('offset')); + requestedOffsets.push(offset); + + return HttpResponse.json( + { + contents: + offset === 0 + ? Array(100) + .fill(null) + .map((_, index) => ({ id: `id${index}` })) + : [], + totalCount: offset === 0 ? 101 : 100, + }, + { status: 200 }, + ); + }), + ); + + const result = await client.getAllContentIds({ + endpoint: 'getAllContentIds-list-type', + }); + + expect(result).toHaveLength(100); + expect(requestedOffsets).toEqual([0, 100]); + }); + test('should fetch all content ids with alternateField field', async () => { server.use( http.get( diff --git a/tests/getAllContents.test.ts b/tests/getAllContents.test.ts index b4ca587..535af27 100644 --- a/tests/getAllContents.test.ts +++ b/tests/getAllContents.test.ts @@ -118,4 +118,45 @@ describe('getAllContents', () => { expect(result).toContainEqual({ id: 'id0' }); expect(result).toContainEqual({ id: 'id249' }); }); + + test('should stop fetching when contents decrease during pagination', async () => { + const requestedOffsets: number[] = []; + + server.use( + http.get(`${testBaseUrl}/getAllContents-list-type`, ({ request }) => { + const url = new URL(request.url); + const limit = Number(url.searchParams.get('limit')); + + if (limit === 0) { + return HttpResponse.json( + { contents: [], totalCount: 101 }, + { status: 200 }, + ); + } + + const offset = Number(url.searchParams.get('offset')); + requestedOffsets.push(offset); + + return HttpResponse.json( + { + contents: + offset === 0 + ? Array(100) + .fill(null) + .map((_, index) => ({ id: `id${index}` })) + : [], + totalCount: offset === 0 ? 101 : 100, + }, + { status: 200 }, + ); + }), + ); + + const result = await client.getAllContents({ + endpoint: 'getAllContents-list-type', + }); + + expect(result).toHaveLength(100); + expect(requestedOffsets).toEqual([0, 100]); + }); });