import { describe, expect, it, vi } from "vitest"; import { createCursorPaginationRuntime } from "../../src/adapters/query-cache/cursor-pagination-runtime.ts"; import { defineMutationIntent, isValidIdempotencyKey, } from "../../src/contracts/mutation-intent.ts"; import type { CursorPage, CursorPaginationProfile, } from "../../src/contracts/cursor-pagination.ts"; import type { Result } from "../../src/application/result.ts"; const PROFILE: CursorPaginationProfile = Object.freeze({ profileId: "TEST_PAGINATION_V1", maxPages: 3, maxTotalItems: 30, maxEstimatedBytes: 32_768, maxCursorBytes: 512, allowSparsePage: false, }); function page( items: readonly number[], nextCursor: string | null, ): CursorPage { return Object.freeze({ items: Object.freeze([...items]), nextCursor, hasMore: nextCursor !== null, snapshotToken: "snapshot-1", }); } /** * OPT-NET-01. A loader rejection is evidence about the data source. Reporting * it as `PAGINATION_ABORTED` because a signal merely *exists* erases a real * network or contract failure and files it under a user decision nobody made. */ describe("OPT-NET-01 cursor pagination abort classification", () => { it("preserves a loader rejection while the signal is still live", async () => { const controller = new AbortController(); const runtime = createCursorPaginationRuntime({ definitionId: "TEST_PAGINATION", profile: PROFILE, loadPage: async () => { throw new TypeError("upstream exploded"); }, }); await expect( runtime.loadAll({ signal: controller.signal }), ).rejects.toThrow("upstream exploded"); }); it("keeps the same rejection when no signal is supplied", async () => { const runtime = createCursorPaginationRuntime({ definitionId: "TEST_PAGINATION", profile: PROFILE, loadPage: async () => { throw new TypeError("upstream exploded"); }, }); await expect(runtime.loadAll({})).rejects.toThrow("upstream exploded"); }); it("classifies a rejection during a real abort as PAGINATION_ABORTED", async () => { const controller = new AbortController(); const runtime = createCursorPaginationRuntime({ definitionId: "TEST_PAGINATION", profile: PROFILE, loadPage: async () => { controller.abort(); throw new TypeError("cancelled upstream"); }, }); const result = await runtime.loadAll({ signal: controller.signal }); expect(result.ok).toBe(false); expect(result.ok ? null : result.error.code).toBe("PAGINATION_ABORTED"); }); it("does not admit a page that resolves after the abort", async () => { const controller = new AbortController(); const loadPage = vi.fn( async (): Promise>> => { controller.abort(); await new Promise((resolve) => setTimeout(resolve, 5)); return { ok: true, value: page([1, 2], null) }; }, ); const runtime = createCursorPaginationRuntime({ definitionId: "TEST_PAGINATION", profile: PROFILE, loadPage, }); const result = await runtime.loadAll({ signal: controller.signal }); expect(result.ok).toBe(false); expect(result.ok ? null : result.error.code).toBe("PAGINATION_ABORTED"); }); }); /** * OPT-NET-02. One idempotency-key authority. Two validators drift, and the * looser one becomes the way a control character reaches a request header. */ describe("OPT-NET-02 shared idempotency key validation", () => { const rejected = [ "", " ", "key\nwith-newline", "key\u0000null", "key\u007fdelete", "key\u009fc1", "a".repeat(257), ]; it("rejects the same values at intent definition and at admission", () => { for (const value of rejected) { expect(isValidIdempotencyKey(value)).toBe(false); expect(() => defineMutationIntent({ intentId: "intent-1", operationId: "OP", canonicalInputIdentity: "identity", idempotencyKey: value, createdAtMonotonicMs: 1, }), ).toThrow(TypeError); } }); it("accepts the bounded printable and Unicode values both sides allow", () => { for (const value of ["key-1", "a".repeat(256), "키-값", "ключ"]) { expect(isValidIdempotencyKey(value)).toBe(true); expect( defineMutationIntent({ intentId: "intent-1", operationId: "OP", canonicalInputIdentity: "identity", idempotencyKey: value, createdAtMonotonicMs: 1, }).idempotencyKey, ).toBe(value); } }); });