import { execSync } from "node:child_process"; import { existsSync, readFileSync } from "node:fs"; import { resolve } from "node:path"; import { beforeAll, describe, expect, it } from "vitest"; import { collectViolations } from "../check-pagination"; const realSchemaPath = resolve(__dirname, "..", "..", "..", "schema.gql"); // schema.gql is gitignored; regen on first run so the SDL assertion below // always runs (skipping a regression-catching test silently is worse than // the ~5s cold-start cost). beforeAll(() => { if (existsSync(realSchemaPath)) { return; } execSync("pnpm --filter @cv/api schema:generate", { stdio: "inherit" }); }); describe("check-pagination.collectViolations", () => { it("returns no violations when every Connection field accepts first and last", () => { const sdl = /* GraphQL */ ` type Query { cvs(first: Int, last: Int, after: String, before: String): CVConnection! } type CVConnection { totalCount: Int! } `; expect(collectViolations(sdl)).toEqual([]); }); it("flags Connection fields that lack first or last", () => { const sdl = /* GraphQL */ ` type Query { cvs: CVConnection! } type CVConnection { totalCount: Int! } `; const violations = collectViolations(sdl); expect(violations).toHaveLength(1); expect(violations[0]).toMatchObject({ parentType: "Query", fieldName: "cvs", returnType: "CVConnection", }); expect(violations[0]?.reason).toMatch(/first/); expect(violations[0]?.reason).toMatch(/last/); }); it("flags fields that accept `limit` instead of Relay args", () => { const sdl = /* GraphQL */ ` type Query { adminAuditLog(limit: Int): AuditLogConnection! } type AuditLogConnection { totalCount: Int! } `; expect(collectViolations(sdl)).toHaveLength(1); }); it("ignores non-Connection list-returning fields (those are a separate audit)", () => { const sdl = /* GraphQL */ ` type Query { myProfiles: [Profile!]! } type Profile { id: ID! } `; expect(collectViolations(sdl)).toEqual([]); }); it("the generated apps/api/schema.gql passes the check (modulo allowlist)", () => { const sdl = readFileSync(realSchemaPath, "utf-8"); const violations = collectViolations(sdl); const unallowedViolations = violations.filter( (v) => `${v.parentType}.${v.fieldName}` !== "Query.queueMessages", ); expect(unallowedViolations).toEqual([]); }); });