import { describe, it, expect, beforeAll, afterAll } from "vitest"; import Fastify from "fastify"; import jwt from "jsonwebtoken"; import prisma from "../config/database"; import { config } from "../config/env"; import customersRoutes from "../routes/admin/customers"; import receivedInvoicesRoutes from "../routes/admin/received-invoices"; /** * Pinning tests for audit finding L3: offset pagination over a non-unique * sort column needs an `{ id }` tiebreak, or rows sharing the sort value can * reorder between page queries — duplicating one row and skipping another. * (Documented determinism convention; siblings issued-orders/trips already * use compound orderings.) * * NOTE: without the tiebreak the misbehavior is probabilistic (MySQL may * return a stable order by luck), so the paging assertion is primarily a * REGRESSION GUARD pinning the exactly-once contract. */ const N = "tiebreak_test_"; let app: ReturnType; let adminToken: string; let customerIds: number[] = []; let invoiceIds: number[] = []; async function cleanup() { await prisma.customers.deleteMany({ where: { name: { startsWith: N } } }); await prisma.received_invoices.deleteMany({ where: { supplier_name: { startsWith: N } }, }); } beforeAll(async () => { await cleanup(); app = Fastify({ logger: false }); await app.register(customersRoutes, { prefix: "/api/admin/customers" }); await app.register(receivedInvoicesRoutes, { prefix: "/api/admin/received-invoices", }); const admin = await prisma.users.findFirst({ where: { roles: { name: "admin" } }, }); if (!admin) throw new Error("Test setup: admin user not found"); adminToken = jwt.sign( { sub: admin.id, username: admin.username, role: "admin" }, config.jwt.secret, { expiresIn: "15m" }, ); // 5 customers sharing one city (the sort key) so every page boundary // falls inside a duplicate-key run. customerIds = []; for (let i = 0; i < 5; i++) { const c = await prisma.customers.create({ data: { name: `${N}c${i}`, city: "Tiebreakov" }, }); customerIds.push(c.id); } // 5 received invoices sharing supplier_name in one far-future month. invoiceIds = []; for (let i = 0; i < 5; i++) { const inv = await prisma.received_invoices.create({ data: { supplier_name: `${N}supplier`, month: 7, year: 2098, amount: 100 + i, currency: "CZK", vat_rate: 21, vat_amount: 17.36, status: "unpaid", }, }); invoiceIds.push(inv.id); } }); afterAll(async () => { if (app) await app.close(); await cleanup(); await prisma.$disconnect(); }); async function collectPages(urlBase: string, pages: number) { const seen: number[] = []; for (let page = 1; page <= pages; page++) { const res = await app.inject({ method: "GET", url: `${urlBase}&page=${page}&limit=2`, headers: { Authorization: `Bearer ${adminToken}` }, }); expect(res.statusCode).toBe(200); for (const row of res.json().data as Array<{ id: number }>) { seen.push(row.id); } } return seen; } describe("offset pagination exactly-once (audit L3)", () => { it("customers sorted by a duplicate city appear exactly once across pages", async () => { const seen = ( await collectPages("/api/admin/customers?sort=city&search=" + N, 3) ).filter((id) => customerIds.includes(id)); expect([...new Set(seen)].sort()).toEqual([...customerIds].sort()); expect(seen.length).toBe(customerIds.length); }); it("received invoices sorted by a duplicate supplier appear exactly once across pages", async () => { const seen = ( await collectPages( "/api/admin/received-invoices?sort=supplier_name&month=7&year=2098", 3, ) ).filter((id) => invoiceIds.includes(id)); expect([...new Set(seen)].sort()).toEqual([...invoiceIds].sort()); expect(seen.length).toBe(invoiceIds.length); }); });