diff --git a/src/app/api/v1/orders/backfill-duplicate-checks/route.test.ts b/src/app/api/v1/orders/backfill-duplicate-checks/route.test.ts index bf1e358..c2e282e 100644 --- a/src/app/api/v1/orders/backfill-duplicate-checks/route.test.ts +++ b/src/app/api/v1/orders/backfill-duplicate-checks/route.test.ts @@ -72,16 +72,16 @@ describe("POST /api/v1/orders/backfill-duplicate-checks", () => { it("returns done=false and advances nextSkip when more rows remain", async () => { const { getAdminPocketBase } = await import("@/lib/server/pocketbase-admin"); - // Fetch of 100 with limit=2 yields 3 rows (limit+1) — implies - // more rows remain after this 2-row chunk. + // perPage=limit=2 returns 2 items, totalItems=5 → hasMore=true + // (2 items consumed, 3 rows remain after offset 2). vi.mocked(getAdminPocketBase).mockResolvedValueOnce({ autoCancellation: vi.fn(), collection: () => ({ getList: vi.fn().mockResolvedValue({ - items: [{ id: "a" }, { id: "b" }, { id: "c" }], - totalItems: 3, + items: [{ id: "a" }, { id: "b" }], + totalItems: 5, page: 1, - perPage: 3, + perPage: 2, }), }), } as unknown as Awaited>); @@ -106,7 +106,7 @@ describe("POST /api/v1/orders/backfill-duplicate-checks", () => { items: [{ id: "x" }], totalItems: 1, page: 5, - perPage: 3, + perPage: 2, }); vi.mocked(getAdminPocketBase).mockResolvedValueOnce({ autoCancellation: vi.fn(), @@ -115,8 +115,8 @@ describe("POST /api/v1/orders/backfill-duplicate-checks", () => { const res = await POST(backfillRequest("?limit=2&skip=8")); expect(res.status).toBe(200); - // skip=8, limit=2 → page = floor(8/2) + 1 = 5 - expect(getListSpy).toHaveBeenCalledWith(5, 3, expect.any(Object)); + // skip=8, limit=2 → page = skip/limit + 1 = 5, perPage = limit = 2 + expect(getListSpy).toHaveBeenCalledWith(5, 2, expect.any(Object)); expect((await res.json()).nextSkip).toBe(9); }); diff --git a/src/app/api/v1/orders/backfill-duplicate-checks/route.ts b/src/app/api/v1/orders/backfill-duplicate-checks/route.ts index 6f67658..0ea8abd 100644 --- a/src/app/api/v1/orders/backfill-duplicate-checks/route.ts +++ b/src/app/api/v1/orders/backfill-duplicate-checks/route.ts @@ -89,22 +89,23 @@ export async function POST(req: NextRequest) { const pb = await getAdminPocketBase(); pb.autoCancellation(false); - // Convert `skip` to a 1-based page number. PB's getList uses page, - // not offset, so we compute `page = floor(skip/limit) + 1`. - const page = Math.floor(skip / limit) + 1; + // PB pagination: getList(page, perPage) returns items at offsets + // [(page-1)*perPage, page*perPage). With perPage = limit and + // page = skip/limit + 1, the slice starts exactly at offset `skip`. + // `hasMore` is derived from `totalItems` rather than fetching +1. + const page = skip / limit + 1; + const perPage = limit; - // Fetch `limit + 1` so we can detect whether more rows remain after - // this chunk without a second `count` query. const orders = await pb.collection("orders").getList<{ id: string }>( page, - limit + 1, + perPage, { fields: "id", sort: "created", }, ); - const slice = orders.items.slice(0, limit); - const hasMore = orders.items.length > limit; + const slice = orders.items; + const hasMore = skip + slice.length < orders.totalItems; let ordersProcessed = 0; const failures: Array<{ orderId: string; error: string }> = [];