41 lines · 1.7 KB
import { describe, expect, mock, test } from "bun:test";

const kv = new Map<string, string>([["backfill:attachment-bytes", "done"]]);
mock.module("cloudflare:workers", () => ({
  env: { KV: { get: async (key: string) => kv.get(key) ?? null, put: async () => undefined } },
}));
mock.module("./db", () => ({
  all: async () => [],
  first: async () => null,
  run: async () => ({ meta: { changes: 0 } }),
}));
const { memoryScopes } = await import("./scoped-db/testing");
const scopes = memoryScopes();
mock.module("./scoped-db", () => scopes.module);

const { attachmentUsage, forgetRepoAttachmentBytes, syncAttachmentBytes } = await import("./attachment-bytes");

function attach(repoId: string, id: string, owner: string, uploader: string, size: number): void {
  scopes
    .raw("repo", repoId)
    .query(
      `INSERT INTO attachments (id, owner_id, repo_id, uploader_id, name, content_type, size, r2_key, created_at)
       VALUES (?, ?, ?, ?, 'f', 'text/plain', ?, 'k', 0)`,
    )
    .run(id, owner, repoId, uploader, size);
}

describe("attachment bytes", () => {
  test("quota totals come from the accounts' own entries, recomputed per repository", async () => {
    attach("r1", "a", "org", "alice", 100);
    attach("r2", "b", "org", "bob", 50);
    attach("r2", "c", "alice", "alice", 7);
    await syncAttachmentBytes("r1", ["org", "alice"]);
    await syncAttachmentBytes("r2", ["org", "alice", "bob"]);
    await syncAttachmentBytes("r2", ["org", "alice", "bob"]);
    expect(await attachmentUsage("org", "alice")).toEqual({ owner_total: 150, uploader_total: 107 });
    await forgetRepoAttachmentBytes("r2");
    expect(await attachmentUsage("org", "bob")).toEqual({ owner_total: 100, uploader_total: 0 });
  });
});