import { describe, expect, it, vi } from "vitest"; import { PERMS } from "@/lib/permission-slugs"; import type { HousekeepingCapabilityContext } from "../../../foundation/contracts"; import { CONTENT_ROUTE_IDS } from "../routes"; import { type ContentQueryData, type ContentQueryInput, createContentQuery, } from "./content-queries"; import { CONTENT_QUERY_DEFINITIONS, loadProductionContentQuery, } from "./content-queries-production"; const queryMocks = vi.hoisted(() => ({ execute: vi.fn(), join: vi.fn((chunks: unknown[], separator: unknown) => ({ chunks, separator, })), raw: vi.fn((statement: string) => ({ statement })), tagged: vi.fn((strings: TemplateStringsArray, ...values: unknown[]) => ({ strings: Array.from(strings), values, })), })); vi.mock("drizzle-orm", () => ({ sql: Object.assign(queryMocks.tagged, { join: queryMocks.join, raw: queryMocks.raw, }), })); vi.mock("@/lib/db", () => ({ db: { execute: queryMocks.execute } })); function context(granted: readonly string[]): HousekeepingCapabilityContext { const permissions = new Set(granted); return { actor: { id: 42, username: "operator", rank: 7 }, isSuperAdmin: false, has: (slug) => permissions.has(slug), hasAny: (...slugs) => slugs.some((slug) => permissions.has(slug)), hasAll: (...slugs) => slugs.every((slug) => permissions.has(slug)), }; } const ready: ContentQueryData = { kind: "editorial", items: [ { id: "18446744073709551615", title: "Published article", status: "published", href: "/ase/content/editorial/articles/18446744073709551615", }, ], total: 1, partialDependencies: [], }; describe("Content query", () => { it("reports totals beyond the former 500-row adapter cap", async () => { const pageRows = Array.from({ length: 25 }, (_, index) => ({ id: index + 1, title: `Article ${index + 1}`, status: "published", updated_at: null, })); queryMocks.execute .mockResolvedValueOnce([[{ total: 600 }]]) .mockResolvedValueOnce([pageRows]); const result = await loadProductionContentQuery({ routeId: "content.editorial.articles", params: {}, list: { search: "", pageSize: 25, offset: 0 }, }); expect(result.total).toBe(600); expect(result.items).toHaveLength(25); expect(queryMocks.execute).toHaveBeenCalledTimes(2); const pageQuery = queryMocks.execute.mock.calls[1]?.[0]; expect(JSON.stringify(pageQuery)).toContain("25"); expect(JSON.stringify(pageQuery)).toContain("0"); }); it("binds search, limit, and offset into the bounded page query", async () => { queryMocks.execute .mockResolvedValueOnce([[{ total: 1 }]]) .mockResolvedValueOnce([ [{ id: 9, title: "Launch", status: "published", updated_at: null }], ]); await loadProductionContentQuery({ routeId: "content.editorial.articles", params: {}, list: { search: "Launch", pageSize: 7, offset: 14 }, }); const serialized = JSON.stringify(queryMocks.execute.mock.calls); expect(serialized).toContain("%launch%"); expect(serialized).toContain("7"); expect(serialized).toContain("14"); }); it("searches only projected aliases for routes without descriptions", async () => { queryMocks.execute .mockResolvedValueOnce([[{ total: 0 }]]) .mockResolvedValueOnce([[]]); await loadProductionContentQuery({ routeId: "content.engagement.events", params: {}, list: { search: "launch", pageSize: 25, offset: 0 }, }); const serialized = JSON.stringify(queryMocks.execute.mock.calls.slice(-2)); expect(serialized).toContain("COALESCE(title"); expect(serialized).not.toContain("COALESCE(description"); }); it("never selects email template bodies into summary query results", () => { const emailTemplates = CONTENT_QUERY_DEFINITIONS["content.help.email-templates"]; expect(emailTemplates.statement).not.toMatch(/\bbody\b|private_payload/iu); }); it("fails closed before production adapters are invoked", async () => { const load = vi.fn(async () => ready); const query = createContentQuery({ load }); const result = await query.run(context([]), { routeId: "content.editorial.articles", list: { search: "", pageSize: 25, offset: 0 }, }); expect(result).toMatchObject({ ok: false, error: { code: "FORBIDDEN" }, }); expect(load).not.toHaveBeenCalled(); }); it("bounds list input, preserves canonical BIGINT strings, and returns real adapter data", async () => { const load = vi.fn(async (_input: ContentQueryInput) => ready); const query = createContentQuery({ load }); const result = await query.run(context([PERMS.NEWS_VIEW]), { routeId: "content.editorial.articles", list: { search: " launch ", pageSize: 1000, offset: 999_999, }, }); expect(load).toHaveBeenCalledWith({ routeId: "content.editorial.articles", params: {}, list: { search: "launch", pageSize: 100, offset: 100_000 }, }); expect(result).toMatchObject({ ok: true, data: { items: [{ id: "18446744073709551615" }], total: 1, partialDependencies: [], }, }); }); it("rejects a fake partial result and maps complete adapter failure", async () => { const malformed = createContentQuery({ load: async () => ({ kind: "media", items: [], total: 0, partialDependencies: ["imaginary"], }) as ContentQueryData, }); const unavailable = createContentQuery({ load: async () => { throw new Error("database unavailable"); }, }); expect( await malformed.run(context([PERMS.PAGES_VIEW]), { routeId: "content.media.library", }), ).toMatchObject({ ok: false, error: { code: "DEPENDENCY_UNAVAILABLE" }, }); expect( await unavailable.run(context([PERMS.PAGES_VIEW]), { routeId: "content.media.library", }), ).toMatchObject({ ok: false, error: { code: "DEPENDENCY_UNAVAILABLE" }, }); }); it("has an authorized production query path for every Content route", async () => { const load = vi.fn(async (input: ContentQueryInput) => ({ kind: input.routeId.split(".")[1] as ContentQueryData["kind"], items: [], total: 0, partialDependencies: [], })); const query = createContentQuery({ load }); const all = context(Object.values(PERMS)); for (const routeId of CONTENT_ROUTE_IDS) { const result = await query.run(all, { routeId }); expect(result.ok, routeId).toBe(true); } expect(load).toHaveBeenCalledTimes(CONTENT_ROUTE_IDS.length); }); });