chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { createCursorPaginationRuntime } from "../../src/adapters/query-cache/cursor-pagination-runtime.ts";
|
||||
|
||||
const profile = {
|
||||
profileId: "bounded-cursor-v1",
|
||||
maxPages: 3,
|
||||
maxTotalItems: 4,
|
||||
maxEstimatedBytes: 1_024,
|
||||
maxCursorBytes: 64,
|
||||
allowSparsePage: false,
|
||||
} as const;
|
||||
|
||||
describe("bounded cursor pagination runtime", () => {
|
||||
it("loads a stable finite chain without exposing cursors in its value", async () => {
|
||||
const loadPage = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
value: {
|
||||
items: ["one"],
|
||||
nextCursor: "cursor-2",
|
||||
hasMore: true,
|
||||
snapshotToken: "snapshot-a",
|
||||
},
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
value: {
|
||||
items: ["two"],
|
||||
nextCursor: null,
|
||||
hasMore: false,
|
||||
snapshotToken: "snapshot-a",
|
||||
},
|
||||
});
|
||||
const runtime = createCursorPaginationRuntime({
|
||||
definitionId: "LIST_ALL",
|
||||
profile,
|
||||
loadPage,
|
||||
});
|
||||
|
||||
await expect(runtime.loadAll({})).resolves.toEqual({
|
||||
ok: true,
|
||||
value: ["one", "two"],
|
||||
});
|
||||
expect(loadPage.mock.calls.map(([cursor]) => cursor)).toEqual([
|
||||
null,
|
||||
"cursor-2",
|
||||
]);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
page: {
|
||||
items: [],
|
||||
nextCursor: "next",
|
||||
hasMore: true,
|
||||
snapshotToken: null,
|
||||
},
|
||||
code: "PAGINATION_PAGE_INVALID",
|
||||
},
|
||||
{
|
||||
page: {
|
||||
items: ["one"],
|
||||
nextCursor: null,
|
||||
hasMore: true,
|
||||
snapshotToken: null,
|
||||
},
|
||||
code: "PAGINATION_PAGE_INVALID",
|
||||
},
|
||||
])("rejects invalid page invariants", async ({ page, code }) => {
|
||||
const runtime = createCursorPaginationRuntime({
|
||||
definitionId: "LIST_ALL",
|
||||
profile,
|
||||
loadPage: async () => ({ ok: true, value: page }),
|
||||
});
|
||||
await expect(runtime.loadAll({})).resolves.toMatchObject({
|
||||
ok: false,
|
||||
error: { kind: "PAGINATION_CONTRACT_VIOLATION", code },
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects cursor loops and snapshot drift", async () => {
|
||||
const loop = createCursorPaginationRuntime({
|
||||
definitionId: "LIST_ALL",
|
||||
profile,
|
||||
loadPage: async () => ({
|
||||
ok: true,
|
||||
value: {
|
||||
items: ["one"],
|
||||
nextCursor: "same",
|
||||
hasMore: true,
|
||||
snapshotToken: null,
|
||||
},
|
||||
}),
|
||||
});
|
||||
await expect(loop.loadAll({})).resolves.toMatchObject({
|
||||
ok: false,
|
||||
error: { code: "PAGINATION_CURSOR_LOOP" },
|
||||
});
|
||||
|
||||
let page = 0;
|
||||
const drift = createCursorPaginationRuntime({
|
||||
definitionId: "LIST_ALL",
|
||||
profile,
|
||||
loadPage: async () => ({
|
||||
ok: true,
|
||||
value: {
|
||||
items: [String(page)],
|
||||
nextCursor: page++ === 0 ? "next" : null,
|
||||
hasMore: page === 1,
|
||||
snapshotToken: page === 1 ? "snapshot-a" : "snapshot-b",
|
||||
},
|
||||
}),
|
||||
});
|
||||
await expect(drift.loadAll({})).resolves.toMatchObject({
|
||||
ok: false,
|
||||
error: { code: "PAGINATION_SNAPSHOT_CHANGED" },
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user