42 lines · 1.5 KB
import { beforeEach, expect, mock, test } from "bun:test";
import { Database } from "bun:sqlite";
import { applyCombinedSchema } from "./test-schema.test-helper";

const db = new Database(":memory:");
applyCombinedSchema(db);

mock.module("./db", () => ({
  all: async (sql: string, ...params: unknown[]) => db.query(sql).all(...(params as never[])),
  run: async (sql: string, ...params: unknown[]) => ({
    meta: { changes: db.query(sql).run(...(params as never[])).changes },
  }),
}));

const { listAdminAudit } = await import("./admin-audit");

beforeEach(() => {
  db.exec("DELETE FROM admin_audit_log");
  const insert = db.query(
    "INSERT INTO admin_audit_log (id, actor_id, action, target_kind, target_id, created_at) VALUES (?, NULL, 'a', 'k', 't', ?)",
  );
  // Five rows share one millisecond, as a moderation action and its abuse resolution do.
  for (const id of ["e1", "e2", "e3", "e4", "e5"]) insert.run(id, 1000);
  insert.run("z9", 2000);
  insert.run("a0", 500);
});

test("keyset pages never drop rows that share a timestamp", async () => {
  const seen: string[] = [];
  let after: { createdAt: number; id: string } | undefined;
  const maxPages = 10;
  for (let page = 0; page < maxPages; page += 1) {
    const entries = await listAdminAudit({ after }, 2);
    if (entries.length === 0) break;
    seen.push(...entries.map((entry) => entry.id));
    const last = entries.at(-1);
    if (!last) break;
    after = { createdAt: last.created_at, id: last.id };
  }
  expect(seen).toEqual(["z9", "e5", "e4", "e3", "e2", "e1", "a0"]);